@augmentd-labs/canvas-web 2.26.44 → 2.26.45
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/assets/CalendarApplet-CsyUmI2B.js +2 -0
- package/dist/assets/{DocumentBodyEditor-CVO5KPCs.js → DocumentBodyEditor-C8AEpqX3.js} +1 -1
- package/dist/assets/{DocumentIcon-9i1uCMQa.js → DocumentIcon-0ya9gycp.js} +1 -1
- package/dist/assets/{EditorOverlay-D5t3xg4w.js → EditorOverlay-DglJkHo3.js} +1 -1
- package/dist/assets/{FiltersApplet-BT3fR4Fu.js → FiltersApplet-CVnCAs-P.js} +1 -1
- package/dist/assets/HomeFab-iC5BF7LV.js +3 -0
- package/dist/assets/{IdentityFields-D-NCTUaE.js → IdentityFields-CB3Ap0bl.js} +1 -1
- package/dist/assets/{LensApplet-CVUc1-vK.js → LensApplet-DA0ZWS1k.js} +1 -1
- package/dist/assets/LinkToCard-oi3wGP0a.js +1 -0
- package/dist/assets/{MarkdownEditor-DbpRWbO6.js → MarkdownEditor-bDoyR7l2.js} +2 -2
- package/dist/assets/{MessageComposerDialog-CdPp-3GR.js → MessageComposerDialog-CLrwtB76.js} +1 -1
- package/dist/assets/{NextShell-ull6WmMg.js → NextShell-DCWxD5TR.js} +1 -1
- package/dist/assets/{NotesApplet-Bbd61mrH.js → NotesApplet-BSJ-9RKl.js} +1 -1
- package/dist/assets/ObjectPropertiesCard-DesXRw-_.js +8 -0
- package/dist/assets/{PathPickerField-CW2yG2Yg.js → PathPickerField-CSIa7jSl.js} +1 -1
- package/dist/assets/PickDocumentsCard-DVdBxx3s.js +1 -0
- package/dist/assets/{SketchApplet-BKnhzTBA.js → SketchApplet--3uyqpmo.js} +1 -1
- package/dist/assets/{SketchEditor-DmGMcbU0.js → SketchEditor-CSA7dTyq.js} +2 -2
- package/dist/assets/{TerminalApplet-D7YxftqM.js → TerminalApplet-BZJx2ztD.js} +1 -1
- package/dist/assets/{TextDocumentEditor-BQnVD0dd.js → TextDocumentEditor-VYIF2XCX.js} +1 -1
- package/dist/assets/{TodosApplet-DU5VWc5i.js → TodosApplet-DY7JzeZS.js} +1 -1
- package/dist/assets/{TodosWidget-CNnfNk4L.js → TodosWidget-DGftEAbs.js} +1 -1
- package/dist/assets/{_agentId_-CrBQ54X2.js → _agentId_-BrzFDu8r.js} +0 -0
- package/dist/assets/{_contextId_-CEcdj7Ec.js → _contextId_-DS6b9Uml.js} +1 -1
- package/dist/assets/_workspaceName_-XRqfa4c6.js +1 -0
- package/dist/assets/{about-CdAN00wx.js → about-hcdKJNm5.js} +1 -1
- package/dist/assets/{add-COjQRnFC.js → add-CEBusWuu.js} +1 -1
- package/dist/assets/{agents-DxqAi3vk.js → agents-BfSFdg41.js} +1 -1
- package/dist/assets/{agents-D_t6cQMW.js → agents-wFp7Vh3p.js} +1 -1
- package/dist/assets/{alert-dialog-CFeuDt0T.js → alert-dialog-BKtaX1_a.js} +1 -1
- package/dist/assets/{api-tokens-drrVuCYc.js → api-tokens-Biqt5TnG.js} +1 -1
- package/dist/assets/{appearance-BnUcoqpz.js → appearance-D5BUjZm8.js} +1 -1
- package/dist/assets/{applet-target-pLdP-R76.js → applet-target-LvhUEvE5.js} +1 -1
- package/dist/assets/{apps-CCC-xyI2.js → apps-65YXCnzH.js} +1 -1
- package/dist/assets/{auth-PqttD05H.js → auth-BwxT5xRB.js} +1 -1
- package/dist/assets/{button-SeUZ_Pvi.js → button--VYZekMF.js} +1 -1
- package/dist/assets/{context-t3rdtGUB.js → context-DY40EDc6.js} +1 -1
- package/dist/assets/{contexts-DDJagU4j.js → contexts-BPO_Wb9W.js} +1 -1
- package/dist/assets/{contexts-Bq8LHUTK2.js → contexts-DBJ0_-AO2.js} +1 -1
- package/dist/assets/{date-time-picker-DCikruos.js → date-time-picker-p3vy4nst.js} +1 -1
- package/dist/assets/{devices-BwU9M8-W.js → devices-DZbbaWYU.js} +1 -1
- package/dist/assets/{document-Cr7GhnVd.js → document-Dv8NCUoM.js} +1 -1
- package/dist/assets/document-list-Bbbx-pTZ.js +3 -0
- package/dist/assets/document-paste-BuTC6S7_.js +1 -0
- package/dist/assets/{embedding-D-I_ygUZ.js → embedding-B9vSy_a9.js} +1 -1
- package/dist/assets/{embedding-BvXcVfij.js → embedding-M2hUmnk0.js} +1 -1
- package/dist/assets/{form-panel-3p-ncEVK.js → form-panel-CEbNKttr.js} +1 -1
- package/dist/assets/{home-DrRSXywO.js → home-vr8b8oR-.js} +1 -1
- package/dist/assets/index-CTvqbEB8.js +16 -0
- package/dist/assets/index-CoVFQpP3.css +2 -0
- package/dist/assets/{inferd-config-editor-B8x_XPOt.js → inferd-config-editor-Dpdj90iN.js} +1 -1
- package/dist/assets/{key-bindings-6SHAlZR8.js → key-bindings-DmqvYSaF.js} +1 -1
- package/dist/assets/{label-BSTdjCWU.js → label-RuOXXC8t.js} +1 -1
- package/dist/assets/{lazy-editor-BUFAkTGl.js → lazy-editor-CShGeIep.js} +2 -2
- package/dist/assets/{logs-DjG8_wJ2.js → logs-7eSz9yjc.js} +1 -1
- package/dist/assets/{offline-ByoT6JJX.js → offline-C-Yvb6LA.js} +1 -1
- package/dist/assets/{page-header-DrDt1sQ2.js → page-header-NyYSXjfY.js} +1 -1
- package/dist/assets/{public-share-CXR25Q1X.js → public-share-0DqS7L2Q.js} +1 -1
- package/dist/assets/{query-debug-lD5vbJGi.js → query-debug-PEWbZsOP.js} +1 -1
- package/dist/assets/registry-g1SKt_fD.js +2 -0
- package/dist/assets/{remotes-RPGcw6fH.js → remotes-DqCLds8Q.js} +1 -1
- package/dist/assets/{roles-sb8vgjV_.js → roles-BwVDE1aQ.js} +1 -1
- package/dist/assets/{roles-p2cRajz-.js → roles-W7oJ5WMg.js} +1 -1
- package/dist/assets/rule-builder-CziPqpNe.js +6 -0
- package/dist/assets/settings-BIoSwdXv.js +16 -0
- package/dist/assets/{settings-CGncOyKY.js → settings-BWCMmkjr.js} +1 -1
- package/dist/assets/{settings-r7slYioS.js → settings-CbMkpipO.js} +1 -1
- package/dist/assets/settings-XMhqrvTu.js +1 -0
- package/dist/assets/{share-target-CoDDhZI5.js → share-target-hDXdgW6o.js} +1 -1
- package/dist/assets/{shared-BLOuO2Is.js → shared-CLjTxih0.js} +1 -1
- package/dist/assets/{shared-bYen6pjw.js → shared-DVlDThVA.js} +1 -1
- package/dist/assets/sketch-lazy-CtjOUfVz.js +2 -0
- package/dist/assets/{subtree-transfer-dialog-DQM5zfx2.js → subtree-transfer-dialog-DtE6JSUr.js} +1 -1
- package/dist/assets/{text-documents-Cdb3U63i.js → text-documents-wu6fzOuw.js} +2 -2
- package/dist/assets/tree-folders-IDoobsGN.js +1 -0
- package/dist/assets/ui-DEgHnkQu.js +41 -0
- package/dist/assets/{use-applet-docs-rimRHjID.js → use-applet-docs-C08GAUm3.js} +1 -1
- package/dist/assets/{use-applet-target-BygCSj5n.js → use-applet-target-BlpVWlnA.js} +1 -1
- package/dist/assets/{use-canvas-pins-BPCW4twY.js → use-canvas-pins-q5Tncavk.js} +2 -2
- package/dist/assets/{use-menu-DIP1zh7I.js → use-menu-CKSpS96u.js} +1 -1
- package/dist/assets/use-settings-back-D_MM9diy.js +1 -0
- package/dist/assets/{use-sortable-data-BBKmqs79.js → use-sortable-data-D7zS2qCC.js} +1 -1
- package/dist/assets/{use-workspace-identity-CSkRwVyo.js → use-workspace-identity-XSv3dATQ.js} +1 -1
- package/dist/assets/{useAddTarget-DeRZWtGg.js → useAddTarget-BvuFR5pN.js} +1 -1
- package/dist/assets/{useAgentModels-BufYelEA.js → useAgentModels-CI9XT6yc.js} +1 -1
- package/dist/assets/{useDocumentThumbnail-Cj4UYyhC.js → useDocumentThumbnail-CZIaXkIi.js} +1 -1
- package/dist/assets/{useEditNoteFields-BT1ASu_q.js → useEditNoteFields-DsVKIHWm.js} +1 -1
- package/dist/assets/{users-DuVk-TEd.js → users-DzlbKZp6.js} +1 -1
- package/dist/assets/workspace-4hWrSEku.js +1 -0
- package/dist/assets/{workspace-content-gate-C54J9Man.js → workspace-content-gate-BlFdbi67.js} +1 -1
- package/dist/assets/{workspace-import-form-DTvV9jYt.js → workspace-import-form-CWs9ZTts.js} +1 -1
- package/dist/assets/{workspaces-CqHYstZv.js → workspaces-67v2hWrl.js} +1 -1
- package/dist/assets/workspaces-ByYQ6L3I.js +1 -0
- package/dist/index.html +36 -38
- package/dist/sw.js +1 -1
- package/package.json +2 -2
- package/dist/assets/CalendarApplet-BNT0vegP.js +0 -2
- package/dist/assets/FileForm-CDiVKvxq.js +0 -1
- package/dist/assets/HomeFab-B17r3232.js +0 -3
- package/dist/assets/LinkToCard-CKVi4Nrg.js +0 -1
- package/dist/assets/ObjectPropertiesCard-D2lrTQqT.js +0 -8
- package/dist/assets/PickDocumentsCard-BWgc-A02.js +0 -1
- package/dist/assets/UploadProgressList-D2wEpa7t.js +0 -1
- package/dist/assets/_workspaceName_-BhZWt9RG.js +0 -1
- package/dist/assets/backend-sync-progress-B3lZB9Ha.js +0 -6
- package/dist/assets/document-list-rzRLzfkF.js +0 -3
- package/dist/assets/index-DOXi2QgX.css +0 -2
- package/dist/assets/index-G_67YwLB.js +0 -16
- package/dist/assets/registry-FfNYUpRF.js +0 -2
- package/dist/assets/settings-CCr9Jr0Q.js +0 -1
- package/dist/assets/settings-Cv6jdSA0.js +0 -16
- package/dist/assets/sketch-lazy-BY_ka1hw.js +0 -2
- package/dist/assets/tree-folders-Q5iu5vpu.js +0 -1
- package/dist/assets/ui-CDoK3F3U.js +0 -41
- package/dist/assets/use-settings-back-DGmLAD63.js +0 -1
- package/dist/assets/workspace-C5V-ZCOE.js +0 -1
- package/dist/assets/workspace-area-menu-BN5ysUpx.js +0 -1
- package/dist/assets/workspaces-Dj9dNIWZ.js +0 -1
- /package/dist/assets/{backend-sync-progress-DU-e9TWi.css → rule-builder-DU-e9TWi.css} +0 -0
|
@@ -0,0 +1,16 @@
|
|
|
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{An as f,Bn as p,Bt as m,Cn as h,Ct as g,Er as _,Fn as v,G as y,Gr as b,H as x,It as S,J as C,Jn as w,Kr as T,Kt as E,L as D,Mt as O,Nt as k,Pt as A,Qt as j,Rr as M,Si as N,Tt as P,U as F,Ur as ee,Wn as te,X as I,Xn as ne,Xt as L,Z as R,Zn as re,_t as ie,an as ae,bt as oe,cn as se,ct as ce,ft as le,hr as ue,ii as de,kt as z,mr as B,pn as fe,pr as pe,pt as me,rt as he,st as ge,vn as _e,xr as ve,yi as V,yr as H}from"./ui-DEgHnkQu.js";import{t as ye}from"./utils-2pl1SbIp.js";import{t as U}from"./button--VYZekMF.js";import{t as W}from"./input-BOtCd4HP.js";import{t as G}from"./label-RuOXXC8t.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-oi3wGP0a.js";import{$ as we,A as Te,Bt as Ee,Et as De,F as Oe,I as ke,P as Ae,Q as je,Qt as Me,T as Ne,U as J,V as Y,Vt as Pe,Wt as Fe,Zt as Ie,_t as Le,cn as Re,d as ze,et as Be,f as Ve,gn as X,it as He,jt as Ue,k as We,ln as Ge,mn as Ke,mt as qe,n as Je,nn as Ye,pn as Xe,r as Ze,rn as Qe,sn as $e,st as et,tn as tt,vt as nt,wt as rt,xt as it}from"./workspace-4hWrSEku.js";import{a as at,c as ot,i as st,n as ct,o as lt,r as ut,s as dt,t as ft}from"./alert-dialog-BKtaX1_a.js";import{t as pt}from"./use-mobile-Db-vWu9Y.js";import{t as mt}from"./useEscapeClose-DR_xKT_k.js";import{o as ht}from"./layer-style-Byl5LNFR.js";import{i as gt,t as _t}from"./color-DFEH4BfK.js";import{a as vt,o as yt,s as bt,t as xt}from"./MessageComposerDialog-CLrwtB76.js";import{n as St}from"./LinkToSidePanel-eCy3nW4R.js";import{n as Ct}from"./use-document-modal-PG8bQyIB.js";import{A as wt,C as Tt,D as Et,E as Dt,F as Ot,L as kt,M as At,N as jt,O as Mt,P as Nt,S as Pt,T as Ft,_ as It,a as Lt,b as Rt,c as zt,d as Bt,f as Vt,g as Ht,h as Ut,i as Wt,k as Gt,n as Kt,p as qt,r as Jt,t as Yt,v as Xt,w as Zt,y as Qt}from"./rule-builder-CziPqpNe.js";import{t as $t}from"./workspace-access-C9nZlBBZ.js";import{i as en}from"./use-menu-CKSpS96u.js";import{t as tn}from"./use-theme-DaxpiICf.js";import{Ot as nn,ct as rn,dt as an,ft as on,lt as sn,mt as cn,pt as ln,ut as un,wt as dn,xt as fn}from"./index-CTvqbEB8.js";import{n as pn}from"./page-header-NyYSXjfY.js";import{c as mn,f as hn,l as gn,m as _n,n as vn,o as yn,p as bn,r as xn,s as Sn,t as Cn}from"./inferd-config-editor-Dpdj90iN.js";import{n as wn,t as Tn}from"./useSocketSubscription-ifW-vnlb.js";import{a as En,n as Dn,o as On,r as kn,t as An}from"./devices-CAa1bRxk.js";import{a as jn,i as Mn,n as Nn,o as Pn,t as Fn}from"./query-debug-PEWbZsOP.js";import{a as In,i as Ln,r as Rn,t as zn}from"./workspace-import-form-CWs9ZTts.js";var Z=e(s(),1),Bn=`workspace:backups`,Vn=e=>`${be.workspaces}/${encodeURIComponent(e)}/db/backups`,Hn=e=>K.get(Vn(e)),Un=e=>K.post(Vn(e),{action:`backup`}),Wn=(e,t)=>K.post(Vn(e),{action:`restore`,backupId:t,confirm:!0}),Gn=new Map;function Kn(e,t,n){let r=JSON.stringify([t,n.id]),i=Gn.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 Hn(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(Bn,{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 Gn.set(r,a),a.finally(()=>Gn.delete(r)).catch(()=>{}),a}var Q=r(),qn={startup:`Startup`,hourly:`Hourly`,manual:`Manual`,"pre-restore":`Before restore`};function Jn({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 Hn(e).then(n=>{if(!(!f.current||t!==m.current)){a(n),s(null);for(let t of n.jobs.filter(e=>e.status===`running`))Kn(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(Bn,t),()=>{f.current=!1,window.removeEventListener(Bn,t)}},[e,h]);let g=async i=>{if(!p.current){p.current=!0,d(!0);try{let n=i?await Wn(e,i.id):await Un(e);f.current&&a(e=>e&&{...e,jobs:[n,...e.jobs]}),await Kn(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)(V,{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)(fe,{className:`mr-2 h-4 w-4 animate-spin`}):(0,Q.jsx)(V,{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)(z,{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)(T,{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:[qn[e.kind],` · `,Y(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)(P,{className:`mr-2 h-3.5 w-3.5`}),`Restore`]})]},e.id))}),(0,Q.jsx)(ft,{open:!!c,onOpenChange:e=>{e||l(null)},children:(0,Q.jsxs)(st,{className:`max-w-[calc(100vw-2rem)] sm:max-w-lg`,children:[(0,Q.jsxs)(dt,{children:[(0,Q.jsx)(ot,{children:`Restore this workspace checkpoint?`}),(0,Q.jsxs)(at,{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)(lt,{children:[(0,Q.jsx)(ut,{children:`Cancel`}),(0,Q.jsx)(ct,{disabled:_,onClick:()=>{c&&g(c)},children:`Back up current & restore`})]})]})})]})}var Yn={collect:`Collect unused blobs`,verify:`Verify integrity`,compact:`Compact index`},Xn=e=>e.status===`queued`||e.status===`running`,Zn=e=>`${be.workspaces}/${encodeURIComponent(e)}/cache/maintenance`,Qn=e=>K.get(Zn(e)),$n=(e,t,n)=>K.post(Zn(e),{store:t,action:n,...n===`verify`?{confirm:!0}:{}});function er(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 · ${Y(t.reclaimedSize||0)} reclaimed`:`${(t.reclaimedCount||0).toLocaleString()} unused blobs removed · ${Y(t.reclaimedSize||0)} reclaimed`}var tr=new Map,nr=`workspace:cache-maintenance`;function rr(e,t,n){let r=JSON.stringify([t,n.id]),i=tr.get(r);if(i)return i;let a=(async()=>{let r=e({title:Yn[n.action],description:`${n.store} · ${er(n)}`});try{let e=n;for(;Xn(e);){let i=await Qn(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(nr,{detail:{workspace:t,status:i}})),!Xn(e))break;r.update({title:Yn[e.action],description:`${e.store} · ${er(e)}`}),await new Promise(e=>setTimeout(e,2e3))}if(e.status===`failed`)throw Error(er(e));r.done({title:`${Yn[e.action]} complete`,description:`${e.store} · ${er(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 tr.set(r,a),a.finally(()=>{tr.get(r)===a&&tr.delete(r)}).catch(()=>{}),a}function ir({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)(()=>Qn(e).then(t=>{if(h.current){a(t),p(null);for(let n of t.jobs.filter(Xn))rr(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(nr,t),()=>{h.current=!1,window.removeEventListener(nr,t)}},[e,g]);let _=async t=>{if(!(m.current||i?.jobs.some(Xn))){m.current=!0,l(!0);try{let n=await $n(e,o,t);h.current&&a(e=>e&&{...e,jobs:[n,...e.jobs.filter(e=>e.id!==n.id)]}),rr(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(Xn),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)(z,{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)(I,{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)(V,{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)(le,{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:[Xn(b)&&(0,Q.jsx)(fe,{className:`h-3.5 w-3.5 animate-spin`}),Yn[b.action],` · `,b.store]}),(0,Q.jsx)(`p`,{className:`mt-1 ${b.status===`failed`?`text-destructive`:`text-muted-foreground`}`,children:er(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)(ft,{open:u,onOpenChange:d,children:(0,Q.jsxs)(st,{className:`max-w-[calc(100vw-2rem)] sm:max-w-lg`,children:[(0,Q.jsxs)(dt,{children:[(0,Q.jsxs)(ot,{children:[`Verify `,o,`?`]}),(0,Q.jsx)(at,{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)(lt,{children:[(0,Q.jsx)(ut,{children:`Cancel`}),(0,Q.jsx)(ct,{disabled:v,onClick:()=>void _(`verify`),children:`Verify and repair`})]})]})})]})}var ar=e=>e.status===`queued`||e.status===`running`,or=e=>`${be.workspaces}/${encodeURIComponent(e)}/db`,sr=e=>K.get(`${or(e)}/maintenance`),cr={"reindex:timelines":`reindex/timelines`,"reindex:mime":`reindex/mime`,"reindex:search":`reindex/search`,optimize:`optimize`};function lr(e,t,n={}){let r=cr[t];return t===`reindex:search`?K.post(`${or(e)}/${r}${n.rebuild?`?rebuild=true`:``}`,{}):K.post(`${or(e)}/${r}`,t===`optimize`&&n.space?{space:n.space}:{})}function ur(e){switch(e.action){case`reindex:timelines`:return`Rebuild timelines`;case`reindex:mime`:return`Rebuild file types`;case`reindex:search`:return e.options.rebuild?`Rebuild word search`:`Index missing documents`;default:return e.options.space?`Optimize ${e.options.space}`:`Optimize indexes`}}var dr=e=>typeof e==`number`?e.toLocaleString():`0`;function fr(e){if(e.status===`failed`)return e.error||`Maintenance failed`;if(e.status===`queued`)return`Waiting for the database…`;if(e.status===`running`){let t=e.progress;return t?e.action===`reindex:search`?`${dr(t.indexed)} indexed`:`${dr(t.scanned)}${typeof t.total==`number`?` / ${dr(t.total)}`:``} documents scanned`:`Working…`}let t=e.result||{};return e.action===`reindex:timelines`?`${dr(t.created)} created, ${dr(t.updated)} updated (${dr(t.scanned)} scanned)`:e.action===`reindex:mime`?`${dr(t.ticked)} of ${dr(t.scanned)} documents across ${dr(t.keys)} file types`:e.action===`reindex:search`?`${dr(t.indexed)} newly indexed (${dr(t.alreadyIndexed)} of ${dr(t.totalDocs)} already were)`:`Indexes compacted`}var pr=new Map;function mr(e,t,n){let r=JSON.stringify([t,n.id]),i=pr.get(r);if(i)return i;let a=(async()=>{let r=e({title:ur(n),description:fr(n)});try{let e=n;for(;ar(e);){let i=(await sr(t)).jobs.find(e=>e.id===n.id);if(!i)throw Error(`Maintenance receipt is no longer available. The workspace may have restarted; run it again.`);if(e=i,!ar(e))break;r.update({title:ur(e),description:fr(e)}),await new Promise(e=>setTimeout(e,2e3))}if(e.status===`failed`)throw Error(fr(e));return r.done({title:`${ur(e)} complete`,description:fr(e)}),e}catch(e){throw r.fail({title:`${ur(n)} needs attention`,description:e instanceof Error?e.message:String(e),variant:`destructive`}),e}})();return pr.set(r,a),a.finally(()=>{pr.get(r)===a&&pr.delete(r)}).catch(()=>{}),a}function hr({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,h]=(0,Z.useState)(r.watch===!0),[g,_]=(0,Z.useState)(!1),[v,y]=(0,Z.useState)(!1),{showToast:b}=q(),x=e=>({value:s[e],onChange:t=>c(n=>({...n,[e]:t.target.value}))});return a?(0,Q.jsxs)(`form`,{onSubmit:async r=>{r.preventDefault(),y(!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]]:[]})),...g?{sessionToken:``}:{}};t?await Xe(e,`s3`,t.address,r):await Je(e,`s3`,r),o(!1),c(i()),b({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){b({title:`Could not connect S3`,description:e instanceof Error?e.message:String(e),variant:`destructive`})}finally{y(!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,{...x(`name`),required:!0,placeholder:`Accounting archive`})]}),(0,Q.jsxs)(`label`,{className:`space-y-1 text-xs`,children:[`Bucket`,(0,Q.jsx)(W,{...x(`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,{...x(`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,{...x(`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,{...x(`prefix`),disabled:!!t,placeholder:`Augmentd/Accounting`})]}),(0,Q.jsxs)(`label`,{className:`space-y-1 text-xs`,children:[`Access key ID`,(0,Q.jsx)(W,{...x(`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,{...x(`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,{...x(`sessionToken`),disabled:g,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:g,onChange:e=>_(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=>h(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:v,onClick:()=>{o(!1),c(i())},children:`Cancel`}),(0,Q.jsx)(U,{disabled:v,children:v?`Checking connection…`:`Save connection`})]})]}):(0,Q.jsxs)(U,{variant:`outline`,size:`sm`,onClick:()=>{c(i()),u(r.forcePathStyle!==!1),f(r.readOnly===!0),h(r.watch===!0),_(!1),o(!0)},children:[t?(0,Q.jsx)(m,{className:`mr-2 h-3.5 w-3.5`}):(0,Q.jsx)(A,{className:`mr-2 h-3.5 w-3.5`}),t?`Edit S3 connection`:`Add S3 bucket`]})}function gr({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 _r=`h-8 rounded-md border bg-background px-2 text-sm outline-none focus:ring-2 focus:ring-ring`;function vr({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)(b,{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 yr({workspaceName:e,value:t,onChange:n,disabled:r}){let[i,a]=(0,Z.useState)(!1);return(0,Q.jsxs)(`div`,{className:`space-y-1`,children:[(0,Q.jsxs)(`label`,{className:`text-xs font-medium`,children:[`Only this folder `,(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:t,disabled:r,onChange:e=>n(e.target.value),placeholder:`Whole workspace`,className:`h-8 font-mono text-sm`}),(0,Q.jsx)(U,{type:`button`,size:`sm`,variant:`outline`,disabled:r,onClick:()=>a(!0),title:`Pick a folder or context from the tree`,children:(0,Q.jsx)(ne,{className:`h-3.5 w-3.5`})})]}),(0,Q.jsxs)(`p`,{className:`text-[11px] text-muted-foreground`,children:[`Includes everything below it. Pick from the tree, or type `,(0,Q.jsx)(`span`,{className:`font-mono`,children:`ctx://work/project`}),` or`,` `,(0,Q.jsx)(`span`,{className:`font-mono`,children:`dir://photos`}),`.`]}),i&&(0,Q.jsx)(St,{onClose:()=>a(!1),children:(0,Q.jsx)(Ce,{sizeClassName:`h-full w-[380px] max-w-[90vw] max-md:w-full`,fixedWorkspaceName:e,multiple:!1,onClose:()=>a(!1),onConfirm:(e,t)=>{let r=e[0];r&&n(`${t.treeType===`directory`?`dir`:`ctx`}://${r.replace(/^\/+/,``)}`),a(!1)}})})]})}function br({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)(()=>Sn(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 tt(!a.paused,t);i({title:e.paused?`Indexing paused`:`Indexing resumed`,description:e.paused?`${e.pending.toLocaleString()} document(s) on hold. Resume any time; a server restart also resumes.`:`${e.pending.toLocaleString()} document(s) being indexed.`}),await l()}catch(e){i({title:`Error`,description:e instanceof Error?e.message:`Failed to pause or resume indexing (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:`document(s) waiting to be indexed`})]}):a.paused?`Paused`:`Up to date`:`Inference service not reachable`}),a?.paused&&a.pending>0&&(0,Q.jsx)(`span`,{className:`text-xs text-muted-foreground`,children:`(paused)`}),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: new documents are not indexed for meaning search; what is already indexed still works`,children:`indexing turned off on the server`})]}),(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; the waiting documents get indexed`:`Pause after the current batch. Stops the CPU-heavy model work; new documents are still found by word search.`,children:s?`…`:a.paused?`Resume`:`Pause`}),(0,Q.jsx)(U,{type:`button`,size:`sm`,variant:`ghost`,onClick:r,title:`Reload`,children:(0,Q.jsx)(z,{className:`h-3.5 w-3.5`})})]})]})}function xr({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 index built with ${t.model||`that model`}. Switching back to it afterwards means indexing every document again instead of being instant.\n\nThis is the only step here that cannot be undone.`)){o(t.name);try{let n=await xn(e,t.name);if(!n.dropped)throw Error(n.error||`Failed to drop the vector table`);i({title:`Deleted`,description:`Removed '${n.name||t.name}'.`}),r()}catch(e){i({title:`Error`,description:e instanceof Error?e.message:`Failed to delete`,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:`Stored indexes unavailable`}),(0,Q.jsx)(`p`,{className:`mt-1 text-xs text-muted-foreground`,children:n})]}):s.length===0?null:(0,Q.jsx)(vr,{title:`Previous models (${s.length})`,hint:`Indexes built with models you switched away from. They stay on disk so switching back is instant. Deleting one frees the space for good.`,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 the index built with this model`,children:[(0,Q.jsx)(I,{className:`mr-1.5 h-3.5 w-3.5`}),a===e.name?`Deleting…`:`Delete`]})]},e.name))})})}function Sr({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);return(0,Q.jsxs)(vr,{title:`Index existing documents`,hint:`Add documents that are not in meaning search yet, for example after switching models. Limit it to one folder to try a model on a small set 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:`Content type`}),(0,Q.jsxs)(`select`,{className:`${_r} w-full`,value:a,onChange:e=>o(e.target.value),children:[(0,Q.jsx)(`option`,{value:``,children:`All content types`}),n.map(e=>(0,Q.jsx)(`option`,{value:e,children:vn(e)},e))]})]}),(0,Q.jsx)(yr,{workspaceName:t,value:s,onChange:c,disabled:d})]}),(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:[`Also redo documents that are already indexed`,l&&s.trim()&&(0,Q.jsx)(`span`,{className:`mt-1 block text-[11px] text-warning`,children:`Even with a folder limit, this clears the whole index for the chosen content type, not just that folder. Leave it off unless you want to redo everything.`})]})]}),(0,Q.jsx)(U,{type:`button`,size:`sm`,className:`mt-3`,onClick:async()=>{f(!0);try{let t=await gn(e,{space:a||void 0,reindex:l,scope:s.trim()||void 0});t.ingestDisabled?i({title:`Indexing is turned off on the server`,description:`CANVAS_INFERD_INGEST_DISABLED=true: nothing was queued. What is already indexed still works.`,variant:`destructive`}):i({title:`Indexing queued`,description:`${t.enqueued.toLocaleString()} document(s) queued${t.scope?` under ${t.scope}`:``}. The status line above shows progress.`}),r()}catch(e){i({title:`Indexing failed to start`,description:e instanceof Error?e.message:`Failed to queue`,variant:`destructive`})}finally{f(!1)}},disabled:d,children:d?`Queueing…`:`Start indexing`})]})}var Cr=[{provider:`blip`,model:`HuggingFaceTB/SmolVLM-500M-Instruct`,task:`instruct`,label:`SmolVLM 500M: follows your instructions (recommended)`,note:`About 500 MB. Follows the instructions below, so you can ask for what matters to you, such as style, materials or setting.`},{provider:`blip`,model:`HuggingFaceTB/SmolVLM-256M-Instruct`,task:`instruct`,label:`SmolVLM 256M: smaller, follows your instructions`,note:`About 250 MB. Same instructions, less detail; for servers short on memory.`},{provider:`blip`,model:`onnx-community/Florence-2-base-ft`,task:`florence2`,label:`Florence-2 base: detailed, fixed style`,note:`Writes several detailed sentences, but ignores instructions.`},{provider:`blip`,model:`Xenova/vit-gpt2-image-captioning`,task:`caption`,label:`vit-gpt2: one short sentence (smallest)`,note:`One terse sentence per image, sometimes repetitive. Uses the least memory.`}],wr=`custom`,Tr=[{value:`instruct`,label:`Follows instructions (instruct VLM)`},{value:`florence2`,label:`Florence-2 family`},{value:`caption`,label:`Plain captioner, image only`}],Er=`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 Dr({workspaceId:e,workspaceName:t,config:n,saving:r,onSave:i}){let{showToast:a}=q(),o=n.workspace||{},s=n.effective||{},c=Cr[0],l=o.summarize?.image||s.summarize?.image||{},[u,d]=(0,Z.useState)(()=>s.summarize?.image?.enabled===!0||o.summarize?.image?.enabled===!0),[f,p]=(0,Z.useState)(()=>{let e=Cr.find(e=>e.provider===(l.provider||c.provider)&&e.model===l.model);return e?`${e.provider}::${e.model}`:l.model?wr:`${c.provider}::${c.model}`}),[m,h]=(0,Z.useState)(()=>Cr.some(e=>e.model===l.model)?``:l.model||``),[g,_]=(0,Z.useState)(()=>l.task||`instruct`),[v,y]=(0,Z.useState)(()=>l.prompt||``),[b,x]=(0,Z.useState)(!1),[S,C]=(0,Z.useState)(null),[w,T]=(0,Z.useState)(!1),[E,D]=(0,Z.useState)(!1),[O,k]=(0,Z.useState)(!1),[A,j]=(0,Z.useState)(``),M=Cr.find(e=>`${e.provider}::${e.model}`===f),N=f===wr,P=N?{provider:c.provider,model:m.trim(),task:g,label:`Custom`,note:``}:M||c,F=P.task===`instruct`,ee=(0,Z.useCallback)(()=>Sn(e).then(e=>C(e.summarize||null)).catch(()=>{}),[e]);(0,Z.useEffect)(()=>{ee();let e=window.setInterval(()=>{C(e=>(e?.running&&ee(),e))},2e3);return()=>window.clearInterval(e)},[ee]);let te=async()=>{await i({...o,summarize:{...o.summarize||{},image:{enabled:u,provider:P.provider,model:P.model,task:P.task,...F&&v.trim()?{prompt:v.trim()}:{}}}}),x(!1)},I=async()=>{D(!0);try{C(await _n(e)),a({title:`Stopping image descriptions`,description:`The current image finishes first; the rest are left for a later run.`})}catch(e){a({title:`Could not stop`,description:e instanceof Error?e.message:`Failed to stop`,variant:`destructive`})}finally{D(!1)}},ne=async(t=!1)=>{T(!0);try{let n=await bn(e,{force:t,scope:A.trim()||void 0});C(n),a({title:t?`Describing all images again`:`Describing images`,description:`${n.total.toLocaleString()} image(s)${n.scope?` under ${n.scope}`:``}. The first run downloads the model, which can take a few minutes.`})}catch(e){a({title:`Image descriptions failed to start`,description:e instanceof Error?e.message:`Failed to start`,variant:`destructive`})}finally{T(!1)}},L=s.summarize?.image?.enabled===!0,R=S?.running===!0,re=S?.errors?.[0]?.error;return(0,Q.jsx)(vr,{title:`Image descriptions`,hint:`A vision model writes a few sentences about each image, so you can find photos by what is in them.`,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:u,disabled:r,onChange:e=>{d(e.target.checked),x(!0)}}),`Describe images`]}),(0,Q.jsxs)(`select`,{className:_r,value:f,disabled:r||!u,onChange:e=>{p(e.target.value),x(!0)},children:[Cr.map(e=>(0,Q.jsx)(`option`,{value:`${e.provider}::${e.model}`,children:e.label},`${e.provider}::${e.model}`)),(0,Q.jsx)(`option`,{value:wr,children:`Custom model…`})]}),!N&&(0,Q.jsx)(`span`,{className:`font-mono text-xs text-muted-foreground`,children:P.model})]}),N&&(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 (Hugging Face, ONNX)`,value:m,disabled:r||!u,onChange:e=>{h(e.target.value),x(!0)}}),(0,Q.jsx)(`select`,{className:_r,value:g,disabled:r||!u,onChange:e=>{_(e.target.value),x(!0)},children:Tr.map(e=>(0,Q.jsx)(`option`,{value:e.value,children:e.label},e.value))})]}),u&&P.note&&(0,Q.jsx)(`p`,{className:`text-xs text-muted-foreground`,children:P.note}),u&&F&&(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:Er,value:v,disabled:r,onChange:e=>{y(e.target.value),x(!0)}}),(0,Q.jsx)(`p`,{className:`text-xs text-muted-foreground`,children:`What to ask about every image. Leave empty to use the default shown above. Descriptions become searchable, so ask for the details you would search by.`})]}),S&&(S.running||S.total>0||S.finishedAt)&&(0,Q.jsxs)(`p`,{className:ye(`text-xs`,S.aborted?`text-destructive`:`text-muted-foreground`),children:[R?`Running`:S.aborted?`Stopped`:S.cancelled?`Cancelled`:`Last run`,S.scope?(0,Q.jsxs)(Q.Fragment,{children:[` under `,(0,Q.jsx)(`span`,{className:`font-mono`,children:S.scope})]}):null,`: `,S.described,`/`,S.total,` described`,S.skipped?`, ${S.skipped} skipped`:``,S.failed?`, ${S.failed} failed`:``,S.aborted?`. ${S.abortedReason||`The model stopped working`}. The remaining images were not tried; run again to retry.`:S.cancelled?`. Stopped by you. The remaining images were left untouched; run again to continue.`:re?`. ${re}`:``]}),(0,Q.jsx)(yr,{workspaceName:t,value:A,onChange:j,disabled:r||R}),(0,Q.jsxs)(`label`,{className:`flex items-start gap-2 text-xs`,children:[(0,Q.jsx)(`input`,{type:`checkbox`,className:`mt-0.5`,checked:O,disabled:r||R,onChange:e=>k(e.target.checked)}),(0,Q.jsxs)(`span`,{children:[`Also redo images that already have a description`,O&&(0,Q.jsxs)(`span`,{className:`mt-1 block text-[11px] text-warning`,children:[`Replaces existing descriptions`,A.trim()?` in that folder`:``,`, including ones written by another model. Leave this off to describe only images that have none yet.`]})]})]}),(0,Q.jsxs)(`div`,{className:`flex flex-wrap items-center justify-between gap-3`,children:[(0,Q.jsx)(`p`,{className:`text-xs text-muted-foreground`,children:`Turn on and save, then run. Descriptions are generated on this server; the first run downloads the model.`}),(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:r||!b,children:`Save`}),(0,Q.jsx)(U,{size:`sm`,onClick:()=>void ne(O),disabled:r||b||w||R||!L,title:L?b?`Save your changes first`:O?`Describe every image again, replacing existing descriptions`:`Describe images that have no description yet`:`Turn on image descriptions and save first`,children:R||w?`Running…`:O?`Describe all again`:`Describe missing`}),R&&(0,Q.jsx)(U,{size:`sm`,variant:`destructive`,onClick:()=>void I(),disabled:E,title:`Finish the current image, then stop. The rest are left for later.`,children:E?`Stopping…`:`Stop`})]})]})]})})}function Or({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)(()=>yn(e).then(e=>{i(e),m(null)}).catch(e=>m(e instanceof Error?e.message:`Failed to load the search settings`)).finally(()=>u(!1)),[e]),b=(0,Z.useCallback)(()=>mn(e).then(e=>{o(e.tables||[]),c(e.error)}).catch(e=>c(e instanceof Error?e.message:`Failed to list stored indexes`)),[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 hn(e,t);g(r.movedSpaces||[]),r.movedSpaces?.length?n({title:`Saved: now index your existing documents`,description:`${r.movedSpaces.map(vn).join(`, `)}: new model in use. New documents use it right away; existing ones need indexing (button on the row).`}):r.applied===!1?n({title:`Saved: applies on next start`,description:`The workspace is not running, so the changes take effect when it starts.`}):n({title:`Saved`,description:`Search settings 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 gn(e,{space:t});if(r.ingestDisabled){n({title:`Indexing is turned off on the server`,description:`CANVAS_INFERD_INGEST_DISABLED=true: nothing was queued. What is already indexed still works.`,variant:`destructive`});return}n({title:`Indexing ${vn(t).toLowerCase()}`,description:`${r.enqueued.toLocaleString()} document(s) queued. The status line above shows progress.`}),g(e=>e.filter(e=>e!==t)),v(e=>e+1),b()}catch(e){n({title:`Indexing failed to start`,description:e instanceof Error?e.message:`Failed to queue`,variant:`destructive`})}};if(l)return(0,Q.jsxs)(`div`,{className:`flex items-center gap-2 text-sm text-muted-foreground`,children:[(0,Q.jsx)(z,{className:`h-4 w-4 animate-spin`}),`Loading search settings…`]});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:`Search settings 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 w=Object.keys(r.effective.spaces||{}).sort();return(0,Q.jsxs)(`div`,{className:`space-y-5`,children:[(0,Q.jsx)(br,{workspaceId:e,workspaceName:t,refreshKey:_,onRefresh:x}),(0,Q.jsx)(Cn,{value:r.workspace||{},effective:r.effective||{},inherited:r.inherited||{},resolvedSpaces:r.spaces,invalid:r.invalid,attention:h,onFill:C,saving:d,saveLabel:`Save models`,onSave:S},JSON.stringify(r.workspace)),(0,Q.jsxs)(`div`,{className:`space-y-3`,children:[(0,Q.jsx)(Dr,{workspaceId:e,workspaceName:t,config:r,saving:d,onSave:S},`summarize:${JSON.stringify(r.workspace?.summarize||{})}`),(0,Q.jsx)(Sr,{workspaceId:e,workspaceName:t,spaces:w,onDone:()=>{b(),v(e=>e+1)}}),(0,Q.jsx)(xr,{workspaceId:e,tables:a,error:s,onChanged:()=>{b()}})]}),(0,Q.jsxs)(`p`,{className:`text-xs text-muted-foreground`,children:[`These settings are saved with the workspace (in its `,(0,Q.jsx)(`span`,{className:`font-mono`,children:`workspace.json`}),`), so they move with it. Anything not set here comes from your personal defaults, then the server's. Changes apply immediately, with no restart.`]})]})}function kr(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 Ar({value:e,onChange:t,path:r,className:i=``}){let a=(0,Z.useMemo)(()=>kr(r),[r]),{resolvedScheme:o}=tn();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}})}function jr(e,t){if(![`download`,`store`].includes(e.action)||e.recursive===!0&&!t)return null;let n=String(e.folder||``);return n.includes(`{{`)?null:[n,(e.recursive===!0?e.pathMode===`full`?t?.path:t?.rel:``)||``].join(`/`).split(`/`).filter(e=>e&&e!==`.`&&e!==`..`).join(`/`)}function Mr(e,t){if(![`download`,`store`].includes(e.action)||typeof e.to!=`string`)return null;if(e.recursive===!0&&!t)return`Missing filing path — decline and trigger the rule again`;let n=jr(e,t);return n===null?null:`${e.to}/${n}`}function Nr(e,t){let n=jr(e,t);if(n===null||e.recursive!==!0||e.autoLink===!0)return{...e};let r={...e,folder:n};return delete r.recursive,delete r.pathMode,r}function Pr(e,t){let n=structuredClone({actions:e.actions});for(let[r,i]of Object.entries(t)){if(!e.editable.includes(r))continue;let t=r.split(`.`);if(t[0]!==`actions`||t.some(e=>[`__proto__`,`prototype`,`constructor`].includes(e)))continue;let a=n;for(let e of t.slice(0,-1)){if(!a[e]||typeof a[e]!=`object`){a={};break}a=a[e]}a[t[t.length-1]]=i}return n.actions}var Fr=[`pending`,`approved`,`declined`,`failed`,`expired`,`all`],Ir={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 Lr(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 Rr(e,t){return t.split(`.`).reduce((e,t)=>typeof e==`object`&&e?e[t]:void 0,e)}function zr(e){let t=e.split(`.`);return t.slice(Math.max(t.length-2,2)).join(`.`)}var Br=/(body|content|message|text|prompt)$/i,Vr=2e3;function Hr({workspaceId:e,id:t,children:n}){let{open:r}=Ct(),{showToast:i}=q(),[a,o]=(0,Z.useState)(!1),s=async n=>{n.stopPropagation(),o(!0);try{r(await Be(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 Ur({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)(Hr,{workspaceId:e,id:Number(r[1])},n):(0,Q.jsx)(Z.Fragment,{children:t},n)})})}function Wr({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=Rr(e,r),a=typeof i==`string`,o=r in t?t[r]:i,s=a?String(o??``):JSON.stringify(o,null,2),c=a&&(Br.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:[zr(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 Gr({workspaceId:e,onPendingCount:t}){let{showToast:n}=q(),r=wn(),[i,a]=(0,Z.useState)(`pending`),[o,s]=(0,Z.useState)([]),[c,l]=(0,Z.useState)(0),[u,d]=(0,Z.useState)(!0),[f,p]=(0,Z.useState)(new Set),[m,h]=(0,Z.useState)(null),[g,_]=(0,Z.useState)({}),[v,y]=(0,Z.useState)(!1),x=(0,Z.useRef)(0),S=(0,Z.useCallback)(()=>{let r=++x.current,a=i===`pending`?null:Mt(e,{status:`pending`,limit:1}).then(({total:e})=>e);return Mt(e,{status:i===`all`?void 0:i,limit:200}).then(async({actions:e,total:n})=>{let i=a===null?n:await a;r===x.current&&(s(e),l(n),p(t=>new Set(e.filter(e=>e.status===`pending`&&t.has(e.actionId)).map(e=>e.actionId))),t?.(i))}).catch(()=>{r===x.current&&n({title:`Error`,description:`Failed to load pending actions`,variant:`destructive`})}).finally(()=>{r===x.current&&d(!1)})},[e,i]),C=(0,Z.useCallback)(async()=>{d(!0),await S()},[S]),[w,E]=(0,Z.useState)(`${e}|${i}`),O=`${e}|${i}`;O!==w&&(E(O),d(!0)),(0,Z.useEffect)(()=>{S()},[S]);let k=(0,Z.useRef)(S);(0,Z.useEffect)(()=>{k.current=S});let A=(0,Z.useRef)(null),j=(0,Z.useRef)(0);(0,Z.useEffect)(()=>()=>{A.current!==null&&window.clearTimeout(A.current)},[]),Tn(r,`workspace:${e}`,(0,Z.useMemo)(()=>{let e=()=>{if(A.current!==null)return;let e=Math.max(0,j.current+Vr-Date.now());A.current=window.setTimeout(()=>{A.current=null,j.current=Date.now(),k.current()},e)};return{"action.proposed":e,"action.approved":e,"action.declined":e,"action.failed":e}},[]));let[M,N]=(0,Z.useState)(null),P=M?.state===`running`;(0,Z.useEffect)(()=>{let t=!1;return Xt(e).then(e=>{t||N(e)}).catch(()=>{}),()=>{t=!0}},[e]),(0,Z.useEffect)(()=>{if(!P)return;let t=window.setInterval(()=>{Xt(e).then(e=>{N(e),k.current()}).catch(()=>{})},Vr);return()=>window.clearInterval(t)},[e,P]);let F=async(t,r,i)=>{if(window.confirm(i))try{N(await kt(e,{decision:t,nothingToDo:r}))}catch(e){n({title:`Could not start`,description:e instanceof Error?e.message:`Unknown error`,variant:`destructive`})}},te=o.filter(e=>e.status===`pending`),I=te.length>0&&te.every(e=>f.has(e.actionId)),ne=()=>{p(I?new Set:new Set(te.map(e=>e.actionId)))},L=e=>{p(t=>{let n=new Set(t);return n.has(e)?n.delete(e):n.add(e),n})},R=(e,t,n)=>{_(r=>({...r,[e]:{...r[e],[t]:n}}))},re=async(t,r)=>{if(!(!t.length&&!r.length)){y(!0);try{let i=await qt(e,{approve:t.map(e=>{let t=g[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}),_(e=>{let n={...e};for(let e of[...t,...r])delete n[e];return n}),p(new Set),m&&[...t,...r].includes(m)&&h(null),await C()}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:Fr.map(e=>(0,Q.jsx)(`option`,{value:e,children:e},e))}),(0,Q.jsx)(U,{size:`sm`,variant:`ghost`,onClick:()=>void C(),title:`Reload`,children:(0,Q.jsx)(z,{className:`h-4 w-4`})}),c>o.length&&(0,Q.jsxs)(`span`,{className:`text-xs text-muted-foreground`,children:[`Showing `,o.length,` of `,c]})]}),f.size>0&&(0,Q.jsxs)(`div`,{className:`flex items-center gap-2`,children:[(0,Q.jsxs)(`span`,{className:`text-xs text-muted-foreground`,children:[f.size,` selected`]}),(0,Q.jsxs)(U,{size:`sm`,disabled:v,onClick:()=>void re([...f],[]),children:[(0,Q.jsx)(T,{className:`mr-1 h-3.5 w-3.5`}),` Approve`]}),(0,Q.jsxs)(U,{size:`sm`,variant:`outline`,disabled:v,onClick:()=>void re([],[...f]),children:[(0,Q.jsx)(D,{className:`mr-1 h-3.5 w-3.5`}),` Decline`]})]})]}),i===`pending`&&(c>0||M)&&(0,Q.jsxs)(`div`,{className:`flex items-center gap-2 flex-wrap text-xs`,children:[c>0&&(0,Q.jsxs)(Q.Fragment,{children:[(0,Q.jsx)(U,{size:`sm`,variant:`outline`,disabled:P||v,onClick:()=>void F(`approve`,!1,`Approve and run all ${c} pending actions?`),children:`Approve all`}),(0,Q.jsx)(U,{size:`sm`,variant:`outline`,disabled:P||v,title:`Declines the actions whose document is gone or has nothing for them to work on, such as an email without a matching attachment`,onClick:()=>void F(`decline`,!0,`Decline the pending actions that have nothing to do? The rest of the ${c} stay in the queue.`),children:`Decline those with nothing to do`}),(0,Q.jsx)(U,{size:`sm`,variant:`outline`,disabled:P||v,onClick:()=>void F(`decline`,!1,`Decline all ${c} pending actions?`),children:`Decline all`})]}),M&&(0,Q.jsxs)(`span`,{className:M.state===`failed`?`text-destructive`:`text-muted-foreground`,children:[M.state===`running`?`Running: `:M.state===`failed`?`Stopped (${M.error}): `:`Last run: `,M.decision===`approve`?`approved`:`declined`,` `,M.decided,` of `,M.total,M.kept>0&&`, kept ${M.kept}`,M.failed>0&&`, ${M.failed} failed`]})]}),(0,Q.jsx)(`div`,{className:`border rounded-lg overflow-auto max-h-[560px]`,children:u?(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:te.length>0&&(0,Q.jsx)(`input`,{type:`checkbox`,checked:I,onChange:ne,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=m===t.actionId,i=r?Pr(t,n?g[t.actionId]??{}:{}):t.actions,a=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:()=>h(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:f.has(t.actionId),onChange:()=>L(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)(b,{className:`h-3 w-3 shrink-0`}):(0,Q.jsx)(ee,{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)(Ur,{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,a?.id!=null&&(0,Q.jsxs)(Q.Fragment,{children:[` · `,(0,Q.jsx)(Hr,{workspaceId:e,id:a.id})]})]})]}),(0,Q.jsx)(`td`,{className:`px-3 py-2 text-xs whitespace-nowrap`,title:t.ts,children:Lr(t.ts)}),(0,Q.jsxs)(`td`,{className:`px-3 py-2 text-xs`,children:[(0,Q.jsx)(`span`,{className:Ir[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:v,title:t.status===`failed`?`Retry (re-approve)`:`Approve and execute`,onClick:()=>void re([t.actionId],[]),children:(0,Q.jsx)(T,{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:v,title:`Decline`,onClick:()=>void re([],[t.actionId]),children:(0,Q.jsx)(D,{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)(Wr,{action:t,amend:g[t.actionId]??{},onAmend:(e,n)=>R(t.actionId,e,n)}),i.map((e,n)=>{let r=Mr(e,t.pathMatch);return r&&(0,Q.jsxs)(`p`,{className:`text-xs`,children:[(0,Q.jsx)(`span`,{className:`font-medium`,children:`Destination folder: `}),(0,Q.jsx)(`span`,{className:`font-mono break-all`,children:r})]},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(i.map(e=>Nr(e,t.pathMatch)),null,2)})]}),(0,Q.jsxs)(`details`,{className:`text-xs text-muted-foreground`,children:[(0,Q.jsx)(`summary`,{className:`cursor-pointer`,children:`Rule action settings`}),(0,Q.jsx)(`pre`,{className:`mt-1 rounded border bg-background p-2 font-mono overflow-auto max-h-56`,children:JSON.stringify(i,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 Kr=`#!/usr/bin/env bash
|
|
2
|
+
# Called from a hook via: spawn('bash', [script, ...args])
|
|
3
|
+
set -euo pipefail
|
|
4
|
+
`;function qr({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,p]=(0,Z.useState)(!1),[m,h]=(0,Z.useState)([]),[_,y]=(0,Z.useState)(!0),[x,C]=(0,Z.useState)(null),[w,T]=(0,Z.useState)(``),[E,M]=(0,Z.useState)(!1),[N,F]=(0,Z.useState)(!1),[ee,ne]=(0,Z.useState)(!1),[L,re]=(0,Z.useState)(null),[ie,se]=(0,Z.useState)(`document.inserted`),[le,ue]=(0,Z.useState)(``),[B,fe]=(0,Z.useState)([]),[pe,me]=(0,Z.useState)(!1),[he,_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 Pt(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,je]=(0,Z.useState)(()=>It()),[Me,Ne]=(0,Z.useState)(()=>String(It())),J=()=>{let e=Nt(Vt(Me));je(e),Ne(String(e))};(0,Z.useEffect)(()=>{let t=!1;return Mt(e,{status:`pending`,limit:1}).then(({total:e})=>{t||De(e)}).catch(()=>{}),()=>{t=!0}},[e]),mt(()=>Te(!1),we);let Y=async t=>{if(confirm(`Replay ${t.handlerType} "${t.handler}" for ${t.event} (docs ${(t.docIds||[]).join(`, `)})?`)){Ce(t.runId);try{let n=await wt(e,t.runId);o({title:`Replayed`,description:`${t.handler} → ${n.status}`}),await Ie()}catch(e){o({title:`Replay failed`,description:e instanceof Error?e.message:`Unknown error`,variant:`destructive`})}finally{Ce(null)}}},Pe=s===`scripts`?{list:Wt,get:Jt,save:Lt,del:Kt}:{list:Et,get:Qt,save:jt,del:Ut},Fe=s===`hooks`||s===`scripts`,Ie=async()=>{if(s!==`rules`&&s!==`pending`){if(s===`runs`){try{y(!0),_e(await Gt(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),h(await Pe.list(e))}catch{o({title:`Error`,description:`Failed to load ${s}`,variant:`destructive`})}finally{y(!1)}}};(0,Z.useEffect)(()=>{Ie()},[e,s,ve,H]);let Le=e=>{e!==s&&(E&&!confirm(`Discard unsaved changes?`)||(c(e),C(null),T(``),M(!1),ne(!1)))},Re=async t=>{if(!(E&&!confirm(`Discard unsaved changes?`)))try{C(t),T(await Pe.get(e,t)),M(!1)}catch{o({title:`Error`,description:`Failed to open ${t}`,variant:`destructive`})}},ze=async()=>{if(x)try{F(!0),await Pe.save(e,x,w),M(!1),o({title:`Saved`,description:`${x} saved`}),await Ie()}catch{o({title:`Error`,description:`Failed to save ${x}`,variant:`destructive`})}finally{F(!1)}},Be=async t=>{let n=Dt(t);try{let r=await Ot(e,t,!n);x===t&&C(r),await Ie(),o({title:n?`Disabled`:`Enabled`,description:r})}catch{o({title:`Error`,description:`Failed to toggle ${t}`,variant:`destructive`})}},Ve=async t=>{let n=Zt(t);if(!n||!Dt(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 Bt(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 At(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)}}},X=async t=>{if(confirm(`Delete ${s===`hooks`?`hook`:`script`} ${t}?`))try{await Pe.del(e,t),x===t&&(C(null),T(``),M(!1)),await Ie(),o({title:`Deleted`,description:`${t} deleted`})}catch{o({title:`Error`,description:`Failed to delete ${t}`,variant:`destructive`})}},He=async()=>{if(!L)try{re(await Rt(e))}catch{o({title:`Error`,description:`Failed to load hook metadata`,variant:`destructive`})}},Ue=async()=>{ne(!ee),s===`hooks`&&await He()},We=async()=>{p(!d),await He()},Ge=async()=>{let t=le.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 Lt(e,t,Kr),ne(!1),ue(``),await Ie(),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(!le.trim()){o({title:`Error`,description:`Give the hook a name`,variant:`destructive`});return}try{me(!0);let{path:t}=await Ht(e,{event:ie,name:le.trim(),actions:B});ne(!1),ue(``),fe([]),await Ie(),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`?Tt(m):{scripts:m},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)(bt,{className:`border-b-0`,active:s,onChange:Le,tabs:[{id:`pending`,label:Ee>0?`Pending (${Ee})`:`Pending`,icon:f},{id:`rules`,label:`Rules`,icon:ge},{id:`hooks`,label:`Hooks`,icon:te},{id:`scripts`,label:`Scripts`,icon:R},{id:`runs`,label:`Runs`,icon:v}]})})})]}),(s===`runs`||Fe)&&(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)(D,{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:Ie,title:`Reload`,children:[(0,Q.jsx)(z,{className:`mr-1.5 h-4 w-4`}),` Reload`]})]}),Fe&&(0,Q.jsxs)(Q.Fragment,{children:[(0,Q.jsxs)(U,{size:`sm`,variant:`ghost`,onClick:Ie,title:`Reload`,children:[(0,Q.jsx)(z,{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)(de,{className:`mr-1.5 h-4 w-4`}),` Reference`]}),(0,Q.jsxs)(U,{size:`sm`,variant:`outline`,onClick:Ue,children:[(0,Q.jsx)(A,{className:`mr-2 h-4 w-4`}),` `,s===`hooks`?`New Hook`:`New Script`]})]})]}),s===`pending`&&(0,Q.jsx)(Gr,{workspaceId:e,onPendingCount:De}),s===`rules`&&(0,Q.jsx)(Yt,{workspaceId:e,backfillLimit:Ae,onShowRuns:e=>{ye(e),Le(`runs`)},prefill:t,onPrefillConsumed:n,onOpenJson:async()=>{try{await Qt(e,`rules.json`)}catch{await jt(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...`}):he.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:he.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)(oe,{className:`h-3.5 w-3.5`}),(0,Q.jsx)(b,{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:()=>Y(e),children:(0,Q.jsx)(P,{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)(Jr,{state:G})})})]},e.runId))})]})}),Fe&&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.`]})]}),ee&&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:le,onChange:e=>ue(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:()=>ne(!1),children:`Cancel`})]}),ee&&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:ie,onChange:e=>se(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===ie)?.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:le,onChange:e=>ue(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:()=>ne(!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).`]})]})]}),Fe&&(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...`}):m.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=Dt(e.path);return(0,Q.jsxs)(`div`,{className:`flex items-center justify-between rounded px-2 py-1 cursor-pointer text-sm ${x===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`&&Ft(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`&&Zt(e.path)&&(0,Q.jsx)(U,{size:`sm`,variant:`ghost`,className:`h-6 w-6 p-0 touch-target`,title:Zt(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)(S,{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)(O,{className:`h-3 w-3`}):(0,Q.jsx)(k,{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(),X(e.path)},children:(0,Q.jsx)(I,{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:x?(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:[x,E?` •`:``]}),(0,Q.jsxs)(`div`,{className:`flex items-center gap-1`,children:[s===`hooks`&&Zt(x)&&(0,Q.jsxs)(Q.Fragment,{children:[(0,Q.jsx)(U,{size:`sm`,variant:`outline`,className:`h-8`,title:Dt(x)?`Disable this hook (renames it with a disabled- prefix)`:`Enable this hook (strips the prefix)`,onClick:()=>Be(x),children:Dt(x)?(0,Q.jsxs)(Q.Fragment,{children:[(0,Q.jsx)(O,{className:`mr-1.5 h-3.5 w-3.5`}),` Enabled`]}):(0,Q.jsxs)(Q.Fragment,{children:[(0,Q.jsx)(k,{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||E,onClick:()=>Ve(x),children:[(0,Q.jsx)(S,{className:`mr-1.5 h-3.5 w-3.5 ${Oe===x?`animate-pulse`:``}`}),` Run`]})]}),(0,Q.jsxs)(U,{size:`sm`,onClick:ze,disabled:N||!E,children:[(0,Q.jsx)(g,{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)(j,{className:`h-4 w-4`}):(0,Q.jsx)(ae,{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)(Ar,{value:w,path:x,onChange:e=>{T(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)(ce,{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:Me,onChange:e=>Ne(e.target.value),onBlur:J,onKeyDown:e=>{e.key===`Enter`&&(J(),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)(te,{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 Jr({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 Yr({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 Xr({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 Zr(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 Qr({workspaceId:e}){let{policies:t,revision:n,status:r,loading:i,error:a,refresh:o}=sn(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 cn(e,t.map(e=>e.id===r.id?{...e,enabled:!e.enabled}:e),n),rn(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 cn(e,t.filter(e=>e.id!==r.id),n),rn(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 an(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)(w,{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:()=>{ln(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)(w,{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)(Xr,{children:`subfolders`}),(0,Q.jsx)(Xr,{children:Zr(e)}),!e.enabled&&(0,Q.jsx)(Xr,{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)(Xr,{children:`sub-path`}),e.recreateDirectories&&(0,Q.jsx)(Xr,{children:`folders`}),e.key&&(0,Q.jsx)(Xr,{title:e.key,children:`named`}),e.onConflict&&e.onConflict!==`error`&&(0,Q.jsx)(Xr,{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)(Yr,{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)(m,{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)(S,{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)(N,{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)(I,{className:`mr-1 h-3.5 w-3.5`}),`Delete`]})]})]},e.id)}),c&&(0,Q.jsx)(un,{workspaceId:e,path:c.path,policy:c,onClose:()=>l(null),onSaved:()=>{rn(e),o()}})]})}var $r=e=>`${be.workspaces}/${encodeURIComponent(e)}`;async function ei(e){return await K.get(`${$r(e)}/mirrors`)||[]}async function ti(e,t){await K.delete(`${$r(e)}/mirrors/${encodeURIComponent(t)}`)}async function ni(e,t=10){return K.get(`${$r(e)}/mirrors/protection?sample=${t}`)}async function ri(e,t,n){return(await K.patch(`${$r(e)}/mirrors/${encodeURIComponent(t)}`,n)).replica}async function ii(e){return await K.get(`${$r(e)}/sync/conflicts`)||[]}async function ai(e,t,n){return K.post(`${$r(e)}/sync/conflicts/${t}/resolve`,{keep:n})}var oi=e=>{if(e==null||e===``)return`—`;let t=new Date(e);return Number.isNaN(t.getTime())?`—`:t.toLocaleString(void 0,{dateStyle:`medium`,timeStyle:`short`})},si=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`,ci=e=>e?e.slice(0,12):`—`;function li({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)(h,{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 `,oi(a.lastSync)]}),(0,Q.jsxs)(`span`,{children:[`seen `,oi(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)(I,{className:`h-3.5 w-3.5`})})]})})}function ui({p:e}){let t=e.required.length===0,n=e.unprotected??0,r=t||n>0?me:le,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:[`· `,oi(e.mtime)]})]},`${e.docId}:${e.key}`)),e.oldestUnprotected.length>5&&(0,Q.jsxs)(`li`,{children:[`… and `,e.oldestUnprotected.length-5,` more`]})]})]})]})})}function di({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)(C,{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`}),` · `,oi(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:[ci(e.hub.sha256),` · `,si(e.hub.size),` · `,oi(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:[ci(e.incoming.sha256),` · `,si(e.incoming.size),` · `,oi(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 fi({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([ei(e),ii(e),ni(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 ti(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 ri(e,t,{required:r});i(e=>e.map(e=>e.deviceId===t?{...e,replica:{role:n.role,required:n.required}}:e)),c(await ni(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 ai(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)(z,{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)(z,{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)(ui,{p:s}),r.map(e=>(0,Q.jsx)(li,{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)(di,{conflict:e,busy:d===`conflict:${e.docId}`,onResolve:t=>y(e,t)},e.docId))})]})}function pi({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 vt(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?`)&&yt(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 Xe(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 mi(){return(await K.get(`/oauth/google/redirect-uri`)).redirectUri}async function hi(e){let t=window.open(`about:blank`,`canvas-google-oauth`,`popup=yes,width=540,height=720`);if(!t)throw Error(`Popup blocked — allow popups for this site and try again`);let{backend:n,...r}=e,i;try{i=await K.post(`/oauth/google/start`,{...r,exchangeInWorkspace:!!n})}catch(e){throw t?.close(),e}t.location.href=i.url;let a=new URL(xe,window.location.origin).origin;return new Promise((r,o)=>{let s=!1,c=e=>{s||(s=!0,window.removeEventListener(`message`,l),window.clearInterval(u),window.clearTimeout(d),e())},l=s=>{if(s.origin!==a||s.source!==t)return;let l=s.data;!l||l.type!==`canvas:oauth`||l.provider!==`google`||l.state!==i.state||(l.ok&&l.code&&l.redirectUri&&n?c(()=>{let t=`/workspaces/${encodeURIComponent(n.workspaceId)}/backends/${encodeURIComponent(n.driver)}/${encodeURIComponent(n.address)}`;K.post(`${t}/oauth/google/exchange`,{clientId:e.clientId,code:l.code,redirectUri:l.redirectUri}).then(r,o)}):l.ok&&l.refreshToken?c(()=>r({refreshToken:l.refreshToken,account:l.account})):c(()=>o(Error(l.error||`Google sign-in failed`))))};window.addEventListener(`message`,l);let u=window.setInterval(()=>{t.closed&&window.setTimeout(()=>c(()=>o(Error(`Sign-in window closed before finishing`))),500)},500),d=window.setTimeout(()=>c(()=>{t.close(),o(Error(`Sign-in timed out`))}),i.expiresInMs||6e5)})}function gi({clientId:e,clientSecret:t,scope:n,onLinked:r,disabled:i,backend:a}){let[o,s]=(0,Z.useState)(!1),[l,u]=(0,Z.useState)(null),{showToast:d}=q(),f=e.trim().length>0&&(t.trim().length>0||!!a);(0,Z.useEffect)(()=>{let e=!1;return mi().then(t=>{e||u(t)}).catch(()=>{}),()=>{e=!0}},[]);let p=async()=>{s(!0);try{let i=await hi({clientId:e.trim(),clientSecret:t.trim(),scope:n,backend:t.trim()?void 0:a});r(i),d({title:`Google account linked`,description:i.account?`Refresh token obtained for ${i.account}`:`Refresh token obtained`})}catch(e){d({title:`Google sign-in failed`,description:e instanceof Error?e.message:String(e),variant:`destructive`})}finally{s(!1)}};return(0,Q.jsxs)(`div`,{className:`space-y-1`,children:[(0,Q.jsxs)(U,{type:`button`,variant:`outline`,size:`sm`,disabled:i||o||!f,onClick:()=>{p()},title:f?`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`}),o?`Waiting for Google…`:`Sign in with Google to get a refresh token`]}),!f&&(0,Q.jsx)(`p`,{className:`text-[11px] text-muted-foreground`,children:`Enter the OAuth client id and client secret to sign in.`}),f&&a&&!t.trim()&&(0,Q.jsx)(`p`,{className:`text-[11px] text-muted-foreground`,children:`Uses your saved client secret. After signing in, save the form to apply the new refresh token.`}),l&&(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:l})]})]})}var _i={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,placeholder:`primary
|
|
6
|
+
team@group.calendar.google.com = Team`,hint:`One per line. "<id> = <name>" names the calendar in Canvas (its own timeline and a toggle in the Calendar app); unnamed ones use the account label (primary) or the id.`}]},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,placeholder:`personal
|
|
7
|
+
work = Work`,hint:`One per line. "<name> = <calendar name>" names the calendar in Canvas (its own timeline and a toggle in the Calendar app); otherwise the CalDAV display name is used.`},{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 vi(e){return e.split(/[\n,]/).map(e=>e.trim()).filter(Boolean)}function yi({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,h]=(0,Z.useState)(!1),g=(0,Z.useCallback)(async()=>{let t=await nt(e).catch(()=>[]);r(t.filter(e=>e.kind===`connector`))},[e]),_=async()=>{h(!0);try{await g()}finally{h(!1)}};(0,Z.useEffect)(()=>{let t=!1;return nt(e).then(e=>{t||r(e.filter(e=>e.kind===`connector`))}).catch(()=>{}),()=>{t=!0}},[e]);let v=n.some(e=>e.status===`syncing`);(0,Z.useEffect)(()=>{if(!v)return;let e=setInterval(()=>{g()},4e3);return()=>clearInterval(e)},[v,g]);let y=e=>{let t=_i[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(`
|
|
8
|
+
`):``;continue}t!=null&&(r[i.key]=String(t))}u(r),a(e.driver),s(e)},b=()=>{a(null),s(null),u({})},x=async()=>{if(!i)return;let n=_i[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?vi(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 Xe(e,o.driver,o.address,r):await Je(e,i,r),b(),await g(),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 Xe(e,n.driver,n.address,{enabled:!0}):await $e(e,n.driver,n.address),t({title:`Syncing ${n.driver}/${n.address}`}),setTimeout(()=>{g()},1500)}catch(e){t({title:`Sync failed`,description:e instanceof Error?e.message:String(e),variant:`destructive`})}},C=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 Ee(e,n.driver,n.address),await g()}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:()=>{_()},title:`Refresh`,children:(0,Q.jsx)(z,{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=_i[t.driver];return(0,Q.jsxs)(`div`,{className:`rounded-md border p-3`,children:[t.driver===`whatsapp`&&(0,Q.jsx)(pi,{workspaceId:e,address:t.address,selectedChats:Array.isArray(t.config?.chats)?t.config.chats:[],onSaved:g}),(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)(z,{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:()=>y(t),title:`Edit connector`,children:(0,Q.jsx)(m,{className:`h-3.5 w-3.5`})}),(0,Q.jsx)(U,{type:`button`,variant:`ghost`,size:`sm`,onClick:()=>{C(t)},disabled:t.driver===`git`&&t.enabled===!1,title:t.driver===`git`?`Stop syncing`:`Remove connector`,children:(0,Q.jsx)(I,{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:_i[i].icon,width:16,height:16}),(0,Q.jsx)(`span`,{className:`text-sm font-medium`,children:o?`Edit ${_i[i].label}`:_i[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:b,className:`p-1 text-muted-foreground hover:text-foreground`,"aria-label":`Cancel`,children:(0,Q.jsx)(D,{className:`h-3.5 w-3.5`})})]}),(0,Q.jsx)(`p`,{className:`mt-1 text-xs text-muted-foreground`,children:_i[i].blurb}),(0,Q.jsx)(`div`,{className:`mt-2 grid gap-2 sm:grid-cols-2`,children:_i[i].fields.map(t=>(0,Q.jsxs)(`label`,{className:`text-xs ${t.list||t.bool?`sm:col-span-2`:``}`,children:[!t.bool&&(0,Q.jsx)(`span`,{className:`text-muted-foreground`,children:t.label}),t.bool?(0,Q.jsxs)(`span`,{className:`mt-1 flex items-center gap-2`,children:[(0,Q.jsx)(`input`,{type:`checkbox`,checked:l[t.key]===`true`,onChange:e=>u(n=>({...n,[t.key]:e.target.checked?`true`:``})),className:`h-4 w-4 accent-primary`}),(0,Q.jsx)(`span`,{children:t.label})]}):t.list?(0,Q.jsx)(`textarea`,{value:l[t.key]||``,onChange:e=>u(n=>({...n,[t.key]:e.target.value})),placeholder:t.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:t.secret?`password`:`text`,value:l[t.key]||``,disabled:!!o&&t.key===`address`,onChange:e=>u(n=>({...n,[t.key]:e.target.value})),placeholder:o&&t.secret?`Leave blank to keep current`:t.placeholder,autoComplete:`off`,className:`mt-1 h-8 text-xs`}),t.hint&&(0,Q.jsx)(`span`,{className:`mt-0.5 block text-[11px] text-muted-foreground/80`,children:t.hint}),t.oauth===`google`&&(0,Q.jsx)(`span`,{className:`mt-1.5 block`,children:(0,Q.jsx)(gi,{clientId:l.clientId||``,clientSecret:l.clientSecret||``,scope:t.oauthScope||`calendar.readonly`,disabled:d,backend:o&&l.clientId?.trim()===o.config?.clientId?{workspaceId:e,driver:o.driver,address:o.address}:void 0,onLinked:({refreshToken:e,account:n})=>u(r=>({...r,[t.key]:e,address:(r.address||``).trim()||(n?n.replace(/[^a-z0-9]+/gi,`-`).replace(/^-|-$/g,``).toLowerCase():``)}))})})]},t.key))}),(0,Q.jsx)(U,{type:`button`,size:`sm`,className:`mt-3`,disabled:d,onClick:()=>{x()},children:d?`Saving…`:o?`Save changes & sync`:`Save & sync`})]}):(0,Q.jsx)(`div`,{className:`mt-3 flex flex-wrap gap-2`,children:Object.entries(_i).map(([e,t])=>(0,Q.jsxs)(U,{type:`button`,variant:`outline`,size:`sm`,onClick:()=>{a(e),u({})},children:[(0,Q.jsx)(A,{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 bi={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 xi(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:{...bi.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 nt(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),[f,p]=(0,Z.useState)(bi),[m,h]=(0,Z.useState)(!1),[_,v]=(0,Z.useState)(!1),[y,x]=(0,Z.useState)(!1),S=(0,Z.useRef)(0),[C,w]=(0,Z.useState)([]),[T,E]=(0,Z.useState)([`INBOX`]),[D,O]=(0,Z.useState)(!0),[k,A]=(0,Z.useState)(!1),[j,M]=(0,Z.useState)(!1),[N,P]=(0,Z.useState)(null),{showToast:F,showProgress:te}=q(),ne=(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]),R=(0,Z.useCallback)(()=>nt(e,`imap`).then(e=>r(e)).catch(e=>{F({title:`Error`,description:e instanceof Error?e.message:`Failed to load IMAP accounts`,variant:`destructive`})}).finally(()=>O(!1)),[e,F]),ie=(0,Z.useCallback)(async()=>{O(!0),await R()},[R]),[ae,oe]=(0,Z.useState)(e);e!==ae&&(oe(e),O(!0),l(null),d(!1),p(bi),w([]),E([`INBOX`]),x(!1),h(!1),v(!1)),(0,Z.useEffect)(()=>{R()},[R]),(0,Z.useEffect)(()=>()=>{S.current++},[e]);let ce=(0,Z.useCallback)(async(t,n)=>{let r=++S.current;A(!0);try{let i=t?await Le(e,`imap`,t.address):await Te(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===S.current&&w(i.filter(e=>e.selectable))}catch(e){r===S.current&&F({title:`IMAP Error`,description:e instanceof Error?e.message:`Failed to discover folders`,variant:`destructive`})}finally{r===S.current&&A(!1)}},[e,F]),le=()=>{S.current++,A(!1),d(!1),l(null),p(bi),w([]),E([`INBOX`]),h(!1),v(!1),x(!1)},ue=e=>{if(!j){if(c===e.address){le();return}S.current++,A(!1),d(!1),l(e.address),p(xi(e)),w([]),E((e.containers||[]).map(e=>e.name)),h(!1),v(!1),x(!1)}},de=()=>{le(),d(!0)},B=(e,t)=>{x(!0),p(n=>({...n,[e]:t}))},pe=e=>{x(!0),E(t=>t.includes(e)?t.filter(t=>t!==e):[...t,e])},me=(0,Z.useMemo)(()=>{let e=new Map(C.map(e=>[e.path,e]));for(let t of T)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))},[C,T]),he=async()=>{M(!0);try{let t=T.length?T:[`INBOX`],n=Si(f,!!L,m,_);if(L){await Xe(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 Ze(e,`imap`,L.address,i);for(let t of a)await Pe(e,`imap`,L.address,t)}else{await Je(e,`imap`,{...n,password:f.password,folder:t[0]});let r=f.user.trim();t.length>1&&await Ze(e,`imap`,r,t.slice(1))}await ie(),d(!1),l(f.user.trim()),p({...f,password:``,smtp:{...f.smtp,password:``}}),h(!1),v(!1),x(!1),F({title:`Saved`,description:`IMAP account ${f.user.trim()} saved; sync runs in the background`})}catch(e){F({title:`Error`,description:e instanceof Error?e.message:`Failed to save IMAP account`,variant:`destructive`})}finally{M(!1)}},ge=async()=>{if(!(!L||!window.confirm(`Delete IMAP account ${L.address} and all its folders?`))){P(`delete:${L.address}`);try{await Ee(e,`imap`,L.address),await ie(),le(),F({title:`Deleted`,description:`IMAP account ${L.address} deleted`})}catch(e){F({title:`Error`,description:e instanceof Error?e.message:`Failed to delete account`,variant:`destructive`})}finally{P(null)}}},_e=async(t,n)=>{P(`${t}:${n}`);try{t===`test`?await Ge(e,`imap`,n):await zt(te,e,`imap`,n,e=>{r(t=>t.map(t=>t.address===n?e:t))}),await ie(),t===`test`&&F({title:`Success`,description:`${n} saved account test completed`})}catch(e){F({title:`Error`,description:e instanceof Error?e.message:`Failed to ${t}`,variant:`destructive`})}finally{P(null)}},ve=async(t,n)=>{P(`folder-sync:${t}:${n}`);try{await Re(e,`imap`,t,n),await ie(),F({title:`Sync started`,description:`${t} · ${n} · rescanning the saved lookback window`})}catch(e){F({title:`Error`,description:e instanceof Error?e.message:`Failed to sync folder`,variant:`destructive`})}finally{P(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:f.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:f.user,readOnly:!!L,onChange:e=>B(`user`,e.target.value),placeholder:`me@example.com`})]}),(0,Q.jsx)(`div`,{className:`grid gap-2`,children:!L||m?(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:f.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:()=>h(!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(f.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,f),disabled:k||!!L&&y||!L&&(!f.host.trim()||!f.user.trim()||!f.password),children:[k?(0,Q.jsx)(fe,{className:`h-3 w-3 animate-spin`}):(0,Q.jsx)(re,{className:`h-3 w-3`}),` Discover folders`]})]}),(0,Q.jsx)(`div`,{className:`max-h-56 overflow-y-auto rounded-md border p-2`,children:me.length===0?(0,Q.jsx)(`p`,{className:`text-xs text-muted-foreground`,children:k?`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:me.map(e=>{let t=e.path.split(e.delimiter||`/`).length-1,n=C.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:T.includes(e.path),onChange:()=>pe(e.path)}),(0,Q.jsx)(`span`,{className:`font-mono truncate`,children:e.path}),!n&&C.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:[T.length,` folder`,T.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||!f.smtp.enabled||j,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)(xt,{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:f.smtp.enabled,onChange:e=>B(`smtp`,{...f.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:f.smtp[e],autoComplete:e===`user`?`section-smtp username`:`off`,onChange:t=>B(`smtp`,{...f.smtp,[e]:t.target.value})})]},e)),f.smtp.enabled&&(!L||_)&&(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:f.smtp.password,onChange:e=>B(`smtp`,{...f.smtp,password:e.target.value})})]}),L&&f.smtp.enabled&&!_&&(0,Q.jsx)(U,{type:`button`,size:`sm`,variant:`outline`,onClick:()=>v(!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:f.smtp.port,onChange:e=>B(`smtp`,{...f.smtp,port:Number(e.target.value)})})]})]}),(0,Q.jsxs)(`label`,{className:`flex gap-2 text-xs`,children:[(0,Q.jsx)(`input`,{type:`checkbox`,checked:f.smtp.secure,onChange:e=>B(`smtp`,{...f.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:f.smtp.appendSent,onChange:e=>B(`smtp`,{...f.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:f.smtp.allowAgentSend,onChange:e=>B(`smtp`,{...f.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(f.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(f.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:f.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:f.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)(se,{className:`h-3 w-3`}),` `,u?`Cancel`:`New Account`]}),(0,Q.jsxs)(U,{size:`sm`,variant:`ghost`,onClick:ie,disabled:D,children:[D?(0,Q.jsx)(fe,{className:`h-3 w-3 animate-spin`}):(0,Q.jsx)(z,{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:ne.length===0?(0,Q.jsx)(`p`,{className:`text-xs text-muted-foreground`,children:`No IMAP accounts configured.`}):ne.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)(b,{className:`h-3 w-3 shrink-0`}):(0,Q.jsx)(ee,{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:!!N||y||j,children:N===`folder-sync:${t.address}:${n.name}`?(0,Q.jsx)(fe,{className:`h-3 w-3 animate-spin`}):(0,Q.jsx)(z,{className:`h-3 w-3`})}),(0,Q.jsx)(`button`,{title:`Remove folder`,className:`p-1 hover:bg-destructive/10 text-destructive rounded`,onClick:()=>Pe(e,`imap`,t.address,n.name).then(ie).catch(e=>F({title:`Error`,description:e instanceof Error?e.message:`Failed to remove folder`,variant:`destructive`})),disabled:!!N,children:(0,Q.jsx)(I,{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)(jn,{value:n.removalPolicy,backends:i,source:`imap:${n.mailboxId}`,destination:!1,onSave:async r=>{await Pn(e,t.address,n.name,r),await ie()}})]},`removal:${n.name}`)),V,(0,Q.jsx)(`p`,{className:`text-xs text-muted-foreground`,children:y?`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:j||!y,children:[j?(0,Q.jsx)(fe,{className:`h-3 w-3 animate-spin`}):(0,Q.jsx)(g,{className:`h-3 w-3`}),` Save`]}),(0,Q.jsxs)(U,{size:`sm`,variant:`outline`,disabled:!!N||y||j,onClick:()=>_e(`test`,t.address),children:[N===`test:${t.address}`?(0,Q.jsx)(fe,{className:`h-3 w-3 animate-spin`}):(0,Q.jsx)(se,{className:`h-3 w-3`}),` Test`]}),(0,Q.jsxs)(U,{size:`sm`,variant:`outline`,disabled:!!N||y||j,onClick:()=>_e(`sync`,t.address),children:[N===`sync:${t.address}`?(0,Q.jsx)(fe,{className:`h-3 w-3 animate-spin`}):(0,Q.jsx)(z,{className:`h-3 w-3`}),` Resync all`]}),(0,Q.jsxs)(U,{size:`sm`,variant:`destructive`,disabled:!!N,onClick:ge,children:[N===`delete:${t.address}`?(0,Q.jsx)(fe,{className:`h-3 w-3 animate-spin`}):(0,Q.jsx)(I,{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:j,children:[j?(0,Q.jsx)(fe,{className:`h-3 w-3 animate-spin`}):(0,Q.jsx)(g,{className:`h-3 w-3`}),` Save`]}),(0,Q.jsx)(U,{size:`sm`,variant:`ghost`,onClick:le,children:`Cancel`})]})]})]})}function wi({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,y]=(0,Z.useState)(new Set),[b,x]=(0,Z.useState)(``),[S,C]=(0,Z.useState)(!1),{showToast:w}=q(),[E,D]=(0,Z.useState)(e);e!==E&&(D(e),i(!0));let O=(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)(()=>{O()},[O]);let k=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&&x(r.token),c(``),u([`read`]),f(``),m(!0),g(!1),i(!0),await O(),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)}},j=async t=>{try{await K.delete(`/workspaces/${e}/tokens/${t}`),w({title:`Success`,description:`Token revoked successfully`}),i(!0),await O()}catch{w({title:`Error`,description:`Failed to revoke token`,variant:`destructive`})}},M=e=>{let t=new Set(v);t.has(e)?t.delete(e):t.add(e),y(t)},N=e=>v.has(e)?e:`${e.substring(0,8)}...${e.substring(e.length-8)}`,P=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)(A,{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:k,disabled:a,children:`Create Token`}),(0,Q.jsx)(U,{variant:`outline`,onClick:()=>g(!1),children:`Cancel`})]})]}),b&&(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(b),C(!0),w({title:`Success`,description:`Token copied to clipboard`}),setTimeout(()=>C(!1),2e3)}catch{w({title:`Error`,description:`Failed to copy token to clipboard`,variant:`destructive`})}},children:S?(0,Q.jsxs)(Q.Fragment,{children:[(0,Q.jsx)(T,{className:`mr-2 h-4 w-4`}),`Copied`]}):(0,Q.jsxs)(Q.Fragment,{children:[(0,Q.jsx)(_,{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:b}),(0,Q.jsx)(U,{variant:`outline`,size:`sm`,className:`mt-2`,onClick:()=>x(``),children:`Dismiss`})]}),(0,Q.jsx)(Ti,{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:N(e.hash)}),(0,Q.jsx)(U,{size:`sm`,variant:`ghost`,onClick:()=>M(e.hash),children:v.has(e.hash)?(0,Q.jsx)(B,{className:`h-3 w-3`}):(0,Q.jsx)(pe,{className:`h-3 w-3`})}),(0,Q.jsx)(U,{size:`sm`,variant:`ghost`,onClick:()=>P(e.hash),children:(0,Q.jsx)(_,{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:()=>j(e.hash),className:`text-destructive hover:text-destructive`,children:(0,Q.jsx)(I,{className:`h-4 w-4`})})]},e.hash))})]})}function Ti({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 Ei=[{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`}],Di=e=>e.includes(`admin`)?`admin`:e.includes(`write`)?`write`:`read`;function Oi({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,h]=(0,Z.useState)(e);e!==m&&(h(e),a(!0));let g=(0,Z.useCallback)(()=>rt(e).then(r).catch(()=>r(null)).finally(()=>a(!1)),[e]);(0,Z.useEffect)(()=>{g()},[g]);let _=n?.isOwner===!0,v=n?.members??[],b=async n=>{n.preventDefault();let r=u.trim();if(r){s(`grant`);try{let n=await et(e,c===`user`?{email:r}:{group:r},[f]);d(``),await g();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)}}},S=async(n,r)=>{let i=`${n.type}:${n.principal}`;s(i);try{await X(e,n.type,n.principal,[r]),await g()}catch(e){t({title:`Could not update`,description:e instanceof Error?e.message:`Unknown error`,variant:`destructive`})}finally{s(null)}},C=async n=>{let r=`${n.type}:${n.principal}`;s(r);try{await Ie(e,n.type,n.principal),await g(),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:_?`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),g()},disabled:i,children:[(0,Q.jsx)(z,{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…`}):v.length===0?(0,Q.jsx)(`div`,{className:`rounded-md border border-dashed p-4 text-sm text-muted-foreground`,children:`Not shared with anyone.`}):v.map(e=>{let t=`${e.type}:${e.principal}`,n=Di(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)(x,{className:`mt-0.5 h-4 w-4 shrink-0 text-muted-foreground`}):(0,Q.jsx)(F,{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)(M,{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:[_?(0,Q.jsx)(`select`,{value:n,disabled:o===t,onChange:t=>S(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:Ei.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:Ei.find(e=>e.value===n)?.label}),_&&(0,Q.jsx)(U,{type:`button`,variant:`outline`,size:`sm`,className:`text-destructive hover:text-destructive`,disabled:o===t,onClick:()=>C(e),title:`Revoke access`,children:(0,Q.jsx)(y,{className:`h-3.5 w-3.5`})})]})]},t)})]}),_&&(0,Q.jsxs)(`form`,{onSubmit:b,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:Ei.find(e=>e.value===f)?.hint,children:Ei.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 ki({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,g]=(0,Z.useState)(null),[_,v]=(0,Z.useState)(!1),y=(0,Z.useCallback)(async()=>{try{o(await it(e))}catch{o([])}finally{c(!1)}},[e]);(0,Z.useEffect)(()=>{y()},[y]);let b=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 ke(e,{stop:!0});f(t.stoppedWorkspace),i({title:`Workspace exported`,description:`${t.name} — ${Y(t.size)}`}),await y(),t.stoppedWorkspace&&r?.()}catch(e){i({title:`Export failed`,description:e instanceof Error?e.message:`Failed to export workspace`,variant:`destructive`})}finally{u(!1)}}},x=async()=>{m(!0);try{await Qe(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)}},S=async e=>{g(e.name);try{await Ae(e.name)}catch(e){i({title:`Download failed`,description:e instanceof Error?e.message:`Failed to download archive`,variant:`destructive`})}finally{g(null)}},C=async e=>{if(window.confirm(`Delete ${e.name}? The archive is removed from the server; any copy you already downloaded is untouched.`)){g(e.name);try{await Ne(e.name),await y()}catch(e){i({title:`Error`,description:e instanceof Error?e.message:`Failed to delete archive`,variant:`destructive`})}finally{g(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)(E,{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:b,children:[(0,Q.jsx)(E,{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 y(),children:[(0,Q.jsx)(z,{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:x,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:[Y(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:()=>S(e),title:`Download (${Y(e.size)})`,children:(0,Q.jsx)(H,{className:`h-3.5 w-3.5`})}),(0,Q.jsx)(U,{type:`button`,variant:`outline`,size:`sm`,disabled:h===e.name,onClick:()=>C(e),className:`text-destructive hover:text-destructive`,title:`Delete archive`,children:(0,Q.jsx)(I,{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:()=>v(e=>!e),children:_?`Close import`:`Import workspace…`}),_&&(0,Q.jsx)(zn,{disabled:l,onImported:()=>r?.()})]})]})}var Ai={"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.`}},ji=[{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 Mi({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 Ni({workspaceId:e,backend:t,onAdded:n}){let r=t?.config||{},[i,a]=(0,Z.useState)(!1),[o,s]=(0,Z.useState)(String(r.label||t?.address||``)),[c,l]=(0,Z.useState)(String(r.root||``)),[u,d]=(0,Z.useState)(r.watch===!0),[f,p]=(0,Z.useState)(r.readOnly===!0),[m,h]=(0,Z.useState)(!1),{showToast:g}=q(),_=()=>{s(String(r.label||t?.address||``)),l(String(r.root||``)),d(r.watch===!0),p(r.readOnly===!0)};return i?(0,Q.jsxs)(`form`,{onSubmit:async r=>{if(r.preventDefault(),!(!o.trim()||!c.trim())){h(!0);try{t?await Xe(e,`file`,t.address,{label:o.trim(),root:c.trim(),watch:u,readOnly:f}):await Je(e,`file`,{name:o.trim(),path:c.trim(),watch:u,readOnly:f}),g({title:t?`Saved`:`Added`,description:t?`Folder settings updated`:`${o.trim()} mounted; initial scan running in the background`}),_(),a(!1),await n()}catch(e){g({title:`Error`,description:e instanceof Error?e.message:`Failed to add folder backend`,variant:`destructive`})}finally{h(!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)(re,{className:`h-4 w-4 text-muted-foreground`}),(0,Q.jsx)(`h2`,{className:`text-sm font-semibold`,children:t?`Edit folder`:`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:o,onChange:e=>s(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:c,onChange:e=>l(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)(Mi,{checked:u,onClick:()=>d(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)(Mi,{checked:f,onClick:()=>p(e=>!e)})]})]}),(0,Q.jsxs)(`div`,{className:`flex gap-2`,children:[(0,Q.jsx)(U,{type:`submit`,size:`sm`,disabled:m||!o.trim()||!c.trim(),children:m?`Saving…`:t?`Save folder`:`Add folder`}),(0,Q.jsx)(U,{type:`button`,variant:`ghost`,size:`sm`,disabled:m,onClick:()=>{_(),a(!1)},children:`Cancel`})]})]}):(0,Q.jsxs)(U,{type:`button`,variant:`outline`,size:`sm`,onClick:()=>{_(),a(!0)},children:[(0,Q.jsx)(re,{className:`mr-2 h-3.5 w-3.5`}),t?`Edit folder`:`Add local folder`]})}function Pi({workspaceId:e,backend:t,onAdded:n}){let r=t?.config||{},i=()=>({name:String(r.label||t?.address||``),clientId:String(r.clientId||``),clientSecret:``,refreshToken:``,folderId:String(r.folderId||``)}),[a,o]=(0,Z.useState)(!1),[s,l]=(0,Z.useState)(i),[u,d]=(0,Z.useState)(r.watch===!0),[f,p]=(0,Z.useState)(r.readOnly===!0),[m,h]=(0,Z.useState)(!1),{showToast:g}=q(),_=e=>t=>l(n=>({...n,[e]:t.target.value})),v=()=>{l(i()),d(r.watch===!0),p(r.readOnly===!0)},y=s.name.trim()&&s.clientId.trim()&&(t||s.clientSecret.trim()&&s.refreshToken.trim());return a?(0,Q.jsxs)(`form`,{onSubmit:async r=>{if(r.preventDefault(),y){h(!0);try{let r={label:s.name.trim(),clientId:s.clientId.trim(),...s.clientSecret.trim()?{clientSecret:s.clientSecret.trim()}:{},...s.refreshToken.trim()?{refreshToken:s.refreshToken.trim()}:{},folderId:s.folderId.trim()||`root`,watch:u,readOnly:f};t?await Xe(e,`gdrive`,t.address,r):await Je(e,`gdrive`,r),g({title:t?`Saved`:`Added`,description:t?`Google Drive connection updated`:`${s.name.trim()} connected; initial scan running in the background`}),v(),o(!1),await n()}catch(e){g({title:`Error`,description:e instanceof Error?e.message:`Failed to add Google Drive backend`,variant:`destructive`})}finally{h(!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:t?`Edit Google Drive`:`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:s.name,onChange:_(`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:s.folderId,onChange:_(`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:s.clientId,onChange:_(`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:s.clientSecret,onChange:_(`clientSecret`),required:!t,placeholder:t?`Leave blank to keep stored value`:``,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:s.refreshToken,onChange:_(`refreshToken`),required:!t,className:`font-mono`,autoComplete:`off`,placeholder:t?`Leave blank to keep stored value`:`Sign in below, or paste an offline-access refresh token`}),(0,Q.jsx)(gi,{clientId:s.clientId,clientSecret:s.clientSecret,backend:t&&s.clientId.trim()===r.clientId?{workspaceId:e,driver:`gdrive`,address:t.address}:void 0,scope:f?`drive.readonly`:`drive`,disabled:m,onLinked:({refreshToken:e,account:t})=>l(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)(Mi,{checked:u,onClick:()=>d(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)(Mi,{checked:f,onClick:()=>p(e=>!e)})]})]}),(0,Q.jsxs)(`div`,{className:`flex gap-2`,children:[(0,Q.jsx)(U,{type:`submit`,size:`sm`,disabled:m||!y,children:m?`Checking…`:t?`Save Drive`:`Add Drive`}),(0,Q.jsx)(U,{type:`button`,variant:`ghost`,size:`sm`,disabled:m,onClick:()=>{v(),o(!1)},children:`Cancel`})]})]}):(0,Q.jsxs)(U,{type:`button`,variant:`outline`,size:`sm`,onClick:()=>{v(),o(!0)},children:[(0,Q.jsx)(c,{icon:`mdi:google-drive`,width:14,height:14,className:`mr-2`}),t?`Edit Google Drive`:`Add Google Drive`]})}function Fi({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 J(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)(p,{className:`h-3 w-3 ${i?`animate-pulse`:``}`}),i?`Calculating…`:n?Y(n.bytes):`Size`]})}function Ii({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 Ve(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)(I,{className:`mr-2 h-3.5 w-3.5 ${t?`animate-pulse`:``}`}),t?`Clearing…`:`Clear thumbnails`]})}function Li({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)(p,{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:Y(t.bytes)}),(0,Q.jsxs)(U,{type:`button`,variant:`outline`,size:`sm`,disabled:r,onClick:async()=>{i(!0);try{n(await we(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)(z,{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:Y(t)})]},e))})]})}function Ri({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(`
|
|
9
|
+
`)),[d,f]=(0,Z.useState)(!1),p=i.join(`
|
|
10
|
+
`),[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/**
|
|
11
|
+
**/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(`
|
|
12
|
+
`).map(e=>e.trim()).filter(Boolean).join(`
|
|
13
|
+
`)===i.join(`
|
|
14
|
+
`),onClick:()=>n(l.split(`
|
|
15
|
+
`).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(`
|
|
16
|
+
`)})]})]})}function zi({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)(L,{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)(T,{className:`h-3.5 w-3.5`})}),(0,Q.jsx)(U,{type:`button`,variant:`ghost`,size:`sm`,onClick:p,children:(0,Q.jsx)(D,{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)(m,{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)(y,{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)(_e,{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 Bi({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)(z,{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)(z,{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)(zi,{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 Vi({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)(Fn);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 Ye(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=>{Nn(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 Hi({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)(z,{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:`Index maintenance and counts from this workspace's database.`}),(0,Q.jsxs)(U,{type:`button`,variant:`outline`,size:`sm`,onClick:n,disabled:t,children:[(0,Q.jsx)(z,{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:`Document database`})]}),(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)(N,{className:`h-4 w-4 text-muted-foreground`}),(0,Q.jsx)(`h2`,{className:`text-sm font-semibold`,children:`Word search index`})]}),`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)(N,{className:`h-4 w-4 text-muted-foreground`}),(0,Q.jsx)(`h2`,{className:`text-sm font-semibold`,children:`Meaning 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:`Goes to ${vn(e).toLowerCase()}`,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:`${vn(e)} (${t.dim??`?`}-d)`,value:t.error?(0,Q.jsx)(`span`,{className:`text-destructive`,children:t.error}):`${(t.embeddedDocs??0).toLocaleString()} documents indexed · ${(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)(Vi,{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 Ui(){let{workspaceName:e}=l();return(0,Q.jsx)(Wi,{},e)}function Wi(){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`),h=n.get(`addRuleStoreTo`),g=n.get(`addRuleStoreFolder`),v=(0,Z.useMemo)(()=>f===`store`||f===`download`?!p&&!h&&!g?null:{kind:f,path:p||``,storeTo:h||``,storeFolder:g||``}:p?{kind:`mirror`,path:p,target:m||`dir:/`}:null,[f,p,m,h,g]),b=(0,Z.useCallback)(()=>{r(e=>{for(let t of[`addRuleKind`,`addRulePath`,`addRuleTarget`,`addRuleStoreTo`,`addRuleStoreFolder`])e.delete(t);return e},{replace:!0})},[r]),x=u(),{showToast:S,showProgress:C}=q(),w=pt(),{goBack:T}=en(),E=dn(t);(0,Z.useEffect)(()=>{t!==E&&x(`/workspaces/${e}/settings/${E}`,{replace:!0})},[t,E,e,x]);let[D,O]=(0,Z.useState)(null),k=$t(D),[A,j]=(0,Z.useState)(0),[M,P]=(0,Z.useState)(!0),[F,ee]=(0,Z.useState)(!1),[te,ne]=(0,Z.useState)(!1),[L,R]=(0,Z.useState)(``),[re,ae]=(0,Z.useState)(``),[oe,se]=(0,Z.useState)(`#FFFFFF`),[ce,le]=(0,Z.useState)(null),[de,B]=(0,Z.useState)(null),[fe,pe]=(0,Z.useState)([]),[me,ge]=(0,Z.useState)(null),[_e,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,we]=(0,Z.useState)([]),[Te,ke]=(0,Z.useState)(!1),[Ae,Ne]=(0,Z.useState)(null),J=e||D?.id||``,Y=(0,Z.useRef)(J);(0,Z.useEffect)(()=>{Y.current=J});let Pe=(0,Z.useCallback)(async e=>{let t=e??Y.current;if(t)try{let e=await De(t);pe(e.publicCanvasShares)}catch(e){S({title:`Error`,description:e instanceof Error?e.message:`Failed to load shares`,variant:`destructive`})}},[S]),Ie=(0,Z.useCallback)(async e=>{let t=e??Y.current;if(t){xe(!0);try{let e=await je(t);G(e)}catch(e){S({title:`Error`,description:e instanceof Error?e.message:`Failed to load DB stats`,variant:`destructive`})}finally{xe(!1)}}},[S]),Le=(0,Z.useCallback)(async e=>{let t=e??Y.current;if(t){ke(!0);try{let[e,n]=await Promise.all([Dn(),kn(t)]);Se(e),we(n)}catch(e){S({title:`Error`,description:e instanceof Error?e.message:`Failed to load devices`,variant:`destructive`})}finally{ke(!1)}}},[S]),Re=async e=>{Ne(e);try{let t=K.find(t=>t.deviceId===e);if(!t)throw Error(`Device not found in your account`);await An(J,t),await Le(),S({title:`Linked`,description:`Device linked to workspace`})}catch(e){S({title:`Error`,description:e instanceof Error?e.message:`Failed to link device`,variant:`destructive`})}finally{Ne(null)}},Be=async e=>{Ne(e);try{await En(J,e),we(t=>t.filter(t=>t.deviceId!==e)),S({title:`Unlinked`,description:`Device removed from workspace`})}catch(e){S({title:`Error`,description:e instanceof Error?e.message:`Failed to unlink device`,variant:`destructive`})}finally{Ne(null)}},Ve=async(e,t)=>{Ne(`edit:${e}`);try{let n=await On(e,t);Se(t=>t.map(t=>t.deviceId===e?{...t,...n}:t)),S({title:`Saved`,description:`Device updated`})}catch(e){S({title:`Error`,description:e instanceof Error?e.message:`Failed to update device`,variant:`destructive`})}finally{Ne(null)}},X=(0,Z.useCallback)(async e=>{let t=e??Y.current;if(!t)return;let[n,r]=await Promise.all([He(t).catch(()=>null),nt(t).catch(()=>[])]);ge(n),ve(r.filter(e=>e.kind===`storage`))},[]),Je=_e.some(e=>e.resyncing);(0,Z.useEffect)(()=>{if(!k||!Je||!J)return;let e=setInterval(()=>{X()},4e3);return()=>clearInterval(e)},[k,Je,J,X]),(0,Z.useEffect)(()=>{let t=!1;async function n(){try{let n=await Ue();if(t)return;let r=n.find(t=>t.id===e)??n.find(t=>t.name===e);if(O(r??null),r){if(!$t(r))return;R(r.label||``),ae(r.description||``),se(r.color||`#FFFFFF`),le(r.icon??null),r.status===`active`&&await Promise.all([Pe(e||r.id),X(e||r.id)])}}catch{t||S({title:`Error`,description:`Failed to load workspace`,variant:`destructive`})}finally{t||P(!1)}}return n(),()=>{t=!0}},[e,Pe,X,S,A]),(0,Z.useEffect)(()=>{let e=()=>j(e=>e+1);return window.addEventListener(`workspaces:refresh`,e),()=>window.removeEventListener(`workspaces:refresh`,e)},[]);let Ye=(0,Z.useRef)(!1),Ze=(0,Z.useRef)(!1);(0,Z.useEffect)(()=>{Ye.current=!!ye||be,Ze.current=Te||K.length>0}),(0,Z.useEffect)(()=>{k&&(D?.status===`active`&&E===`db`&&J&&!Ye.current&&Ie(),D?.status===`active`&&E===`devices`&&J&&!Ze.current&&Le())},[k,E,J,D?.status,Ie,Le]);let Qe=async e=>{if(e.preventDefault(),D){ee(!0);try{await Ke(J,{label:L.trim(),description:re.trim(),color:oe,icon:ce}),window.dispatchEvent(new CustomEvent(`workspaces:refresh`)),S({title:`Saved`,description:`Workspace settings updated`})}catch(e){S({title:`Error`,description:e instanceof Error?e.message:`Save failed`,variant:`destructive`})}finally{ee(!1)}}},$e=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){ne(!0);try{await Fe(J),window.dispatchEvent(new CustomEvent(`workspaces:refresh`)),S({title:`Destroyed`,description:`${D.label||D.name} has been removed`}),x(`/workspaces`)}catch(e){S({title:`Error`,description:e instanceof Error?e.message:`Destroy failed`,variant:`destructive`})}finally{ne(!1)}}},et=()=>{for(let t of[e,D?.name,D?.id])t&&qe(t,`backends`);window.dispatchEvent(new CustomEvent(`workspace:tree:refresh`,{detail:{workspaceName:J}}))},tt=async e=>{if(e.address!==`workspace:data`&&e.config?.supported!==!1){H(`backend:${e.address}`);try{await Xe(J,e.driver,e.address,{enabled:!e.enabled}),await X(),et(),S({title:`Saved`,description:`${e.address} ${e.enabled?`disabled`:`enabled`}`})}catch(e){S({title:`Error`,description:e instanceof Error?e.message:`Failed to update backend`,variant:`destructive`})}finally{H(null)}}},rt=async(e,t,n)=>{H(`${n}:${e.address}`);try{await Xe(J,e.driver,e.address,t),await X()}catch(e){S({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 Ee(J,e.driver,e.address),await X(),et(),S({title:`Removed`,description:`${t} unmounted`})}catch(e){S({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 zt(C,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 Ge(J,e.driver,e.address),S({title:`Connected`,description:`${e.address}: credentials and root folder verified`})}catch(e){S({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 ze(J,e.driver,e.address),await X(),S({title:`Sync stopped`,description:`${e.address}: indexed files are kept; Re-sync resumes where it left off`})}catch(e){S({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 Oe(J,e),await X(),S({title:t?`Disabled`:`Enabled`,description:`${e} ${t?`disabled`:`enabled`}`})}catch(e){S({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),S({title:`Copied`,description:t})},ut=async e=>{if(D&&window.confirm(`Revoke public share for "${e.path}"?`)){H(`share:${e.code}`);try{await Me(J,e.code),await Pe(J),window.dispatchEvent(new CustomEvent(`workspace:tree:refresh`,{detail:{workspaceName:J}})),S({title:`Revoked`,description:`Public canvas link no longer works`})}catch(e){S({title:`Error`,description:e instanceof Error?e.message:`Failed to revoke share`,variant:`destructive`})}finally{H(null)}}},dt=fn.find(e=>e.id===E);return M?(0,Q.jsx)(`div`,{className:`p-6 text-sm text-muted-foreground`,children:`Loading...`}):D?k?(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)(pn,{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:w?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)(Jn,{workspaceId:J,onRestored:()=>j(e=>e+1)}),E===`general`&&(0,Q.jsxs)(`div`,{className:`space-y-6`,children:[(0,Q.jsxs)(`form`,{onSubmit:Qe,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:L,onChange:e=>R(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:re,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=>B({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:ce||`ph:stack-fill`,width:22,height:22,color:gt(oe)})}),(0,Q.jsx)(W,{id:`ws-color`,type:`color`,value:oe,onChange:e=>se(e.target.value),className:`h-10 w-16 p-1`}),(0,Q.jsx)(U,{type:`button`,variant:`outline`,size:`sm`,onClick:()=>se(_t()),children:`Randomize`})]})]}),(0,Q.jsx)(U,{type:`submit`,disabled:F||!L.trim(),children:F?`Saving...`:`Save Changes`})]}),de&&(0,Q.jsx)(nn,{x:de.x,y:de.y,current:{icon:ce??void 0,color:oe},onChange:e=>{`icon`in e&&le(e.icon??null),`color`in e&&e.color&&se(e.color)},onClose:()=>B(null)}),(0,Q.jsx)(gr,{currentMode:D.protection?.mode,workspaceId:J,isActive:D.status===`active`,onChanged:()=>j(e=>e+1)}),D.status===`active`&&(0,Q.jsx)(Gi,{workspaceName:e}),D.status===`active`&&(0,Q.jsx)(Li,{workspaceId:J}),(0,Q.jsx)(ki,{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:te,onClick:$e,children:[(0,Q.jsx)(I,{className:`mr-2 h-4 w-4`}),te?`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)(Oi,{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:()=>Pe(),children:[(0,Q.jsx)(z,{className:`mr-2 h-3.5 w-3.5`}),`Refresh`]})]}),fe.length===0?(0,Q.jsx)(`div`,{className:`rounded-md border p-4 text-sm text-muted-foreground`,children:`No public canvas shares. Good.`}):fe.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)(_,{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)(ue,{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)(y,{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)(wi,{workspaceId:J})]})]}),D.status===`active`&&E===`data`&&(0,Q.jsxs)(`div`,{className:`space-y-3`,children:[_e.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&&!Ai[n],s=Ai[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=ht(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}),(0,Q.jsxs)(`label`,{className:`ml-auto flex shrink-0 items-center gap-2 text-xs text-muted-foreground`,title:u?`Required workspace storage; cannot be disabled`:void 0,children:[`Enabled`,(0,Q.jsx)(Mi,{checked:u||!!e.enabled,disabled:!d||V===`backend:${n}`,onClick:()=>tt(e)})]}),!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)(z,{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)(Mi,{checked:t.scanOnStart===!0,disabled:V===`startup:${n}`,onClick:()=>rt(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)(Mi,{checked:!!t.watch,disabled:!e.enabled||V===`watch:${n}`,onClick:()=>rt(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)(Mi,{checked:t.readOnly===!0,disabled:V===`readonly:${n}`,onClick:()=>rt(e,{readOnly:t.readOnly!==!0},`readonly`)})]}),(e.driver===`file`||e.driver===`cacache`)&&l&&(0,Q.jsx)(Fi,{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)(N,{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)(he,{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)(z,{className:`mr-2 h-3.5 w-3.5 ${V===`resync:${n}`?`animate-spin`:``}`}),`Re-sync`]})),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)(I,{className:`h-3.5 w-3.5`})})]})]}),l&&(0,Q.jsx)(jn,{value:t.removalPolicy,backends:_e,source:n,destination:t.propagatedRemovalSupported===!0,onSave:async t=>{await Xe(J,e.driver,e.address,{removalPolicy:t}),await X()}}),i&&(0,Q.jsx)(Pi,{workspaceId:J,backend:e,onAdded:async()=>{await X(),et()}}),e.driver===`file`&&(0,Q.jsx)(Ni,{workspaceId:J,backend:e,onAdded:async()=>{await X(),et()}}),a&&(0,Q.jsx)(hr,{workspaceId:J,backend:e,onSaved:async()=>{await X(),et()}}),e.driver===`file`&&l&&!u&&(0,Q.jsx)(Ri,{backend:e,busy:V===`exclude:${n}`,onSave:t=>rt(e,{exclude:t},`exclude`)})]},n)}),(0,Q.jsx)(ir,{workspaceId:J,children:(0,Q.jsx)(Ii,{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)(Ni,{workspaceId:J,onAdded:async()=>{await X(),et()}}),(0,Q.jsx)(Pi,{workspaceId:J,onAdded:async()=>{await X(),et()}}),(0,Q.jsx)(hr,{workspaceId:J,onSaved:async()=>{await X(),et()}})]}),e&&(0,Q.jsx)(`section`,{className:`border-t pt-4`,children:(0,Q.jsx)(Mn,{workspaceName:e})})]}),D.status===`active`&&E===`db`&&(0,Q.jsxs)(`div`,{className:`space-y-8`,children:[(0,Q.jsx)(qi,{}),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:`Meaning search & image descriptions`}),(0,Q.jsx)(`p`,{className:`text-xs text-muted-foreground`,children:`Which AI models read this workspace's text and images, and where they run.`})]}),(0,Q.jsx)(Or,{workspaceId:J,workspaceName:e})]}),(0,Q.jsx)(Hi,{stats:ye,isLoading:be,onRefresh:()=>Ie(),workspaceName:e})]}),D.status===`active`&&E===`devices`&&(0,Q.jsx)(Bi,{allDevices:K,linkedDevices:Ce,isLoading:Te,deviceBusy:Ae,onRefresh:()=>Le(),onLink:Re,onUnlink:Be,onUpdate:Ve}),D.status===`active`&&E===`sync`&&(0,Q.jsx)(`section`,{className:`rounded-lg border p-4`,children:(0,Q.jsx)(fi,{workspaceId:J,workspaceUuid:D?.id})}),D.status===`active`&&E===`services`&&(0,Q.jsxs)(`div`,{className:`space-y-4`,children:[J&&(0,Q.jsx)(yi,{workspaceId:J}),(0,Q.jsx)(`div`,{className:`space-y-3`,children:ji.map(e=>{let t=me?.[e.id]||me?.[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)(ie,{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)(Mi,{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:!!(me?.imap?.enabled||me?.imapSync?.enabled)})})]}),D.status===`active`&&E===`policies`&&(0,Q.jsx)(Qr,{workspaceId:J}),D.status===`active`&&E===`hooks`&&(0,Q.jsx)(`section`,{className:`rounded-lg border p-4`,children:(0,Q.jsx)(qr,{workspaceId:J,prefillRule:v,onPrefillConsumed:b,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:()=>x(`/workspaces/${e}`),children:`Back to workspace`})]}):(0,Q.jsx)(`div`,{className:`p-6 text-sm text-muted-foreground`,children:`Workspace not found`})}function Gi({workspaceName:e}){let{selected:t,setSelected:n,tree:r,setTree:i,toggle:a}=In(),[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)(Rn,{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 Ln(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})]})]})}var Ki=[{title:`Word search`,body:`Every document's text is indexed as soon as it is added, so exact words and phrases are found. Always on; no AI involved.`,detail:`full-text index`},{title:`Meaning search`,body:`An AI model turns each document and image into a numeric fingerprint, so "beach at sunset" finds a matching photo even when those words appear nowhere. Text and images each have their own model.`,detail:`embeddings`},{title:`Image descriptions`,body:`Optionally, a vision model writes a few sentences about each image. Those sentences become searchable, by words and by meaning.`,detail:`captions`}];function qi(){return(0,Q.jsxs)(`section`,{className:`rounded-lg border bg-muted/30 p-4`,children:[(0,Q.jsx)(`h2`,{className:`text-sm font-semibold`,children:`How search works in this workspace`}),(0,Q.jsx)(`ol`,{className:`mt-3 grid gap-3 sm:grid-cols-3`,children:Ki.map((e,t)=>(0,Q.jsxs)(`li`,{className:`min-w-0`,children:[(0,Q.jsxs)(`p`,{className:`text-xs font-medium`,children:[(0,Q.jsx)(`span`,{className:`mr-1.5 inline-flex h-4 w-4 items-center justify-center rounded-full bg-primary/10 text-[10px] tabular-nums text-primary`,children:t+1}),e.title,(0,Q.jsxs)(`span`,{className:`ml-1.5 font-normal text-muted-foreground`,children:[`(`,e.detail,`)`]})]}),(0,Q.jsx)(`p`,{className:`mt-1 text-xs text-muted-foreground`,children:e.body})]},e.title))}),(0,Q.jsxs)(`p`,{className:`mt-3 text-xs text-muted-foreground`,children:[`Results from all three are combined. The models run in Canvas's inference service on this server by default, with nothing to set up. You can point them at a faster machine, such as a GPU box running Ollama or vLLM, under `,(0,Q.jsx)(`span`,{className:`font-medium`,children:`Where models run`}),` below.`]})]})}function Ji({workspaceName:e,vectorSpaces:t,onDone:n}){let{showProgress:r}=q(),[i,a]=(0,Z.useState)(null),[o,s]=(0,Z.useState)(null),c=(0,Z.useCallback)(t=>{a(t),mr(r,e,t).then(e=>{s(`${ur(e)}: ${fr(e)}`),n()}).catch(e=>s(e instanceof Error?e.message:`Reindex failed`)).finally(()=>a(null))},[r,e,n]);(0,Z.useEffect)(()=>{let t=!1;return sr(e).then(e=>{let n=e.jobs.find(ar);n&&!t&&c(n)}).catch(()=>{}),()=>{t=!0}},[e,c]);let l=async(t,n={},r)=>{if(!(r&&!window.confirm(r))){s(null);try{c(await lr(e,t,n))}catch(e){s(e instanceof Error?e.message:`Reindex failed`)}}},u=[{title:`Dates & file types`,ops:[{key:`timelines`,label:`Rebuild timelines`,description:`Recompute created and updated dates used by date filters and the timeline.`,action:`reindex:timelines`},{key:`mime`,label:`Rebuild file types`,description:`Recompute which documents are images, PDFs, videos and so on (used by type filters and image descriptions).`,action:`reindex:mime`}]},{title:`Word search (full-text, BM25)`,ops:[{key:`fts`,label:`Add missing`,description:`Index documents that word search does not know about yet. Fast; skips what is already indexed.`,action:`reindex:search`},{key:`fts-rebuild`,label:`Rebuild`,description:`Delete the word-search index and build it again. Use when search misses documents it should find, or after a schema change to what gets indexed.`,confirm:`Delete the word-search index and rebuild it from scratch?`,action:`reindex:search`,options:{rebuild:!0}},{key:`fts-optimize`,label:`Optimize`,description:`Tidy the index on disk: merge small pieces and drop old versions.`,action:`optimize`,options:{space:`fts`}}]},...t.length>0?[{title:`Meaning search (vector tables)`,ops:t.map(e=>({key:`${e}-optimize`,label:`Optimize ${vn(e).toLowerCase()}`,description:`Tidy the ${vn(e).toLowerCase()} index on disk and rebuild its fast lookup.`,action:`optimize`,options:{space:e}}))}]:[]],d=i?u.flatMap(e=>e.ops).find(e=>e.action===i.action&&(e.options?.rebuild??!1)===(i.options.rebuild??!1)&&(e.options?.space??null)===(i.options.space??null))?.key??i.id:null;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)(z,{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:`Repairs for when search or filters look wrong. All of these are safe to run again.`}),(0,Q.jsx)(`div`,{className:`space-y-4`,children:u.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:i!==null,onClick:()=>l(e.action,e.options,e.confirm),children:[d===e.key&&(0,Q.jsx)(z,{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))}),o&&(0,Q.jsx)(`p`,{className:`mt-3 text-xs text-muted-foreground`,children:o})]})}export{Ui 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{
|
|
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{pi as i}from"./ui-DEgHnkQu.js";import{t as a}from"./input-BOtCd4HP.js";import{jt as o}from"./workspace-4hWrSEku.js";import{p as s}from"./context-DY40EDc6.js";import{i as c,m as l,p as u}from"./use-menu-CKSpS96u.js";import{$ as d,bt as f,xt as p,yt as m}from"./index-CTvqbEB8.js";import{n as h}from"./page-header-NyYSXjfY.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:p,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:f,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:m,load:async()=>(await d()).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};
|