@camstack/addon-admin-ui 1.2.398 → 1.2.400
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/{AddonAssetIcon-DsJ0DXLM.js → AddonAssetIcon-B8b_Ij3Q.js} +1 -1
- package/dist/assets/{Addons-Brc2uHGt.js → Addons-Csw_pkUq.js} +1 -1
- package/dist/assets/{AdminPage-oBqVY0PY.js → AdminPage-LJSPjGaV.js} +1 -1
- package/dist/assets/{AdminSectionedPage-DMUZLoRM.js → AdminSectionedPage-Dr8lqQvA.js} +1 -1
- package/dist/assets/{AiProfiles-DBJzloun.js → AiProfiles-BUQCPfG2.js} +2 -2
- package/dist/assets/BrokerForm-CDY_h_x_.js +1 -0
- package/dist/assets/Build-DowgaoRs.js +40 -0
- package/dist/assets/{Calendars-B0XRw41_.js → Calendars-CjwnNTaU.js} +1 -1
- package/dist/assets/{Cluster-HI92k9yP.js → Cluster-B1dInAlz.js} +1 -1
- package/dist/assets/{ClusterStepModel-B55nIWGG.js → ClusterStepModel-DvaE2Fpn.js} +1 -1
- package/dist/assets/{CompositionEditor-HXmEBkgY.js → CompositionEditor-D8K5gqDy.js} +1 -1
- package/dist/assets/{CompositionEditorPage-CtJL1OC7.js → CompositionEditorPage-ILN_BH-0.js} +1 -1
- package/dist/assets/{Data-SxS7BieV.js → Data-hUPfS0O6.js} +1 -1
- package/dist/assets/{DeviceDetail-DGlIF3Gw.js → DeviceDetail-ClcUBiDj.js} +1 -1
- package/dist/assets/{DeviceDoActionFields-bOKvr1zw.js → DeviceDoActionFields-CvF7Gep-.js} +1 -1
- package/dist/assets/{Devices-D_Q3xRzv.js → Devices-CbF91f1O.js} +1 -1
- package/dist/assets/{EmbedPlayerPage-BuiWNrg0.js → EmbedPlayerPage-CZkHg-Z5.js} +1 -1
- package/dist/assets/Identity-Xw1YjqEJ.js +1 -0
- package/dist/assets/{IntegrationDetail-Ds8wLQKm.js → IntegrationDetail-CuUjr1ni.js} +1 -1
- package/dist/assets/IntegrationHub-4oemdKQs.js +1 -0
- package/dist/assets/Integrations-CsXxPeGb.js +1 -0
- package/dist/assets/{Integrations-yqDYXFpy.js → Integrations-DPziOQvV.js} +1 -1
- package/dist/assets/{Library-BoSMfFQN.js → Library-OaGdN5V3.js} +1 -1
- package/dist/assets/{Locations-D1Z_8vL-.js → Locations-CPEee4O7.js} +1 -1
- package/dist/assets/Logs-BertXlLF.js +1 -0
- package/dist/assets/{MotionZonesSettings-BQocc7WN.js → MotionZonesSettings-VFYF5C3z.js} +1 -1
- package/dist/assets/{MyAccess-C07EOSEt.js → MyAccess-HQF6tebp.js} +1 -1
- package/dist/assets/Network-CK42t6W_.js +1 -0
- package/dist/assets/{NodeAddonsSettingsPanel-C7Cwrd5N.js → NodeAddonsSettingsPanel-DRU_bmGt.js} +1 -1
- package/dist/assets/{Notifications-BG1NH2xT.js → Notifications-D5aHMhvU.js} +1 -1
- package/dist/assets/{PrivacyMaskSettings-CIOFdwm5.js → PrivacyMaskSettings-CcLxP0Q6.js} +1 -1
- package/dist/assets/{Recordings-DAatpC-O.js → Recordings-dPu5GCbo.js} +1 -1
- package/dist/assets/{ScanNetworkModal-DbBbJLdj.js → ScanNetworkModal-XJW0dYom.js} +1 -1
- package/dist/assets/{SceneMonitorEditor-B4Xa4X5o.js → SceneMonitorEditor-DB1NGsuu.js} +1 -1
- package/dist/assets/{SessionsList-vIu2aubr.js → SessionsList-ZafI0kEX.js} +1 -1
- package/dist/assets/{Showroom-D7o4A6Zv.js → Showroom-CMomGEBv.js} +1 -1
- package/dist/assets/{System-CjZYKFiH.js → System-SugJLaIm.js} +1 -1
- package/dist/assets/{Terminal-D8oUxmjN.js → Terminal-CMZkgAtq.js} +1 -1
- package/dist/assets/{Viewer-DvlAA2tM.js → Viewer-hCw4DvsX.js} +1 -1
- package/dist/assets/{_virtual_mf-localSharedImportMap___mfe_internal__admin_ui_host-CSL8Q62G.js → _virtual_mf-localSharedImportMap___mfe_internal__admin_ui_host-D4evVvwt.js} +1 -1
- package/dist/assets/{archive-C3YvN3bo.js → archive-D3wNUMUT.js} +1 -1
- package/dist/assets/{authed-media-9LcBXXjL.js → authed-media-B-33JcKf.js} +1 -1
- package/dist/assets/{boxes-I78qynMn.js → boxes-PrddLfOG.js} +1 -1
- package/dist/assets/circle-slash-BOrPYytm.js +1 -0
- package/dist/assets/client-1JBRoe76.js +357 -0
- package/dist/assets/crop-ChT009Qt.js +1 -0
- package/dist/assets/{device-integration-icon-C-z9nIbi.js → device-integration-icon-DoswOpEp.js} +1 -1
- package/dist/assets/device-picker-model-DD88fyzH.js +1 -0
- package/dist/assets/dist-CEvY94M_.js +357 -0
- package/dist/assets/{dist-BXnRXTui.js → dist-_pgvIUGn.js} +1 -1
- package/dist/assets/{format-size-D8E_VMta.js → format-size-CI1h5otl.js} +1 -1
- package/dist/assets/{history-GFLGqkV_.js → history-B0sqAabA.js} +1 -1
- package/dist/assets/{hostInit-CVDgcuw-.js → hostInit-D4-UgrFJ.js} +1 -1
- package/dist/assets/{index-D-W8ahNU.js → index-B-BE8EYQ.js} +3 -3
- package/dist/assets/{index-DtcBoptX.css → index-C6r9IlW4.css} +1 -1
- package/dist/assets/{layers-D5TeeKqH.js → layers-Bg1NrUAX.js} +1 -1
- package/dist/assets/{location-owned-usage-B5FzlDTE.js → location-owned-usage-DxjPP5ha.js} +1 -1
- package/dist/assets/log-in-DwkWn-LJ.js +1 -0
- package/dist/assets/{mf-entry-bootstrap-0-4e8bce30.js → mf-entry-bootstrap-0-50b2491c.js} +2 -2
- package/dist/assets/{monitor-smartphone-CEvv3FjX.js → monitor-smartphone-J97h7fMI.js} +1 -1
- package/dist/assets/mount-DD88fyzH.js +1 -0
- package/dist/assets/{notification-schedule-field-BCLPcRoY.js → notification-schedule-field-Bnhm4lQH.js} +1 -1
- package/dist/assets/{octagon-alert-bX5m_c3-.js → octagon-alert-D1Sw5N86.js} +1 -1
- package/dist/assets/{player-overlays-mP7nvLXZ.js → player-overlays-9CMoB0XN.js} +1 -1
- package/dist/assets/remoteEntry-nN8yf3LF.js +1 -0
- package/dist/assets/{send-3vh-Sv9u.js → send-D9__QB6r.js} +1 -1
- package/dist/assets/{settings-2-CIYonoB2.js → settings-2-DEwPBiUG.js} +1 -1
- package/dist/assets/{src-BHYGdVQH.js → src-Ca4gNAmP.js} +7 -7
- package/dist/assets/{use-scenes-feed-kjfl7MJf.js → use-scenes-feed-72EeOPdE.js} +1 -1
- package/dist/assets/{use-stationary-objects-KzlE_gx1.js → use-stationary-objects-BxmIxHKh.js} +1 -1
- package/dist/assets/useEventInvalidation-DD88fyzH.js +1 -0
- package/dist/assets/{useNodeUrlSelection-DXEYvc00.js → useNodeUrlSelection-yjHHXKaf.js} +1 -1
- package/dist/assets/user-round-BuAM59Jp.js +1 -0
- package/dist/assets/{virtual_mf-REMOTE_ENTRY_ID___mfe_internal__admin_ui_host__remoteEntry-_hash_-CPx0vdv_.js → virtual_mf-REMOTE_ENTRY_ID___mfe_internal__admin_ui_host__remoteEntry-_hash_-qbX0hoWb.js} +2 -2
- package/dist/index.html +28 -10
- package/dist/server/addon.js +9 -2
- package/package.json +1 -1
- package/dist/assets/AddonIntegrationBody-DEH_hiBL.js +0 -1
- package/dist/assets/BrokerForm-Bp2vNPeo.js +0 -1
- package/dist/assets/Build-SSL4ELc6.js +0 -40
- package/dist/assets/Identity-CPexVh1x.js +0 -1
- package/dist/assets/Integrations-CctFzaK6.js +0 -1
- package/dist/assets/Logs-Ch6MJzzi.js +0 -1
- package/dist/assets/Network-BhKzyC3v.js +0 -1
- package/dist/assets/circle-slash-C5FeKGvp.js +0 -1
- package/dist/assets/client-D5nV-lIF.js +0 -357
- package/dist/assets/crop-GRVPmcCZ.js +0 -1
- package/dist/assets/device-picker-model-DNeYaBzl.js +0 -1
- package/dist/assets/dist-CgEPs7ts.js +0 -357
- package/dist/assets/mount-DNeYaBzl.js +0 -1
- package/dist/assets/remoteEntry-B52b8k7t.js +0 -1
- package/dist/assets/useEventInvalidation-DNeYaBzl.js +0 -1
- package/dist/assets/user-round-D2N1DqIB.js +0 -1
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
import{I as e,K as t,M as n,Q as r,S as i,T as a,W as o,a as s,b as c,q as l,t as u,x as d,z as f}from"./_virtual_mf___mfe_internal__admin_ui_host__loadShare___mf_0_tanstack_mf_1_react_mf_2_query__loadShare__.js-DhP-Lgiq.js";import{_t as p,ct as m,gt as h,ot as g,yt as _}from"./player-overlays-mP7nvLXZ.js";import{$u as v,Bi as y,Dt as b,Ed as x,Gn as S,Hn as C,Ht as w,Ii as T,L as E,Ld as D,Li as O,Lt as k,Mi as A,Ni as j,Of as M,Ql as N,Ri as P,Tt as F,U as I,Ut as ee,Xl as L,Yl as R,Zl as z,dd as B,fd as V,gu as te,id as H,jf as ne,ji as re,ki as ie,ku as ae,md as U,nr as oe,ql as W,sf as se,un as ce,ur as G,va as le,vt as ue,z as de,zi as fe,zn as pe}from"./src-BHYGdVQH.js";import{t as K}from"./boxes-I78qynMn.js";import{Ar as me,Dr as he,Er as ge,Or as _e,Sr as ve,cr as ye,gr as be,jr as xe,kr as Se,lr as Ce,or as q,qt as we,sr as Te,vi as Ee,xr as De}from"./client-D5nV-lIF.js";import{t as Oe}from"./useTranslation-Bc5yH9V9.js";import{$ as ke,C as Ae,H as je,I as Me,S as Ne,Y as Pe,tt as Fe}from"./index-D-W8ahNU.js";import{t as Ie}from"./AdminSectionedPage-DMUZLoRM.js";import{t as Le}from"./DeviceDoActionFields-bOKvr1zw.js";import{E as Re,O as ze,t as Be}from"./CompositionEditor-HXmEBkgY.js";var Ve=_(`braces`,[[`path`,{d:`M8 3H7a2 2 0 0 0-2 2v5a2 2 0 0 1-2 2 2 2 0 0 1 2 2v5c0 1.1.9 2 2 2h1`,key:`ezmyqa`}],[`path`,{d:`M16 21h1a2 2 0 0 0 2-2v-5c0-1.1.9-2 2-2a2 2 0 0 1-2-2V5a2 2 0 0 0-2-2h-1`,key:`e1hn23`}]]),He=_(`code-xml`,[[`path`,{d:`m18 16 4-4-4-4`,key:`1inbqp`}],[`path`,{d:`m6 8-4 4 4 4`,key:`15zrgr`}],[`path`,{d:`m14.5 4-5 16`,key:`e7oirm`}]]),Ue=_(`ungroup`,[[`rect`,{width:`8`,height:`6`,x:`5`,y:`4`,rx:`1`,key:`nzclkv`}],[`rect`,{width:`8`,height:`6`,x:`11`,y:`14`,rx:`1`,key:`4tytwb`}]]),We=`const AUTOMATION_RECIPE =`,Ge=`// EJECTED from the visual automation editor — this file is now hand-owned.
|
|
2
|
-
// The recipe that produced it was removed with it, on purpose: while it was
|
|
3
|
-
// here the editor could re-open this block and overwrite everything below.`;function Ke(e){return e.includes(We)}function qe(e){let t=e.indexOf(We);if(t<0)return null;let n=e.indexOf(`{`,t+25);if(n<0)return null;let r=0,i=!1,a=!1;for(let o=n;o<e.length;o+=1){let s=e.charAt(o);if(i){a?a=!1:s===`\\`?a=!0:s===`"`&&(i=!1);continue}if(s===`"`){i=!0;continue}if(s===`{`)r+=1;else if(s===`}`&&(--r,r===0))return{declStart:t,literalEnd:o+1,json:e.slice(n,o+1)}}return null}function Je(e){let t=qe(e);if(t===null)return{ok:!1,reason:`this block carries no AUTOMATION_RECIPE literal`};let n;try{n=JSON.parse(t.json)}catch(e){return{ok:!1,reason:`the embedded recipe is not readable JSON: ${e instanceof Error?e.message:String(e)}`}}let r=_e.safeParse(n);if(!r.success){let e=r.error.issues[0];return{ok:!1,reason:`the embedded recipe does not match the recipe schema${e===void 0?``:` at ${e.path.join(`.`)}: ${e.message}`}`}}return{ok:!0,recipe:r.data}}function Ye(e){let t=qe(e);if(t===null)return e;let n=e.slice(t.literalEnd),r=/^\s*as\s+const/.exec(n),i=t.literalEnd+(r===null?0:r[0].length);return`${e.slice(0,t.declStart)}${Ge}${e.slice(i)}`}r();var Xe=6e4;function Ze(){let e=le({projection:`slim`});return o(()=>(e.data??[]).filter(e=>we(String(e.type))).map(e=>({id:e.id,name:e.name,stableId:e.stableId,type:String(e.type),location:e.location??null})).toSorted((e,t)=>e.name.localeCompare(t.name)),[e.data])}function Qe(e){let t=g();return s({queryKey:[`automation`,`wireable-fields`,e],enabled:e>0,staleTime:Xe,queryFn:async()=>(await t.trpcClient.deviceManager.getWireableFields.query({deviceId:e})).caps})}function $e(e,t){return e.find(e=>e.cap===t)?.fields??[]}function J({children:e}){return d(`span`,{className:`mb-1 block text-xs text-foreground-subtle`,children:e})}function et(e){let t=Qe(e.deviceId);if(t.data!==void 0)return $e(t.data,e.cap).find(t=>t.path===e.fieldPath)}function tt({value:e,devices:t,onChange:n}){let r=Qe(e.deviceId),a=r.data??[],o=$e(a,e.cap);return i(c,{children:[i(`label`,{className:`min-w-[10rem] flex-1`,children:[d(J,{children:`Device`}),d(W,{value:String(e.deviceId),onChange:e=>n({deviceId:Number(e.target.value),cap:``,fieldPath:``}),options:[{value:`0`,label:`Select a device…`},...t.map(e=>({value:String(e.id),label:e.name}))]})]}),i(`label`,{className:`min-w-[9rem] flex-1`,children:[d(J,{children:`Capability`}),d(W,{value:e.cap,disabled:e.deviceId===0,onChange:t=>n({...e,cap:t.target.value,fieldPath:``}),options:[{value:``,label:nt(e.deviceId,a.length,r.isLoading)},...a.map(e=>({value:e.cap,label:e.cap}))]})]}),i(`label`,{className:`min-w-[9rem] flex-1`,children:[d(J,{children:`Field`}),d(W,{value:e.fieldPath,disabled:e.cap===``,onChange:t=>n({...e,fieldPath:t.target.value}),options:[{value:``,label:o.length===0?`No readable fields`:`Select a field…`},...o.map(e=>({value:e.path,label:`${e.path} (${e.kind})`}))]})]})]})}function nt(e,t,n){return e===0?`Pick a device first`:n?`Reading this device…`:t===0?`This device reports no readable capabilities`:`Select a capability…`}var rt=[`eq`,`ne`,`gt`,`gte`,`lt`,`lte`,`contains`,`in`];function it(e){switch(e){case`boolean`:return[`eq`,`ne`];case`number`:return[`eq`,`ne`,`gt`,`gte`,`lt`,`lte`];case`string`:case`enum`:return[`eq`,`ne`,`contains`,`in`];default:return rt}}function at(e){switch(e){case`eq`:return`is`;case`ne`:return`is not`;case`gt`:return`is above`;case`gte`:return`is at least`;case`lt`:return`is below`;case`lte`:return`is at most`;case`contains`:return`contains`;default:return`is one of`}}function ot(e){return it(e).map(e=>({value:e,label:at(e)}))}function st(e){switch(e){case`device-state`:return`A device field changes`;case`device-event`:return`An event happens`;case`schedule`:return`On a schedule`;default:return`Only when run by hand`}}var ct={triggers:[],actions:[]};function lt(e){switch(e){case`device-state`:return{kind:e,deviceId:0,cap:``,fieldPath:``};case`device-event`:return{kind:e,category:``};case`schedule`:return{kind:e,cron:`0 30 23 * * *`};default:return{kind:`manual`}}}function ut(e){switch(e){case`wait`:return{kind:e,seconds:5};case`code`:return{kind:e,code:`// runs inside this automation, with ctx in scope
|
|
4
|
-
`};default:return{kind:`cap`,deviceId:0,cap:``,method:``}}}function dt(){return{kind:`condition`,deviceId:0,cap:``,fieldPath:``,operator:`eq`,value:``}}function ft(){return{kind:`expression`,expr:``,bindings:{}}}function pt(e,t,n){return e.map((e,r)=>r===t?n:e)}function mt(e,t){return e.filter((e,n)=>n!==t)}function ht(e,t,n){let r=e[t];if(r===void 0)return e;let i=Math.min(Math.max(n,0),e.length-1);if(i===t)return e;let a=mt(e,t);return[...a.slice(0,i),r,...a.slice(i)]}function gt(e,t){return{...e,triggers:[...t]}}function _t(e,t){return{...e,actions:[...t]}}var vt=[{value:`cap`,label:`Do something`},{value:`wait`,label:`Wait`},{value:`code`,label:`Run code`}];function yt({actions:e,devices:t,devicesState:n=`answered`,onChange:r}){return i(`div`,{className:`space-y-2`,children:[e.length===0&&d(`p`,{className:`text-xs text-warning`,children:`No actions yet. An automation that does nothing is not an automation.`}),e.map((a,o)=>i(`div`,{className:`rounded border border-border p-3 space-y-2`,children:[i(`div`,{className:`flex flex-wrap items-end gap-2`,children:[d(G,{variant:`default`,children:o+1}),i(`label`,{className:`w-40`,children:[d(J,{children:`Step`}),d(W,{value:a.kind,onChange:t=>r(pt(e,o,ut(bt(t.target.value)))),options:vt.map(e=>({value:e.value,label:e.label}))})]}),i(`div`,{className:`ml-auto flex items-center gap-1`,children:[d(S,{variant:`ghost`,disabled:o===0,onClick:()=>r(ht(e,o,o-1)),title:`Run this step earlier`,children:d(M,{className:`h-3.5 w-3.5`})}),d(S,{variant:`ghost`,disabled:o===e.length-1,onClick:()=>r(ht(e,o,o+1)),title:`Run this step later`,children:d(ne,{className:`h-3.5 w-3.5`})}),d(S,{variant:`ghost`,onClick:()=>r(mt(e,o)),title:`Remove this step`,children:d(m,{className:`h-3.5 w-3.5`})})]})]}),a.kind===`wait`&&i(`label`,{className:`block w-32`,children:[d(J,{children:`Seconds`}),d(N,{type:`number`,min:0,max:300,value:String(a.seconds),onChange:t=>r(pt(e,o,{kind:`wait`,seconds:Number(t.target.value)}))})]}),a.kind===`code`&&i(`label`,{className:`block`,children:[d(J,{children:`Code`}),d(`textarea`,{className:`h-32 w-full rounded-md border border-border bg-surface p-2 font-mono text-xs text-foreground focus:outline-none focus:ring-1 focus:ring-primary`,value:a.code,spellCheck:!1,onChange:t=>r(pt(e,o,{kind:`code`,code:t.target.value}))}),i(`span`,{className:`mt-1 block text-xs text-foreground-subtle`,children:[`Compiled into this automation's own block by the same compiler as a script — not evaluated at run time. `,d(`code`,{children:`ctx`}),` and `,d(`code`,{children:`vars`}),` are in scope.`]})]}),a.kind===`cap`&&d(St,{action:a,devices:t,devicesState:n,onChange:t=>r(pt(e,o,t))})]},`${a.kind}-${String(o)}`)),i(`div`,{className:`flex items-center gap-2`,children:[i(S,{variant:`outline`,onClick:()=>r([...e,ut(`cap`)]),children:[d(B,{className:`h-4 w-4`}),` Step`]}),e.length>1&&d(`span`,{className:`text-xs text-foreground-subtle`,children:`Steps run in order and stop at the first failure.`})]})]})}function bt(e){return vt.find(t=>t.value===e)?.value??`cap`}function xt(e){return typeof e==`number`?e:typeof e==`object`&&e&&`$var`in e?{$var:e.$var}:[...e]}function St({action:e,devices:t,devicesState:n,onChange:r}){return d(Le,{value:e,allowTriggerDevice:!0,allowMultiSameFamily:!0,devices:t,listState:n,onChange:e=>r({kind:`cap`,deviceId:xt(e.deviceId),cap:e.cap,method:e.method,...e.args===void 0?{}:{args:{...e.args}}})})}function Ct({value:e,field:t,onChange:n}){return t?.kind===`boolean`?d(W,{value:e===!0?`true`:`false`,onChange:e=>n(e.target.value===`true`),options:[{value:`true`,label:`true`},{value:`false`,label:`false`}]}):t?.kind===`enum`&&t.enumValues!==void 0&&t.enumValues.length>0?d(W,{value:String(e),onChange:e=>n(e.target.value),options:t.enumValues.map(e=>({value:e,label:e}))}):t?.kind===`number`?d(N,{type:`number`,value:typeof e==`number`?String(e):``,placeholder:`0`,onChange:e=>{let t=Number(e.target.value);e.target.value!==``&&!Number.isNaN(t)&&n(t)}}):d(N,{value:String(e),placeholder:`value`,onChange:e=>n(e.target.value)})}function wt({value:e,field:t,onChange:n}){return d(N,{value:e.join(`, `),placeholder:`one, two, three`,onChange:e=>{let r=e.target.value.split(`,`).map(e=>e.trim()).filter(e=>e!==``);n(t?.kind===`number`?r.map(e=>Number(e)).filter(e=>!Number.isNaN(e)):r)}})}function Tt(e){return typeof e==`object`}function Et(e){return Tt(e)?e[0]??``:e}function Dt(e){return Tt(e)?e:typeof e==`boolean`?[]:[e]}function Ot({leaf:e,devices:t,onChange:n}){let r={deviceId:e.deviceId,cap:e.cap,fieldPath:e.fieldPath},a=et(r),o=ot(a?.kind),s=e.operator===`in`,c=t=>{n({...e,deviceId:t.deviceId,cap:t.cap,fieldPath:t.fieldPath})},l=t=>{let r=kt(t,o);n({...e,operator:r,value:r===`in`?Dt(e.value):Et(e.value)})};return i(`div`,{className:`flex flex-wrap items-end gap-2`,children:[d(tt,{value:r,devices:t,onChange:c}),i(`label`,{className:`min-w-[8rem]`,children:[d(J,{children:`Comparison`}),d(W,{value:e.operator,onChange:e=>l(e.target.value),options:o.map(e=>({value:e.value,label:e.label}))})]}),i(`label`,{className:`min-w-[9rem] flex-1`,children:[d(J,{children:`Value`}),s?d(wt,{value:Dt(e.value),field:a,onChange:t=>n({...e,value:t})}):d(Ct,{value:Et(e.value),field:a,onChange:t=>n({...e,value:t})})]})]})}function kt(e,t){return t.find(t=>t.value===e)?.value??`eq`}var At={kind:`global`,sourceStableId:``,cap:``,fieldPath:``};function jt({leaf:e,devices:t,onChange:n}){let r=Object.entries(e.bindings),a=t=>{n({kind:`expression`,expr:e.expr,bindings:t})};return i(`div`,{className:`space-y-2`,children:[i(`label`,{className:`block`,children:[d(J,{children:`Expression`}),d(N,{value:e.expr,placeholder:`indoor - outdoor > 5`,onChange:t=>n({kind:`expression`,expr:t.target.value,bindings:e.bindings})}),d(`span`,{className:`mt-1 block text-xs text-foreground-subtle`,children:`Must evaluate to true or false. Every name in it has to be bound below — an unbound name is refused when the automation is saved, not when it runs.`})]}),i(`div`,{className:`space-y-1.5`,children:[r.length===0&&d(`p`,{className:`text-xs text-foreground-subtle`,children:`No names bound yet. An expression reads devices only through these.`}),r.map(([n,r])=>d(Mt,{alias:n,source:r,devices:t,onRename:t=>a(Rt(e.bindings,n,t)),onChange:t=>a({...e.bindings,[n]:t}),onRemove:()=>a(zt(e.bindings,n))},n)),i(S,{variant:`ghost`,onClick:()=>a({...e.bindings,[Lt(e.bindings)]:At}),children:[d(B,{className:`h-3.5 w-3.5`}),` Name`]})]})]})}function Mt({alias:e,source:t,devices:n,onRename:r,onChange:a,onRemove:o}){let s=t.kind===`literal`,c=s?{deviceId:0,cap:``,fieldPath:``}:{deviceId:Ft(n,Pt(t)),cap:t.cap,fieldPath:t.fieldPath};return i(`div`,{className:`flex flex-wrap items-end gap-2 rounded border border-border p-2`,children:[i(`label`,{className:`w-32`,children:[d(J,{children:`Name`}),d(N,{value:e,placeholder:`indoor`,onChange:e=>r(e.target.value)})]}),i(`label`,{className:`w-36`,children:[d(J,{children:`Reads`}),d(W,{value:s?`literal`:`global`,onChange:e=>a(e.target.value===`literal`?{kind:`literal`,value:``}:At),options:[{value:`global`,label:`A device field`},{value:`literal`,label:`A constant`}]})]}),s?i(`label`,{className:`min-w-[9rem] flex-1`,children:[d(J,{children:`Constant`}),d(N,{value:t.value===null?``:String(t.value),placeholder:`5`,onChange:e=>a({kind:`literal`,value:Nt(e.target.value)})})]}):d(tt,{value:c,devices:n,onChange:e=>a({kind:`global`,sourceStableId:It(n,e.deviceId),cap:e.cap,fieldPath:e.fieldPath})}),d(S,{variant:`ghost`,onClick:o,title:`Remove this name`,children:d(m,{className:`h-3.5 w-3.5`})})]})}function Nt(e){let t=e.trim();return t===`true`?!0:t===`false`?!1:t!==``&&!Number.isNaN(Number(t))?Number(t):e}function Pt(e){return e.kind===`global`?e.sourceStableId:``}function Ft(e,t){return e.find(e=>e.stableId===t)?.id??0}function It(e,t){return e.find(e=>e.id===t)?.stableId??``}function Lt(e){for(let t=0;t<26;t+=1){let n=String.fromCharCode(97+t);if(!(n in e))return n}return`n${String(Object.keys(e).length)}`}function Rt(e,t,n){let r={};for(let[i,a]of Object.entries(e))i===t?r[n]=a:r[i]=a;return r}function zt(e,t){let n={};for(let[r,i]of Object.entries(e))r!==t&&(n[r]=i);return n}function Y(e){return e.kind===`all`||e.kind===`any`}function X(e){let t=e.conditions;return t===void 0?{kind:`all`,children:[]}:Y(t)?t:{kind:`all`,children:[t]}}function Bt(e,t){return t===null||Y(t)&&t.children.length===0?{triggers:e.triggers,actions:e.actions}:{triggers:e.triggers,actions:e.actions,conditions:t}}function Vt(e){return Bt(e,X(e))}function Ht(e,t){if(t.length===0)return e;let[n,...r]=t;if(n===void 0)return e;if(e.kind===`not`)return n===0?Ht(e.child,r):null;if(!Y(e))return null;let i=e.children[n];return i===void 0?null:Ht(i,r)}function Z(e,t,n){if(t.length===0)return n(e);let[r,...i]=t;if(r===void 0)return n(e);if(e.kind===`not`){if(r!==0)return e;let t=Z(e.child,i,n);return t===null?null:{kind:`not`,child:t}}if(!Y(e))return e;let a=e.children[r];if(a===void 0)return e;let o=Z(a,i,n),s=o===null?[...e.children.slice(0,r),...e.children.slice(r+1)]:[...e.children.slice(0,r),o,...e.children.slice(r+1)];return s.length===0?null:{kind:e.kind,children:s}}function Ut(e){return e===null?null:xe({triggers:[{kind:`manual`}],actions:[{kind:`wait`,seconds:0}],conditions:e})}function Wt(e){let t=X(e);return{depth:t.children.length===0?0:Se(t),leaves:t.children.length===0?0:me(t),depthLimit:3,leafLimit:32}}function Gt(e,t){let n=Ut(t);return n===null?{ok:!0,recipe:Bt(e,t)}:{ok:!1,reason:n}}function Kt(e,t){let n=X(e);return Gt(e,{kind:n.kind,children:[...n.children,t]})}function qt(e,t,n){return Bt(e,Z(X(e),t,()=>n))}function Jt(e,t){return Bt(e,Z(X(e),t,()=>null))}function Yt(e,t,n){return Bt(e,Z(X(e),t,e=>Y(e)?{kind:n,children:e.children}:e))}function Xt(e,t){return Ht(X(e),t)===null?{ok:!1,reason:`that row is no longer there`}:Gt(e,Z(X(e),t,e=>e.kind===`not`?e.child:{kind:`not`,child:e}))}function Zt(e){if(e.length===0)return null;let t=e[e.length-1];return t===void 0?null:{path:e.slice(0,-1),index:t}}function Qt(e,t,n){if(t.length<2)return{ok:!1,reason:`select at least two rows — a group of one is just the row`};if(new Set(t.map(e=>e.slice(0,-1).join(`/`))).size!==1)return{ok:!1,reason:`those rows are in different groups — a group can only be made from rows side by side`};let r=Zt(t[0]??[]);if(r===null)return{ok:!1,reason:`the root list cannot be grouped into itself`};let i=X(e),a=Ht(i,r.path);if(a===null||!Y(a))return{ok:!1,reason:`those rows are inside a NOT — remove it before grouping them`};let o=[...t.map(e=>e[e.length-1]??-1)].sort((e,t)=>e-t),s=o.map(e=>a.children[e]).filter(e=>e!==void 0);if(s.length!==o.length)return{ok:!1,reason:`one of the selected rows is no longer there`};let c=a.children.filter((e,t)=>!o.includes(t)),l=o[0]??0,u={kind:n,children:s},d={kind:a.kind,children:[...c.slice(0,l),u,...c.slice(l)]};return Gt(e,Z(i,r.path,()=>d))}function $t(e,t){if(t.length===0)return{ok:!1,reason:`the outer list is not a group — there is nothing to dissolve`};let n=X(e),r=Ht(n,t);if(r===null)return{ok:!1,reason:`that group is no longer there`};if(r.kind===`not`)return Gt(e,Z(n,t,()=>r.child));if(!Y(r))return{ok:!1,reason:`that row is a condition, not a group`};let i=Zt(t);if(i===null)return{ok:!1,reason:`that group has no parent to dissolve into`};let a=Ht(n,i.path);if(a===null||!Y(a))return{ok:!1,reason:`a group inside a NOT cannot be dissolved in place`};let o={kind:a.kind,children:[...a.children.slice(0,i.index),...r.children,...a.children.slice(i.index+1)]};return Gt(e,Z(n,i.path,()=>o))}r();var en=[{value:`all`,label:`ALL of`},{value:`any`,label:`ANY of`}];function tn(e){return e.join(`/`)}function nn(e){return e===``?[]:e.split(`/`).map(e=>Number(e))}function rn({recipe:e,devices:t,onChange:n}){let[r,a]=l([]),[s,c]=l(null),u=X(e),f=Wt(e),p=o(()=>r.map(nn),[r]),m=e=>{if(!e.ok){c(e.reason);return}c(null),a([]),n(e.recipe)},h=Qt(e,p,`all`),g=Qt(e,p,`any`),_=p.length===1?Xt(e,p[0]??[]):{ok:!1,reason:`select exactly one row to negate`},v={devices:t,selected:new Set(r),onToggleSelect:e=>a(t=>t.includes(e)?t.filter(t=>t!==e):[...t,e]),onUpdate:(t,r)=>n(qt(e,t,r)),onRemove:t=>{a([]),n(Jt(e,t))},onSetKind:(t,r)=>n(Yt(e,t,r)),onUngroup:t=>m($t(e,t))};return i(`div`,{className:`space-y-2`,children:[i(`div`,{className:`flex flex-wrap items-center gap-2`,children:[d(G,{variant:`default`,children:`And only if`}),d(`div`,{className:`w-32`,children:d(W,{value:u.kind,onChange:t=>n(Yt(e,[],an(t.target.value))),options:en})}),i(`span`,{className:`text-xs text-foreground-subtle`,children:[`depth `,f.depth,`/`,f.depthLimit,` · `,f.leaves,`/`,f.leafLimit,` conditions`]})]}),u.children.length===0?d(`p`,{className:`text-xs text-foreground-subtle`,children:`No conditions. The actions run every time a trigger fires.`}):d(on,{container:u,path:[],handlers:v}),i(`div`,{className:`flex flex-wrap items-center gap-2`,children:[i(S,{variant:`outline`,onClick:()=>m(Kt(e,dt())),title:`Compare one device field to a value`,children:[d(B,{className:`h-4 w-4`}),` Condition`]}),i(S,{variant:`outline`,onClick:()=>m(Kt(e,ft())),title:`Arithmetic across device fields — the bounded expression engine`,children:[d(Ve,{className:`h-4 w-4`}),` Expression`]}),d(`span`,{className:`mx-1 h-5 w-px bg-border`}),d(S,{variant:`ghost`,disabled:!h.ok,onClick:()=>m(h),title:h.ok?`All selected rows must be true`:h.reason,children:`Group as ALL`}),d(S,{variant:`ghost`,disabled:!g.ok,onClick:()=>m(g),title:g.ok?`Any one of the selected rows is enough`:g.reason,children:`Group as ANY`}),d(S,{variant:`ghost`,disabled:!_.ok,onClick:()=>m(_),title:_.ok?`Invert the selected row`:_.reason,children:`NOT`}),r.length>0&&i(`span`,{className:`text-xs text-foreground-subtle`,children:[r.length,` selected`]})]}),s!==null&&d(`p`,{className:`rounded border border-warning/40 bg-warning/10 p-2 text-xs text-warning`,children:s})]})}function an(e){return e===`any`?`any`:`all`}function on({container:e,path:t,handlers:n}){return d(`div`,{className:`space-y-1.5`,children:e.children.map((e,r)=>d(sn,{node:e,path:[...t,r],handlers:n},tn([...t,r])))})}function sn({node:e,path:t,handlers:n}){let r=tn(t),a=d(ce,{checked:n.selected.has(r),onChange:()=>n.onToggleSelect(r),"aria-label":`Select this row for grouping`});return Y(e)?i(`div`,{className:`rounded border border-border bg-surface-subtle/40 p-2`,children:[i(`div`,{className:`mb-1.5 flex flex-wrap items-center gap-2`,children:[a,d(`div`,{className:`w-32`,children:d(W,{value:e.kind,onChange:e=>n.onSetKind(t,an(e.target.value)),options:en})}),i(S,{variant:`ghost`,onClick:()=>n.onUngroup(t),title:`Dissolve this group, keeping its rows`,children:[d(Ue,{className:`h-3.5 w-3.5`}),` Ungroup`]}),d(S,{variant:`ghost`,onClick:()=>n.onRemove(t),title:`Delete this group and everything in it`,children:d(m,{className:`h-3.5 w-3.5`})})]}),d(`div`,{className:`pl-4`,children:d(on,{container:e,path:t,handlers:n})})]}):e.kind===`not`?i(`div`,{className:`rounded border border-border p-2`,children:[i(`div`,{className:`mb-1.5 flex flex-wrap items-center gap-2`,children:[a,d(G,{variant:`warning`,children:`NOT`}),i(S,{variant:`ghost`,onClick:()=>n.onUngroup(t),title:`Remove the NOT, keeping the row`,children:[d(Ue,{className:`h-3.5 w-3.5`}),` Remove NOT`]})]}),d(`div`,{className:`pl-4`,children:d(sn,{node:e.child,path:[...t,0],handlers:n})})]}):i(`div`,{className:`flex items-start gap-2 rounded border border-border p-2`,children:[d(`div`,{className:`pt-6`,children:a}),d(`div`,{className:`min-w-0 flex-1`,children:e.kind===`condition`?d(Ot,{leaf:e,devices:n.devices,onChange:e=>n.onUpdate(t,e)}):d(jt,{leaf:e,devices:n.devices,onChange:e=>n.onUpdate(t,e)})}),d(S,{variant:`ghost`,onClick:()=>n.onRemove(t),title:`Remove this condition`,children:d(m,{className:`h-3.5 w-3.5`})})]})}r();function cn({code:e,error:t,canEject:n,ejecting:r,onEject:a}){let[o,s]=l(!1),c=oe(),u=async()=>{await c({title:`Eject to code — this cannot be undone`,message:`The recipe is removed from the block and it becomes an ordinary script. The visual editor will no longer open it, and there is no way back to it except authoring a new automation from scratch. The code itself is kept exactly as it is now.`,confirmLabel:`Eject to code`,cancelLabel:`Keep it visual`,variant:`warning`})&&a()};return i(`div`,{className:`rounded border border-border`,children:[i(`div`,{className:`flex flex-wrap items-center gap-2 px-3 py-2`,children:[i(S,{variant:`ghost`,onClick:()=>s(!o),children:[d(o?p:h,{className:`h-4 w-4`}),`Generated code`]}),d(`span`,{className:`text-xs text-foreground-subtle`,children:`Derived from the recipe above — regenerated on every save, never edited here.`}),i(S,{className:`ml-auto`,variant:`ghost`,disabled:!n||e===null||r,onClick:()=>void u(),title:n?`Take permanent ownership of this code as a script`:`Save the automation first — there is no stored block to eject yet`,children:[d(Fe,{className:`h-4 w-4`}),` Eject to code`]})]}),o&&d(`div`,{className:`border-t border-border p-3`,children:e===null?d(`p`,{className:`text-xs text-warning`,children:t??`The recipe is not complete enough to generate a block yet.`}):d(k,{maxHeight:420,children:e})})]})}var ln=[`device-state`,`device-event`,`schedule`,`manual`],un=Object.values(Ee).sort((e,t)=>e.localeCompare(t));function dn({triggers:e,devices:t,onChange:n}){return i(`div`,{className:`space-y-2`,children:[e.length===0&&d(`p`,{className:`text-xs text-warning`,children:`No triggers yet. Without one nothing can ever start this automation — not even "Run actions", which needs a manual trigger to have somewhere to arrive.`}),e.map((r,i)=>d(fn,{trigger:r,devices:t,position:i,onChange:t=>n(pt(e,i,t)),onRemove:()=>n(mt(e,i))},`${r.kind}-${String(i)}`)),i(S,{variant:`outline`,onClick:()=>n([...e,lt(`device-state`)]),title:`Add another way this automation can start`,children:[d(B,{className:`h-4 w-4`}),` Trigger`]})]})}function fn({trigger:e,devices:t,position:n,onChange:r,onRemove:a}){return i(`div`,{className:`rounded border border-border p-3`,children:[i(`div`,{className:`flex flex-wrap items-end gap-2`,children:[d(G,{variant:`default`,children:n===0?`When`:`or when`}),i(`label`,{className:`min-w-[13rem] flex-1`,children:[d(J,{children:`Trigger`}),d(W,{value:e.kind,onChange:e=>r(lt(pn(e.target.value))),options:ln.map(e=>({value:e,label:st(e)}))})]}),d(S,{variant:`ghost`,onClick:a,title:`Remove this trigger`,children:d(m,{className:`h-4 w-4`})})]}),i(`div`,{className:`mt-2 flex flex-wrap items-end gap-2`,children:[e.kind===`device-state`&&d(hn,{trigger:e,devices:t,onChange:r}),e.kind===`device-event`&&d(_n,{trigger:e,devices:t,onChange:r}),e.kind===`schedule`&&i(`label`,{className:`min-w-[14rem] flex-1`,children:[d(J,{children:`Cron expression`}),d(N,{value:e.cron,placeholder:`0 30 23 * * *`,onChange:e=>r({kind:`schedule`,cron:e.target.value})}),i(`span`,{className:`mt-1 block text-xs text-foreground-subtle`,children:[`Six fields, seconds first. `,d(`code`,{children:`0 30 23 * * *`}),` is every night at 23:30.`]})]}),e.kind===`manual`&&i(`p`,{className:`text-xs text-foreground-subtle`,children:[`Runs only when someone presses `,d(`strong`,{children:`Run actions`}),`. The conditions are still evaluated — a manual run is a run, not an override.`]})]})]})}function pn(e){return ln.find(t=>t===e)??`manual`}function mn(e,t){return{kind:`device-state`,deviceId:e.deviceId,cap:e.cap,fieldPath:e.fieldPath,...t.becomes===void 0?{}:{becomes:t.becomes},...t.edge===void 0?{}:{edge:t.edge},...t.forMs===void 0?{}:{forMs:t.forMs},...t.debounceMs===void 0?{}:{debounceMs:t.debounceMs}}}function hn({trigger:e,devices:t,onChange:n}){let r={deviceId:e.deviceId,cap:e.cap,fieldPath:e.fieldPath},a=et(r),o={...e.becomes===void 0?{}:{becomes:e.becomes},...e.edge===void 0?{}:{edge:e.edge},...e.forMs===void 0?{}:{forMs:e.forMs},...e.debounceMs===void 0?{}:{debounceMs:e.debounceMs}};return i(c,{children:[d(tt,{value:r,devices:t,onChange:e=>n(mn(e,o))}),i(`div`,{className:`min-w-[11rem] flex-1`,children:[d(J,{children:`Becomes`}),i(`div`,{className:`flex items-center gap-2`,children:[i(`label`,{className:`flex items-center gap-1.5 text-xs text-foreground-subtle`,children:[d(ce,{checked:e.becomes===void 0,onChange:e=>n(mn(r,{...o,...e.target.checked?{becomes:void 0}:{becomes:``}}))}),`any change`]}),e.becomes!==void 0&&d(Ct,{value:e.becomes,field:a,onChange:e=>n(mn(r,{...o,becomes:e}))})]})]}),i(`label`,{className:`flex items-center gap-1.5 text-xs text-foreground-subtle`,children:[d(ce,{checked:e.edge===!0,onChange:e=>n(mn(r,{...o,...e.target.checked?{edge:!0}:{edge:void 0}}))}),`only on a change`]}),i(`label`,{className:`w-28`,children:[d(J,{children:`Held for (s)`}),d(N,{type:`number`,min:0,value:e.forMs===void 0?``:String(e.forMs/1e3),placeholder:`0`,onChange:e=>n(mn(r,{...o,forMs:gn(e.target.value)}))})]}),i(`label`,{className:`w-28`,children:[d(J,{children:`Debounce (s)`}),d(N,{type:`number`,min:0,value:e.debounceMs===void 0?``:String(e.debounceMs/1e3),placeholder:`0`,onChange:e=>n(mn(r,{...o,debounceMs:gn(e.target.value)}))})]})]})}function gn(e){if(e.trim()===``)return;let t=Number(e);if(!(Number.isNaN(t)||t<0))return Math.round(t*1e3)}function _n({trigger:e,devices:t,onChange:n}){return i(c,{children:[i(`label`,{className:`min-w-[15rem] flex-1`,children:[d(J,{children:`Event category`}),d(W,{value:e.category,onChange:t=>n({kind:`device-event`,category:t.target.value,...e.deviceId===void 0?{}:{deviceId:e.deviceId}}),options:[{value:``,label:`Select an event…`},...un.map(e=>({value:e,label:e}))]})]}),i(`label`,{className:`min-w-[11rem] flex-1`,children:[d(J,{children:`From device`}),d(W,{value:e.deviceId===void 0?``:String(e.deviceId),onChange:t=>n({kind:`device-event`,category:e.category,...t.target.value===``?{}:{deviceId:Number(t.target.value)}}),options:[{value:``,label:`Any device`},...t.map(e=>({value:String(e.id),label:e.name}))]})]})]})}r();var vn=`hub`;function yn({existing:e,initialRecipe:t,saving:n,saveError:r,ejecting:a,onSave:s,onEject:c,onCancel:u}){let[f,p]=l(e?.name??``),[m,h]=l(()=>Vt(t)),[g,_]=l(!1),y=Ze(),b=xe(m),C=o(()=>xn(m),[m]),w=o(()=>Sn(f.trim()===``?`Untitled automation`:f.trim(),m),[f,m]),T=f.trim()===``,E=T||b!==null||C!==null||w.code===null;return i(`div`,{className:`space-y-4`,children:[d(`div`,{className:`flex flex-wrap items-center gap-2`,children:d(G,{variant:`info`,children:`runs on the hub`})}),i(`label`,{className:`block max-w-md text-sm`,children:[d(`span`,{className:`text-foreground-subtle`,children:`Name`}),d(N,{className:`mt-1`,value:f,onChange:e=>p(e.target.value),placeholder:`What this automation does`})]}),d(bn,{step:`1`,title:`When`,subtitle:`Any one of these starts a run.`,children:d(dn,{triggers:m.triggers,devices:y,onChange:e=>h(gt(m,e))})}),d(bn,{step:`2`,title:`And only if`,subtitle:`Checked after a trigger fires. Nothing runs unless this is true.`,children:d(rn,{recipe:m,devices:y,onChange:h})}),d(bn,{step:`3`,title:`Then`,subtitle:`In this order, stopping at the first failure.`,children:d(yt,{actions:m.actions,devices:y,onChange:e=>h(_t(m,e))})}),d(cn,{code:w.code,error:w.error,canEject:e!==null,ejecting:a,onEject:c}),b!==null&&d(`p`,{className:`rounded border border-warning/40 bg-warning/10 p-3 text-xs text-warning`,children:b}),b===null&&C!==null&&d(`p`,{className:`rounded border border-warning/40 bg-warning/10 p-3 text-xs text-warning`,children:C}),r!==null&&d(`p`,{className:`rounded border border-danger/40 bg-danger/10 p-3 text-xs text-danger`,children:r}),i(`div`,{className:`flex flex-wrap items-center gap-2`,children:[i(S,{onClick:()=>{w.code!==null&&s({name:f.trim(),code:w.code,enabled:e?.enabled??!0,placement:vn})},disabled:n||E,children:[n&&d(x,{className:`h-4 w-4 animate-spin`}),` Save`]}),i(S,{variant:`ghost`,onClick:()=>_(!g),disabled:e===null,title:e===null?`Logs appear once the automation has been saved and started`:`Show this automation's process logs`,"data-testid":`automation-logs-toggle`,children:[d(v,{className:`h-4 w-4`}),` Logs `,g?`▾`:`▸`]}),d(S,{variant:`ghost`,onClick:u,disabled:n,children:`Close`}),T&&d(G,{variant:`warning`,children:`Name required`})]}),g&&e!==null&&i(`div`,{className:`rounded border border-border`,"data-testid":`automation-logs-panel`,children:[i(`div`,{className:`border-b border-border px-3 py-1.5 text-xs text-foreground-subtle`,children:[`Logs — `,d(`code`,{children:q(e.id)})]}),d(F,{addonId:q(e.id),maxHeight:`max-h-64`,showFilters:!1})]})]})}function bn({step:e,title:t,subtitle:n,children:r}){return i(`section`,{className:`border-l-2 border-border pl-4`,children:[i(`div`,{className:`mb-2 flex flex-wrap items-baseline gap-2`,children:[d(`span`,{className:`text-xs font-mono text-foreground-subtle`,children:e}),d(`h4`,{className:`text-sm font-medium uppercase tracking-wide`,children:t}),d(`span`,{className:`text-xs text-foreground-subtle`,children:n})]}),r]})}function xn(e){let t=_e.safeParse(e);if(t.success)return null;let n=t.error.issues[0];return n===void 0?`this automation is not complete yet`:`${n.path.length===0?``:`${n.path.join(`.`)}: `}${n.message}`}function Sn(e,t){try{return{code:he({name:e,recipe:t}),error:null}}catch(e){return{code:null,error:e instanceof Error?e.message:String(e)}}}function Cn(e){if(!e.enabled)return{tone:`idle`,label:`Disabled`,detail:`Not started — the block is turned off.`};switch(e.status){case`running`:return{tone:`ok`,label:`Running`,detail:`One process, supervised.`};case`starting`:return{tone:`pending`,label:`Starting`,detail:e.lastError??`Waiting for the process to report ready.`};case`failed`:return{tone:`error`,label:`Failed`,detail:e.lastError??`Failed with no reason recorded — check the node logs.`};default:return{tone:`error`,label:`Not running`,detail:e.lastError??`Enabled but no process is running. The supervisor reconciles every 15s; if this persists, the node may not be running a block supervisor.`}}}function wn(e){switch(e){case`ok`:return`success`;case`pending`:return`warning`;case`error`:return`danger`;default:return`default`}}function Tn(e,t){let n=e.find(e=>e.id===t.id);return n===void 0?t:n.updatedAt>=t.updatedAt?n:t}function En(e){let t=e=>{let t=Cn(e).tone;return t===`error`?0:t===`pending`?1:t===`ok`?2:3};return[...e].sort((e,n)=>t(e)-t(n)||e.name.localeCompare(n.name))}function Dn(e){if(e.length===0)return`No blocks yet.`;let t=e.filter(e=>Cn(e).tone===`error`).length,n=e.filter(e=>Cn(e).tone===`ok`).length,r=[`${String(e.length)} block${e.length===1?``:`s`}`];return n>0&&r.push(`${String(n)} running`),t>0&&r.push(`${String(t)} NOT running`),r.join(` · `)}function On({block:e,busy:t,confirming:n,logsOpen:r,unreadable:a,onEdit:o,onToggle:s,onToggleLogs:c,onAskDelete:l,onCancelDelete:u,onConfirmDelete:f}){let p=Cn(e),h=ue(e.updatedAt);return i(`li`,{className:`flex flex-col gap-2 p-3`,children:[i(`div`,{className:`flex flex-col gap-2 sm:flex-row sm:items-center sm:gap-3`,children:[i(`div`,{className:`min-w-0 flex-1`,children:[i(`div`,{className:`flex flex-wrap items-center gap-2`,children:[d(`span`,{className:`truncate font-medium`,children:e.name}),d(G,{variant:wn(p.tone),children:p.label}),h!==null&&i(`span`,{className:`text-xs text-foreground-subtle`,children:[`edited `,h]})]}),d(`p`,{className:`mt-0.5 text-xs text-foreground-subtle`,children:p.detail}),a!==null&&i(`p`,{className:`mt-0.5 text-xs text-warning`,children:[`Its recipe cannot be read (`,a,`), so it cannot be opened visually. The code is intact and still running — edit it as a script.`]})]}),n?i(`div`,{className:`flex justify-end gap-1`,children:[d(`span`,{className:`text-xs text-foreground-subtle`,children:`Delete this automation?`}),d(S,{variant:`danger`,onClick:e=>{e.stopPropagation(),f()},disabled:t,children:`Delete`}),d(S,{variant:`ghost`,onClick:e=>{e.stopPropagation(),u()},children:`Cancel`})]}):i(`div`,{className:`flex justify-end gap-1`,children:[d(S,{variant:`ghost`,onClick:e=>{e.stopPropagation(),o()},disabled:a!==null,title:a===null?`Edit`:`This block cannot be opened visually`,children:d(U,{className:`h-4 w-4`})}),i(R,{children:[d(z,{children:d(S,{variant:`ghost`,disabled:!0,title:`Run actions`,children:d(V,{className:`h-4 w-4`})})}),d(L,{children:`Running an automation by hand needs the automation-control authority, which is not wired up yet. There is deliberately no second way to execute one.`})]}),d(S,{variant:`ghost`,onClick:e=>{e.stopPropagation(),c()},title:`Logs`,children:d(v,{className:`h-4 w-4`})}),d(S,{variant:`ghost`,onClick:t=>{t.stopPropagation(),s(!e.enabled)},disabled:t,title:e.enabled?`Disable`:`Enable`,children:e.enabled?`Disable`:`Enable`}),d(S,{variant:`ghost`,onClick:e=>{e.stopPropagation(),l()},title:`Delete`,children:d(m,{className:`h-4 w-4`})})]})]}),r&&i(`div`,{className:`rounded border border-border`,children:[i(`div`,{className:`border-b border-border px-3 py-1.5 text-xs text-foreground-subtle`,children:[`Logs — `,d(`code`,{children:q(e.id)})]}),d(F,{addonId:q(e.id),maxHeight:`max-h-64`,showFilters:!1})]})]})}function kn({onCreate:e}){return d(pe,{children:i(`div`,{className:`space-y-2 p-4`,children:[d(`p`,{className:`text-sm text-foreground`,children:`No automations yet.`}),d(`p`,{className:`max-w-2xl text-xs text-foreground-subtle`,children:`An automation is a trigger, a set of conditions and a list of actions. It compiles to an ordinary block and runs in its own supervised process — there is no separate engine, so anything that goes wrong shows up here with a reason.`}),d(S,{onClick:e,children:`New automation`})]})})}r();var An=[`coreBlocks`,`list`],jn=5e3;function Mn(e){return e instanceof Error?e.message:String(e)}function Nn({onOpenScripts:e,initialBlockId:n,onBlockIdChange:r,createRequested:a=!1,onCreateConsumed:c}){let p=g(),{state:m,openNew:h,openEdit:_,close:v}=E({editKey:`block`,newKey:`new`}),[y,b]=l(null),[x,S]=l(null),[T,D]=l(null),O=t(null),k=m.mode!==`closed`;f(()=>{a&&(b(null),h(),r?.(null),c?.())},[a,c,r,h]);let A=s({queryKey:An,refetchInterval:jn,queryFn:async()=>(await p.trpcClient.coreBlocks.list.query({})).blocks}),j=()=>{A.refetch()},M=A.data;f(()=>{n===void 0||O.current===n||M?.find(e=>e.id===n&&Ke(e.code))!==void 0&&(O.current=n,_(n),r?.(n))},[n,M,_,r]);let N=u({mutationFn:e=>p.trpcClient.coreBlocks.create.mutate({block:e}),onSuccess:e=>{b(e.block),_(e.block.id),r?.(e.block.id),j()}}),P=u({mutationFn:e=>p.trpcClient.coreBlocks.update.mutate(e),onSuccess:e=>{b(e.block),_(e.block.id),r?.(e.block.id),j()}}),F=u({mutationFn:e=>p.trpcClient.coreBlocks.delete.mutate({blockId:e}),onSuccess:()=>{S(null),j()}}),I=u({mutationFn:e=>p.trpcClient.coreBlocks.setEnabled.mutate(e),onSuccess:j}),L=u({mutationFn:e=>p.trpcClient.coreBlocks.update.mutate({blockId:e.id,block:{name:e.name,code:Ye(e.code),enabled:e.enabled,placement:e.placement,...e.integrationId===void 0?{}:{integrationId:e.integrationId}}}),onSuccess:t=>{v(),b(null),j(),e(t.block.id)}}),R=A.data,z=En((R??[]).filter(e=>Ke(e.code))),B=R!==void 0&&z.length===0,V=m.mode===`edit`?z.find(e=>e.id===m.id)??(y!==null&&y.id===m.id?y:null):null,H=m.mode===`new`||V===null?null:Tn(z,V),ne=H===null?null:Je(H.code),re=N.error===null?P.error===null?L.error===null?null:Mn(L.error):Mn(P.error):Mn(N.error),ie=o(()=>[{key:`row`,header:`Automation`,nowrap:!1,notSortableReason:`Composite status row`,render:e=>{let t=Je(e.code);return d(`ul`,{className:`list-none`,children:d(On,{block:e,busy:I.isPending||F.isPending,confirming:x===e.id,logsOpen:T===e.id,unreadable:t.ok?null:t.reason,onEdit:()=>{b(e),_(e.id),r?.(e.id)},onToggle:t=>I.mutate({blockId:e.id,enabled:t}),onToggleLogs:()=>D(T===e.id?null:e.id),onAskDelete:()=>S(e.id),onCancelDelete:()=>S(null),onConfirmDelete:()=>F.mutate(e.id)})})},sortValue:e=>e.name}],[x,T,r,_,F,I]);return i(`div`,{className:`space-y-3`,children:[d(`p`,{className:`text-xs text-foreground-subtle`,children:R===void 0?``:Dn(z)}),A.error!=null&&d(C,{title:`Could not read the automations`,message:Mn(A.error),onRetry:j}),B?d(kn,{onCreate:()=>{b(null),h(),r?.(null)}}):d(w,{columns:ie,rows:z,rowKey:e=>e.id,pageSize:25,maxBodyHeight:ee,mobileMode:`scroll`,emptyMessage:R===void 0?`Loading automations…`:`No automations yet.`,onRowClick:e=>{b(e),_(e.id),r?.(e.id)}}),d(de,{open:k,onClose:()=>{N.reset(),P.reset(),L.reset(),b(null),v(),r?.(null)},title:H===null?`New automation`:`Edit ${H.name}`,icon:te,width:`xl`,testId:`automation-editor-panel`,children:d(yn,{existing:H,initialRecipe:ne!==null&&ne.ok?ne.recipe:ct,saving:N.isPending||P.isPending,saveError:re,ejecting:L.isPending,onSave:e=>{H===null?N.mutate(e):P.mutate({blockId:H.id,block:e})},onEject:()=>{H!==null&&L.mutate(H)},onCancel:()=>{N.reset(),P.reset(),L.reset(),b(null),v(),r?.(null)}},H?.id??`new`)}),F.error!=null&&d(C,{message:Mn(F.error)}),I.error!=null&&d(C,{message:Mn(I.error)})]})}function Pn(e){return{state:`answered`,byRef:new Map(e.map(e=>[De(e),e]))}}function Fn(e,t){if(e.state!==`unanswered`)return e.byRef.get(De(t))??null}function In(e){return e===void 0?`unknown`:e===null?`missing`:`present`}function Ln(e){return ve(e).map(e=>e.source)}function Rn(e){let t=e.block.source?.kind===`composition`?e.block.source.composition:null;if(t===null)return[];let n=[];for(let e of t.features){if(e.kind===`passthrough`){n.push(e.source);continue}for(let t of Object.values(e.fields))n.push(...Ln(t));for(let t of Object.values(e.items??{}))for(let e of Object.values(t.fields))n.push(...Ln(e))}let r=new Set;return n.filter(e=>{let t=De(e);return r.has(t)?!1:(r.add(t),!0)})}function zn(e,t){return`${String(e)} ${t}${e===1?``:`s`}`}function Bn(e){let t=e.fields.filter(e=>e.state===`unavailable`),n=e.fields.filter(e=>e.state===`stale`);if(t.length===0&&n.length===0)return{tone:`ok`,label:`Running`,reason:null};let r=[...t.length>0?[`${zn(t.length,`field`)} unavailable`]:[],...n.length>0?[`${zn(n.length,`field`)} stale`]:[]].join(`, `),i=t[0]??n[0];return{tone:`pending`,label:`Running`,reason:`${r}${i===void 0?``:` — ${i.cap}.${i.path}: ${i.reason??`no reason recorded`}`}`}}function Vn(e,t){let n=e.reason??t??null;switch(e.status){case`running`:return Bn(e);case`degraded`:return{tone:`pending`,label:`Degraded`,reason:n??`Running with a field handed back to the device; no reason recorded.`};case`starting`:return{tone:`pending`,label:`Starting`,reason:n};case`failed`:return{tone:`error`,label:`Failed`,reason:n??`Failed with no reason recorded — check the composer logs.`};default:return{tone:`error`,label:`Not running`,reason:n??`Enabled but the composer is not running it.`}}}function Hn(e){let{block:t,state:n}=e;return t.enabled?n.kind===`unreachable`?{tone:`pending`,label:`Unknown`,reason:`The composer could not be asked: ${n.error}`}:n.state===null?{tone:`pending`,label:`Not reported yet`,reason:`The composer holds no record of this block yet (stored status: ${t.lastError===void 0?t.status:`${t.status}: ${t.lastError}`}).`}:Vn(n.state,t.lastError):{tone:`idle`,label:`Off`,reason:null}}function Un(e){return De(e)}function Wn(e,t){let n=Fn(t,e);return{key:De(e),label:n?n.name:Un(e),presence:In(n),deviceId:n?n.id:null}}function Gn(e){return e.state.kind===`answered`&&e.state.state!==null?e.state.state.deviceId:null}function Kn(e,t){let n=e.block.source?.kind===`composition`?e.block.source.composition:null;if(n===null)return null;let r=n.target,i=r.kind===`existing`?r.device:be(e.block.id),a=De(i),o=Rn(e).filter(e=>De(e)!==a).map(e=>Wn(e,t)),s=n.features.map(e=>e.enabled===!1?`${e.cap} (off)`:e.cap),c=Fn(t,i),l=Gn(e),u=Hn(e);if(r.kind===`new`)return{blockId:e.block.id,kind:`composed`,name:e.block.name,namePresence:`present`,type:r.role===void 0?r.type:`${r.type} · ${r.role}`,features:s,state:u,sources:o,reportedDeviceId:l};let d=In(c);return{blockId:e.block.id,kind:`customization`,name:c?c.name:Un(r.device),namePresence:d,type:c?c.type:null,features:s,state:u,sources:o,reportedDeviceId:l}}function qn(e){return e.state.tone===`error`?0:e.state.tone===`pending`?1:e.state.tone===`ok`?2:3}function Jn(e,t){return qn(e)-qn(t)||e.name.localeCompare(t.name)}function Yn(e,t){let n=e.flatMap(e=>{let n=Kn(e,t);return n===null?[]:[n]});return{composed:n.filter(e=>e.kind===`composed`).toSorted(Jn),customizations:n.filter(e=>e.kind===`customization`).toSorted(Jn)}}r();var Xn=1e4;function Zn(e){return e instanceof Error?e.message:typeof e==`object`&&e&&`message`in e?String(e.message):String(e)}function Qn(e){let t=e.trim();return t===``?{}:{name:t}}function $n(){let e=je();return e.data===void 0?{state:`unanswered`,reason:e.error==null?`the device list has not answered yet`:`the device list could not be read (${Zn(e.error)})`}:Pn(e.data)}function er({createRequested:e=!1,onCreateConsumed:t}={}){let n=T({},{refetchInterval:Xn}),r=$n(),a=E({editKey:`block`,newKey:`new`});f(()=>{e&&(a.openNew(),t?.())},[e]);let o=n.data;if(n.error!=null&&o===void 0)return d(C,{title:`Could not read the compositions`,message:Zn(n.error),onRetry:()=>void n.refetch()});if(o===void 0)return d(`p`,{className:`p-4 text-sm text-foreground-subtle`,children:`Loading compositions…`});if(o.state===`not-loaded`)return d(pe,{children:i(`div`,{className:`space-y-1 p-4`,children:[d(`p`,{className:`text-sm text-warning`,children:`The composition store has not been read yet.`}),i(`p`,{className:`text-xs text-foreground-subtle`,children:[`This is not the same as having none — the list appears once the hub has read it (`,o.reason,`).`]})]})});let s=n.error==null?null:Zn(n.error),l=Yn(o.views,r),u=a.state.mode===`edit`?a.state.id:null,p=a.state.mode===`new`;return i(c,{children:[d(rr,{lists:l,fleet:r,refreshError:s,onEditComposed:e=>a.openEdit(e)}),d(de,{open:p||u!==null,onClose:()=>a.close(),title:p?`New composed device`:`Edit composed device`,icon:K,width:`xl`,testId:`composed-device-editor`,children:p?d(tr,{onClose:()=>a.close(),onCreated:e=>{n.refetch(),a.openEdit(e)}}):u===null?null:d(nr,{blockId:u,onClose:()=>a.close(),onSaved:()=>void n.refetch()})})]})}function tr({onClose:e,onCreated:t}){let n=ie();return d(Be,{initial:ze(ge.Sensor),blockId:null,deviceType:ge.Sensor,nativeCaps:null,targetFields:null,showTemplates:!0,onCancel:e,save:async(e,r)=>{try{let{block:i}=await n.mutateAsync({block:{...Qn(r),enabled:!0,composition:e}});return t(i.id),{kind:`saved`}}catch(e){return{kind:`refused`,reason:Zn(e)}}}})}function nr({blockId:e,onClose:t,onSaved:n}){let{t:r}=Oe(),i=y(),a=j({blockId:e});if(a.status===`pending`)return d(`p`,{role:`status`,children:r(`compositionEditor.loading`)});if(a.status===`error`)return d(C,{title:r(`compositionEditor.loadFailed`),message:a.error.message});if(a.data.state===`not-loaded`)return d(`p`,{role:`status`,children:r(`customize.status.notLoaded`,{reason:a.data.reason})});let o=a.data.view;if(o===null)return d(`p`,{children:r(`compositionEditor.notFound`,{blockId:e})});let s=Te(o.block);if(s===null)return d(`p`,{children:r(`compositionEditor.notComposition`)});if(s.target.kind===`existing`){let e=o.state.kind===`answered`?o.state.state?.deviceId??null:null;return e===null?d(`p`,{children:r(`compositionEditor.customizationUnknownDevice`)}):d(Pe,{className:`text-primary`,to:Ne(e),children:r(`compositionEditor.openOnDevice`)})}return d(Be,{initial:Re(s,o.block.name),blockId:o.block.id,deviceType:s.target.type,nativeCaps:null,targetFields:null,onCancel:t,save:async(e,t)=>{try{return await i.mutateAsync({blockId:o.block.id,expectedUpdatedAt:o.block.updatedAt,block:{...Qn(t),composition:e}}),await a.refetch(),n(),{kind:`saved`}}catch(e){return{kind:`refused`,reason:Zn(e)}}}},`${o.block.id}@${o.block.updatedAt}`)}function rr({lists:e,fleet:t,refreshError:n,onEditComposed:r}){return i(`div`,{className:`space-y-4`,children:[n!==null&&i(`p`,{className:`text-xs text-warning`,children:[`Showing the last answer — the latest refresh failed: `,n]}),t.state===`unanswered`&&i(`p`,{className:`text-xs text-warning`,children:[`Device names are shown as references: `,t.reason,`. No source is marked missing until it has.`]}),d(ir,{title:`Composed devices`,hint:`A new device whose state is derived from other devices.`,rows:e.composed,nameHeader:`Name`,emptyMessage:`No composed devices.`,kind:`composed`,onEditComposed:r}),d(ir,{title:`Customizations`,hint:`Fields added to or replaced on an existing device — one per device, edited from the device.`,rows:e.customizations,nameHeader:`Device`,emptyMessage:`No customizations.`,kind:`customization`,onEditComposed:r})]})}function ir({title:e,hint:t,rows:n,nameHeader:r,emptyMessage:a,kind:o,onEditComposed:s}){return i(`section`,{className:`space-y-2`,"aria-label":e,children:[i(`div`,{children:[d(`h3`,{className:`font-medium`,children:e}),d(`p`,{className:`text-xs text-foreground-subtle`,children:t})]}),d(pe,{variant:`flat`,className:`p-0`,children:d(w,{columns:ar(r,o,s),rows:n,rowKey:e=>e.blockId,minWidthPx:760,bordered:!1,mobileMode:`scroll`,maxBodyHeight:ee,emptyMessage:a,onRowClick:o===`composed`?e=>s(e.blockId):void 0})})]})}function ar(e,t,n){let r=[{key:`name`,header:e,sortValue:e=>e.name.toLowerCase(),render:e=>d(or,{row:e})},{key:`type`,header:`Type`,sortValue:e=>e.type,render:e=>d(`span`,{className:`text-xs text-foreground-subtle`,children:e.type??`unknown`})},{key:`state`,header:`State`,nowrap:!1,notSortableReason:`A state and its reason are read together, not ordered.`,render:e=>i(`div`,{className:`space-y-0.5`,children:[d(G,{variant:wn(e.state.tone),children:e.state.label}),e.state.reason!==null&&d(`p`,{className:`max-w-md whitespace-normal text-xs text-foreground-subtle`,children:e.state.reason})]})},{key:`sources`,header:`Sources`,nowrap:!1,notSortableReason:`A list of devices has no single order.`,render:e=>d(cr,{sources:e.sources})}];return t===`composed`&&r.push({key:`actions`,header:``,align:`right`,notSortableReason:`Row actions`,nowrap:!0,render:e=>d(`div`,{className:`flex justify-end gap-1`,children:d(S,{type:`button`,size:`sm`,variant:`secondary`,onClick:t=>{t.stopPropagation(),n(e.blockId)},children:`Edit`})})}),r}function or({row:e}){return i(`div`,{className:`space-y-0.5`,children:[d(sr,{row:e}),e.namePresence===`missing`&&d(`span`,{className:`ml-1.5 text-[10px] text-danger`,children:`device missing`}),d(`p`,{className:`text-[11px] text-foreground-subtle`,children:e.features.join(`, `)})]})}function sr({row:e}){return e.kind===`composed`?d(`span`,{className:`text-xs font-medium text-foreground`,children:e.name}):e.reportedDeviceId===null?i(`span`,{className:`text-xs text-foreground-subtle`,children:[e.name,` — device not reported yet`]}):d(Pe,{to:Ne(e.reportedDeviceId),className:`text-xs text-foreground hover:underline`,onClick:e=>e.stopPropagation(),children:e.name})}function cr({sources:e}){return e.length===0?d(`span`,{className:`text-xs text-foreground-subtle`,children:`—`}):d(`ul`,{className:`space-y-0.5`,children:e.map(e=>i(`li`,{className:`text-xs`,children:[e.deviceId===null?d(`span`,{className:e.presence===`missing`?`text-danger`:`text-foreground`,children:e.label}):d(Pe,{to:`/devices/${String(e.deviceId)}`,className:`hover:underline`,children:e.label}),e.presence===`missing`&&d(`span`,{className:`ml-1.5 text-[10px] text-danger`,children:`missing`})]},e.key))})}function lr(e,t){(t==null||t>e.length)&&(t=e.length);for(var n=0,r=Array(t);n<t;n++)r[n]=e[n];return r}function ur(e){if(Array.isArray(e))return e}function dr(e,t,n){return(t=br(t))in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function fr(e,t){var n=e==null?null:typeof Symbol<`u`&&e[Symbol.iterator]||e[`@@iterator`];if(n!=null){var r,i,a,o,s=[],c=!0,l=!1;try{if(a=(n=n.call(e)).next,t!==0)for(;!(c=(r=a.call(n)).done)&&(s.push(r.value),s.length!==t);c=!0);}catch(e){l=!0,i=e}finally{try{if(!c&&n.return!=null&&(o=n.return(),Object(o)!==o))return}finally{if(l)throw i}}return s}}function pr(){throw TypeError(`Invalid attempt to destructure non-iterable instance.
|
|
5
|
-
In order to be iterable, non-array objects must have a [Symbol.iterator]() method.`)}function mr(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter(function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable})),n.push.apply(n,r)}return n}function hr(e){for(var t=1;t<arguments.length;t++){var n=arguments[t]==null?{}:arguments[t];t%2?mr(Object(n),!0).forEach(function(t){dr(e,t,n[t])}):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(n)):mr(Object(n)).forEach(function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(n,t))})}return e}function gr(e,t){if(e==null)return{};var n,r,i=_r(e,t);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);for(r=0;r<a.length;r++)n=a[r],t.indexOf(n)===-1&&{}.propertyIsEnumerable.call(e,n)&&(i[n]=e[n])}return i}function _r(e,t){if(e==null)return{};var n={};for(var r in e)if({}.hasOwnProperty.call(e,r)){if(t.indexOf(r)!==-1)continue;n[r]=e[r]}return n}function vr(e,t){return ur(e)||fr(e,t)||xr(e,t)||pr()}function yr(e,t){if(typeof e!=`object`||!e)return e;var n=e[Symbol.toPrimitive];if(n!==void 0){var r=n.call(e,t);if(typeof r!=`object`)return r;throw TypeError(`@@toPrimitive must return a primitive value.`)}return(t===`string`?String:Number)(e)}function br(e){var t=yr(e,`string`);return typeof t==`symbol`?t:t+``}function xr(e,t){if(e){if(typeof e==`string`)return lr(e,t);var n={}.toString.call(e).slice(8,-1);return n===`Object`&&e.constructor&&(n=e.constructor.name),n===`Map`||n===`Set`?Array.from(e):n===`Arguments`||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)?lr(e,t):void 0}}function Sr(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function Cr(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter(function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable})),n.push.apply(n,r)}return n}function wr(e){for(var t=1;t<arguments.length;t++){var n=arguments[t]==null?{}:arguments[t];t%2?Cr(Object(n),!0).forEach(function(t){Sr(e,t,n[t])}):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(n)):Cr(Object(n)).forEach(function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(n,t))})}return e}function Tr(){var e=[...arguments];return function(t){return e.reduceRight(function(e,t){return t(e)},t)}}function Er(e){return function t(){var n=this,r=[...arguments];return r.length>=e.length?e.apply(this,r):function(){var e=[...arguments];return t.apply(n,[].concat(r,e))}}}function Dr(e){return{}.toString.call(e).includes(`Object`)}function Or(e){return!Object.keys(e).length}function kr(e){return typeof e==`function`}function Ar(e,t){return Object.prototype.hasOwnProperty.call(e,t)}function jr(e,t){return Dr(t)||Q(`changeType`),Object.keys(t).some(function(t){return!Ar(e,t)})&&Q(`changeField`),t}function Mr(e){kr(e)||Q(`selectorType`)}function Nr(e){kr(e)||Dr(e)||Q(`handlerType`),Dr(e)&&Object.values(e).some(function(e){return!kr(e)})&&Q(`handlersType`)}function Pr(e){e||Q(`initialIsRequired`),Dr(e)||Q(`initialType`),Or(e)&&Q(`initialContent`)}function Fr(e,t){throw Error(e[t]||e.default)}var Q=Er(Fr)({initialIsRequired:`initial state is required`,initialType:`initial state should be an object`,initialContent:`initial state shouldn't be an empty object`,handlerType:`handler should be an object or a function`,handlersType:`all handlers should be a functions`,selectorType:`selector should be a function`,changeType:`provided value of changes should be an object`,changeField:`it seams you want to change a field in the state which is not specified in the "initial" state`,default:"an unknown error accured in `state-local` package"}),Ir={changes:jr,selector:Mr,handler:Nr,initial:Pr};function Lr(e){var t=arguments.length>1&&arguments[1]!==void 0?arguments[1]:{};Ir.initial(e),Ir.handler(t);var n={current:e},r=Er(Br)(n,t),i=Er(zr)(n),a=Er(Ir.changes)(e),o=Er(Rr)(n);function s(){var e=arguments.length>0&&arguments[0]!==void 0?arguments[0]:function(e){return e};return Ir.selector(e),e(n.current)}function c(e){Tr(r,i,a,o)(e)}return[s,c]}function Rr(e,t){return kr(t)?t(e.current):t}function zr(e,t){return e.current=wr(wr({},e.current),t),t}function Br(e,t,n){return kr(t)?t(e.current):Object.keys(n).forEach(function(n){return t[n]?.call(t,e.current[n])}),n}var Vr={create:Lr},Hr={paths:{vs:`https://cdn.jsdelivr.net/npm/monaco-editor@0.55.1/min/vs`}};function Ur(e){return function t(){var n=this,r=[...arguments];return r.length>=e.length?e.apply(this,r):function(){var e=[...arguments];return t.apply(n,[].concat(r,e))}}}function Wr(e){return{}.toString.call(e).includes(`Object`)}function Gr(e){return e||Yr(`configIsRequired`),Wr(e)||Yr(`configType`),e.urls?(Kr(),{paths:{vs:e.urls.monacoBase}}):e}function Kr(){console.warn(Jr.deprecation)}function qr(e,t){throw Error(e[t]||e.default)}var Jr={configIsRequired:`the configuration object is required`,configType:`the configuration object should be an object`,default:"an unknown error accured in `@monaco-editor/loader` package",deprecation:`Deprecation warning!
|
|
6
|
-
You are using deprecated way of configuration.
|
|
7
|
-
|
|
8
|
-
Instead of using
|
|
9
|
-
monaco.config({ urls: { monacoBase: '...' } })
|
|
10
|
-
use
|
|
11
|
-
monaco.config({ paths: { vs: '...' } })
|
|
12
|
-
|
|
13
|
-
For more please check the link https://github.com/suren-atoyan/monaco-loader#config
|
|
14
|
-
`},Yr=Ur(qr)(Jr),Xr={config:Gr},Zr=function(){var e=[...arguments];return function(t){return e.reduceRight(function(e,t){return t(e)},t)}};function Qr(e,t){return Object.keys(t).forEach(function(n){t[n]instanceof Object&&e[n]&&Object.assign(t[n],Qr(e[n],t[n]))}),hr(hr({},e),t)}var $r={type:`cancelation`,msg:`operation is manually canceled`};function ei(e){var t=!1,n=new Promise(function(n,r){e.then(function(e){return t?r($r):n(e)}),e.catch(r)});return n.cancel=function(){return t=!0},n}var ti=[`monaco`],ni=vr(Vr.create({config:Hr,isInitialized:!1,resolve:null,reject:null,monaco:null}),2),ri=ni[0],ii=ni[1];function ai(e){var t=Xr.config(e),n=t.monaco,r=gr(t,ti);ii(function(e){return{config:Qr(e.config,r),monaco:n}})}function oi(){var e=ri(function(e){return{monaco:e.monaco,isInitialized:e.isInitialized,resolve:e.resolve}});if(!e.isInitialized){if(ii({isInitialized:!0}),e.monaco)return e.resolve(e.monaco),ei(pi);if(window.monaco&&window.monaco.editor)return di(window.monaco),e.resolve(window.monaco),ei(pi);Zr(si,li)(ui)}return ei(pi)}function si(e){return document.body.appendChild(e)}function ci(e){var t=document.createElement(`script`);return e&&(t.src=e),t}function li(e){var t=ri(function(e){return{config:e.config,reject:e.reject}}),n=ci(`${t.config.paths.vs}/loader.js`);return n.onload=function(){return e()},n.onerror=t.reject,n}function ui(){var e=ri(function(e){return{config:e.config,resolve:e.resolve,reject:e.reject}}),t=window.require;t.config(e.config),t([`vs/editor/editor.main`],function(t){var n=t.m||t;di(n),e.resolve(n)},function(t){e.reject(t)})}function di(e){ri().monaco||ii({monaco:e})}function fi(){return ri(function(e){return e.monaco})}var pi=new Promise(function(e,t){return ii({resolve:e,reject:t})}),mi={config:ai,init:oi,__getMonacoInstance:fi};r();var hi={wrapper:{display:`flex`,position:`relative`,textAlign:`initial`},fullWidth:{width:`100%`},hide:{display:`none`}},gi={container:{display:`flex`,height:`100%`,width:`100%`,justifyContent:`center`,alignItems:`center`}};function _i({children:e}){return a.createElement(`div`,{style:gi.container},e)}var vi=_i;function yi({width:e,height:t,isEditorReady:n,loading:r,_ref:i,className:o,wrapperProps:s}){return a.createElement(`section`,{style:{...hi.wrapper,width:e,height:t},...s},!n&&a.createElement(vi,null,r),a.createElement(`div`,{ref:i,style:{...hi.fullWidth,...!n&&hi.hide},className:o}))}var bi=n(yi);function xi(e){f(e,[])}var Si=xi;function Ci(e,n,r=!0){let i=t(!0);f(i.current||!r?()=>{i.current=!1}:e,n)}var $=Ci;function wi(){}function Ti(e,t,n,r){return Ei(e,r)||Di(e,t,n,r)}function Ei(e,t){return e.editor.getModel(Oi(e,t))}function Di(e,t,n,r){return e.editor.createModel(t,n,r?Oi(e,r):void 0)}function Oi(e,t){return e.Uri.parse(t)}function ki({original:n,modified:r,language:i,originalLanguage:o,modifiedLanguage:s,originalModelPath:c,modifiedModelPath:u,keepCurrentOriginalModel:d=!1,keepCurrentModifiedModel:p=!1,theme:m=`light`,loading:h=`Loading...`,options:g={},height:_=`100%`,width:v=`100%`,className:y,wrapperProps:b={},beforeMount:x=wi,onMount:S=wi}){let[C,w]=l(!1),[T,E]=l(!0),D=t(null),O=t(null),k=t(null),A=t(S),j=t(x),M=t(!1);Si(()=>{let e=mi.init();return e.then(e=>(O.current=e)&&E(!1)).catch(e=>e?.type!==`cancelation`&&console.error(`Monaco initialization: error:`,e)),()=>D.current?F():e.cancel()}),$(()=>{if(D.current&&O.current){let e=D.current.getOriginalEditor(),t=Ti(O.current,n||``,o||i||`text`,c||``);t!==e.getModel()&&e.setModel(t)}},[c],C),$(()=>{if(D.current&&O.current){let e=D.current.getModifiedEditor(),t=Ti(O.current,r||``,s||i||`text`,u||``);t!==e.getModel()&&e.setModel(t)}},[u],C),$(()=>{let e=D.current.getModifiedEditor();e.getOption(O.current.editor.EditorOption.readOnly)?e.setValue(r||``):r!==e.getValue()&&(e.executeEdits(``,[{range:e.getModel().getFullModelRange(),text:r||``,forceMoveMarkers:!0}]),e.pushUndoStop())},[r],C),$(()=>{D.current?.getModel()?.original.setValue(n||``)},[n],C),$(()=>{let{original:e,modified:t}=D.current.getModel();O.current.editor.setModelLanguage(e,o||i||`text`),O.current.editor.setModelLanguage(t,s||i||`text`)},[i,o,s],C),$(()=>{O.current?.editor.setTheme(m)},[m],C),$(()=>{D.current?.updateOptions(g)},[g],C);let N=e(()=>{if(!O.current)return;j.current(O.current);let e=Ti(O.current,n||``,o||i||`text`,c||``),t=Ti(O.current,r||``,s||i||`text`,u||``);D.current?.setModel({original:e,modified:t})},[i,r,s,n,o,c,u]),P=e(()=>{!M.current&&k.current&&(D.current=O.current.editor.createDiffEditor(k.current,{automaticLayout:!0,...g}),N(),O.current?.editor.setTheme(m),w(!0),M.current=!0)},[g,m,N]);f(()=>{C&&A.current(D.current,O.current)},[C]),f(()=>{!T&&!C&&P()},[T,C,P]);function F(){let e=D.current?.getModel();d||e?.original?.dispose(),p||e?.modified?.dispose(),D.current?.dispose()}return a.createElement(bi,{width:v,height:_,isEditorReady:C,loading:h,_ref:k,className:y,wrapperProps:b})}n(ki);function Ai(e){let n=t();return f(()=>{n.current=e},[e]),n.current}var ji=Ai,Mi=new Map;function Ni({defaultValue:n,defaultLanguage:r,defaultPath:i,value:o,language:s,path:c,theme:u=`light`,line:d,loading:p=`Loading...`,options:m={},overrideServices:h={},saveViewState:g=!0,keepCurrentModel:_=!1,width:v=`100%`,height:y=`100%`,className:b,wrapperProps:x={},beforeMount:S=wi,onMount:C=wi,onChange:w,onValidate:T=wi}){let[E,D]=l(!1),[O,k]=l(!0),A=t(null),j=t(null),M=t(null),N=t(C),P=t(S),F=t(),I=t(o),ee=ji(c),L=t(!1),R=t(!1);Si(()=>{let e=mi.init();return e.then(e=>(A.current=e)&&k(!1)).catch(e=>e?.type!==`cancelation`&&console.error(`Monaco initialization: error:`,e)),()=>j.current?B():e.cancel()}),$(()=>{let e=Ti(A.current,n||o||``,r||s||``,c||i||``);e!==j.current?.getModel()&&(g&&Mi.set(ee,j.current?.saveViewState()),j.current?.setModel(e),g&&j.current?.restoreViewState(Mi.get(c)))},[c],E),$(()=>{j.current?.updateOptions(m)},[m],E),$(()=>{!j.current||o===void 0||(j.current.getOption(A.current.editor.EditorOption.readOnly)?j.current.setValue(o):o!==j.current.getValue()&&(R.current=!0,j.current.executeEdits(``,[{range:j.current.getModel().getFullModelRange(),text:o,forceMoveMarkers:!0}]),j.current.pushUndoStop(),R.current=!1))},[o],E),$(()=>{let e=j.current?.getModel();e&&s&&A.current?.editor.setModelLanguage(e,s)},[s],E),$(()=>{d!==void 0&&j.current?.revealLine(d)},[d],E),$(()=>{A.current?.editor.setTheme(u)},[u],E);let z=e(()=>{if(!(!M.current||!A.current)&&!L.current){P.current(A.current);let e=c||i,t=Ti(A.current,o||n||``,r||s||``,e||``);j.current=A.current?.editor.create(M.current,{model:t,automaticLayout:!0,...m},h),g&&j.current.restoreViewState(Mi.get(e)),A.current.editor.setTheme(u),d!==void 0&&j.current.revealLine(d),D(!0),L.current=!0}},[n,r,i,o,s,c,m,h,g,u,d]);f(()=>{E&&N.current(j.current,A.current)},[E]),f(()=>{!O&&!E&&z()},[O,E,z]),I.current=o,f(()=>{E&&w&&(F.current?.dispose(),F.current=j.current?.onDidChangeModelContent(e=>{R.current||w(j.current.getValue(),e)}))},[E,w]),f(()=>{if(E){let e=A.current.editor.onDidChangeMarkers(e=>{let t=j.current.getModel()?.uri;if(t&&e.find(e=>e.path===t.path)){let e=A.current.editor.getModelMarkers({resource:t});T?.(e)}});return()=>{e?.dispose()}}return()=>{}},[E,T]);function B(){F.current?.dispose(),_?g&&Mi.set(c,j.current.saveViewState()):j.current.getModel()?.dispose(),j.current.dispose()}return a.createElement(bi,{width:v,height:y,isEditorReady:E,loading:p,_ref:M,className:b,wrapperProps:x})}var Pi=n(Ni);r();function Fi(e,t){let n=[];for(let[r,i]of Object.entries(e))r!==t&&(n.push({filePath:`file:///${r}`,content:i}),r.endsWith(`.ts`)&&n.push({filePath:`file:///${r.slice(0,-3)}.js`,content:i}));return n}var Ii={target:99,module:99,moduleResolution:2,strict:!0,noImplicitAny:!0,strictNullChecks:!0,allowNonTsExtensions:!0,isolatedModules:!0};function Li(e,t){let n=e.languages.typescript.typescriptDefaults;n.setCompilerOptions({...n.getCompilerOptions(),...Ii}),n.setExtraLibs(t.map(e=>({filePath:e.filePath,content:e.content})))}function Ri({value:n,onChange:r,extraLibs:i=[],path:a=`file:///block.ts`,onDiagnostics:o,readOnly:s=!1,heightPx:c=420,dark:l=!0}){let u=t(null),p=e((e,t)=>{if(u.current=t,Li(t,i),o===void 0)return;let n=e.getModel();if(n===null)return;let r=()=>{o(t.editor.getModelMarkers({resource:n.uri}).map(e=>({line:e.startLineNumber,column:e.startColumn,message:e.message,severity:e.severity})))};t.editor.onDidChangeMarkers(r),r()},[i,o]);return f(()=>{let e=u.current;e!==null&&Li(e,i)},[i]),d(Pi,{height:c,defaultLanguage:`typescript`,path:a,theme:l?`vs-dark`:`vs`,value:n,onChange:e=>r(e??``),onMount:p,options:{readOnly:s,minimap:{enabled:!1},fontSize:12,lineNumbers:`on`,scrollBeyondLastLine:!1,automaticLayout:!0,tabSize:2,renderWhitespace:`selection`}})}function zi(e){return e.some(e=>e.severity>=8)}var Bi=[{id:`blank`,name:`Blank script`,description:`Minimal hello + timer disposer`,entry:`main.ts`,files:{"main.ts":`import type { AddonContext } from '@camstack/types'
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* A core block. Runs in its own process, with the same \`ctx\` an addon gets.
|
|
18
|
-
* Return a function to release whatever you took (timers, sockets, listeners).
|
|
19
|
-
*
|
|
20
|
-
* Declaring devices: import { reconcileBlockDevices, exposeBlockDevices } from
|
|
21
|
-
* '@camstack/types' and hang them off the shared Blocks integration.
|
|
22
|
-
*/
|
|
23
|
-
export default function block(ctx: AddonContext) {
|
|
24
|
-
ctx.logger.info('hello from a core block')
|
|
25
|
-
|
|
26
|
-
const timer = setInterval(() => {
|
|
27
|
-
ctx.logger.info('tick')
|
|
28
|
-
}, 60_000)
|
|
29
|
-
|
|
30
|
-
return () => {
|
|
31
|
-
clearInterval(timer)
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
`}}];function Vi(e){let t=e instanceof Set?e:new Set(e);if(!t.has(`file.ts`))return`file.ts`;let n=2;for(;t.has(`file-${String(n)}.ts`);)n+=1;return`file-${String(n)}.ts`}var Hi=Bi.find(e=>e.id===`blank`)?.files[`main.ts`]??`import type { AddonContext } from '@camstack/types'
|
|
35
|
-
|
|
36
|
-
export default function block(ctx: AddonContext) {
|
|
37
|
-
ctx.logger.info('hello from a core block')
|
|
38
|
-
return () => undefined
|
|
39
|
-
}
|
|
40
|
-
`;function Ui({existing:e,dirty:t,saving:n,restarting:r}){return e===null?`Save the script first — there is no process to restart yet.`:n?`Saving…`:r?`Restarting…`:t?`Save first — a restart now would run the last SAVED code, not what is on screen.`:e.enabled?null:`This script is disabled. Enable it instead of restarting it.`}r();var Wi=5e3;function Gi(e){if(!Array.isArray(e))return[];let t=[];for(let n of e){if(typeof n!=`object`||!n)continue;let e=Reflect.get(n,`id`),r=Reflect.get(n,`name`),i=Reflect.get(n,`type`),a=Reflect.get(n,`stableId`);typeof e!=`number`||typeof r!=`string`||typeof i!=`string`||t.push({id:e,name:r,type:i,...typeof a==`string`?{stableId:a}:{}})}return t}function Ki({blockId:e}){let t=q(e),n=le({addonId:t,projection:`slim`},{refetchInterval:Wi}),r=o(()=>Gi(n.data),[n.data]);return n.isLoading?i(`p`,{className:`flex items-center gap-2 text-xs text-foreground-subtle`,children:[d(x,{className:`h-3.5 w-3.5 animate-spin`}),` Loading devices owned by this script…`]}):n.isError?i(`p`,{className:`text-xs text-danger`,children:[`Could not list devices:`,` `,n.error instanceof Error?n.error.message:String(n.error)]}):i(`section`,{className:`space-y-3 rounded border border-border p-3`,"data-testid":`script-devices-panel`,children:[i(`div`,{className:`flex items-center justify-between gap-2`,children:[i(`div`,{children:[d(`h4`,{className:`text-sm font-medium`,children:`Devices`}),i(`p`,{className:`text-xs text-foreground-subtle`,children:[`Declared by this script under the shared Blocks integration (`,d(`code`,{className:`text-[11px]`,children:t}),`). Open a device for its export section.`]})]}),d(G,{variant:`default`,children:r.length})]}),r.length===0?i(`p`,{className:`text-xs text-foreground-subtle`,children:[`No devices yet. Call `,d(`code`,{children:`reconcileBlockDevices(ctx, …)`}),` from the script (and Restart) to declare them.`]}):d(`ul`,{className:`space-y-3`,children:r.map(e=>d(`li`,{className:`rounded border border-border/80 px-3 py-2`,"data-testid":`script-device-${e.id}`,children:i(`div`,{className:`flex flex-wrap items-baseline gap-2`,children:[d(Pe,{to:`/devices/${String(e.id)}`,className:`text-sm font-medium hover:underline`,title:`Open device detail`,children:e.name}),d(G,{variant:`info`,style:`outline`,children:e.type}),e.stableId!==void 0&&i(`span`,{className:`text-[11px] text-foreground-subtle`,children:[`stableId `,d(`code`,{children:e.stableId})]}),i(`span`,{className:`text-[11px] text-foreground-subtle`,children:[`#`,e.id]})]})},e.id))})]})}r();function qi(e){return e===`refresh-channel-catalog`||e===`send-tv-schedule-now`||e===`refresh-nordvpn-catalog`||e===`probe-nordvpn-italy`||e===`test-nordvpn-endpoint`||e===`refresh-unifi-catalog`}function Ji(e){let t=Date.now();switch(e){case`refresh-channel-catalog`:return{channelCatalogRefreshAt:t};case`send-tv-schedule-now`:return{sendNowAt:t};case`refresh-nordvpn-catalog`:return{nordVpnCatalogRefreshAt:t};case`probe-nordvpn-italy`:return{nordVpnProbeAt:t};case`test-nordvpn-endpoint`:return{nordVpnTestAt:t};case`refresh-unifi-catalog`:return{unifiCatalogRefreshAt:t}}}function Yi(e){switch(e){case`refresh-channel-catalog`:return`Script enabled so the channel catalog can scrape.`;case`send-tv-schedule-now`:return`Script enabled so the TV schedule can send.`;case`refresh-nordvpn-catalog`:return`Script enabled so the NordVPN catalog can refresh.`;case`probe-nordvpn-italy`:return`Script enabled so Italy stations can be probed.`;case`test-nordvpn-endpoint`:return`Script enabled so the VPN endpoint can be tested.`;case`refresh-unifi-catalog`:return`Script enabled so UniFi IDs can be fetched.`}}function Xi(e){if(typeof e!=`object`||!e||!(`key`in e))return null;let t=Reflect.get(e,`key`);return typeof t==`string`?t:null}function Zi(e){if(!(typeof e!=`object`||!e||!(`value`in e)))return Reflect.get(e,`value`)}function Qi(e){let t={};for(let n of e.sections)for(let e of n.fields){let n=Xi(e);n!==null&&(t[n]=Zi(e))}return t}function $i(e){return typeof e!=`object`||!e?!1:Array.isArray(Reflect.get(e,`sections`))}function ea({blockId:t}){let n=g(),r=re({blockId:t},{retry:!1}),a=A({blockId:t},{refetchInterval:1e4,retry:!1}),s=fe({onSuccess:()=>{a.refetch()}}),c=O(),u=P(),[f,p]=l(null),[m,h]=l(null),[_,v]=l(null),[y,b]=l(!1),C=$i(a.data)?a.data:null,w=o(()=>C===null?{}:Qi(C),[C]),T=f??w,E=e(e=>{p(e)},[]),D=f!==null,k=e(()=>{f!==null&&(h(null),s.mutate({blockId:t,patch:f},{onSuccess:()=>p(null),onError:e=>h(e instanceof Error?e.message:String(e))}))},[t,f,s]),j=e(async(e,i)=>{if(qi(e)){b(!0),h(null),v(null);try{f!==null&&(await n.trpcClient.coreBlocks.updateBlockSettings.mutate({blockId:t,patch:f}),p(null)),await n.trpcClient.coreBlocks.updateBlockSettings.mutate({blockId:t,patch:Ji(e)}),r.data?.block?.enabled!==!0&&(await u.mutateAsync({blockId:t,enabled:!0}),v(Yi(e))),await c.mutateAsync({blockId:t}),await a.refetch(),e===`send-tv-schedule-now`?v(`Sending tonight’s schedule now…`):e===`probe-nordvpn-italy`?v(`Probing NordVPN Italy stations (no UniFi change)…`):e===`test-nordvpn-endpoint`?v(`Testing VPN endpoint on UniFi…`):e===`refresh-nordvpn-catalog`?v(`Refreshing NordVPN Italy catalog…`):e===`refresh-unifi-catalog`&&v(`Refreshing UniFi ID catalogs…`)}catch(e){h(e instanceof Error?e.message:String(e))}finally{b(!1)}}},[t,r.data?.block?.enabled,f,c,u,a,n.trpcClient.coreBlocks]);return a.isLoading?i(`p`,{className:`flex items-center gap-2 text-xs text-foreground-subtle`,children:[d(x,{className:`h-3.5 w-3.5 animate-spin`}),` Loading script settings…`]}):a.isError?i(`p`,{className:`text-xs text-danger`,"data-testid":`script-settings-error`,children:[`Could not load settings:`,` `,a.error instanceof Error?a.error.message:String(a.error)]}):C===null?null:i(`section`,{className:`space-y-3 rounded border border-border p-3`,"data-testid":`script-settings-panel`,children:[i(`div`,{className:`flex items-center justify-between gap-2`,children:[i(`div`,{children:[d(`h4`,{className:`text-sm font-medium`,children:`Settings`}),i(`p`,{className:`text-xs text-foreground-subtle`,children:[`Declared by this script's `,d(`code`,{children:`settingsSchema`}),` export.`]})]}),i(S,{size:`sm`,variant:`primary`,onClick:k,disabled:!D||s.isPending||y,children:[s.isPending&&d(x,{className:`h-3.5 w-3.5 animate-spin`}),`Save settings`]})]}),d(Me,{schema:C,values:T,onChange:E,onAction:j}),m===null?null:d(`p`,{className:`text-xs text-danger`,"data-testid":`script-settings-save-error`,children:m}),_===null?null:d(`p`,{className:`text-xs text-foreground-subtle`,"data-testid":`script-settings-info`,children:_})]})}r();var ta=`hub`;function na({existing:e,typeDefs:t,typeDefsLoading:n,saving:r,saveError:a,restarting:s,restartError:f,onRestart:p,onSave:m,onCancel:h}){let _=g(),y=oe(),{resolvedMode:C}=b(),w=Ce({code:e?.code??Hi,files:e?.files,entry:e?.entry}),[T,E]=l(e?.name??``),[D,O]=l(w.files),[k,A]=l(w.entry),[j,M]=l(w.entry),[P,ee]=l(e?.placement??ta),[L,R]=l(e?.integrationId??``),[z,B]=l([]),[V,te]=l(!1),[ne,re]=l(`blank`),[ie,U]=l(null),[W,ce]=l(``),le=D[j]??``,ue=e=>{O(t=>({...t,[j]:e}))},de=Object.keys(D).toSorted(),fe=Bi.length>1,pe=o(()=>[...t?.libs??[],...Fi(D,j)],[t?.libs,D,j]),K=u({mutationFn:()=>_.trpcClient.coreBlocks.compile.mutate({files:D,entry:k})}),me=o(()=>z.filter(e=>e.severity>=8),[z]),he=zi(z),ge=T.trim()===``,_e=e===null?null:Ce(e),ve=e===null||_e===null||JSON.stringify(D)!==JSON.stringify(_e.files)||k!==_e.entry||T.trim()!==e.name||P!==e.placement||(L.trim()===``?void 0:L.trim())!==e.integrationId,ye=()=>{K.reset(),m({name:T.trim(),files:D,entry:k,enabled:e?.enabled??!0,placement:P,...L.trim()===``?{}:{integrationId:L.trim()}})},be=()=>{let e=Vi(Object.keys(D));O(t=>({...t,[e]:``})),M(e),U(e),ce(e)},xe=e=>{U(e),ce(e)},Se=()=>{if(ie===null)return;let e=ie,t=W.trim();if(U(null),!(t===``||t===e)){if(D[t]!==void 0){M(t);return}O(n=>{let r={};for(let[i,a]of Object.entries(n))r[i===e?t:i]=a;return r}),k===e&&A(t),M(t)}},we=async e=>{let t=Bi.find(t=>t.id===e);if(t===void 0)return;let n=Bi.find(e=>e.id===`blank`);!(n!==void 0&&JSON.stringify(D)===JSON.stringify(n.files)&&k===n.entry)&&!await y({title:`Load example`,message:`Replace the current editor contents with this example?`,confirmLabel:`Replace`,variant:`warning`})||(re(e),O({...t.files}),A(t.entry),M(t.entry),(T.trim()===``||ne===`blank`)&&E(t.id===`blank`?``:t.name))},Te=e===null?null:Cn(e),Ee=Ui({existing:e,dirty:ve,saving:r,restarting:s});return i(`div`,{className:`space-y-4`,children:[Te!==null&&d(`div`,{className:`flex flex-wrap items-center gap-2`,children:d(G,{variant:wn(Te.tone),children:Te.label})}),i(`div`,{className:`grid gap-3 sm:grid-cols-2`,children:[i(`label`,{className:`block text-sm`,children:[d(`span`,{className:`text-foreground-subtle`,children:`Name`}),d(N,{className:`mt-1`,value:T,onChange:e=>E(e.target.value),placeholder:`What this script does`})]}),i(`label`,{className:`block text-sm`,children:[d(`span`,{className:`text-foreground-subtle`,children:`Integration (optional)`}),d(N,{className:`mt-1`,value:L,onChange:e=>R(e.target.value),placeholder:`Leave empty for the shared system integration`})]})]}),e===null&&fe&&i(`label`,{className:`block text-sm`,children:[d(`span`,{className:`text-foreground-subtle`,children:`Start from`}),d(`select`,{className:`mt-1 w-full rounded border border-border bg-surface px-2 py-1.5 text-sm`,value:ne,onChange:e=>void we(e.target.value),"data-testid":`script-example-select`,children:Bi.map(e=>i(`option`,{value:e.id,children:[e.name,` — `,e.description]},e.id))})]}),i(`div`,{children:[d(I,{selectedNodeId:P,onSelect:ee,label:`Runs on`,alwaysShow:!0}),P!==ta&&i(`p`,{className:`mt-2 flex items-start gap-2 text-xs text-warning`,children:[d(ae,{className:`mt-0.5 h-3.5 w-3.5 shrink-0`}),i(`span`,{children:[`Block execution is hub-only today. Saved with this placement the block will be stored and compiled, but it will report `,d(`strong`,{children:`Failed`}),` instead of running.`]})]})]}),i(`div`,{className:`flex min-h-[56vh] flex-col rounded border border-border`,children:[i(`div`,{className:`flex flex-wrap items-center gap-1 border-b border-border px-2 py-1.5`,children:[de.map(e=>ie===e?d(N,{value:W,onChange:e=>ce(e.target.value),onBlur:()=>Se(),onKeyDown:e=>{e.key===`Enter`&&(e.preventDefault(),Se()),e.key===`Escape`&&(e.preventDefault(),U(null))},className:`h-7 w-36 px-2 py-0 text-xs`,autoFocus:!0,"data-testid":`script-file-rename`,"aria-label":`Rename file`},e):i(`button`,{type:`button`,className:`rounded px-2 py-1 text-xs ${e===j?`bg-muted font-medium text-foreground`:`text-foreground-subtle hover:text-foreground`}`,onClick:()=>M(e),onDoubleClick:()=>xe(e),title:`Double-click to rename`,"data-testid":`script-file-tab-${e}`,children:[e,e===k?` · entry`:``]},e)),d(`button`,{type:`button`,className:`ml-1 rounded px-2 py-1 text-xs text-foreground-subtle hover:text-foreground`,onClick:be,"data-testid":`script-file-add`,children:`+ file`}),j!==k&&d(`button`,{type:`button`,className:`rounded px-2 py-1 text-xs text-foreground-subtle hover:text-foreground`,onClick:()=>A(j),title:`Use this file as the compile entry`,children:`Set as entry`})]}),n?d(`p`,{className:`p-4 text-sm text-foreground-subtle`,children:`Loading the node's type definitions…`}):d(Ri,{value:le,onChange:ue,path:`file:///${j}`,extraLibs:pe,onDiagnostics:B,dark:C===`dark`,heightPx:560})]}),t!==void 0&&t.libs.length===0&&d(`p`,{className:`text-xs text-warning`,children:`The node returned no type definitions, so this code is NOT being checked against the system's types. The server-side compile still applies on save.`}),me.length>0&&i(`div`,{className:`space-y-1 rounded border border-danger/40 bg-danger/10 p-3 text-xs`,children:[i(`p`,{className:`font-medium text-danger`,children:[me.length,` error`,me.length===1?``:`s`,` — the block cannot be saved.`]}),me.slice(0,5).map(e=>i(`p`,{className:`text-danger`,children:[`line `,e.line,`: `,e.message]},`${String(e.line)}:${String(e.column)}:${e.message}`)),me.length>5&&i(`p`,{className:`text-foreground-subtle`,children:[`…and `,me.length-5,` more.`]})]}),K.data!==void 0&&(K.data.ok?i(`p`,{className:`flex items-center gap-2 text-xs text-success`,children:[d(se,{className:`h-3.5 w-3.5`}),` The node compiled this code. Nothing was saved.`]}):i(`div`,{className:`rounded border border-danger/40 bg-danger/10 p-3 text-xs text-danger`,children:[K.data.line===void 0?``:`line ${String(K.data.line)}: `,K.data.error??`the code does not compile`]})),K.error!==null&&i(`div`,{className:`rounded border border-danger/40 bg-danger/10 p-3 text-xs text-danger`,children:[`Could not reach the compiler: `,ra(K.error)]}),a!==null&&d(`div`,{className:`rounded border border-danger/40 bg-danger/10 p-3 text-xs text-danger`,children:a}),f!==null&&d(`div`,{className:`rounded border border-danger/40 bg-danger/10 p-3 text-xs text-danger`,children:f}),e!==null&&i(`div`,{className:`space-y-3`,children:[d(ea,{blockId:e.id}),d(Ki,{blockId:e.id})]}),i(`div`,{className:`flex flex-wrap items-center gap-2`,children:[i(S,{variant:`outline`,onClick:()=>K.mutate(),disabled:K.isPending||r,title:`Type-check on the node without saving`,children:[K.isPending&&d(x,{className:`h-4 w-4 animate-spin`}),` Compile`]}),i(S,{onClick:ye,disabled:r||he||ge,children:[r&&d(x,{className:`h-4 w-4 animate-spin`}),` Save`]}),i(S,{variant:`outline`,onClick:()=>p?.(),disabled:Ee!==null||p===void 0,title:Ee??`Stop the process and let the supervisor bring it back`,children:[s?d(H,{className:`h-4 w-4 animate-spin`}):d(H,{className:`h-4 w-4`}),` `,`Restart`]}),i(S,{variant:`ghost`,onClick:()=>te(!V),title:`Show this script's process logs`,"data-testid":`script-logs-toggle`,children:[d(v,{className:`h-4 w-4`}),` Logs `,V?`▾`:`▸`]}),d(S,{variant:`ghost`,onClick:h,disabled:r,children:`Close`}),i(`span`,{className:`ml-auto text-xs text-foreground-subtle`,children:[`errors: `,me.length,ve&&e!==null?` · unsaved changes`:``]}),ge&&d(G,{variant:`warning`,children:`Name required`})]}),V&&i(`div`,{className:`rounded border border-border`,"data-testid":`script-logs-panel`,children:[d(`div`,{className:`border-b border-border px-3 py-1.5 text-xs text-foreground-subtle`,children:e===null?`Logs appear once the script has been saved and started.`:i(c,{children:[`Logs — `,d(`code`,{children:q(e.id)})]})}),e!==null&&d(F,{addonId:q(e.id),maxHeight:`max-h-[560px]`,showFilters:!1})]})]})}function ra(e){return e instanceof Error?e.message:String(e)}r();var ia=[`coreBlocks`,`list`],aa=[`coreBlocks`,`getTypeDefs`],oa=5e3;function sa(e){return e instanceof Error?e.message:String(e)}function ca({initialBlockId:e,onBlockIdChange:n,createRequested:r=!1,onCreateConsumed:a}={}){let c=g(),{state:p,openNew:h,openEdit:_,close:y}=E({editKey:`block`,newKey:`new`}),[b,x]=l(null),[T,D]=l(null),[O,k]=l(null),A=s({queryKey:ia,refetchInterval:oa,queryFn:async()=>(await c.trpcClient.coreBlocks.list.query({})).blocks}),j=p.mode!==`closed`,M=s({queryKey:aa,enabled:j,staleTime:1/0,gcTime:1/0,queryFn:()=>c.trpcClient.coreBlocks.getTypeDefs.query({})}),N=()=>{A.refetch()},P=t(null),I=A.data;f(()=>{e===void 0||P.current===e||I?.find(t=>t.id===e&&ye(t)===`code`)!==void 0&&(P.current=e,_(e),n?.(e))},[e,I,_,n]),f(()=>{r&&(x(null),h(),n?.(null),a?.())},[r,a,n,h]);let L=u({mutationFn:e=>c.trpcClient.coreBlocks.create.mutate({block:e}),onSuccess:e=>{x(e.block),_(e.block.id),n?.(e.block.id),N()}}),R=u({mutationFn:e=>c.trpcClient.coreBlocks.update.mutate(e),onSuccess:e=>{x(e.block),_(e.block.id),n?.(e.block.id),N()}}),z=u({mutationFn:e=>c.trpcClient.coreBlocks.delete.mutate({blockId:e}),onSuccess:()=>{D(null),N()}}),B=u({mutationFn:e=>c.trpcClient.coreBlocks.setEnabled.mutate(e),onSuccess:N}),V=u({mutationFn:e=>c.trpcClient.coreBlocks.restart.mutate({blockId:e}),onSuccess:N}),te=En((A.data??[]).filter(e=>ye(e)===`code`)),ne=A.data!==void 0&&te.length===0,re=L.isPending||R.isPending,ie=L.error===null?R.error===null?null:sa(R.error):sa(L.error),ae=p.mode===`edit`?te.find(e=>e.id===p.id)??(b!==null&&b.id===p.id?b:null):null,U=p.mode===`new`||ae===null?null:Tn(te,ae),oe=o(()=>[{key:`name`,header:`Name`,render:e=>{let t=Cn(e),n=ue(e.updatedAt);return i(`div`,{className:`min-w-0`,children:[i(`div`,{className:`flex flex-wrap items-center gap-2`,children:[d(`span`,{className:`truncate font-medium`,children:e.name}),d(G,{variant:wn(t.tone),children:t.label}),d(G,{variant:`info`,children:e.placement}),n!==null&&i(`span`,{className:`text-xs text-foreground-subtle`,children:[`edited `,n]})]}),d(`p`,{className:`mt-0.5 text-xs text-foreground-subtle`,children:t.detail}),O===e.id&&i(`div`,{className:`mt-2 rounded border border-border`,children:[i(`div`,{className:`border-b border-border px-3 py-1.5 text-xs text-foreground-subtle`,children:[`Logs — `,d(`code`,{children:q(e.id)})]}),d(F,{addonId:q(e.id),maxHeight:`max-h-64`,showFilters:!1})]})]})},sortValue:e=>e.name,nowrap:!1},{key:`actions`,header:``,align:`right`,notSortableReason:`Row actions`,render:e=>{let t=B.isPending||z.isPending,r=V.isPending&&V.variables===e.id;return T===e.id?i(`div`,{className:`flex justify-end gap-1`,children:[d(`span`,{className:`text-xs text-foreground-subtle`,children:`Delete this script?`}),d(S,{variant:`danger`,onClick:()=>z.mutate(e.id),disabled:t,children:`Delete`}),d(S,{variant:`ghost`,onClick:()=>D(null),children:`Cancel`})]}):i(`div`,{className:`flex justify-end gap-1`,children:[d(S,{variant:`ghost`,onClick:t=>{t.stopPropagation(),x(e),_(e.id),n?.(e.id)},title:`Edit`,children:`Edit`}),d(S,{variant:`ghost`,onClick:t=>{t.stopPropagation(),V.mutate(e.id)},disabled:t||r||!e.enabled,title:e.enabled?`Restart`:`Enable it instead — a disabled script has no process`,children:d(H,{className:`h-4 w-4${r?` animate-spin`:``}`})}),d(S,{variant:`ghost`,onClick:t=>{t.stopPropagation(),k(O===e.id?null:e.id)},title:`Logs`,children:d(v,{className:`h-4 w-4`})}),d(S,{variant:`ghost`,onClick:t=>{t.stopPropagation(),B.mutate({blockId:e.id,enabled:!e.enabled})},disabled:t,title:e.enabled?`Disable`:`Enable`,children:e.enabled?`Disable`:`Enable`}),d(S,{variant:`ghost`,onClick:t=>{t.stopPropagation(),D(e.id)},title:`Delete`,children:d(m,{className:`h-4 w-4`})})]})}}],[T,O,n,_,z,V.isPending,V.variables,B]);return i(`div`,{className:`space-y-3`,children:[d(`p`,{className:`text-xs text-foreground-subtle`,children:A.data===void 0?``:Dn(te)}),A.error!=null&&d(C,{title:`Could not read the scripts`,message:sa(A.error),onRetry:N}),d(w,{columns:oe,rows:te,rowKey:e=>e.id,pageSize:25,maxBodyHeight:ee,mobileMode:`scroll`,emptyMessage:ne?`No scripts yet. A script is TypeScript the hub runs in its own process.`:A.data===void 0?`Loading scripts…`:`No scripts yet.`,onRowClick:e=>{x(e),_(e.id),n?.(e.id)}}),d(de,{open:j,onClose:()=>{L.reset(),R.reset(),V.reset(),x(null),y(),n?.(null)},title:U===null?`New script`:`Edit ${U.name}`,icon:He,width:`xl`,testId:`script-editor-panel`,children:d(na,{existing:U,typeDefs:M.data,typeDefsLoading:M.isLoading,saving:re,saveError:ie,restarting:V.isPending,restartError:V.error===null?null:sa(V.error),onRestart:U===null?void 0:()=>V.mutate(U.id),onSave:e=>{let t=U;t===null?L.mutate(e):R.mutate({blockId:t.id,block:e})},onCancel:()=>{L.reset(),R.reset(),V.reset(),x(null),y(),n?.(null)}})}),z.error!=null&&d(C,{message:sa(z.error)}),B.error!=null&&d(C,{message:sa(B.error)}),V.error!=null&&d(C,{message:sa(V.error)})]})}var la=[`automations`,`scripts`,`custom-device`];function ua(e){return e!==null&&la.some(t=>t===e)}var da=[`coreBlocks`,`list`,`build-counts`],fa=[`block`,`create`];function pa(e){return e.filter(e=>ye(e)===`code`).length}function ma(e){return e.filter(e=>Ke(e.code)).length}function ha(e,t){return t===null?e:`${e} (${String(t)})`}function ga(){let{t:e}=Oe(),t=g(),[n,r]=ke(),a=n.get(`tab`),o=ua(a)?a:`automations`,c=n.get(`create`)===`1`,l=s({queryKey:da,refetchInterval:5e3,queryFn:async()=>(await t.trpcClient.coreBlocks.list.query({})).blocks}).data,u=l===void 0?null:pa(l),f=l===void 0?null:ma(l),p=s({queryKey:[`coreBlocks`,`listCompositions`,`build-counts`],refetchInterval:1e4,queryFn:async()=>t.trpcClient.coreBlocks.listCompositions.query({})}),m=p.data?.state===`loaded`?p.data.views.length:null,h=e=>{r(t=>{let n=new URLSearchParams(t);return n.set(`tab`,`scripts`),n.delete(`create`),e===void 0?n.delete(`block`):n.set(`block`,e),n},{replace:!0})},_=e=>{r(t=>{let n=new URLSearchParams(t);return n.set(`tab`,`automations`),n.delete(`create`),e===null?n.delete(`block`):n.set(`block`,e),n},{replace:!0})},v=e=>{r(t=>{let n=new URLSearchParams(t);return n.set(`tab`,`scripts`),n.delete(`create`),e===null?n.delete(`block`):n.set(`block`,e),n},{replace:!0})},y=()=>{r(e=>{let t=new URLSearchParams(e);return t.set(`create`,`1`),t.delete(`block`),t},{replace:!0})},b=()=>{r(e=>{let t=new URLSearchParams(e);return t.delete(`create`),t},{replace:!0})},x=o===`automations`?`New automation`:o===`scripts`?`New script`:`New composition`,C=Ae(`/build`,{t:e,isAdmin:!0})??{icon:D,title:e(`nav.build`,`Build-your-self`)};return d(Ie,{icon:C.icon,title:C.title,subtitle:`Automations, scripts and composed devices — what this installation does on its own.`,defaultSection:`automations`,sectionsLabel:`${C.title} sections`,fill:!0,actions:i(S,{type:`button`,onClick:y,"data-testid":`build-new`,children:[d(B,{className:`h-4 w-4`}),` `,x]}),sections:[{id:`automations`,label:ha(`Automations`,f),ownedParams:fa,content:d(Nn,{onOpenScripts:h,initialBlockId:o===`automations`?n.get(`block`)??void 0:void 0,onBlockIdChange:_,createRequested:o===`automations`&&c,onCreateConsumed:b})},{id:`scripts`,label:ha(`Scripts`,u),ownedParams:fa,content:d(ca,{initialBlockId:o===`scripts`?n.get(`block`)??void 0:void 0,onBlockIdChange:v,createRequested:o===`scripts`&&c,onCreateConsumed:b})},{id:`custom-device`,label:ha(`Custom Device`,m),ownedParams:fa,content:d(er,{createRequested:o===`custom-device`&&c,onCreateConsumed:b})}]})}export{ga as BuildPage};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{I as e,K as t,Q as n,S as r,W as i,a,b as o,o as s,q as c,t as l,x as u,z as d}from"./_virtual_mf___mfe_internal__admin_ui_host__loadShare___mf_0_tanstack_mf_1_react_mf_2_query__loadShare__.js-DhP-Lgiq.js";import{_t as f,ct as p,gt as m,ot as h,yt as g}from"./player-overlays-mP7nvLXZ.js";import{$u as _,Ad as v,Al as y,An as b,Bl as x,Dl as S,Ed as C,Fl as w,Fn as T,Ft as E,G as D,Gd as O,Gn as k,Hl as A,Hn as j,Ht as ee,Il as te,In as M,Jl as N,Ju as P,L as ne,Ll as F,Md as I,Ml as L,Mn as R,Mr as z,N as re,Nl as ie,Od as ae,Ol as oe,P as se,Pl as ce,Pn as B,Ql as V,Rl as le,Tn as ue,Un as de,Vl as fe,Vu as pe,Wn as me,X as he,Yu as H,Z as ge,cf as _e,dd as ve,dn as U,fu as W,jd as ye,jl as be,jn as G,jr as xe,kl as Se,md as Ce,mu as we,nr as K,ql as Te,qu as q,rr as Ee,ur as De,va as Oe,wd as ke,wu as Ae,z as je,zl as Me}from"./src-BHYGdVQH.js";import{t as Ne}from"./history-GFLGqkV_.js";import{t as Pe}from"./monitor-smartphone-CEvv3FjX.js";import{_ as J,a as Fe,b as Ie,c as Le,d as Re,f as ze,g as Be,h as Y,i as Ve,l as He,m as Ue,n as We,o as Ge,p as Ke,s as qe,t as Je,u as Ye,v as X,y as Xe}from"./SessionsList-vIu2aubr.js";import{a as Ze,hi as Qe,i as $e,mi as et,o as tt,pi as nt,s as rt}from"./client-D5nV-lIF.js";import{t as it}from"./useTranslation-Bc5yH9V9.js";import{$ as at,C as ot,R as st,X as ct,Y as lt,at as ut,c as dt,p as ft,rt as pt,s as mt,tt as ht}from"./index-D-W8ahNU.js";import{t as gt}from"./AdminSectionedPage-DMUZLoRM.js";import{t as _t}from"./AdminPage-oBqVY0PY.js";import{n as vt,r as yt,t as bt}from"./AddonIntegrationBody-DEH_hiBL.js";import{n as xt,t as St}from"./addon-display-names-BkwMrExv.js";var Ct=g(`book-open`,[[`path`,{d:`M12 7v14`,key:`1akyts`}],[`path`,{d:`M3 18a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1h5a4 4 0 0 1 4 4 4 4 0 0 1 4-4h5a1 1 0 0 1 1 1v13a1 1 0 0 1-1 1h-6a3 3 0 0 0-3 3 3 3 0 0 0-3-3z`,key:`ruj8y`}]]),wt=g(`key-square`,[[`path`,{d:`M12.4 2.7a2.5 2.5 0 0 1 3.4 0l5.5 5.5a2.5 2.5 0 0 1 0 3.4l-3.7 3.7a2.5 2.5 0 0 1-3.4 0L8.7 9.8a2.5 2.5 0 0 1 0-3.4z`,key:`165ttr`}],[`path`,{d:`m14 7 3 3`,key:`1r5n42`}],[`path`,{d:`m9.4 10.6-6.814 6.814A2 2 0 0 0 2 18.828V21a1 1 0 0 0 1 1h3a1 1 0 0 0 1-1v-1a1 1 0 0 1 1-1h1a1 1 0 0 0 1-1v-1a1 1 0 0 1 1-1h.172a2 2 0 0 0 1.414-.586l.814-.814`,key:`1ubxi2`}]]);n();function Tt(){let e=K(),t=W.useUtils(),n=Xe(),{data:r,isLoading:a}=w(),o=i(()=>r??[],[r]),s=W.useQueries(e=>o.map(t=>e.userManagement.listExternalIdentities({userId:t.id}))),l=W.auth.listLoginMethods.useQuery(),[d,f]=c(null),p=fe({onSuccess:()=>void t.userManagement.listExternalIdentities.invalidate(),onError:e=>f(`Remove failed: ${e.message}`)}),m=i(()=>new Map((l.data??[]).flatMap(e=>e.kind===`redirect`?[[e.id,e.label]]:[])),[l.data]),h=o.flatMap((e,t)=>s[t]?.data??[]),g=h.map(e=>({...Re(e,n),kind:m.get(e.provider)??e.provider})),_=async t=>{let n=h.find(e=>Ye(e)===t.id);n!==void 0&&await e({title:`Remove sign-in link`,message:`"${t.ownerName}" will no longer be able to sign in with this ${t.kind} account.`,confirmLabel:`Remove`,variant:`danger`})&&(f(null),p.mutate({userId:n.userId,provider:n.provider,subject:n.subject}))};return u(J,{notice:X(d),onDismissNotice:()=>{f(null)},id:`external-identities`,title:`Sign-in links`,icon:ut,description:`External accounts (single sign-on) each user linked to their CamStack account. Only the owner can link one, from their profile; an admin can remove it.`,children:u(Y,{rows:g,label:`sign-in links`,isLoading:a||s.some(e=>e.isLoading),loadError:s.find(e=>e.isError)?.error?.message??null,emptyText:`No user has linked a sign-in account.`,expiresNone:`—`,action:{label:`Remove`,icon:Ie,isAvailable:()=>!0,onRun:e=>void _(e),busyId:null,disabled:p.isPending}})})}n();function Et(){let e=K(),t=s(),n=Xe(),r=L(),[a,o]=c(null),l=F({onSuccess:()=>void t.invalidateQueries({queryKey:[[`userManagement`,`listApiKeys`]]}),onError:e=>o(`Revoke failed: ${e.message}`)}),d=i(()=>(r.data??[]).map(e=>He(e,n)),[r.data,n]),f=async t=>{await e({title:`Revoke "${t.name}"?`,message:`Any client using this key loses access immediately. This cannot be undone.`,confirmLabel:`Revoke`,variant:`danger`})&&(o(null),l.mutate({id:t.id}))};return u(J,{notice:X(a),onDismissNotice:()=>{o(null)},id:`legacy-api-keys`,title:`Legacy API keys`,icon:Ne,description:`Keys from before scoped tokens. Nothing creates them any more; revoke them and issue a scoped token instead.`,children:u(Y,{rows:d,label:`legacy API keys`,isLoading:r.isLoading,loadError:r.isError?r.error?.message??`Request failed`:null,emptyText:`No legacy API keys.`,action:{label:`Revoke`,icon:p,isAvailable:()=>!0,onRun:e=>void f(e),busyId:l.isPending?l.variables?.id??null:null,disabled:l.isPending}})})}n();function Dt(e){if(e.type===`category`){let t=e.target??``;return t===`device`?`all devices`:`all ${t}`}if(e.type===`device`)return`device:[${(e.targets??[]).join(`,`)}]`;let t=e;return`${t.type}:${t.target??``}`}function Ot(){let e=K(),t=s(),n=ce(),[r,a]=c(null),o=le({onSuccess:()=>void t.invalidateQueries({queryKey:[[`userManagement`,`listOauthSessions`]]}),onError:e=>a(`Revoke failed: ${e.message}`)}),l=i(()=>(n.data??[]).map(e=>ze(e,e.scopes.map(Dt).join(`, `))),[n.data]),d=async t=>{await e({title:`Revoke the "${t.name}" link?`,message:`${t.name} loses access for ${t.ownerName} on its next call. Linking it again needs the account-link flow from the start.`,confirmLabel:`Revoke`,variant:`danger`})&&(a(null),o.mutate({id:t.id}))},f=o.isPending?o.variables?.id??null:null;return u(J,{notice:X(r),onDismissNotice:()=>{a(null)},id:`linked-apps`,title:`Linked apps`,icon:ae,description:`External integrations (Alexa, …) linked to this hub through OAuth. Revoking one stops that integration on its next call.`,children:u(Y,{rows:l,label:`linked apps`,isLoading:n.isLoading,loadError:n.isError?n.error?.message??`Request failed`:null,emptyText:`No linked apps — an integration appears here once it completes the account-link flow.`,expiresNone:`—`,action:{label:`Revoke`,icon:p,isAvailable:e=>e.status===`active`,onRun:e=>void d(e),busyId:f,disabled:o.isPending}})})}n();var kt={one:`session`,many:`sessions`};function At(){let e=K(),t=W.useUtils(),n=Xe(),r=W.auth.listSessions.useQuery(),[a,o]=c(null),s=()=>void t.auth.listSessions.invalidate(),l=W.auth.revokeSessions.useMutation({onSuccess:s,onError:e=>o(`Sign out failed: ${e.message}`)}),d=Be(`login-session`,kt,s),f=i(()=>(r.data??[]).map(e=>Ke(e,n,{client:e=>Fe(e).label,method:Ge})),[r.data,n]),p=async t=>{await e({title:`Sign out this session`,message:`${t.ownerName} on ${t.name} will have to sign in again.`,confirmLabel:`Sign out`,variant:`danger`})&&(o(null),l.mutate({id:t.id}))};return u(J,{notice:X(a,d.outcome),onDismissNotice:()=>{o(null),d.dismiss()},id:`login-sessions`,title:`Login sessions`,icon:Pe,description:`Every browser and app signed in to this hub, for every user. Signing one out ends it at once; it has to sign in again. Your own current session is marked and never included in a bulk sign-out.`,children:u(Y,{rows:f,label:`sessions`,isLoading:r.isLoading,loadError:r.isError?r.error?.message??`Request failed`:null,emptyText:`No active sessions.`,action:{label:`Sign out`,icon:ht,isAvailable:e=>e.status===`active`,onRun:e=>void p(e),busyId:l.isPending&&l.variables!==void 0&&`id`in l.variables?l.variables.id:null,disabled:l.isPending||d.busy},bulk:{revoke:d,noun:kt,isSelectable:e=>e.status===`active`}})})}n();function jt({open:e,users:t,onClose:n,onSubmit:i,submitting:a}){let{user:o}=st(),s=o?.isAdmin===!0?null:o?.scopes??[],[l,d]=c(t[0]?.id??``),[f,p]=c(``),[m,h]=c([{type:`capability`,target:``,access:[`view`,`create`]}]),[g,_]=c(``),v=se(m);return u(je,{open:e,onClose:n,title:`Create scoped token`,icon:q,width:`md`,testId:`create-scoped-token-dialog`,footer:r(k,{type:`button`,variant:`primary`,size:`sm`,onClick:()=>{let e=parseInt(g,10),t=Number.isFinite(e)&&e>0?Date.now()+e*864e5:void 0;i({userId:l,name:f.trim(),scopes:m,expiresAt:t})},disabled:!(l!==``&&f.trim().length>0&&m.length>0&&v===null&&!a),children:[a?u(C,{className:`h-3.5 w-3.5 animate-spin`,"aria-hidden":!0}):u(q,{className:`h-3.5 w-3.5`,"aria-hidden":!0}),`Issue token`]}),children:r(`div`,{className:`space-y-3`,children:[r(`div`,{className:`space-y-1`,children:[u(U,{htmlFor:`scoped-token-user`,children:`On behalf of user`}),u(Te,{id:`scoped-token-user`,value:l,onChange:e=>d(e.target.value),options:t.map(e=>({value:e.id,label:e.username}))})]}),u(D,{label:`Name`,required:!0,children:u(V,{id:`scoped-token-name`,value:f,onChange:e=>p(e.target.value),placeholder:`cloudflare-tunnel-route`,autoFocus:!0})}),r(`div`,{className:`space-y-1`,children:[u(U,{children:`Scopes`}),u(re,{value:m,onChange:h,clampToParent:s}),v&&u(`p`,{className:`text-[10px] text-danger`,children:v}),u(`p`,{className:`text-[10px] text-foreground-subtle`,children:`Each scope picks a (cap or addon) target + one or more access flavours. The token can access ONLY what's listed.`})]}),r(`div`,{className:`space-y-1`,children:[u(U,{htmlFor:`scoped-token-days`,children:`Expires in (days)`}),u(V,{id:`scoped-token-days`,type:`number`,min:1,value:g,onChange:e=>_(e.target.value),placeholder:`30`}),u(`p`,{className:`text-[10px] text-foreground-subtle`,children:`Leave blank for non-expiring.`})]})]})})}var Mt={scoped:`newScoped`,admin:`newAdmin`};function Nt({token:e,label:t,onClose:n}){return u(b,{open:!0,onOpenChange:e=>!e&&n(),children:r(R,{width:`md`,scroll:`body`,children:[u(T,{children:u(M,{children:`Token: ${t}`})}),r(G,{className:`space-y-3`,children:[u(`div`,{className:`rounded-md border border-warning/30 bg-warning/10 px-3 py-2 text-xs text-warning`,children:`Copy this token now. Once you close this dialog the secret cannot be displayed again.`}),u(`div`,{className:`select-all break-all rounded-md border border-border bg-background p-2 font-mono text-[11px] text-foreground`,children:e})]}),r(B,{children:[u(E,{value:e,label:`Copy`,srLabel:`token`}),u(k,{type:`button`,variant:`primary`,size:`sm`,onClick:n,children:`Done`})]})]})})}n();function Pt(e){return e.type===`device`?`device:[${ge(e.selector)}]`:`${e.type}:${e.target??``}`}function Ft(){let e=K(),t=s(),n=Xe(),{data:a,isLoading:o}=w(),l=i(()=>a??[],[a]),d=W.useQueries(e=>l.map(t=>e.userManagement.listScopedTokens({userId:t.id}))),[f,m]=c(null),[h,g]=c(null),{state:_,openNew:v,close:y}=ne({newKey:Mt.scoped}),b=()=>void t.invalidateQueries({queryKey:[[`userManagement`,`listScopedTokens`]]}),x=S({onSuccess:({token:e,record:t})=>{b(),y(),g({token:e,label:t.name})},onError:e=>m(`Create failed: ${e.message}`)}),C=Me({onSuccess:b,onError:e=>m(`Revoke failed: ${e.message}`)}),T=Date.now(),E=l.flatMap((e,t)=>(d[t]?.data??[]).map(t=>Ue(t,e.id,n,t.scopes.map(Pt).join(`, `),T))),D=d.find(e=>e.isError)?.error?.message??null,O=async t=>{await e({title:`Revoke "${t.name}"?`,message:`${t.ownerName}'s scoped token stops working on its next use. This cannot be undone.`,confirmLabel:`Revoke`,variant:`danger`})&&(m(null),C.mutate({id:t.id}))};return r(J,{notice:X(f),onDismissNotice:()=>{m(null)},id:`scoped-tokens`,title:`Scoped tokens`,icon:ye,description:`Tokens issued on behalf of a user for one narrow surface — an addon, an integration, a capability or some devices. Every token must be scoped, so revoking one has a predictable blast radius.`,actions:r(k,{type:`button`,variant:`primary`,size:`sm`,onClick:v,disabled:l.length===0,title:l.length===0?`Create a user first`:`Issue a scoped token`,children:[u(q,{className:`h-3.5 w-3.5`,"aria-hidden":!0}),`New token`]}),children:[u(Y,{rows:E,label:`scoped tokens`,isLoading:o||d.some(e=>e.isLoading),loadError:D,emptyText:`No scoped tokens.`,action:{label:`Revoke`,icon:p,isAvailable:()=>!0,onRun:e=>void O(e),busyId:C.isPending?C.variables?.id??null:null,disabled:C.isPending}}),u(jt,{open:_.mode===`new`,users:l.map(e=>({id:e.id,username:e.username})),onClose:y,onSubmit:e=>x.mutate({userId:e.userId,name:e.name,scopes:e.scopes,...e.expiresAt===void 0?{}:{expiresAt:e.expiresAt}}),submitting:x.isPending}),h!==null&&u(Nt,{token:h.token,label:h.label,onClose:()=>g(null)})]})}n();var It={one:`admin token`,many:`admin tokens`},Lt=7,Rt=90,zt=80;function Bt(e){let t=Number(e);return Number.isInteger(t)&&t>=1&&t<=Rt?t:null}function Vt(){let{user:e}=st();return e?.isAdmin?u(Ht,{}):null}function Ht(){let e=K(),t=W.useUtils(),n=W.auth.listAdminTokens.useQuery(),a=Xe(),{requestProof:o,dialog:s}=Ve(`Creating an admin token`),{state:l,openNew:f,close:m}=ne({newKey:Mt.admin}),[h,g]=c(``),[_,v]=c(String(Lt)),[y,b]=c(null),[x,S]=c(null),[w,T]=c(!1),[E,D]=c(null),O=()=>{t.auth.listAdminTokens.invalidate(),t.auth.listAuthAudit.invalidate()},A=W.auth.createAdminToken.useMutation({onSuccess:({token:e,record:t})=>{O(),g(``),v(String(Lt)),S(null),m(),D({token:e,label:t.name})},onError:e=>{b(`Create failed: ${e.message}`),S(`Create failed: ${e.message}`)}}),j=W.auth.revokeAdminToken.useMutation({onSuccess:O,onError:e=>b(`Revoke failed: ${e.message}`)}),ee=Be(`admin-token`,It,O);d(()=>{l.mode===`closed`&&(g(``),v(String(Lt)),S(null),T(!1))},[l.mode]);let te=Date.now(),M=i(()=>(n.data??[]).map(e=>Le(e,a,te)),[n.data,a]),N=h.trim(),F=Bt(_),I=N.length>0&&N.length<=zt&&F!==null&&!A.isPending&&!w,L=async()=>{if(!I||F===null)return;b(null),S(null),T(!0);let e=await o();T(!1),e!==null&&A.mutate({name:N,expiresInDays:F,reauthToken:e})},R=async t=>{await e({title:`Revoke admin token "${t.name}"?`,message:`Every script using it loses access immediately. This cannot be undone.`,confirmLabel:`Revoke`,variant:`danger`})&&(b(null),j.mutate({id:t.id}))};return r(`div`,{"data-testid":`admin-tokens`,children:[s,u(J,{id:`admin-tokens`,title:`Admin tokens`,icon:P,tone:`danger`,notice:X(y,ee.outcome),onDismissNotice:()=>{b(null),ee.dismiss()},description:r(`div`,{className:`space-y-1`,children:[r(`p`,{children:[`An admin token acts as a `,u(`strong`,{children:`full administrator`}),` for scripts, e.g.`,` `,u(`code`,{className:`font-mono`,children:`node scripts/hub/hub.mjs --token-file <file> …`}),`. Anyone holding it can do anything an admin can.`]}),u(`p`,{children:`It expires in at most 90 days and every use is audited. Creating one asks you to confirm it is you.`})]}),actions:r(k,{type:`button`,variant:`danger`,size:`sm`,onClick:f,children:[u(P,{className:`h-3.5 w-3.5`,"aria-hidden":!0}),`New admin token`]}),children:u(Y,{rows:M,label:`admin tokens`,isLoading:n.isLoading,loadError:n.isError?n.error?.message??`Request failed`:null,emptyText:`No admin tokens.`,action:{label:`Revoke`,icon:p,isAvailable:e=>e.status===`active`,onRun:e=>void R(e),busyId:j.isPending?j.variables?.id??null:null,disabled:j.isPending||ee.busy},bulk:{revoke:ee,noun:It,isSelectable:e=>e.status===`active`}})}),u(je,{open:l.mode===`new`,onClose:m,title:`Create admin token`,icon:P,error:x,width:`md`,testId:`admin-token-form-dialog`,footer:r(k,{type:`button`,variant:`danger`,size:`sm`,disabled:!I,onClick:()=>void L(),children:[A.isPending||w?u(C,{className:`h-3.5 w-3.5 animate-spin`,"aria-hidden":!0}):u(P,{className:`h-3.5 w-3.5`,"aria-hidden":!0}),`Create admin token`]}),children:r(`div`,{className:`space-y-3`,children:[r(`div`,{className:`space-y-1`,children:[u(U,{htmlFor:`admin-token-name`,children:`Token name`}),u(V,{id:`admin-token-name`,value:h,maxLength:zt,onChange:e=>g(e.target.value),placeholder:`nightly-backup`,autoFocus:!0})]}),r(`div`,{className:`space-y-1`,children:[u(U,{htmlFor:`admin-token-days`,children:`Expires in (days)`}),u(V,{id:`admin-token-days`,type:`number`,min:1,max:Rt,value:_,onChange:e=>v(e.target.value),state:F===null?`error`:`default`}),F===null?r(`p`,{className:`text-[11px] text-danger`,children:[`Expiry must be a whole number of days, 1 to `,Rt,`.`]}):r(`p`,{className:`text-[11px] text-foreground-subtle`,children:[`Whole number of days, 1 to `,Rt,`.`]})]})]})}),E&&u(Nt,{token:E.token,label:E.label,onClose:()=>D(null)})]})}var Ut={"share-link":`share links`,"admin-token":`admin tokens`,"login-session":`login sessions`};function Z(e,t){let n=e[t];return typeof n==`string`&&n.length>0?n:null}function Wt(e,t){let n=Z(e,`tokenId`);return Z(e,`name`)??(n===null?void 0:t.get(n))??n??`?`}function Gt(e){return e.filter(e=>e!==null)}function Kt(e){let t=Z(e,`prefix`);return t===null?null:`Prefix ${t}…`}function qt(e,t){let n=e.detail??{};switch(e.action){case`admin-token-created`:{let e=n.expiresAt;return{title:`Admin token "${Wt(n,t)}" created`,details:Gt([Kt(n),typeof e==`number`?`Expires ${qe(e)}`:null])}}case`admin-token-used`:{let e=Z(n,`path`);return{title:`Admin token "${Wt(n,t)}" used`,details:Gt([Kt(n),e===null?null:`Call ${e}`])}}case`admin-token-revoked`:return{title:`Admin token "${Wt(n,t)}" revoked`,details:Gt([Kt(n)])};case`credentials-revoked`:{let e=Z(n,`kind`)??`credential`,t=n.revoked,r=n.skipped,i=Ut[e]??`${e}s`;return{title:`${typeof t==`number`?String(t):`?`} ${i} revoked in bulk`,details:Gt([typeof r==`number`&&r>0?`${String(r)} skipped`:null,Z(n,`ids`)===null?null:`Ids ${Z(n,`ids`)??``}`])}}case`second-factor-skipped`:{let e=Z(n,`method`),t=Z(n,`factors`);return{title:`Second factor skipped on a trusted network`,details:Gt([e===null?null:`Signed in with ${Ge(e)}`,t===null?null:`Skipped: ${t.split(`,`).join(`, `)}`])}}default:return{title:e.action,details:Object.entries(n).map(([e,t])=>`${e}: ${String(t)}`)}}}n();var Jt=100;function Yt(){let{user:e}=st();return e?.isAdmin?u(Zt,{}):null}function Xt(){let{data:e}=w(),{data:t}=W.auth.listAdminTokens.useQuery();return i(()=>{let n=new Map;for(let t of e??[])n.set(t.id,t.username);let r=new Map;for(let e of t??[])r.set(e.id,e.name);return{userNameById:n,tokenNameById:r}},[e,t])}function Zt(){let e=W.auth.listAuthAudit.useQuery({limit:Jt}),{userNameById:t,tokenNameById:n}=Xt(),i=e.data??[];return r(`div`,{"data-testid":`auth-audit`,className:`rounded-lg border border-border bg-surface overflow-hidden`,children:[r(`div`,{className:`px-4 py-2.5 border-b border-border flex items-center gap-2`,children:[u(_,{className:`h-3.5 w-3.5 text-foreground-subtle`}),u(`div`,{className:`text-xs font-semibold text-foreground`,children:`Authentication audit`}),r(`span`,{className:`ml-auto text-[10px] text-foreground-subtle`,children:[`Latest `,Jt,`, newest first`]})]}),u(`div`,{className:`p-4`,children:e.isLoading?r(`div`,{className:`flex items-center gap-2 text-xs text-foreground-subtle`,children:[u(C,{className:`h-3.5 w-3.5 animate-spin`}),`Loading audit trail…`]}):e.isError?r(`div`,{className:`text-[11px] text-danger`,children:[`Could not load the audit trail: `,e.error?.message??`Request failed`]}):i.length===0?u(`div`,{className:`rounded border border-dashed border-border px-3 py-4 text-xs text-foreground-subtle text-center`,children:`No authentication events recorded yet.`}):u(`ul`,{className:`divide-y divide-border rounded-md border border-border max-h-[420px] overflow-y-auto`,children:i.map(e=>u(Qt,{record:e,userName:e.userId===void 0?null:t.get(e.userId)??e.userId,tokenNameById:n},e.id))})})]})}function Qt({record:e,userName:t,tokenNameById:n}){let i=qt(e,n);return r(`li`,{"data-testid":`auth-audit-row`,className:`px-3 py-2 text-xs space-y-0.5`,children:[r(`div`,{className:`flex flex-wrap items-baseline gap-x-2`,children:[u(`span`,{className:`font-medium text-foreground`,children:i.title}),u(`span`,{className:`ml-auto text-[10px] text-foreground-subtle`,children:qe(e.at)})]}),r(`div`,{className:`flex flex-wrap gap-x-3 text-[11px] text-foreground-subtle`,children:[t!==null&&u(`span`,{className:`text-foreground`,children:t}),e.ip!==void 0&&u(`span`,{className:`font-mono`,children:e.ip}),i.details.map(e=>u(`span`,{className:`font-mono`,children:e},e))]})]})}var Q={loginSessions:`login-sessions`,linkedApps:`linked-apps`,scopedTokens:`scoped-tokens`,adminTokens:`admin-tokens`,shareLinks:`share-links`,legacyApiKeys:`legacy-api-keys`,externalIdentities:`external-identities`,audit:`auth-audit`},$t={state:`ok`,stateLabel:`available`,showState:!1};function $(e,t,n,r){return{id:e,name:t,fallbackIcon:n,...$t,render:r}}function en(){let e=L(),t=(e.data??[]).length>0;return u(yt,{embedded:!0,icon:wt,title:`Tokens & sessions`,subtitle:`Every credential this hub honours — who holds it, what it can reach, when it was last used and when it expires. Pick a kind; revoke one, or select several and revoke them after typing the count.`,groups:[{id:`sessions`,label:`Sessions`,entries:[$(Q.loginSessions,`Login sessions`,Pe,()=>u(At,{})),$(Q.linkedApps,`Linked apps`,ae,()=>u(Ot,{}))]},{id:`tokens`,label:`Tokens`,entries:[$(Q.scopedTokens,`Scoped tokens`,ye,()=>u(Ft,{})),$(Q.adminTokens,`Admin tokens`,P,()=>u(Vt,{})),$(Q.shareLinks,`Share links`,H,()=>u(We,{})),...t?[$(Q.legacyApiKeys,`Legacy API keys`,Ne,()=>u(Et,{}))]:[]]},{id:`identity`,label:`Identity`,entries:[$(Q.externalIdentities,`Sign-in links`,ut,()=>u(Tt,{})),$(Q.audit,`Audit`,_,()=>u(Yt,{}))]}],isLoading:e.isLoading,emptyTitle:`No credential kinds`,emptyDescription:`This hub offers no credential kinds.`})}n();var tn=[`oidcConnections`,`listConnections`],nn=[`oidcConnections`,`connectionFormSchema`],rn={displayName:`OpenID Connect`,icon:`shield-check`,issuerUrl:``,clientId:``,clientSecret:``,redirectUri:``,scopes:`openid profile email`,usernameClaim:`preferred_username`},an=/^[a-z0-9][a-z0-9-]{0,63}$/;function on(e){return e instanceof Error?e.message:String(e)}function sn(e,t){if(t)return typeof e==`string`&&e.length>0?e:void 0;if(et(e))return nt;if(typeof e==`string`&&e.length>0)return e;Qe(e)}function cn(e){return typeof e!=`object`||!e?!1:Array.isArray(Reflect.get(e,`sections`))}function ln({addonId:n}){let f=h(),m=s(),g=K(),_=ne({editKey:`oidcEdit`,newKey:`oidcNew`}),[v,y]=c(!1),[b,x]=c(null),[S,C]=c(``),[w,T]=c({...rn}),E=t(null),D=a({queryKey:[...tn,n],queryFn:async()=>f.trpcClient.oidcConnections.listConnections.query({})}),O=a({queryKey:nn,queryFn:async()=>f.trpcClient.oidcConnections.connectionFormSchema.query({})}),A=D.data??[],j=cn(O.data)?O.data:void 0,te=_.state,M=te.mode===`edit`?A.find(e=>e.id===te.id)??null:null;d(()=>{if(_.state.mode===`edit`&&M!==null){let e=`edit:${M.id}`;if(E.current===e)return;E.current=e,x(null),C(M.id),T(M.formValues===void 0?{...rn}:{...M.formValues});return}if(_.state.mode===`new`){if(E.current===`new`)return;E.current=`new`,x(null),C(``),T({...rn})}},[M,_.state]);let N=e(async()=>{await m.invalidateQueries({queryKey:tn})},[m]),P=l({mutationFn:async e=>f.trpcClient.oidcConnections.deleteConnection.mutate({id:e}),onSuccess:async()=>N()}),F=e(async e=>{await g({title:`Delete OIDC connection?`,message:`Remove “${e.displayName}” (${e.id}). The login button and IdP credentials for this id are dropped.`,confirmLabel:`Delete`,cancelLabel:`Cancel`,variant:`danger`})&&(await P.mutateAsync(e.id),_.state.mode===`edit`&&_.state.id===e.id&&(_.close(),E.current=null))},[g,P,_]),I=e(async()=>{let e=w.displayName,t=typeof e==`string`&&e.trim()!==``?e.trim():``;if(t===``){x(`Display name is required.`);return}let n=_.state.mode===`new`,r=n?S.trim():M?.id??``;if(n&&!an.test(r)){x(`Instance id must be a lowercase slug (e.g. authentik, google).`);return}if(r===``){x(`That connection is no longer available.`);return}let i=sn(w.clientSecret,n),a=w.icon,o=w.issuerUrl,s=w.clientId,c=w.redirectUri,l=w.scopes,u=w.usernameClaim,d=u===`email`||u===`sub`?u:`preferred_username`;y(!0),x(null);try{n&&await f.trpcClient.oidcConnections.createConnection.mutate({id:r,displayName:t}),await f.trpcClient.oidcConnections.updateConnection.mutate({id:r,displayName:t,icon:typeof a==`string`?a:`shield-check`,issuerUrl:typeof o==`string`?o:``,clientId:typeof s==`string`?s:``,...i===void 0?{}:{clientSecret:i},redirectUri:typeof c==`string`?c:``,scopes:typeof l==`string`?l:`openid profile email`,usernameClaim:d}),await N(),_.close(),E.current=null}catch(e){x(on(e))}finally{y(!1)}},[S,M?.id,_,w,N,f]),L=i(()=>[{key:`id`,header:`Id`,sortValue:e=>e.id,render:e=>u(`span`,{className:`font-mono text-xs text-foreground`,children:e.id})},{key:`name`,header:`Name`,sortValue:e=>e.displayName,render:e=>u(`span`,{className:`truncate font-medium text-foreground`,children:e.displayName})},{key:`issuer`,header:`Issuer`,sortValue:e=>e.issuerUrl,render:e=>u(`span`,{className:`truncate text-xs text-foreground-muted`,children:e.issuerUrl===``?`—`:e.issuerUrl})},{key:`status`,header:`Status`,notSortableReason:`Badge`,render:e=>u(De,{variant:e.configured?`success`:`warning`,children:e.configured?`Configured`:`Not configured`})},{key:`actions`,header:``,align:`right`,notSortableReason:`Row actions`,nowrap:!0,render:e=>r(`div`,{className:`flex justify-end gap-1`,children:[u(k,{type:`button`,size:`sm`,variant:`secondary`,onClick:t=>{t.stopPropagation(),_.openEdit(e.id)},children:`Edit`}),u(k,{type:`button`,size:`sm`,variant:`ghost`,"aria-label":`Delete ${e.displayName}`,title:`Delete`,onClick:t=>{t.stopPropagation(),F(e)},children:u(p,{className:`h-3.5 w-3.5 text-danger`})})]})}],[_,F]),R=_.state.mode===`new`,z=R||M!==null,re=R?`new`:M===null?`closed`:`edit:${M.id}:${M.configured?`cfg`:`empty`}`;return r(`div`,{className:`flex min-h-0 flex-col gap-3`,"data-testid":`oidc-settings-connections`,children:[r(`div`,{className:`flex flex-wrap items-center justify-between gap-2`,children:[r(`p`,{className:`text-sm text-foreground-muted`,children:[`Each row is one IdP. Redirect URI:`,` `,u(`span`,{className:`font-mono text-xs`,children:`/addon/${n}/<id>/callback`})]}),r(k,{type:`button`,size:`sm`,"data-testid":`oidc-connections-add`,onClick:()=>_.openNew(),children:[u(ve,{className:`h-3.5 w-3.5`}),`Add connection`]})]}),D.isError?u(`p`,{className:`text-sm text-danger`,children:`Could not load connections.`}):null,D.isPending?u(me,{className:`h-32 w-full`}):u(ee,{columns:L,rows:A,rowKey:e=>e.id,pageSize:25,mobileMode:`scroll`,emptyMessage:`No OIDC connections yet. Add one to register an IdP.`,onRowClick:e=>_.openEdit(e.id),defaultSort:{columnKey:`name`,dir:`asc`}}),u(je,{open:z,onClose:()=>{_.close(),x(null),E.current=null},title:R?`Add OIDC connection`:`Edit ${M?.displayName??`connection`}`,icon:q,width:`xl`,testId:`oidc-connection-editor`,error:b,footer:u(k,{type:`button`,size:`sm`,disabled:v||R&&S.trim()===``,onClick:()=>void I(),children:v?`Saving…`:`Save`}),children:z?r(o,{children:[R?r(`div`,{children:[u(U,{htmlFor:`oidc-connection-id`,children:`Instance id`}),u(V,{id:`oidc-connection-id`,value:S,onChange:e=>C(e.target.value),placeholder:`authentik`,autoComplete:`off`}),u(`p`,{className:`mt-1 text-xs text-foreground-muted`,children:`URL slug — appears in /addon/${n}/<id>/callback. Stable; renaming is not supported after save.`})]}):r(`p`,{className:`text-sm text-foreground-muted`,children:[`Instance id: `,u(`span`,{className:`font-mono`,children:M?.id})]}),j===void 0?u(`p`,{className:`text-sm text-foreground-muted`,children:`Loading form…`}):u(ue,{schema:j,values:w,onChange:T,bare:!0},re)]}):null})]})}n();var un=3e4,dn=[{capName:`auth-provider`,stageBadge:null},{capName:`user-passkeys`,stageBadge:`Second factor`}],fn={title:`Authentication`,subtitle:`Login providers (local password, OIDC, SAML) and second-factor providers (passkeys / WebAuthn). They run in parallel — enable or disable each independently; disabling one only removes that method, local password login is unaffected.`,emptyTitle:`No authentication providers registered`,emptyDescription:`Local auth should always be available — if this list is empty the local-auth builtin failed to register. Check the Addons page for load errors.`,installLabel:`Add provider`};function pn(e){return e===`local-auth`?{headline:`Username + password (local).`,icon:wt,bullets:[`Users live in the hub’s SQLite database — no IdP roundtrip.`,`TOTP / 2FA is self-enrolled: each user enables it from their own profile page (My access → Two-factor authentication).`,`Scoped tokens, admin tokens and sessions are managed from Identity → Tokens & sessions.`],setupSteps:[`A fresh hub has one account, admin / changeme — the first sign-in asks for a new password. Add users from Users → New user.`,`Hand out either passwords or scoped tokens — both validate through this provider.`]}:e.startsWith(`auth-oidc`)?{headline:`OpenID Connect (Google, Microsoft, Okta, Keycloak, …).`,icon:pt,bullets:[`Three-leg redirect flow with PKCE S256 + nonce.`,`id_token is signature-verified against the IdP’s JWKS — no implicit trust.`,`Explicit linking only: an IdP account signs in only the CamStack user who linked it from their profile (My access → Linked sign-in accounts). No auto-created users, no email matching.`,`The linked user’s role, scopes and two-factor authentication apply.`],setupSteps:[`Register a web app at your IdP and copy the client_id + client_secret.`,"Choose a stable instance id (slug). Set the IdP redirect URI to `https://<this-hub>/addon/auth-oidc/<id>/callback`.",`In Settings below, Add connection — enter the id, issuer, and credentials; Save.`,`Each user then links their IdP account from My access → Linked sign-in accounts.`]}:e.startsWith(`auth-saml`)?{headline:`SAML 2.0 — enterprise SSO (Azure AD, Okta, OneLogin).`,icon:pt,bullets:[`Heavier handshake than OIDC — exchange metadata XML with your IdP.`,`Group → role mapping via SAML assertions.`]}:e.startsWith(`auth-webauthn`)||e.startsWith(`auth-passkey`)?{headline:`Passkeys / WebAuthn — passwordless second factor.`,icon:pe,bullets:[`Enroll a YubiKey, Touch ID, or platform passkey per user.`,`Resistant to phishing — the browser binds the credential to your hub origin.`]}:e.startsWith(`auth-totp`)?{headline:`TOTP — second factor.`,icon:pe,bullets:[`Time-based codes from an authenticator app (RFC 6238).`,`Compatible with Google Authenticator, Aegis, 1Password, Bitwarden, etc.`]}:{headline:`Authentication provider.`,icon:q,bullets:[`Generic authentication provider. No additional hints registered.`]}}function mn(){let e=z({capName:dn[0].capName},{refetchInterval:un}),t=z({capName:dn[1].capName},{refetchInterval:un}),n=xe(void 0,{refetchInterval:un}),r=i(()=>St(n.data??[]),[n.data]),a=i(()=>{let n=[],i=[e.data??[],t.data??[]];for(let[e,t]of dn.entries())for(let a of i[e]??[])n.push({addonId:a.addonId,displayName:r.get(a.addonId)??a.addonId,isActive:a.isActive,capName:t.capName,stageBadge:t.stageBadge});return n},[e.data,t.data,r]),o=[{id:`authentication`,label:`Authentication providers`,install:{capability:dn[0].capName,label:fn.installLabel},entries:a.map(e=>({id:e.addonId,name:e.displayName,fallbackIcon:q,...xt(e.isActive),render:()=>u(bt,{addonId:e.addonId,displayName:e.displayName,capName:e.capName,isActive:e.isActive,badges:e.stageBadge===null?null:u(De,{variant:`info`,className:vt,children:e.stageBadge}),settingsContent:e.addonId.startsWith(`auth-oidc`)?u(ln,{addonId:e.addonId}):void 0,panels:[{id:`about`,title:`About`,icon:pn(e.addonId).icon,content:u(hn,{addonId:e.addonId})}]})}))}];return u(yt,{embedded:!0,icon:q,title:fn.title,subtitle:fn.subtitle,groups:o,isLoading:e.isLoading||t.isLoading,emptyTitle:fn.emptyTitle,emptyDescription:fn.emptyDescription})}function hn({addonId:e}){let t=pn(e),n=t.icon;return r(`div`,{className:`space-y-3 rounded-lg border border-border bg-surface p-2 sm:p-4`,children:[r(`div`,{className:`flex items-start gap-2.5`,children:[u(n,{className:`h-4 w-4 mt-0.5 text-primary shrink-0`}),u(`div`,{className:`text-xs text-foreground font-medium`,children:t.headline})]}),u(`ul`,{className:`text-xs text-foreground-subtle space-y-1.5 pl-6 list-disc`,children:t.bullets.map((e,t)=>u(`li`,{children:e},t))}),t.setupSteps&&t.setupSteps.length>0&&r(`div`,{className:`rounded-md border border-border bg-surface-hover/40 px-3 py-2.5 space-y-1.5`,children:[r(`div`,{className:`flex items-center gap-1.5 text-[10px] font-semibold text-foreground-subtle uppercase tracking-wide`,children:[u(Ct,{className:`h-3 w-3`}),`Setup steps`]}),u(`ol`,{className:`text-xs text-foreground space-y-0.5 pl-4 list-decimal`,children:t.setupSteps.map((e,t)=>u(`li`,{children:e},t))})]}),e===`local-auth`&&r(`div`,{className:`rounded-md border border-info/30 bg-info/5 px-3 py-2 flex items-start gap-2 text-[11px] text-foreground`,children:[u(I,{className:`h-3.5 w-3.5 mt-0.5 text-info shrink-0`}),r(`span`,{children:[`TOTP / 2FA is self-service: each user enrols from their own profile page —`,` `,u(lt,{to:`/system/my-access`,className:`underline text-primary`,children:`My access`}),` `,`→ “Two-factor authentication” → Enable 2FA. Admins can only remove a user’s active enrollment (lost device) from the`,` `,u(lt,{to:`/system/identity?tab=users`,className:`underline text-primary`,children:`Users`}),` `,`page via Actions → “Remove 2FA”.`]})]})]})}function gn({userId:e,username:t,onClose:n}){let i=K(),a=W.useUtils(),o=ie({userId:e}),s=W.auth.listLoginMethods.useQuery(),c=fe({onSuccess:()=>void a.userManagement.listExternalIdentities.invalidate()}),l=new Map((s.data??[]).flatMap(e=>e.kind===`redirect`?[[e.id,e.label]]:[])),d=o.data??[],f=async(n,r)=>{await i({title:`Remove sign-in link`,message:`"${t}" will no longer be able to sign in with this ${r} account.`,confirmLabel:`Remove`,variant:`danger`})&&c.mutate({userId:e,provider:n.provider,subject:n.subject})};return u(b,{open:!0,onOpenChange:e=>!e&&n(),children:r(R,{width:`md`,placement:`sheet-on-mobile`,scroll:`body`,"data-testid":`external-identities-modal`,children:[u(T,{children:r(M,{className:`flex items-center gap-2`,children:[u(ae,{className:`h-4 w-4 text-primary`}),r(`span`,{children:[`Linked sign-in accounts`,r(`span`,{className:`mt-0.5 block text-[11px] font-normal text-foreground-subtle`,children:[`User: `,u(`span`,{className:`font-mono`,children:t})]})]})]})}),u(G,{className:`space-y-3`,children:o.isLoading?r(`div`,{className:`flex items-center gap-2 text-xs text-foreground-subtle`,children:[u(C,{className:`h-3.5 w-3.5 animate-spin`}),`Loading…`]}):d.length===0?u(`p`,{className:`text-xs text-foreground-subtle`,children:`No linked accounts. Only the user can link one, from their own profile page.`}):u(`ul`,{className:`divide-y divide-border rounded-md border border-border`,children:d.map(e=>{let t=l.get(e.provider),n=t??e.provider;return r(`li`,{className:`flex items-center gap-3 px-3 py-2 text-xs`,children:[r(`div`,{className:`flex-1 min-w-0`,children:[r(`div`,{className:`font-medium text-foreground truncate`,children:[n,t===void 0&&u(`span`,{className:`ml-2 rounded bg-foreground-subtle/15 px-1.5 py-0.5 text-[10px] font-normal text-foreground-subtle`,children:`provider unavailable`})]}),r(`div`,{className:`text-[11px] text-foreground-subtle truncate`,children:[e.label??e.subject,` ·`,` `,new Date(e.linkedAt).toLocaleDateString()]})]}),r(`button`,{onClick:()=>void f(e,n),disabled:c.isPending,className:`inline-flex items-center gap-1 rounded px-2 py-1 text-[10px] text-danger hover:bg-danger/10 disabled:opacity-50`,children:[u(Ie,{className:`h-3 w-3`}),`Remove`]})]},`${e.provider}|${e.subject}`)})})}),u(B,{children:u(k,{onClick:n,variant:`secondary`,children:`Close`})})]})})}function _n({userId:e,username:t,onClose:n}){let i=s(),a=K(),c=be({userId:e}),l=c.data,d=()=>i.invalidateQueries({queryKey:[[`userManagement`,`getTotpStatus`]]}),f=y({onSuccess:()=>{d(),n()}}),p=c.isLoading?`loading`:l?.enabled?`enrolled`:`not_enrolled`;return u(b,{open:!0,onOpenChange:e=>!e&&n(),children:r(R,{width:`md`,placement:`sheet-on-mobile`,scroll:`body`,children:[u(T,{children:r(M,{className:`flex items-center gap-2`,children:[u(pe,{className:`h-4 w-4 text-primary`}),r(`span`,{children:[`Two-factor authentication`,r(`span`,{className:`mt-0.5 block text-[11px] font-normal text-foreground-subtle`,children:[`User: `,u(`span`,{className:`font-mono`,children:t})]})]})]})}),r(G,{className:`space-y-4`,children:[p===`loading`&&r(`div`,{className:`flex items-center gap-2 text-xs text-foreground-subtle`,children:[u(C,{className:`h-3.5 w-3.5 animate-spin`}),`Checking status…`]}),p===`not_enrolled`&&r(`div`,{className:`rounded-md border border-border bg-surface-hover/30 px-3 py-2.5 flex items-start gap-2.5 text-xs text-foreground-subtle`,children:[u(I,{className:`h-4 w-4 mt-0.5 shrink-0`}),r(`div`,{children:[u(`div`,{className:`font-medium text-foreground`,children:`2FA not enrolled`}),u(`p`,{className:`mt-0.5`,children:`Only the user themselves can enable 2FA, from their own profile page.`})]})]}),p===`enrolled`&&l&&r(o,{children:[r(`div`,{className:`rounded-md border border-emerald-500/30 bg-emerald-500/5 px-3 py-2.5 flex items-start gap-2.5`,children:[u(_e,{className:`h-4 w-4 mt-0.5 text-emerald-500 shrink-0`}),r(`div`,{className:`text-xs text-foreground space-y-0.5`,children:[u(`div`,{className:`font-medium`,children:`2FA is active`}),r(`div`,{className:`text-foreground-subtle`,children:[`Enrolled on`,` `,l.confirmedAt?new Date(l.confirmedAt).toLocaleString():`—`]})]})]}),u(`div`,{className:`text-[11px] text-foreground-subtle`,children:`Removing here clears the server-side secret. Use this only when the user has lost access — they'll have to re-enroll from their profile page afterwards.`}),r(`div`,{className:`flex justify-end gap-2`,children:[u(k,{onClick:n,variant:`secondary`,children:`Close`}),r(`button`,{onClick:async()=>{await a({title:`Remove 2FA`,message:`Remove two-factor authentication for "${t}"? They'll be able to log in with just their password until they re-enroll from their own profile page.`,confirmLabel:`Remove`,variant:`danger`})&&f.mutate({userId:e})},disabled:f.isPending,className:`rounded bg-danger px-3 py-1.5 text-xs font-medium text-white hover:bg-danger/90 disabled:opacity-50 inline-flex items-center gap-1`,children:[f.isPending?u(C,{className:`h-3 w-3 animate-spin`}):u(we,{className:`h-3 w-3`}),`Remove 2FA`]})]})]})]})]})})}n();function vn({userId:e,username:t,onClose:n}){let{user:i,logout:a}=st(),o=i?.id===e,s=K(),l=W.useUtils(),d=W.auth.listSessions.useQuery({userId:e}),[f,p]=c(null),m=W.auth.revokeSessions.useMutation({onSuccess:(e,t)=>{if(o&&`userId`in t){a();return}l.auth.listSessions.invalidate()},onError:e=>p(`Sign out failed: ${e.message}`)}),h=async e=>{await s({title:`Sign out this session`,message:`"${t}" on ${Fe(e.client).label} will have to sign in again.`,confirmLabel:`Sign out`,variant:`danger`})&&(p(null),m.mutate({id:e.id}))},g=async()=>{await s({title:`Sign out everywhere`,message:o?`Every session of your own account ends, this browser included: you will be signed out here too.`:`Every session of "${t}" ends at once; each device has to sign in again.`,confirmLabel:`Sign out everywhere`,variant:`danger`})&&(p(null),m.mutate({userId:e}))},_=m.isPending&&m.variables!==void 0&&`id`in m.variables?m.variables.id:null;return u(b,{open:!0,onOpenChange:e=>!e&&n(),children:r(R,{width:`lg`,placement:`sheet-on-mobile`,scroll:`body`,"data-testid":`user-sessions-modal`,children:[u(T,{children:r(M,{className:`flex items-center gap-2`,children:[u(Pe,{className:`h-4 w-4 text-primary`}),r(`span`,{children:[`Active sessions`,r(`span`,{className:`mt-0.5 block text-[11px] font-normal text-foreground-subtle`,children:[`User: `,u(`span`,{className:`font-mono`,children:t})]})]})]})}),r(G,{className:`space-y-3`,children:[f!==null&&u(`div`,{className:`text-[11px] text-danger`,children:f}),u(Je,{sessions:d.data??[],isLoading:d.isLoading,loadError:d.isError?d.error?.message??`Request failed`:null,emptyText:`No active sessions.`,onRevoke:e=>void h(e),revokingId:_})]}),r(B,{className:`justify-between`,children:[r(`button`,{onClick:()=>void g(),disabled:m.isPending||(d.data??[]).length===0,className:`inline-flex items-center gap-1.5 rounded border border-danger/40 px-2.5 py-1 text-[11px] font-medium text-danger hover:bg-danger/10 disabled:opacity-50`,children:[m.isPending&&_===null?u(C,{className:`h-3 w-3 animate-spin`}):u(ht,{className:`h-3 w-3`}),`Sign out everywhere`]}),u(k,{onClick:n,variant:`secondary`,children:`Close`})]})]})})}function yn({icon:e,label:t,count:n,all:i,total:a}){return r(`div`,{className:`flex items-center gap-2 text-[11px]`,children:[u(e,{className:`h-3 w-3 shrink-0 text-foreground-subtle`}),u(`span`,{className:`min-w-0 flex-1 text-foreground-subtle`,children:t}),u(`span`,{className:n===0?`text-foreground-subtle`:`font-medium text-foreground`,children:n===0?`none`:i?`all ${a}`:`${n} of ${a}`})]})}function bn({scopes:e,isAdmin:t,invalidReason:n}){let i=h(),s=a({queryKey:[`auth`,`previewScope`,e,t],enabled:n===null,queryFn:()=>i.trpcClient.auth.previewScope.query({scopes:[...e],isAdmin:t})}),{data:c}=Oe({projection:`slim`}),l=c?.length??0;if(n!==null)return u(`div`,{className:`rounded-lg border border-border bg-surface p-3`,children:r(`p`,{className:`flex items-center gap-1.5 text-[11px] text-warning`,children:[u(P,{className:`h-3 w-3 shrink-0`}),n]})});if(s.isLoading)return r(`div`,{className:`flex items-center gap-2 rounded-lg border border-border bg-surface p-3 text-[11px] text-foreground-subtle`,children:[u(C,{className:`h-3 w-3 animate-spin`}),`Resolving…`]});if(s.isError||!s.data)return u(`div`,{className:`rounded-lg border border-border bg-surface p-3`,children:u(`p`,{className:`text-[11px] text-danger`,children:`Could not resolve this grant set. Saving is still allowed — the server enforces the same rules either way.`})});let d=s.data;return u(`div`,{className:`space-y-2 rounded-lg border border-border bg-surface p-3`,children:d.isAdmin?r(`p`,{className:`text-[11px] text-foreground`,children:[u(`span`,{className:`font-medium`,children:`Administrator`}),` — unrestricted. Grants are ignored for an admin.`]}):r(o,{children:[u(yn,{icon:O,label:`Devices they can see`,count:d.device.view.deviceCount,all:d.device.view.all,total:l}),u(yn,{icon:Ce,label:`Devices they can operate`,count:d.device.create.deviceCount,all:d.device.create.all,total:l}),u(yn,{icon:p,label:`Devices they can delete`,count:d.device.delete.deviceCount,all:d.device.delete.all,total:l}),r(`div`,{className:`border-t border-border pt-2 text-[10px] text-foreground-subtle`,children:[`System access: `,d.system.length===0?`none`:d.system.join(`, `),d.capabilities.length>0&&r(o,{children:[` · Extra capabilities: `,d.capabilities.map(e=>e.name).join(`, `)]}),d.addons.length>0&&r(o,{children:[` · Addons: `,d.addons.map(e=>e.name).join(`, `)]})]}),d.device.view.deviceCount===0&&u(`p`,{className:`text-[10px] text-warning`,children:`This user will not be able to see any device.`})]})})}n();function xn(e){let t=tt(e);return t===null?{mode:e.length===0?`role`:`advanced`,roleId:null,selector:{kind:`all`},includeLinked:!0,advancedScopes:e}:{mode:`role`,roleId:t.roleId,selector:t.selector,includeLinked:t.includeLinked,advancedScopes:e}}function Sn(e){return e.mode===`advanced`?e.advancedScopes:e.roleId===null?[]:Ze({roleId:e.roleId,selector:e.selector,includeLinked:e.includeLinked})}function Cn({user:e,onClose:t,onSubmit:n,submitting:a,callerScopes:s}){let l=i(()=>xn(e.scopes??[]),[e.scopes]),[d,p]=c(l),[h,g]=c(l.mode===`advanced`),_=i(()=>Sn(d),[d]),v=!e.isAdmin&&d.mode===`role`&&d.roleId===null,y=i(()=>e.isAdmin?null:d.mode===`role`&&d.roleId===null?`Pick what this user can do to see the result.`:se(_),[e.isAdmin,d.mode,d.roleId,_]),x=e=>p({...d,...e});return u(b,{open:!0,onOpenChange:e=>!e&&t(),children:r(R,{width:`lg`,placement:`sheet-on-mobile`,scroll:`body`,className:`md:max-w-2xl`,children:[r(T,{children:[r(M,{children:[`Access — `,e.username]}),u(`p`,{className:`text-[10px] text-foreground-subtle`,children:e.isAdmin?`This user is an administrator — they can do everything, and the settings below are ignored.`:`Choose what they can do, then which devices it applies to.`})]}),r(G,{className:`space-y-4`,children:[v&&r(`div`,{className:`flex items-start gap-2 rounded-lg border border-warning/40 bg-warning/10 p-3`,children:[u(P,{className:`mt-0.5 h-3.5 w-3.5 shrink-0 text-warning`}),r(`p`,{className:`text-[11px] text-foreground`,children:[u(`span`,{className:`font-medium`,children:`No access.`}),` `,r(`span`,{className:`text-foreground-subtle`,children:[e.username,` cannot see or do anything yet. Configure it below.`]})]})]}),r(`section`,{className:`space-y-2`,children:[u(`h3`,{className:`text-[11px] font-semibold uppercase tracking-wide text-foreground-subtle`,children:`1 · What can they do`}),u(`div`,{className:`space-y-1.5`,children:$e.map(e=>{let t=d.mode===`role`&&d.roleId===e.id;return r(`label`,{className:`flex cursor-pointer items-start gap-2 rounded-lg border p-2.5 transition-colors ${t?`border-primary bg-primary/5`:`border-border hover:bg-foreground-subtle/5`}`,children:[u(`input`,{type:`radio`,name:`access-role`,checked:t,onChange:()=>x({mode:`role`,roleId:e.id}),className:`mt-0.5 border-border focus:ring-primary`}),r(`span`,{className:`min-w-0`,children:[u(`span`,{className:`block text-[12px] font-medium text-foreground`,children:e.label}),u(`span`,{className:`mt-0.5 block text-[10px] text-foreground-subtle`,children:e.description})]})]},e.id)})}),r(`button`,{type:`button`,onClick:()=>{if(!h){p({...d,mode:`advanced`,advancedScopes:_}),g(!0);return}g(!1),p(xn(d.advancedScopes))},className:`inline-flex items-center gap-1 rounded px-1.5 py-1 text-[10px] text-foreground-subtle transition-colors hover:bg-foreground-subtle/10 hover:text-foreground`,children:[u(h?f:m,{className:`h-3 w-3`}),`Advanced — edit raw grants`]}),h&&r(`div`,{className:`rounded-lg border border-border p-2`,children:[u(`p`,{className:`mb-2 text-[10px] text-foreground-subtle`,children:`One row per grant. Anything expressible here overrides the three roles above; close this section to go back to them.`}),u(re,{value:d.advancedScopes,onChange:e=>x({mode:`advanced`,advancedScopes:e}),clampToParent:s,emptyHint:r(o,{children:[`No grants.`,` `,u(`span`,{className:`font-medium text-foreground`,children:e.username}),` cannot call any protected endpoint until one is added.`]})})]})]}),d.mode===`role`&&r(`section`,{className:`space-y-2`,children:[u(`h3`,{className:`text-[11px] font-semibold uppercase tracking-wide text-foreground-subtle`,children:`2 · On which devices`}),u(he,{value:d.selector,onChange:e=>x({selector:e}),includeLinked:d.includeLinked,onIncludeLinkedChange:e=>x({includeLinked:e}),clampToDeviceIds:null})]}),r(`section`,{className:`space-y-2`,children:[u(`h3`,{className:`text-[11px] font-semibold uppercase tracking-wide text-foreground-subtle`,children:d.mode===`role`?`3 · Result`:`2 · Result`}),u(bn,{scopes:_,isAdmin:e.isAdmin,invalidReason:y})]})]}),r(B,{children:[u(k,{variant:`outline`,size:`sm`,onClick:t,children:`Cancel`}),u(k,{size:`sm`,onClick:()=>n([..._]),disabled:a||y!==null,children:a?u(C,{className:`h-3 w-3 animate-spin`}):`Save access`})]})]})})}n();var wn=8;function Tn(){return{username:``,password:``,isAdmin:!1}}function En(e){return{id:e.id,username:e.username,password:``,isAdmin:e.isAdmin}}function Dn(e){return e instanceof Error?e.message:`Something went wrong`}function On(){let e=s(),{user:t}=st(),{state:n,openNew:i,openEdit:a,close:l}=ne(),[f,m]=c(null),[h,g]=c(null),[_,y]=c(!1),{data:b,isLoading:S,isError:T,error:E,refetch:O}=w(),M=K(),P=Ee(),F=()=>e.invalidateQueries({queryKey:[[`userManagement`,`listUsers`]]}),I=oe(),L=Se({onSuccess:F}),R=A({onSuccess:F}),z=te({onSuccess:F}),re=x({onSuccess:()=>{F(),se(null)}}),[ie,se]=c(null),[ce,B]=c(null),[le,ue]=c(null),[fe,me]=c(null),he=b??[],H=n.mode===`edit`?he.find(e=>e.id===n.id)??null:null,ge=()=>{m(null),g(null),y(!1),I.reset(),R.reset(),l()};d(()=>{if(n.mode===`closed`){m(null),g(null),y(!1);return}if(n.mode===`new`){m(e=>e!==null&&e.id===void 0?e:Tn());return}if(H===null){S||l();return}m(e=>e?.id===H.id?e:En(H))},[n,H,S,l]);let _e=n.mode===`new`&&f!==null&&f.id===void 0||n.mode===`edit`&&f!==null&&H!==null;async function U(e){await M({title:`Delete user`,message:`Permanently delete user "${e.username}"? This cannot be undone — any active sessions will be revoked on next request.`,confirmLabel:`Delete`,variant:`danger`})&&(L.mutate({id:e.id}),n.mode===`edit`&&n.id===e.id&&ge())}async function W(e){let t=await P({title:`Reset password`,message:`Set a new password for "${e.username}".`,label:`New password`,inputType:`password`,placeholder:`At least 8 characters`,confirmLabel:`Continue`,validate:e=>e.length<wn?`Password must be at least ${String(wn)} characters.`:null});t===null||t.length===0||await M({title:`Reset password`,message:`Reset the password for "${e.username}"? They will need to use the new password on their next login.`,confirmLabel:`Reset`})&&z.mutate({id:e.id,newPassword:t})}function ye(e,t){t!==e.isAdmin&&R.mutate({id:e.id,isAdmin:t})}async function be(){if(f!==null){g(null);try{if(f.id===void 0){let e=f.username.trim();if(e===``||f.password.length<wn)return;await I.mutateAsync({username:e,password:f.password,isAdmin:f.isAdmin})}else await R.mutateAsync({id:f.id,isAdmin:f.isAdmin});F(),ge()}catch(e){g(Dn(e))}}}function G(e){return e?new Date(typeof e==`number`?e:String(e)).toLocaleDateString(`en-GB`,{day:`2-digit`,month:`short`,year:`numeric`}):`—`}let xe=I.isPending||R.isPending,we=f!==null&&f.id===void 0&&f.username.trim()!==``&&f.password.length>=wn,Te=f!==null&&f.id!==void 0,De=xe||(f?.id===void 0?!we:!Te),Oe=e=>L.isPending&&L.variables?.id===e.id||R.isPending&&R.variables?.id===e.id||z.isPending&&z.variables?.id===e.id;return r(_t,{embedded:!0,icon:Ae,title:`Users`,subtitle:`Hub accounts, admin flag, access scopes, sessions and sign-in links.`,actions:r(k,{size:`sm`,type:`button`,onClick:()=>{g(null),i()},children:[u(ve,{className:`h-3.5 w-3.5`}),`Create User`]}),children:[T&&u(j,{title:`Could not load users`,message:Dn(E),onRetry:()=>void O()}),S&&r(`div`,{className:`flex items-center gap-2 text-xs text-foreground-subtle`,children:[u(C,{className:`h-3.5 w-3.5 animate-spin`}),`Loading…`]}),!S&&!T&&he.length===0&&u(de,{icon:Ae,title:`No users yet`,description:`Create a user to grant hub access.`}),!S&&!T&&he.length>0&&u(ee,{columns:[{key:`username`,header:`Username`,sortValue:e=>e.username,render:e=>u(`span`,{className:`font-medium text-foreground`,"data-testid":`user-open-${e.id}`,children:e.username})},{key:`isAdmin`,header:`Admin`,nowrap:!0,sortValue:e=>+!!e.isAdmin,render:e=>u(`span`,{onClick:e=>e.stopPropagation(),children:r(`label`,{className:`inline-flex items-center gap-1.5 text-xs cursor-pointer`,children:[u(`input`,{type:`checkbox`,checked:e.isAdmin,onChange:t=>ye(e,t.target.checked),disabled:Oe(e),className:`rounded border-border focus:ring-primary disabled:opacity-50`}),u(`span`,{className:e.isAdmin?`text-primary font-medium`:`text-foreground-subtle`,children:e.isAdmin?`Admin`:`Regular`})]})})},{key:`scopes`,header:`Access`,render:e=>e.isAdmin?u(`span`,{className:`text-[10px] italic text-foreground-subtle`,children:`everything (admin)`}):u(`span`,{onClick:e=>e.stopPropagation(),children:r(k,{size:`sm`,variant:`ghost`,type:`button`,onClick:()=>se(e),title:`Configure access`,children:[u(q,{className:`h-3 w-3 shrink-0 text-foreground-subtle`}),u(kn,{scopes:e.scopes??[]})]})})},{key:`createdAt`,header:`Created`,nowrap:!0,sortValue:e=>e.createdAt,render:e=>u(`span`,{className:`text-foreground-subtle`,children:G(e.createdAt)})},{key:`actions`,header:`Actions`,align:`right`,nowrap:!0,notSortableReason:`Row actions`,render:e=>{let n=Oe(e),i=e.id!==t?.id&&e.totpEnabled;return r(`div`,{className:`flex items-center justify-end gap-1`,onClick:e=>e.stopPropagation(),children:[r(k,{size:`sm`,variant:`ghost`,type:`button`,onClick:()=>a(e.id),disabled:n,title:`Edit user`,children:[u(Ce,{className:`h-3 w-3`}),`Edit`]}),i&&r(k,{size:`sm`,variant:`ghost`,type:`button`,onClick:()=>B(e),disabled:n,title:`Remove two-factor authentication`,children:[u(pe,{className:`h-3 w-3`}),`Remove 2FA`]}),r(k,{size:`sm`,variant:`ghost`,type:`button`,onClick:()=>ue(e),disabled:n,title:`Linked sign-in accounts`,children:[u(ae,{className:`h-3 w-3`}),`Sign-in links`]}),r(k,{size:`sm`,variant:`ghost`,type:`button`,onClick:()=>me(e),disabled:n,title:`Active sessions`,children:[u(Pe,{className:`h-3 w-3`}),`Sessions`]}),r(k,{size:`sm`,variant:`ghost`,type:`button`,onClick:()=>void W(e),disabled:n,title:`Reset password`,children:[u(v,{className:`h-3 w-3`}),`Reset password`]}),r(k,{size:`sm`,variant:`ghost`,type:`button`,onClick:()=>void U(e),disabled:n,title:`Delete user`,className:`text-danger hover:bg-danger/10`,children:[n?u(C,{className:`h-3 w-3 animate-spin`}):u(p,{className:`h-3 w-3`}),`Delete`]})]})}}],rows:he,rowKey:e=>e.id,minWidthPx:560,maxBodyHeight:`max-h-[60vh]`,mobileMode:`scroll`,onRowClick:e=>a(e.id)}),u(je,{open:_e,onClose:ge,title:f?.id===void 0?`Create User`:`Edit ${f.username||`user`}`,icon:Ae,error:h,testId:`user-form-dialog`,footer:u(k,{size:`sm`,type:`button`,disabled:De,onClick:()=>void be(),children:xe?r(o,{children:[u(C,{className:`h-3.5 w-3.5 animate-spin`}),`Saving…`]}):f?.id===void 0?`Create`:`Save`}),children:f!==null&&r(`div`,{className:`space-y-3`,children:[u(D,{label:`Username`,required:f.id===void 0,children:u(V,{value:f.username,onChange:e=>m({...f,username:e.target.value}),placeholder:`john`,autoFocus:f.id===void 0,disabled:f.id!==void 0,"data-testid":`user-form-username`})}),f.id===void 0&&u(D,{label:`Password`,required:!0,description:`At least ${String(wn)} characters.`,children:u(N,{value:f.password,onValueChange:e=>m({...f,password:e}),shown:_,onToggleShown:()=>y(e=>!e),placeholder:`••••••••`,"data-testid":`user-form-password`})}),u(D,{label:`Admin`,description:`Unrestricted access to every capability and device.`,children:r(`label`,{className:`inline-flex items-center gap-2 text-xs text-foreground cursor-pointer`,children:[u(`input`,{type:`checkbox`,checked:f.isAdmin,onChange:e=>m({...f,isAdmin:e.target.checked}),className:`rounded border-border focus:ring-primary`,"data-testid":`user-form-admin`}),u(`span`,{children:`Admin`})]})}),f.id!==void 0&&u(`p`,{className:`text-[11px] text-foreground-subtle`,children:`To change the password, use Reset password on the user's row. Access scopes are configured from the Access column.`})]})}),ce&&u(_n,{userId:ce.id,username:ce.username,onClose:()=>B(null)}),le&&u(gn,{userId:le.id,username:le.username,onClose:()=>ue(null)}),fe&&u(vn,{userId:fe.id,username:fe.username,onClose:()=>me(null)}),ie&&u(An,{user:ie,onClose:()=>se(null),onSubmit:e=>re.mutate({userId:ie.id,scopes:e}),submitting:re.isPending})]})}function kn({scopes:e}){if(e.length===0)return u(`span`,{className:`font-medium text-warning`,children:`No access`});let t=tt(e);return t===null?r(`span`,{className:`text-foreground-subtle`,children:[`Custom · `,e.length,` grant`,e.length===1?``:`s`]}):r(`span`,{className:`text-foreground-subtle`,children:[u(`span`,{className:`font-medium text-foreground`,children:rt(t.roleId).label}),` ·`,` `,ge(t.selector)]})}function An(e){let{user:t}=st(),n=t?.isAdmin===!0;return u(Cn,{...e,callerScopes:n?null:t?.scopes??[]})}var jn={"api-keys":Q.scopedTokens,"linked-sessions":Q.linkedApps};function Mn(){let{t:e}=it(),[t]=at(),n=jn[t.get(`tab`)??``];if(n!==void 0)return u(ct,{to:ft(`access`,{integration:n}),replace:!0});let r=ot(`/system/identity`,{t:e,isAdmin:!0})??{icon:ke,title:e(`system.identity`,`Identity & Access`)};return u(gt,{icon:r.icon,title:r.title,defaultSection:`users`,sectionsLabel:`${r.title} sections`,sections:[{id:`users`,label:`Users`,content:u(On,{})},{id:dt.access.tab,label:`Tokens & sessions`,content:u(en,{}),ownedParams:mt.concat([Mt.scoped,Mt.admin])},{id:dt.authentication.tab,label:`Authentication`,content:u(mn,{}),ownedParams:mt}]})}export{Mn as IdentityPage};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{I as e,K as t,Q as n,S as r,W as i,a,b as o,o as s,q as c,t as l,x as u,z as d}from"./_virtual_mf___mfe_internal__admin_ui_host__loadShare___mf_0_tanstack_mf_1_react_mf_2_query__loadShare__.js-DhP-Lgiq.js";import{ct as f,ot as p,yt as m}from"./player-overlays-mP7nvLXZ.js";import{$ as h,An as g,Cd as _,Ci as v,Ed as y,Fn as b,Gi as ee,Gn as x,Hn as S,Ht as te,In as C,L as ne,Mn as w,Nn as T,Q as E,Ql as re,Si as D,Tn as ie,Wn as ae,Yu as O,_u as k,bi as A,dd as oe,dn as se,gi as j,hi as M,jn as N,jr as ce,md as le,mf as P,nr as ue,od as F,ud as I,ur as L,vi as R,xi as z,yi as de,z as fe}from"./src-BHYGdVQH.js";import{t as B}from"./monitor-smartphone-CEvv3FjX.js";import{Cr as V,Nt as pe,tr as H}from"./client-D5nV-lIF.js";import{t as U}from"./useTranslation-Bc5yH9V9.js";import{$ as W,C as me,I as he,Z as G,s as K}from"./index-D-W8ahNU.js";import{t as ge}from"./AdminSectionedPage-DMUZLoRM.js";import{t as _e}from"./NodeAddonsSettingsPanel-C7Cwrd5N.js";import{t as ve}from"./BrokerForm-Bp2vNPeo.js";import{n as q,r as ye,t as be}from"./AddonIntegrationBody-DEH_hiBL.js";var xe=m(`recycle`,[[`path`,{d:`M7 19H4.815a1.83 1.83 0 0 1-1.57-.881 1.785 1.785 0 0 1-.004-1.784L7.196 9.5`,key:`x6z5xu`}],[`path`,{d:`M11 19h8.203a1.83 1.83 0 0 0 1.556-.89 1.784 1.784 0 0 0 0-1.775l-1.226-2.12`,key:`1x4zh5`}],[`path`,{d:`m14 16-3 3 3 3`,key:`f6jyew`}],[`path`,{d:`M8.293 13.596 7.196 9.5 3.1 10.598`,key:`wf1obh`}],[`path`,{d:`m9.344 5.811 1.093-1.892A1.83 1.83 0 0 1 11.985 3a1.784 1.784 0 0 1 1.546.888l3.943 6.843`,key:`9tzpgr`}],[`path`,{d:`m13.378 9.633 4.096 1.098 1.097-4.096`,key:`1oe83g`}]]);n();var J=`kind`,Y=`name`,X=`restore`,Z=`restoreId`;function Se(e){return{sections:[{id:`broker-identity`,title:`Broker`,columns:1,fields:[{type:`select`,key:J,label:`Kind`,options:e.map(e=>({value:e.key,label:e.label})),disabled:e.length<=1},{type:`text`,key:Y,label:`Name`,placeholder:`My Home Assistant`,required:!0},{type:`boolean`,key:X,label:`Restore a broker that was removed, under its original id`,description:`Devices remember the broker id they were discovered under, so restoring the original id reconnects them instead of needing re-discovery. An id that is still in use is refused.`},{type:`text`,key:Z,label:`Original broker id`,placeholder:`ha_004`,required:!0,showWhen:{field:X,equals:!0}}]}]}}function Ce(e){let t=t=>{let n=e[t];return typeof n==`string`?n.trim():``},n=t(J);if(n===``)return{ok:!1,problem:`Pick a kind.`};let r=t(Y);if(r===``)return{ok:!1,problem:`Enter a name before saving.`};if(e.restore!==!0)return{ok:!0,identity:{kindKey:n,name:r,restoreId:null}};let i=t(Z);return i===``?{ok:!1,problem:`Enter the original id, or turn restore off to create a new broker.`}:{ok:!0,identity:{kindKey:n,name:r,restoreId:i}}}var we={"home-assistant":`Home Assistant`,mqtt:`MQTT`};function Q(e){return we[e]??e}function Te(e){return Array.isArray(e)?e.filter(e=>typeof e==`object`&&!!e&&typeof e.addonId==`string`&&Array.isArray(e.kinds)):[]}var Ee={connected:`success`,connecting:`warning`,disconnected:`default`,"auth-failed":`danger`,unreachable:`danger`,error:`danger`};function De({status:e}){return u(L,{variant:Ee[e],className:`text-[10px]`,children:e})}function Oe(e){return Array.isArray(e)?e.filter(e=>typeof e==`object`&&!!e&&typeof e.id==`string`):[]}function ke({title:e,subtitle:t,onClose:n,children:i}){return u(g,{open:!0,onOpenChange:e=>!e&&n(),children:r(w,{width:`md`,placement:`sheet-on-mobile`,scroll:`body`,children:[r(b,{children:[u(C,{children:e}),t&&u(T,{children:t})]}),u(N,{className:`space-y-3 pr-1`,children:i})]})})}function Ae({kinds:e,onClose:t,onDone:n}){let a=s(),o=i(()=>Se(e.map(e=>({key:$(e),label:e.label}))),[e]),[l,d]=c(()=>({[J]:$(e[0]),[Y]:``,[X]:!1,[Z]:``})),[f,p]=c(null),m=e.find(e=>$(e)===l.kind)??e[0],h=m?.kind??``,g=m?.addonId,_=M({onSuccess:()=>{a.invalidateQueries({queryKey:[[`broker`,`list`]]}),n()}}),v=e=>{let t=Ce(l);if(!t.ok){p(t.problem);return}p(null);let{name:n,restoreId:r}=t.identity;_.mutate({kind:h,name:n,settings:e,...r===null?{}:{id:r},...g===void 0?{}:{addonId:g}})};return r(ke,{title:`Add broker`,subtitle:`Register a message broker for exporters and automations.`,onClose:_.isPending?()=>{}:t,children:[u(he,{schema:o,values:l,onChange:d}),f!==null&&u(S,{message:f}),_.isError&&u(S,{message:_.error instanceof Error?_.error.message:String(_.error)}),h&&u(ve,{kind:h,...g===void 0?{}:{addonId:g},onSubmit:v,onCancel:t,submitLabel:_.isPending?`Adding…`:`Add broker`,disabled:_.isPending},$(m))]})}function $(e){return e===void 0?``:`${e.addonId??``}::${e.kind}`}function je({broker:e,onClose:t,onDone:n}){let a=s(),o=j({id:e.id,addonId:e.addonId}),c=D({onSuccess:()=>{a.invalidateQueries({queryKey:[[`broker`,`list`]]}),a.invalidateQueries({queryKey:[[`broker`,`getSettings`],{input:{id:e.id,addonId:e.addonId}}]}),n()}}),l=i(()=>{let e=o.data;return e&&typeof e==`object`?{...e}:{}},[o.data]);return u(ke,{title:`Edit ${e.name}`,subtitle:`${Q(e.kind)} · ${e.id}`,onClose:c.isPending?()=>{}:t,children:o.isLoading?r(`div`,{className:`flex items-center gap-2 text-xs text-foreground-subtle`,children:[u(y,{className:`h-3.5 w-3.5 animate-spin`}),` Loading settings…`]}):o.isError?r(`div`,{className:`space-y-3`,children:[u(S,{message:`Failed to load settings: ${o.error instanceof Error?o.error.message:`Unknown error`}`}),u(`div`,{className:`flex justify-end`,children:u(x,{variant:`outline`,size:`sm`,onClick:t,children:`Close`})})]}):r(`div`,{className:`space-y-3`,children:[c.isError&&u(S,{message:c.error instanceof Error?c.error.message:String(c.error)}),u(ve,{kind:e.kind,addonId:e.addonId,initialValues:l,onSubmit:t=>{c.mutate({id:e.id,settings:t,addonId:e.addonId})},onCancel:t,submitLabel:c.isPending?`Saving…`:`Save`,disabled:c.isPending})]})})}function Me({broker:e,onEdit:t}){let n=s(),i=ue(),[a,o]=c(null),l=v({onSuccess:e=>o(e.ok?{ok:!0}:{ok:!1,error:e.error}),onError:e=>o({ok:!1,error:e instanceof Error?e.message:String(e)})}),d=z({onSuccess:()=>{n.invalidateQueries({queryKey:[[`broker`,`list`]]})}}),p=async()=>{await i({title:`Delete broker`,message:`Delete broker "${e.name}"? Integrations that depend on it will stop working.`,confirmLabel:`Delete`,variant:`danger`})&&d.mutate({id:e.id,addonId:e.addonId})};return r(`tr`,{className:`border-b border-border last:border-0`,children:[u(`td`,{className:`px-3 py-2 text-xs text-foreground font-medium`,children:e.name}),u(`td`,{className:`px-3 py-2 text-xs text-foreground-subtle`,children:Q(e.kind)}),r(`td`,{className:`px-3 py-2`,children:[u(De,{status:e.status}),a&&u(`span`,{className:`ml-2 text-[10px] ${a.ok?`text-success`:`text-danger`}`,children:a.ok?`OK`:a.error??`failed`})]}),u(`td`,{className:`px-3 py-2`,children:r(`div`,{className:`flex items-center justify-end gap-1.5`,children:[r(x,{variant:`outline`,size:`sm`,onClick:()=>l.mutate({id:e.id,addonId:e.addonId}),disabled:l.isPending,title:`Test connection`,children:[l.isPending?u(y,{className:`h-3 w-3 animate-spin`}):u(k,{className:`h-3 w-3`}),`Test`]}),r(x,{variant:`outline`,size:`sm`,onClick:()=>t(e),title:`Edit`,children:[u(le,{className:`h-3 w-3`}),` Edit`]}),r(x,{variant:`outline`,size:`sm`,className:`border-danger/40 text-danger hover:bg-danger/10`,onClick:()=>{p()},disabled:d.isPending,title:`Delete`,children:[d.isPending?u(y,{className:`h-3 w-3 animate-spin`}):u(f,{className:`h-3 w-3`}),`Delete`]})]})})]})}function Ne({addonId:e}={}){let t=de({}),n=A(),a=i(()=>Oe(t.data).filter(t=>e===void 0||t.addonId===e),[t.data,e]),[o,s]=c(!1),[l,d]=c(null),f=i(()=>{let t=new Map;for(let r of Te(n.data))if(!(e!==void 0&&r.addonId!==e))for(let e of r.kinds)t.set(`${r.addonId}::${e.kind}`,{addonId:r.addonId,kind:e.kind,label:e.label});for(let e of a)[...t.values()].some(t=>t.kind===e.kind)||t.set(`::${e.kind}`,{kind:e.kind,label:Q(e.kind)});return[...t.values()]},[a,n.data,e]);return r(`div`,{className:`space-y-3`,children:[u(`div`,{className:`flex items-center justify-end`,children:r(x,{variant:`primary`,size:`sm`,onClick:()=>s(!0),disabled:f.length===0,title:f.length===0?`No broker providers available`:void 0,children:[u(oe,{className:`h-3.5 w-3.5`}),` Add broker`]})}),u(`div`,{className:`rounded-lg border border-border bg-surface overflow-x-auto`,children:r(`table`,{className:`w-full min-w-[36rem] border-collapse`,children:[u(`thead`,{children:r(`tr`,{className:`border-b border-border bg-background/50`,children:[u(`th`,{className:`px-3 py-2 text-left text-[10px] font-semibold uppercase tracking-wider text-foreground-subtle`,children:`Name`}),u(`th`,{className:`px-3 py-2 text-left text-[10px] font-semibold uppercase tracking-wider text-foreground-subtle`,children:`Kind`}),u(`th`,{className:`px-3 py-2 text-left text-[10px] font-semibold uppercase tracking-wider text-foreground-subtle`,children:`Status`}),u(`th`,{className:`px-3 py-2 text-right text-[10px] font-semibold uppercase tracking-wider text-foreground-subtle`,children:`Actions`})]})}),r(`tbody`,{children:[t.isLoading&&u(`tr`,{children:r(`td`,{colSpan:4,className:`px-3 py-6 text-center text-xs text-foreground-subtle`,children:[u(y,{className:`inline h-4 w-4 animate-spin`}),` Loading brokers…`]})}),t.isError&&u(`tr`,{children:r(`td`,{colSpan:4,className:`px-3 py-4 text-center text-xs text-danger`,children:[`Failed to load brokers:`,` `,t.error instanceof Error?t.error.message:`Unknown error`]})}),!t.isLoading&&!t.isError&&a.length===0&&u(`tr`,{children:u(`td`,{colSpan:4,className:`px-3 py-6 text-center text-xs text-foreground-subtle`,children:`No brokers registered yet. Add one to publish device events for downstream automations.`})}),a.map(e=>u(Me,{broker:e,onEdit:d},e.id))]})]})}),o&&u(Ae,{kinds:f,onClose:()=>s(!1),onDone:()=>s(!1)}),l&&u(je,{broker:l,onClose:()=>d(null),onDone:()=>d(null)})]})}function Pe(e){if(typeof e==`string`)return e;if(typeof e==`object`&&e){let t=e.name;if(typeof t==`string`)return t}return null}function Fe(e){let t=e,n=t?.manifest;if(n?.id===void 0)return null;let r=(n.capabilities??[]).map(Pe).filter(e=>e!==null);return{addonId:n.id,displayName:n.packageDisplayName??n.name??n.id,capNames:r,processState:t?.process?.state??null}}function Ie(e,t){return Object.entries(e).filter(([,e])=>e===t).map(([e])=>e)}function Le(e,t){return t.find(t=>e.capNames.includes(t))??null}function Re(e){switch(e){case`running`:return{state:`ok`,label:`running`};case`crashed`:case`failed`:return{state:`error`,label:e};case`stopped`:case`disabled`:return{state:`off`,label:`stopped`};default:return{state:`unknown`,label:e??`unknown`}}}n();var ze=[`wasteCollections`,`listSources`],Be=[`wasteCollections`,`listCountries`],Ve=/^[a-z0-9][a-z0-9-]{0,63}$/;function He(e){return e instanceof Error?e.message:String(e)}function Ue(e){return typeof e!=`object`||!e?!1:Array.isArray(Reflect.get(e,`sections`))}function We(e){if(!Array.isArray(e))return[];let t=[];for(let n of e){if(typeof n!=`object`||!n)continue;let e=Reflect.get(n,`id`),r=Reflect.get(n,`label`),i=Reflect.get(n,`module`);if(typeof e!=`string`||typeof r!=`string`||typeof i!=`string`)continue;let a=Reflect.get(n,`argsJson`),o=Reflect.get(n,`enabled`),s=Reflect.get(n,`formValues`),c=Reflect.get(n,`serviceId`),l=Reflect.get(n,`countryId`);t.push({id:e,label:r,module:i,argsJson:typeof a==`string`?a:`{}`,enabled:typeof o==`boolean`?o:!0,formValues:typeof s==`object`&&s&&!Array.isArray(s)?s:void 0,serviceId:typeof c==`string`?c:void 0,countryId:typeof l==`string`?l:void 0})}return t}function Ge(e){return Array.isArray(e)?e.flatMap(e=>{if(typeof e!=`object`||!e)return[];let t=Reflect.get(e,`id`),n=Reflect.get(e,`label`);return typeof t!=`string`||typeof n!=`string`?[]:[{id:t,label:n}]}):[]}function Ke(e){return Array.isArray(e)?e.flatMap(e=>{if(typeof e!=`object`||!e)return[];let t=Reflect.get(e,`id`),n=Reflect.get(e,`label`),r=Reflect.get(e,`module`);if(typeof t!=`string`||typeof n!=`string`||typeof r!=`string`)return[];let i=Reflect.get(e,`title`);return[{id:t,label:n,title:typeof i==`string`&&i.length>0?i:n,module:r}]}):[]}function qe(e,t,n){return{sections:[{id:`picker`,title:`Country & service`,columns:2,fields:[{type:`select`,key:`countryId`,label:`Country`,options:[{value:``,label:`Select country…`},...e.map(e=>({value:e.id,label:e.label}))]},{type:`select`,key:`serviceId`,label:`Service`,options:[{value:``,label:n===``?`Pick a country first`:`Select service…`},...t.map(e=>({value:e.id,label:e.label}))]}]}]}}function Je({addonId:n,capName:m}){let h=p(),g=s(),_=ue(),v=ne({editKey:`wasteEdit`,newKey:`wasteNew`}),[y,b]=c(!1),[ee,S]=c(null),[C,w]=c(``),[T,E]=c({countryId:``,serviceId:``}),[D,O]=c({label:`New source`,enabled:!0}),k=t(null),A=typeof T.countryId==`string`?T.countryId:``,j=typeof T.serviceId==`string`?T.serviceId:``,M=e((e,t)=>h.trpcClient.addons.custom.mutate({addonId:n,action:e,input:t}),[n,h]),N=a({queryKey:[...ze,n],queryFn:async()=>We(await M(`listSources`,{}))}),ce=a({queryKey:[...Be,n],queryFn:async()=>Ge(await M(`listCountries`,{}))}),le=a({queryKey:[`wasteCollections`,`listServices`,n,A],enabled:A!==``,queryFn:async()=>Ke(await M(`listServices`,{countryId:A}))}),P=a({queryKey:[`wasteCollections`,`serviceFormSchema`,n,j],enabled:j!==``,queryFn:async()=>M(`serviceFormSchema`,{serviceId:j})}),F=N.data??[],I=ce.data??[],R=le.data??[],z=Ue(P.data)?P.data:void 0,de=i(()=>qe(I,A===``?[]:R,A),[I,R,A]),B=v.state,V=B.mode===`edit`?F.find(e=>e.id===B.id)??null:null;d(()=>{if(v.state.mode===`edit`&&V!==null){let e=`edit:${V.id}`;if(k.current===e)return;k.current=e,S(null),w(V.id),E({countryId:V.countryId??``,serviceId:V.serviceId??``}),O(V.formValues===void 0?{label:V.label,enabled:V.enabled}:{...V.formValues});return}if(v.state.mode===`new`){if(k.current===`new`)return;k.current=`new`,S(null),w(``),E({countryId:``,serviceId:``}),O({label:`New source`,enabled:!0})}},[V,v.state]),d(()=>{if(v.state.mode!==`new`||j===``)return;let e=R.find(e=>e.id===j);e&&O(t=>{let n=t.label;return typeof n==`string`&&n!==``&&n!==`New source`?t:{...t,label:e.title}})},[v.state.mode,j,R]);let pe=e(e=>{E(t=>{let n=typeof e.countryId==`string`?e.countryId:``;return n===(typeof t.countryId==`string`?t.countryId:``)?e:{countryId:n,serviceId:``}})},[]),H=e(async()=>{await g.invalidateQueries({queryKey:ze})},[g]),U=l({mutationFn:async e=>M(`deleteSource`,{id:e}),onSuccess:async()=>H()}),W=e(async e=>{await _({title:`Delete waste source?`,message:`Remove “${e.label}” (${e.id}). Its calendar under System → Calendars is dropped.`,confirmLabel:`Delete`,cancelLabel:`Cancel`,variant:`danger`})&&(await U.mutateAsync(e.id),v.state.mode===`edit`&&v.state.id===e.id&&(v.close(),k.current=null))},[_,U,v]),me=e(async()=>{let e=D.label,t=typeof e==`string`&&e.trim()!==``?e.trim():``;if(t===``){S(`Label is required.`);return}if(j===``){S(`Pick a country and a service.`);return}let n=v.state.mode===`new`,r=n?C.trim():V?.id??``;if(n&&!Ve.test(r)){S(`Source id must be a lowercase slug (e.g. ics-home, junker).`);return}if(r===``){S(`That source is no longer available.`);return}let i=D.enabled,a={...D};delete a.label,delete a.enabled,b(!0),S(null);try{n?await M(`createSource`,{id:r,label:t,serviceId:j,args:a,enabled:typeof i==`boolean`?i:!0}):await M(`updateSource`,{id:r,label:t,serviceId:j,args:a,enabled:typeof i==`boolean`?i:!0}),await H(),v.close(),k.current=null}catch(e){S(He(e))}finally{b(!1)}},[M,V?.id,v,D,H,j,C]),he=i(()=>[{key:`id`,header:`Id`,sortValue:e=>e.id,render:e=>u(`span`,{className:`font-mono text-xs text-foreground`,children:e.id})},{key:`label`,header:`Label`,sortValue:e=>e.label,render:e=>u(`span`,{className:`text-sm text-foreground`,children:e.label})},{key:`module`,header:`Module`,sortValue:e=>e.module,render:e=>u(`span`,{className:`font-mono text-xs text-foreground-muted`,children:e.module})},{key:`enabled`,header:`Enabled`,sortValue:e=>+!!e.enabled,render:e=>u(L,{variant:e.enabled?`success`:`default`,children:e.enabled?`On`:`Off`})},{key:`actions`,header:``,notSortableReason:`actions`,render:e=>u(x,{type:`button`,size:`sm`,variant:`ghost`,"aria-label":`Delete ${e.label}`,"data-testid":`waste-source-delete-${e.id}`,onClick:t=>{t.stopPropagation(),W(e)},children:u(f,{className:`h-3.5 w-3.5 text-danger`})})}],[W]),G=v.state.mode===`new`,K=G||V!==null,ge=`${j}:${G?`new`:V?.id??`closed`}`;return r(`div`,{className:`flex min-h-0 flex-col gap-4`,"data-testid":`waste-settings-sources`,children:[r(`div`,{className:`flex flex-wrap items-center justify-between gap-2`,children:[u(`p`,{className:`text-sm text-foreground-muted`,children:`Pick a country and a service — configuration fields come from that HACS source.`}),r(x,{type:`button`,size:`sm`,"data-testid":`waste-sources-add`,onClick:()=>v.openNew(),children:[u(oe,{className:`h-3.5 w-3.5`}),`Add source`]})]}),N.isError?u(`p`,{className:`text-sm text-danger`,children:`Could not load sources.`}):null,N.isPending?u(ae,{className:`h-32 w-full`}):u(te,{columns:he,rows:F,rowKey:e=>e.id,pageSize:25,mobileMode:`scroll`,emptyMessage:`No waste sources yet. Add one to register a municipal service.`,onRowClick:e=>v.openEdit(e.id),defaultSort:{columnKey:`label`,dir:`asc`}}),u(fe,{open:K,onClose:()=>{v.close(),S(null),k.current=null},title:G?`Add waste source`:`Edit ${V?.label??`source`}`,icon:xe,width:`xl`,testId:`waste-source-editor`,error:ee,footer:u(x,{type:`button`,size:`sm`,disabled:y||j===``||G&&C.trim()===``,onClick:()=>void me(),children:y?`Saving…`:`Save`}),children:K?r(o,{children:[G?r(`div`,{children:[u(se,{htmlFor:`waste-source-id`,children:`Source id`}),u(re,{id:`waste-source-id`,value:C,onChange:e=>w(e.target.value),placeholder:`home-zew`,autoComplete:`off`}),u(`p`,{className:`mt-1 text-xs text-foreground-muted`,children:`Stable calendar local id. Renaming is not supported after save.`})]}):r(`p`,{className:`text-sm text-foreground-muted`,children:[`Source id: `,u(`span`,{className:`font-mono`,children:V?.id})]}),u(`div`,{"data-testid":`waste-country-service`,children:u(ie,{schema:de,values:T,onChange:pe},`picker:${A}:${R.length}`)}),j!==``&&z!==void 0?u(ie,{schema:z,values:D,onChange:O,bare:!0},ge):j!==``&&P.isPending?u(`p`,{className:`text-sm text-foreground-muted`,children:`Loading fields…`}):u(`p`,{className:`text-sm text-foreground-muted`,children:`Choose a country and service to see the required fields.`})]}):null}),u(_e,{nodeId:`hub`,addonIds:[n],level:`global`,capability:m,hideSectionChrome:!0})]})}n();var Ye=1e4,Xe={"device-export":`exporters`,exporters:`exporters`,email:`email`,broker:`brokers`,brokers:`brokers`,"message-brokers":`brokers`,calendar:`calendars`,calendars:`calendars`,"calendar-source":`calendars`},Ze=[{sectionId:`exporters`,kind:`device-export`,label:`Exporters`,icon:O,installLabel:`Add export`,emptyTitle:`No device exporters installed`,emptyDescription:`Install an export addon to publish devices to Alexa, HomeKit, Google Home or Home Assistant.`},{sectionId:`email`,kind:`email`,label:`Email`,icon:_,installLabel:`Add email provider`,emptyTitle:`No email providers installed`,emptyDescription:`Install an SMTP or Gmail addon to send notification email from this hub.`},{sectionId:`brokers`,kind:`broker`,label:`Brokers`,icon:F,installLabel:`Add broker addon`,emptyTitle:`No message brokers installed`,emptyDescription:`Install a broker addon to connect Home Assistant, Homematic, MQTT, Matter and similar systems.`},{sectionId:`calendars`,kind:`calendar`,label:`Calendars`,icon:P,installLabel:`Add calendar source`,emptyTitle:`No calendar sources installed`,emptyDescription:`Install a calendar-source addon (Google Calendar, waste collections, …) to feed System → Calendars.`}];function Qe(e,t,n){let i=Ie(pe,e.kind),a=t.flatMap(t=>{let a=Le(t,i);if(a===null)return[];let s=Re(t.processState);return[{id:t.addonId,name:t.displayName,fallbackIcon:e.icon,state:s.state,stateLabel:s.label,render:()=>u(be,{addonId:t.addonId,displayName:t.displayName,capName:a,isActive:t.processState===`running`,badges:r(o,{children:[u(nt,{processState:t.processState}),u(rt,{kind:e.kind,addonId:t.addonId})]}),lead:e.kind===`device-export`?u(h,{addonId:t.addonId}):void 0,panels:tt(e.kind,t.addonId,e=>n(`/devices/${e}`)),settingsContent:t.addonId===`waste-collections`?u(Je,{addonId:t.addonId,capName:a}):void 0})}]});return{id:e.sectionId,label:e.label,entries:a,install:{capability:i[0]??e.kind,label:e.installLabel}}}function $e({descriptor:e,rows:t,isLoading:n}){let r=G(),a=i(()=>Qe(e,t,r),[e,t,r]);return u(ye,{embedded:!0,icon:e.icon,title:e.label,subtitle:null,groups:[a],isLoading:n,emptyTitle:e.emptyTitle,emptyDescription:e.emptyDescription})}function et(){let{t:e}=U(),t=ce(void 0,{refetchInterval:Ye}),[n,r]=W(),a=me(`/system/integrations`,{t:e,isAdmin:!0})??{icon:I,title:`Connectivity`};d(()=>{let e=n.get(`tab`);if(e===null)return;let t=Xe[e];t===void 0||t===e||r(e=>{let n=new URLSearchParams(e);return n.set(`tab`,t),n},{replace:!0})},[n,r]);let o=i(()=>(t.data??[]).map(Fe).filter(e=>e!==null),[t.data]),s=i(()=>Ze.map(e=>({id:e.sectionId,label:e.label,ownedParams:K,content:u($e,{descriptor:e,rows:o,isLoading:t.isLoading})})),[t.isLoading,o]);return u(ge,{icon:a.icon,title:a.title,subtitle:`External systems this hub talks to — exporters, email, brokers and calendar sources.`,defaultSection:`exporters`,sectionsLabel:`Connectivity sections`,sections:s})}function tt(e,t,n){return e===`device-export`?[{id:`devices`,title:`Devices`,icon:B,placement:`end`,content:u(E,{addonId:t,onOpenDevice:n,showSetup:!1})}]:e===`broker`?[{id:`connections`,title:`Connections`,icon:F,content:u(Ne,{addonId:t})}]:[]}function nt({processState:e}){let{state:t,label:n}=Re(e);return u(L,{variant:t===`ok`?`success`:t===`error`?`danger`:`default`,className:`${q} capitalize`,children:n})}function rt({kind:e,addonId:t}){return e===`broker`?u(it,{addonId:t}):e===`device-export`?u(at,{addonId:t}):null}function it({addonId:e}){let{data:t}=R({addonId:e},{refetchInterval:Ye,retry:!1}),n=V.safeParse(t);if(!n.success)return null;let{brokerCount:r,connectedCount:i}=n.data;return u(L,{variant:`info`,className:q,children:`${r} connection${r===1?``:`s`}${i>0?` · ${i} connected`:``}`})}function at({addonId:e}){let{data:t}=ee({addonId:e},{refetchInterval:Ye,retry:!1}),n=H.safeParse(t);if(!n.success)return null;let{linkState:r,exposedDeviceCount:i}=n.data;return u(L,{variant:r===`linked`?`success`:r===`error`?`danger`:`default`,className:q,children:`${r} · ${i} exposed`})}export{et as IntegrationsPage};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{Q as e,S as t,W as n,q as r,x as i}from"./_virtual_mf___mfe_internal__admin_ui_host__loadShare___mf_0_tanstack_mf_1_react_mf_2_query__loadShare__.js-DhP-Lgiq.js";import{vt as a,yt as o}from"./player-overlays-mP7nvLXZ.js";import{$u as s,Ed as c,Ff as l,Mr as u,Tt as d,ff as f,jr as p,ll as m,sl as h,su as g}from"./src-BHYGdVQH.js";import{t as _}from"./layers-D5TeeKqH.js";import{t as v}from"./useTranslation-Bc5yH9V9.js";import{C as y,c as b,s as x}from"./index-D-W8ahNU.js";import{t as S}from"./AdminSectionedPage-DMUZLoRM.js";import{t as C}from"./AdminPage-oBqVY0PY.js";import{r as w,t as T}from"./AddonIntegrationBody-DEH_hiBL.js";import{n as E,t as D}from"./addon-display-names-BkwMrExv.js";var O=o(`file-text`,[[`path`,{d:`M6 22a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.704.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2z`,key:`1oefj6`}],[`path`,{d:`M14 2v5a1 1 0 0 0 1 1h5`,key:`wfsgrz`}],[`path`,{d:`M10 9H8`,key:`b1mrlr`}],[`path`,{d:`M16 13H8`,key:`t4e002`}],[`path`,{d:`M16 17H8`,key:`z1uh3a`}]]),k=o(`timer-reset`,[[`path`,{d:`M10 2h4`,key:`n1abiw`}],[`path`,{d:`M12 14v-4`,key:`1evpnu`}],[`path`,{d:`M4 13a8 8 0 0 1 8-7 8 8 0 1 1-5.3 14L4 17.6`,key:`1ts96g`}],[`path`,{d:`M9 17H4v5`,key:`8t5av`}]]);e();var A=5e3,j=`log-destination`,M={"winston-logging":`Rotated log files on this node’s disk.`,"console-logging":`stdout / stderr of the node process.`,"loki-logging":`Pushes to a Grafana Loki instance. Off until enabled with a URL.`,"hub-forwarder":`Agent-only — forwards this node’s entries to the hub.`};function N(){let e=u({capName:`log-destination`},{refetchInterval:A}),t=p(void 0,{refetchInterval:A}),r=n(()=>D(t.data??[]),[t.data]),a=n(()=>{let e=new Map;for(let n of t.data??[]){let t=n.description,r=n.id;typeof r==`string`&&typeof t==`string`&&t.length>0&&e.set(r,t)}return e},[t.data]),o=n(()=>(e.data??[]).map(e=>({addonId:e.addonId,displayName:r.get(e.addonId)??e.addonId,isActive:e.isActive,caption:a.get(e.addonId)??M[e.addonId]??``})),[e.data,r,a]);return i(w,{embedded:!0,icon:s,title:`Destinations`,subtitle:`Every registered log destination. Entries are fanned to all enabled destinations at once — enabling one does not disable another.`,groups:[{id:j,label:`Log destinations`,install:{capability:j,label:`Add destination`},entries:o.map(e=>({id:e.addonId,name:e.displayName,fallbackIcon:O,...E(e.isActive),render:()=>i(T,{addonId:e.addonId,displayName:e.displayName,capName:j,isActive:e.isActive,badges:e.caption.length>0?i(`span`,{className:`text-[11px] text-foreground-subtle`,children:e.caption}):null})}))}],isLoading:e.isLoading,emptyTitle:`No log destinations registered`,emptyDescription:`This should not happen — the console and rotated-file destinations are builtins. If the list is empty, the node is still booting.`})}e();var P=[12e4,3e5,6e5,18e5],F={"request-census":`Counts every tRPC call by TRANSPORT PLANE — http, ws, mesh — and by procedure, caller, address and user-agent. Subscriptions are counted apart from calls, so a long-lived stream cannot pass for a storm. Arm it, then restart: the deadline is persisted, so the window is still running when clients reconnect.`};function I(){let{t:e}=v(),n=h({},{refetchInterval:5e3}),a=m(),[o,s]=r(null),u=(e,t)=>{s(e),a.mutate({patch:{diagnostics:[{id:e,armMs:t}]}},{onSettled:()=>{s(null),n.refetch()}})},d=n.data?.activeWindows??[],f=n.data?.channels??[],p=n.data?.activeChannels??[],g=(e,t,r)=>{s(e),a.mutate({patch:{channels:[{channel:e,armMs:t,deviceIds:r}]}},{onSettled:()=>{s(null),n.refetch()}})};return t(C,{embedded:!0,icon:l,title:e(`logging.diagnostics.title`,`Diagnostics`),subtitle:e(`logging.diagnostics.subtitle`,`Diagnostics are armed for a window, never switched on. Each carries a deadline and expires by itself — one left running is an incident of its own.`),children:[n.isLoading?t(`div`,{className:`flex items-center gap-2 text-xs text-foreground-subtle`,children:[i(c,{className:`h-3.5 w-3.5 animate-spin`}),e(`common.loading`,`Loading…`)]}):i(`div`,{className:`rounded-lg border border-border bg-surface divide-y divide-border`,children:d.map(e=>i(R,{window:e,busy:o===e.id,onArm:t=>u(e.id,t)},e.id))}),i(`h3`,{className:`mt-6 text-sm font-medium text-foreground`,children:e(`logging.diagnostics.channels`,`Component channels`)}),i(`p`,{className:`mb-2 max-w-3xl text-[11px] text-foreground-subtle`,children:e(`logging.diagnostics.channelsSubtitle`,`Declared by the addons that own them — this list is not maintained by hand. Arming a channel opens a window with a deadline; it closes by itself.`)}),f.length===0?i(`p`,{className:`text-[11px] text-foreground-subtle`,children:e(`logging.diagnostics.noChannels`,`No addon on this cluster declares a log channel, or none could be reached right now.`)}):i(`div`,{className:`rounded-lg border border-border bg-surface divide-y divide-border`,children:f.map(e=>i(L,{channel:e,state:p.find(t=>t.channel===e.name),busy:o===e.name,onArm:(t,n)=>g(e.name,t,n)},e.name))})]})}function L({channel:e,state:n,busy:o,onArm:s}){let{t:c}=v(),[l,u]=r(``),d=n?.armed===!0,f=()=>{let e=l.split(/[\s,+]+/).filter(e=>/^\d+$/.test(e)).map(e=>Number(e));return e.length>0?e:null};return t(`div`,{className:`flex flex-col gap-2 p-3 lg:flex-row lg:items-center lg:justify-between`,children:[t(`div`,{className:`min-w-0`,children:[t(`div`,{className:`flex flex-wrap items-center gap-2`,children:[i(`span`,{className:`text-sm font-medium text-foreground`,children:e.name}),d?t(`span`,{className:`inline-flex items-center gap-1 rounded bg-warning/10 px-1.5 py-0.5 text-[10px] font-semibold text-warning`,children:[i(k,{className:`h-3 w-3`}),c(`logging.diagnostics.expiresIn`,`expires in {{left}}`,{left:z(n?.remainingMs??0)})]}):i(`span`,{className:`rounded border border-border px-1.5 py-0.5 text-[10px] text-foreground-subtle`,children:c(`logging.diagnostics.disarmed`,`disarmed`)}),d&&n?.deviceIds!==null&&n?.deviceIds!==void 0&&t(`span`,{className:`inline-flex items-center gap-1 rounded bg-primary/10 px-1.5 py-0.5 text-[10px] font-semibold text-primary`,children:[i(a,{className:`h-3 w-3`}),n.deviceIds.join(`, `)]}),!e.perDevice&&i(`span`,{className:`rounded border border-border px-1.5 py-0.5 text-[10px] text-foreground-subtle`,children:c(`logging.diagnostics.notPerCamera`,`not filterable per camera`)})]}),i(`p`,{className:`mt-0.5 max-w-2xl text-[11px] text-foreground-subtle`,children:e.description})]}),t(`div`,{className:`flex shrink-0 flex-wrap items-center gap-1`,children:[e.perDevice&&i(`input`,{type:`text`,inputMode:`numeric`,value:l,disabled:o,onChange:e=>u(e.target.value),placeholder:c(`logging.diagnostics.camerasPlaceholder`,`all cameras`),className:`w-28 rounded border border-border bg-surface px-2 py-1 text-[11px] text-foreground`}),P.map(e=>i(`button`,{type:`button`,disabled:o,onClick:()=>s(e,f()),className:`rounded border border-border px-2 py-1 text-[11px] text-foreground-subtle hover:text-foreground disabled:opacity-50`,children:z(e)},e)),i(`button`,{type:`button`,disabled:o||!d,onClick:()=>s(0,null),className:`rounded bg-primary px-2 py-1 text-[11px] font-medium text-primary-foreground disabled:opacity-40`,children:c(`logging.diagnostics.disarm`,`Disarm`)})]})]})}function R({window:e,busy:n,onArm:r}){let{t:a}=v();return t(`div`,{className:`flex flex-col gap-2 p-3 lg:flex-row lg:items-center lg:justify-between`,children:[t(`div`,{className:`min-w-0`,children:[t(`div`,{className:`flex items-center gap-2`,children:[i(`span`,{className:`text-sm font-medium text-foreground`,children:e.id}),e.armed?t(`span`,{className:`inline-flex items-center gap-1 rounded bg-warning/10 px-1.5 py-0.5 text-[10px] font-semibold text-warning`,children:[i(k,{className:`h-3 w-3`}),a(`logging.diagnostics.expiresIn`,`expires in {{left}}`,{left:z(e.remainingMs)})]}):i(`span`,{className:`rounded border border-border px-1.5 py-0.5 text-[10px] text-foreground-subtle`,children:a(`logging.diagnostics.disarmed`,`disarmed`)}),e.armed&&!e.persisted&&i(`span`,{className:`text-[10px] text-warning`,children:a(`logging.diagnostics.notPersisted`,`not persisted — will not survive a restart`)})]}),i(`p`,{className:`mt-0.5 max-w-2xl text-[11px] text-foreground-subtle`,children:F[e.id]??``})]}),t(`div`,{className:`flex shrink-0 flex-wrap items-center gap-1`,children:[P.map(e=>i(`button`,{type:`button`,disabled:n,onClick:()=>r(e),className:`rounded border border-border px-2 py-1 text-[11px] text-foreground-subtle hover:text-foreground disabled:opacity-50`,children:z(e)},e)),i(`button`,{type:`button`,disabled:n||!e.armed,onClick:()=>r(0),className:`rounded bg-primary px-2 py-1 text-[11px] font-medium text-primary-foreground disabled:opacity-40`,children:a(`logging.diagnostics.disarm`,`Disarm`)})]})]})}function z(e){let t=Math.max(0,Math.round(e/1e3)),n=Math.floor(t/60),r=t%60;return n===0?`${r}s`:r===0?`${n}m`:`${n}m ${r}s`}e();var B=[`debug`,`info`,`warn`,`error`],V=``,H=``;function U(){let{t:e}=v(),[a,o]=r(V),[s,l]=r(H),{nodes:u}=g(),d=n(()=>u.map(e=>({id:e.id,label:e.name||e.id})),[u]),f=h({...a===V?{}:{scopeNodeId:a},...s===H?{}:{scopeComponent:s}},{refetchInterval:15e3}),p=m(),y=f.data,b=p.isPending||f.isFetching,x=y?.channels??[],S=(e,t)=>{p.mutate({...e.nodeId===null?{}:{scopeNodeId:e.nodeId},...e.component===null?{}:{scopeComponent:e.component},patch:{level:t}},{onSuccess:()=>void f.refetch()})};return i(C,{embedded:!0,icon:_,title:e(`logging.levels.title`,`Levels`),subtitle:e(`logging.levels.subtitle`,`Cluster → node, most specific wins. The floor gates what reaches the log destinations; the in-memory viewer keeps every line either way.`),actions:t(`div`,{className:`flex flex-wrap items-center gap-3`,children:[t(`label`,{className:`flex items-center gap-2 text-xs text-foreground-subtle`,children:[e(`logging.levels.scope`,`Scope`),t(`select`,{className:`rounded border border-border bg-surface px-2 py-1 text-xs text-foreground disabled:opacity-50`,value:a,disabled:s!==H,onChange:e=>o(e.target.value),children:[i(`option`,{value:V,children:e(`logging.levels.cluster`,`Cluster`)}),d.map(e=>i(`option`,{value:e.id,children:e.label},e.id))]})]}),t(`label`,{className:`flex items-center gap-2 text-xs text-foreground-subtle`,children:[e(`logging.levels.component`,`Component`),t(`select`,{className:`rounded border border-border bg-surface px-2 py-1 text-xs text-foreground`,value:s,onChange:e=>l(e.target.value),children:[i(`option`,{value:H,children:e(`logging.levels.noComponent`,`None`)}),x.map(e=>i(`option`,{value:e.name,children:e.name},e.name))]})]})]}),children:f.isLoading||y===void 0?t(`div`,{className:`flex items-center gap-2 text-xs text-foreground-subtle`,children:[i(c,{className:`h-3.5 w-3.5 animate-spin`}),e(`common.loading`,`Loading…`)]}):t(`div`,{className:`space-y-3`,children:[i(G,{level:y.effective.level,source:y.effective.levelSource,persisted:y.persisted}),i(`div`,{className:`rounded-lg border border-border bg-surface divide-y divide-border`,children:y.explicit.layers.map(e=>i(K,{layer:e,inForce:e.scope===y.effective.levelSource,busy:b,onSet:t=>S(e,t),onClear:()=>S(e,null)},`${e.scope}:${e.nodeId??e.component??`cluster`}`))}),s!==H&&i(`p`,{className:`text-[11px] text-foreground-subtle`,children:x.find(e=>e.name===s)?.description??``}),i(`p`,{className:`text-[11px] text-foreground-subtle`,children:e(`logging.levels.componentNote`,`A component is a log channel an addon declared. Its level is cluster-wide and beats both the node and the cluster layer.`)}),i(W,{days:y.opsLogRetentionDays,busy:b,onSet:e=>p.mutate({patch:{opsLogRetentionDays:e}},{onSuccess:()=>void f.refetch()})})]})})}function W({days:e,busy:n,onSet:r}){let{t:a}=v();return t(`div`,{className:`rounded-lg border border-border bg-surface p-3 space-y-1`,children:[t(`label`,{className:`flex items-center justify-between gap-3`,children:[i(`span`,{className:`text-xs font-medium`,children:a(`logging.levels.opsLogRetention`,`Retention audit trail`)}),t(`span`,{className:`flex items-center gap-1.5`,children:[i(`input`,{type:`number`,min:0,max:3650,step:1,defaultValue:e,disabled:n,onBlur:t=>{let n=Number.parseInt(t.target.value,10);Number.isFinite(n)&&n!==e&&r(n)},className:`w-20 rounded border border-border bg-surface px-2 py-1 text-xs`}),i(`span`,{className:`text-[11px] text-foreground-subtle`,children:a(`common.days`,`days`)})]})]}),i(`p`,{className:`text-[11px] text-foreground-subtle`,children:a(`logging.levels.opsLogRetentionNote`,`How long the record of what retention deleted is kept. Cluster-wide, and deliberately longer than any camera window — it is the history of the sweep, not of the camera, and it is what makes a storage leak explainable after the fact. 0 keeps it forever.`)})]})}function G({level:e,source:n,persisted:r}){let{t:a}=v();return t(`div`,{className:`rounded-lg border border-border bg-surface p-3`,children:[t(`div`,{className:`flex items-center gap-2`,children:[i(`span`,{className:`text-xs text-foreground-subtle`,children:a(`logging.levels.effective`,`In force here`)}),i(`span`,{className:`rounded bg-primary/10 px-2 py-0.5 text-xs font-semibold text-primary`,children:e}),i(`span`,{className:`text-[11px] text-foreground-subtle`,children:n==="default"?a(`logging.levels.fromDefault`,`nothing is set anywhere — this is the default`):a(`logging.levels.fromLayer`,`decided by the {{source}} layer`,{source:n})})]}),!r&&i(`p`,{className:`mt-1 text-[11px] text-warning`,children:a(`logging.levels.notPersisted`,`The settings store could not be read or written — what you see is live, but it would not survive a restart.`)})]})}function K({layer:e,inForce:n,busy:r,onSet:a,onClear:o}){let{t:s}=v();return t(`div`,{className:`flex flex-col gap-2 p-3 sm:flex-row sm:items-center sm:justify-between`,children:[t(`div`,{className:`min-w-0`,children:[t(`div`,{className:`flex items-center gap-2`,children:[i(`span`,{className:`text-sm font-medium text-foreground`,children:e.scope===`cluster`?s(`logging.levels.cluster`,`Cluster`):e.scope===`component`?e.component??``:e.nodeId??``}),n&&t(`span`,{className:`inline-flex items-center gap-1 rounded bg-primary/10 px-1.5 py-0.5 text-[10px] font-semibold text-primary`,children:[i(f,{className:`h-3 w-3`}),s(`logging.levels.inForce`,`in force`)]})]}),i(`p`,{className:`mt-0.5 text-[11px] text-foreground-subtle`,children:e.level===null?s(`logging.levels.inherits`,`inherits — nothing set at this layer`):s(`logging.levels.setHere`,`set here: {{level}}`,{level:e.level})})]}),t(`div`,{className:`flex shrink-0 items-center gap-1`,children:[B.map(t=>i(`button`,{type:`button`,disabled:r,onClick:()=>a(t),className:`rounded px-2 py-1 text-[11px] font-medium transition-colors disabled:opacity-50 ${e.level===t?`bg-primary text-primary-foreground`:`border border-border text-foreground-subtle hover:text-foreground`}`,children:t},t)),i(`button`,{type:`button`,disabled:r||e.level===null,onClick:o,className:`rounded border border-border px-2 py-1 text-[11px] text-foreground-subtle hover:text-foreground disabled:opacity-40`,children:s(`logging.levels.clear`,`Clear`)})]})]})}function q(){return i(C,{embedded:!0,children:i(`div`,{className:`rounded-lg border border-border bg-surface overflow-hidden`,children:i(d,{limit:500,maxHeight:`max-h-[calc(100vh-310px)]`,showScope:!0,showFilters:!0})})})}function J(){let{t:e}=v(),t=y(`/system/logs`,{t:e,isAdmin:!0})??{icon:s,title:e(`system.logs`,`Logs`)};return i(S,{icon:t.icon,title:t.title,defaultSection:`viewer`,sectionsLabel:`${t.title} sections`,sections:[{id:`viewer`,label:`Viewer`,content:i(q,{})},{id:b.logging.tab,label:`Destinations`,content:i(N,{}),ownedParams:x},{id:`levels`,label:`Levels`,content:i(U,{})},{id:`diagnostics`,label:`Diagnostics`,content:i(I,{})}]})}export{J as LogsPage};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{I as e,K as t,Q as n,S as r,W as i,b as a,o,q as s,x as c,z as l}from"./_virtual_mf___mfe_internal__admin_ui_host__loadShare___mf_0_tanstack_mf_1_react_mf_2_query__loadShare__.js-DhP-Lgiq.js";import{D as u,L as d,Z as f,ct as p,lt as m,ot as h,ut as g,yt as _}from"./player-overlays-mP7nvLXZ.js";import{$d as v,Ao as y,Bo as b,Bt as x,Ed as S,El as C,Fo as ee,Gn as w,Go as T,Gu as E,Hn as D,Ho as O,Ht as k,Io as te,It as ne,Kd as re,Ko as ie,Ku as ae,Lo as oe,Mo as se,Mr as A,Po as ce,Ql as le,Qo as ue,Ro as de,Rt as fe,Ru as pe,Uo as me,Vo as he,Vt as j,Wo as ge,Xo as _e,Zo as ve,Zu as M,_u as ye,ad as be,dd as xe,fd as Se,ff as Ce,gd as N,jo as we,jr as P,ku as F,mu as Te,ql as Ee,qo as De,td as Oe,un as ke,ur as I,va as Ae,wd as je,zn as Me,zo as Ne}from"./src-BHYGdVQH.js";import{t as L}from"./boxes-I78qynMn.js";import{Gn as Pe,Kn as Fe,Un as Ie,yn as Le}from"./client-D5nV-lIF.js";import{t as Re}from"./useTranslation-Bc5yH9V9.js";import"./mount-DNeYaBzl.js";import{C as ze,c as R,rt as z,s as B,tt as Be}from"./index-D-W8ahNU.js";import{t as Ve}from"./AdminSectionedPage-DMUZLoRM.js";import{t as V}from"./AdminPage-oBqVY0PY.js";import{t as He}from"./NodeAddonsSettingsPanel-C7Cwrd5N.js";import{n as Ue,r as H,t as U}from"./AddonIntegrationBody-DEH_hiBL.js";import{n as We,t as W}from"./addon-display-names-BkwMrExv.js";var Ge=_(`file-up`,[[`path`,{d:`M6 22a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.704.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2z`,key:`1oefj6`}],[`path`,{d:`M14 2v5a1 1 0 0 0 1 1h5`,key:`wfsgrz`}],[`path`,{d:`M12 12v6`,key:`3ahymv`}],[`path`,{d:`m15 15-3-3-3 3`,key:`15xj92`}]]),Ke=_(`log-in`,[[`path`,{d:`m10 17 5-5-5-5`,key:`1bsop3`}],[`path`,{d:`M15 12H3`,key:`6jk70r`}],[`path`,{d:`M15 3h4a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2h-4`,key:`u53s6r`}]]);n();var G=5e3,K=`network-access`,qe={connected:!1,endpoint:null};function Je(){let e=o(),t=A({capName:`network-access`},{refetchInterval:G}),n=P(void 0,{refetchInterval:G}),r=i(()=>W(n.data??[]),[n.data]),a=i(()=>(t.data??[]).map(e=>({addonId:e.addonId,displayName:r.get(e.addonId)??e.addonId,isActive:e.isActive})),[t.data,r]),[l,u]=s(null),[d,f]=s({}),p=(t,n)=>{let r=n?.addonId;e.invalidateQueries({queryKey:[[`networkAccess`,`getStatus`],{input:{addonId:r},type:`query`}]}),u(null),f(e=>{if(r===void 0||!(r in e))return e;let t={...e};return delete t[r],t})},m=(e,t)=>{u(null);let n=t?.addonId;if(n===void 0)return;let r=e instanceof Error?e.message:String(e);f(e=>({...e,[n]:r}))},h=ve({onSuccess:p,onError:m}),g=ue({onSuccess:p,onError:m});return c(H,{embedded:!0,icon:z,title:`Remote Access`,subtitle:`Public-facing tunnels that expose this hub to the internet (Cloudflare Tunnel, ngrok, …).`,groups:[{id:K,label:`Remote access`,install:{capability:K,label:`Add tunnel`},entries:a.map(e=>({id:e.addonId,name:e.displayName,fallbackIcon:z,...We(e.isActive),render:()=>c(U,{addonId:e.addonId,displayName:e.displayName,capName:K,isActive:e.isActive,badges:c(Xe,{addonId:e.addonId}),error:d[e.addonId]??null,primaryAction:c(Ze,{addonId:e.addonId,busyId:l,onStart:e=>{u(e),h.mutate({addonId:e})},onStop:e=>{u(e),g.mutate({addonId:e})}})})}))}],isLoading:t.isLoading,emptyTitle:`No remote-access providers installed`,emptyDescription:`Install a tunnel addon (e.g. @camstack/addon-cloudflare) to expose this hub publicly.`})}function Ye(e){let{data:t}=_e({addonId:e},{refetchInterval:G,retry:!1}),n=t?Ie.safeParse(t):null;return n?.success?n.data:qe}function Xe({addonId:e}){let t=Ye(e);return r(a,{children:[c(I,{variant:t.connected?`success`:`default`,className:Ue,children:t.connected?`Connected`:`Disconnected`}),t.endpoint&&r(`a`,{href:t.endpoint.url,target:`_blank`,rel:`noopener noreferrer`,className:`flex min-w-0 items-center gap-1 break-all text-[11px] text-success hover:underline`,children:[c(re,{className:`h-3 w-3`}),t.endpoint.url]})]})}function Ze({addonId:e,busyId:t,onStart:n,onStop:i}){let a=Ye(e),o=t===e;return a.connected?r(w,{size:`sm`,variant:`secondary`,disabled:o,onClick:()=>i(e),children:[o?c(S,{className:`h-3 w-3 animate-spin`}):c(m,{className:`h-3 w-3`}),`Stop`]}):r(w,{size:`sm`,variant:`primary`,disabled:o,onClick:()=>n(e),children:[o?c(S,{className:`h-3 w-3 animate-spin`}):c(Se,{className:`h-3 w-3`}),`Start`]})}n();var Qe=3e4,q=`turn-provider`;function $e(e){let t=[];for(let n of e)typeof n.urls==`string`?t.push(n.urls):t.push(...n.urls);return t}function et(){let e=A({capName:`turn-provider`},{refetchInterval:Qe}),t=P(void 0,{refetchInterval:Qe}),n=C(),a=n.data??[],o=i(()=>W(t.data??[]),[t.data]),s=i(()=>(e.data??[]).map(e=>({addonId:e.addonId,displayName:o.get(e.addonId)??e.addonId,isActive:e.isActive})),[e.data,o]);return c(H,{embedded:!0,icon:N,title:`TURN Servers`,subtitle:`ICE servers used for WebRTC NAT traversal. Multiple providers can coexist — the WebRTC layer concatenates servers from all enabled providers per session.`,groups:[{id:q,label:`TURN providers`,install:{capability:q,label:`Add provider`},entries:s.map(e=>({id:e.addonId,name:e.displayName,fallbackIcon:N,...We(e.isActive),render:()=>c(U,{addonId:e.addonId,displayName:e.displayName,capName:q,isActive:e.isActive,panels:[{id:`ice-servers`,title:`Discovered ICE servers`,icon:N,content:c(tt,{addonId:e.addonId,enabled:e.isActive})}]})}))}],isLoading:e.isLoading,emptyTitle:`No TURN providers installed`,emptyDescription:`Install a TURN addon (e.g. @camstack/addon-cloudflare) to enable WebRTC behind restrictive NATs.`,trailing:c(j,{headingId:`turn-combined-ice`,title:`Combined ICE server list — all enabled providers`,icon:N,actions:c(w,{size:`sm`,variant:`secondary`,onClick:()=>n.refetch(),disabled:n.isFetching,children:`Refresh`}),children:c(`div`,{className:u,children:a.length===0?c(`div`,{className:`text-xs text-foreground-subtle`,children:`No servers from enabled providers.`}):c(`div`,{className:`flex flex-col gap-3`,children:a.map((e,t)=>{let n=Array.isArray(e.urls)?e.urls:[e.urls];return r(`div`,{className:`rounded border border-border bg-surface`,children:[(e.username||e.credential)&&r(`div`,{className:`px-3 py-1.5 border-b border-border text-[10px] font-mono text-foreground-subtle flex items-center gap-3`,children:[e.username&&r(`span`,{children:[c(`span`,{className:`text-foreground-subtle`,children:`user:`}),` `,c(`span`,{className:`text-foreground`,children:e.username})]}),e.credential&&r(`span`,{children:[c(`span`,{className:`text-foreground-subtle`,children:`credential:`}),` `,c(`span`,{className:`text-foreground`,children:`••••••••`})]})]}),c(`ul`,{className:`divide-y divide-border`,children:n.map(e=>c(`li`,{className:`px-3 py-1.5 font-mono text-xs text-foreground break-all`,children:e},e))})]},t)})})})})})}function tt({addonId:e,enabled:t}){let n=C({addonId:e},{enabled:t,retry:!1}),r=i(()=>$e(n.data??[]),[n.data]),a=t?n.isLoading?`Loading…`:r.length===0?`This provider returned no servers.`:null:`Disabled — enable the provider to fetch its servers.`;return c(`div`,{className:`rounded-lg border border-border bg-surface p-2 sm:p-3`,children:a===null?c(`div`,{className:`flex flex-wrap gap-1`,children:r.map(e=>c(`span`,{className:`inline-block break-all rounded bg-foreground-subtle/10 px-1.5 py-0.5 font-mono text-[10px] text-foreground`,children:e},e))}):c(`div`,{className:`text-xs text-foreground-subtle`,children:a})})}n();var nt={lan:N,wifi:ye,docker:L,vpn:je,loopback:M,other:M},rt={lan:`LAN`,wifi:`Wi-Fi`,docker:`Docker`,vpn:`VPN`,loopback:`Loopback`,other:`Other`},it=[`lan`,`wifi`,`vpn`,`docker`,`other`,`loopback`];function at(e){let{interfaces:t,disabled:n,hideLoopback:a,ipv4Only:o,emptyMessage:s}=e,l=i(()=>t.filter(e=>!(a&&e.kind===`loopback`||o&&e.family!==`IPv4`)),[t,a,o]),u=i(()=>{let e=new Map;for(let t of l){let n=e.get(t.kind)??[];n.push(t),e.set(t.kind,n)}return it.filter(t=>e.has(t)).map(t=>({kind:t,items:e.get(t)}))},[l]),d=t=>e.mode===`single`?e.value===t:e.value.includes(t),f=t=>{if(n||t.kind===`loopback`)return;if(e.mode===`single`){e.onChange(e.value===t.address?null:t.address);return}let r=new Set(e.value);r.has(t.address)?r.delete(t.address):r.add(t.address),e.onChange([...r])};return l.length===0?c(`div`,{className:`text-xs text-foreground-subtle italic`,children:s??`No addresses available.`}):c(`div`,{className:`flex flex-col gap-3`,children:u.map(({kind:t,items:i})=>r(`div`,{className:`rounded-md border border-border overflow-hidden`,children:[r(`div`,{className:`flex items-center gap-2 px-3 py-1.5 bg-surface-hover/30 border-b border-border`,children:[c(nt[t]??M,{className:`h-3.5 w-3.5 text-foreground-subtle`}),c(`span`,{className:`text-[11px] font-semibold uppercase tracking-wide text-foreground-subtle`,children:rt[t]??t}),r(`span`,{className:`text-[10px] text-foreground-subtle`,children:[`· `,i.length]})]}),c(`ul`,{className:`divide-y divide-border`,children:i.map(t=>{let i=d(t.address),a=t.kind===`loopback`,o=!t.plausible&&!a;return r(`li`,{className:`flex items-center gap-3 px-3 py-2 ${a?`opacity-60 cursor-not-allowed`:n?`opacity-60`:o?`cursor-pointer hover:bg-surface-hover/30 bg-amber-500/[0.04]`:`cursor-pointer hover:bg-surface-hover/30`}`,onClick:()=>f(t),children:[c(`span`,{className:`flex items-center justify-center h-4 w-4 rounded ${e.mode===`single`?`rounded-full`:`rounded`} border ${i?`border-primary bg-primary text-primary-foreground`:`border-border bg-surface`}`,children:i&&c(Ce,{className:`h-3 w-3`})}),r(`div`,{className:`flex-1 min-w-0 ${o?`text-foreground-subtle`:``}`,children:[r(`div`,{className:`flex items-center gap-2 flex-wrap`,children:[c(`span`,{className:`font-mono text-xs font-semibold ${o?`text-foreground-subtle`:`text-foreground`}`,children:t.name}),c(`span`,{className:`text-[10px] rounded px-1.5 py-0.5 bg-foreground-subtle/10 text-foreground-subtle font-mono`,children:t.family}),t.preferred&&c(`span`,{className:`text-[10px] rounded-full px-2 py-0.5 font-medium bg-primary/15 text-primary`,children:`Auto-preferred`}),a&&c(`span`,{className:`text-[10px] rounded-full px-2 py-0.5 font-medium bg-foreground-subtle/15 text-foreground-subtle`,children:`Always included`}),o&&r(`span`,{className:`inline-flex items-center gap-1 text-[10px] rounded-full px-2 py-0.5 font-medium bg-amber-500/15 text-amber-700 dark:text-amber-300`,title:t.plausibleReason||void 0,children:[c(F,{className:`h-2.5 w-2.5`}),`Unlikely usable`]})]}),r(`div`,{className:`text-xs text-foreground-subtle mt-0.5 font-mono break-all`,children:[t.address,t.cidr&&t.cidr!==t.address?` (${t.cidr})`:``]})]})]},`${t.name}-${t.family}-${t.address}`)})})]},t))})}var J=[{role:`appConnections`,title:`App connections`,single:!1,help:`Addresses the CamStack apps try when connecting; tick several. Automatic: LAN addresses plus your public tunnel.`},{role:`notificationTaps`,title:`Notification taps`,single:!0,help:`Only where tapping a notification opens; one address. Automatic: the CamStack app link (opens the app if installed, otherwise the web app).`},{role:`publicEndpoint`,title:`Public endpoint`,single:!0,help:`Every link that must work away from home: sign-in, single sign-on and account linking, passkeys, and the buttons, attachments and admin links in notifications; one address. Automatic: your public tunnel, then Tailscale, then the LAN. Changing it invalidates registered passkeys.`},{role:`internal`,title:`Internal`,single:!0,help:`Where other CamStack machines and LAN devices download addons, models and media from; one address. Automatic: the hub's LAN address.`}],ot=`A role with nothing ticked is automatic. Add a custom address when the hub is reached at an address it cannot see itself (NAT, reverse proxy, VPN, Docker bridge).`;function Y(e,t){return e.inUse.includes(t)}function st(e,t){if(!e.eligible.includes(t))return{kind:`none`};if(e.kind===`app-link`)return Y(e,t)?{kind:`none`}:{kind:`set`,input:{role:t,baseUrl:null,enabled:!0}};if(e.flags.includes(t)||t===`appConnections`&&Y(e,t))return{kind:`set`,input:{role:t,baseUrl:e.baseUrl,enabled:!1}};let n={role:t,baseUrl:e.baseUrl,enabled:!0};return t===`publicEndpoint`&&e.baseUrl.startsWith(`http:`)?{kind:`confirm-http`,input:n,reason:Pe}:{kind:`set`,input:n}}function ct(e){return J.map(({role:t,title:n})=>{if(t===`appConnections`){let r=e.roles.appConnections;return{role:t,title:n,inUse:r.resolved.length===0?`nothing reachable`:r.resolved.join(`, `),automatic:r.source!==`flagged`}}let r=e.roles[t];return{role:t,title:n,inUse:r.resolved===null?`nothing reachable`:t===`notificationTaps`&&r.source===`auto`?`CamStack app link`:r.resolved,automatic:r.source!==`flagged`}})}n();var lt={"lan-ipv4":{label:`IPv4`,className:`bg-emerald-500/15 text-emerald-700 dark:text-emerald-300`},"lan-ipv6":{label:`IPv6`,className:`bg-violet-500/15 text-violet-700 dark:text-violet-300`},public:{label:`Public`,className:`bg-blue-500/15 text-blue-700 dark:text-blue-300`},"app-link":{label:`App link`,className:`bg-amber-500/15 text-amber-700 dark:text-amber-300`},custom:{label:`Custom`,className:`bg-sky-500/15 text-sky-700 dark:text-sky-300`},loopback:{label:`Loopback`,className:`bg-foreground-subtle/15 text-foreground-subtle`}};function ut({book:e,loading:t,saving:n,error:i,onSetRole:a,onAdd:o,onRemove:l}){let[u,d]=s(``),[f,m]=s(``),[h,g]=s(null),[_,v]=s(null),y=()=>{let e=Fe(u);if(!e.ok){g(e.reason);return}g(null),d(``),m(``),o(e.baseUrl,f.trim())},b=(e,t)=>{let n=st(e,t.role);if(n.kind!==`none`){if(n.kind===`confirm-http`){v({input:n.input,reason:n.reason});return}v(null),a(n.input)}},x=[{key:`url`,header:`Address`,render:t=>r(`div`,{className:`min-w-0 py-0.5`,children:[c(`div`,{className:`font-mono text-[12px] break-all ${t.plausible?`text-foreground`:`text-foreground-subtle`}`,children:t.baseUrl}),r(`div`,{className:`mt-0.5 flex flex-wrap items-center gap-1.5 text-[11px] text-foreground-subtle`,children:[c(`span`,{children:t.label}),t.kind===`app-link`&&r(`span`,{className:`font-mono`,children:[`hub= `,e?.appLinkHub??`none (no tunnel or Tailscale)`]}),t.custom&&t.detected&&c(`span`,{children:`also added as a custom address`}),t.kind!==`custom`&&t.interfaceKind!==`lan`&&t.interfaceKind!==`public`&&t.interfaceKind!==`other`&&c(`span`,{className:`uppercase tracking-wide`,children:t.interfaceKind}),!t.plausible&&t.interfaceKind!==`loopback`&&r(`span`,{className:`inline-flex items-center gap-1 text-amber-700 dark:text-amber-300`,title:t.plausibleReason||void 0,children:[c(F,{className:`h-2.5 w-2.5`}),`Unlikely usable`]})]})]})},{key:`kind`,header:`Kind`,nowrap:!0,render:e=>{let t=lt[e.kind]??lt.loopback;return c(`span`,{className:`text-[10px] rounded-full px-2 py-0.5 font-medium ${t.className}`,children:t.label})}},...J.map(e=>({key:e.role,headerRender:()=>c(`span`,{title:e.help,children:e.title}),align:`center`,nowrap:!0,width:`w-24`,render:t=>{let i=t.eligible.includes(e.role),a=t.flags.includes(e.role),o=Y(t,e.role);return r(`label`,{className:`inline-flex items-center justify-center gap-1 ${i?`cursor-pointer`:`cursor-not-allowed opacity-40`}`,title:i?o&&!a?`${e.title}: in use automatically. ${e.help}`:e.help:`This address cannot be used for this.`,children:[c(ke,{checked:i&&o,disabled:!i||n,onChange:()=>b(t,e),"aria-label":`${e.title}: ${t.baseUrl}`}),o&&!a&&i&&c(`span`,{className:`text-[9px] text-foreground-subtle`,children:`auto`})]})}})),{key:`actions`,header:``,align:`right`,nowrap:!0,render:e=>e.custom?c(w,{size:`sm`,variant:`ghost`,disabled:n,onClick:()=>l(e.baseUrl),"aria-label":`Remove ${e.baseUrl}`,title:`Remove this custom address (its roles turn automatic)`,children:c(p,{className:`h-3 w-3`})}):null}],S=e===void 0?[]:ct(e);return r(Me,{children:[r(`div`,{className:`px-4 py-3 border-b border-border flex flex-col gap-2`,children:[r(`div`,{children:[c(`div`,{className:`text-sm font-semibold`,children:`Addresses`}),c(`div`,{className:`text-xs text-foreground-subtle max-w-3xl`,children:`Every address this hub is reached at, detected or added by you. Tick what each one is used for.`}),r(`details`,{className:`mt-1.5 text-xs max-w-3xl`,"data-testid":`address-roles-help`,children:[c(`summary`,{className:`cursor-pointer select-none text-foreground-subtle hover:text-foreground`,children:`What are roles?`}),c(`dl`,{className:`mt-1.5 grid grid-cols-[auto_1fr] gap-x-3 gap-y-1`,children:J.map(e=>r(`div`,{className:`contents`,children:[c(`dt`,{className:`font-medium`,children:e.title}),c(`dd`,{className:`text-foreground-subtle`,children:e.help})]},e.role))}),c(`p`,{className:`mt-1.5 text-foreground-subtle`,children:ot})]})]}),S.length>0&&c(`div`,{className:`grid grid-cols-[auto_1fr_auto] gap-x-3 gap-y-1 items-baseline text-xs`,"data-testid":`address-roles-summary`,children:S.map(e=>r(`div`,{className:`contents`,children:[c(`span`,{className:`text-foreground-subtle`,children:e.title}),r(`span`,{className:`font-mono break-all`,"data-testid":`address-role-${e.role}`,children:[e.inUse,c(`span`,{className:`ml-1.5 font-sans text-foreground-subtle`,children:e.automatic?`(automatic)`:`(set by you)`})]}),e.automatic?c(`span`,{}):c(w,{size:`sm`,variant:`secondary`,disabled:n,onClick:()=>a({role:e.role,baseUrl:null,enabled:!1}),children:`Use automatic`})]},e.role))}),_!==null&&r(`div`,{className:`rounded-md border border-amber-500/40 bg-amber-500/10 px-3 py-2 text-xs flex flex-wrap items-center gap-2`,children:[c(F,{className:`h-3.5 w-3.5 text-amber-600`}),c(`span`,{className:`flex-1 min-w-[220px]`,children:_.reason}),c(w,{size:`sm`,variant:`secondary`,onClick:()=>{a({..._.input,acceptInsecureHttp:!0}),v(null)},children:`Use http anyway`}),c(w,{size:`sm`,variant:`ghost`,onClick:()=>v(null),children:`Cancel`})]}),i!==null&&c(`div`,{className:`text-[11px] text-red-600 dark:text-red-400`,role:`alert`,children:i})]}),c(k,{columns:x,rows:e?.rows??[],rowKey:e=>`${e.kind}-${e.baseUrl}`,minWidthPx:760,bordered:!1,mobileMode:`scroll`,emptyMessage:t?`Loading…`:`No addresses available.`,rowClassName:e=>e.plausible?void 0:`opacity-80`}),r(`div`,{className:`px-4 py-3 border-t border-border flex flex-wrap items-start gap-2`,children:[r(`div`,{className:`flex-1 min-w-[220px]`,children:[c(le,{value:u,onChange:e=>{d(e.target.value),g(null)},placeholder:`https://cams.example.com or https://192.168.1.50:4443`,"aria-label":`Custom address`,disabled:n}),h!==null&&c(`div`,{className:`mt-1 text-[11px] text-red-600 dark:text-red-400`,children:h})]}),c(`div`,{className:`w-48`,children:c(le,{value:f,onChange:e=>m(e.target.value),placeholder:`Label (optional)`,"aria-label":`Custom address label`,maxLength:80,disabled:n})}),r(w,{size:`sm`,variant:`secondary`,disabled:n||u.trim()===``,onClick:y,children:[c(xe,{className:`h-3 w-3`}),`Add custom address`]})]})]})}n();function dt(){let e=o(),t=()=>{e.invalidateQueries({queryKey:[[`localNetwork`]]})},n=ee(void 0,{refetchInterval:3e4}),a=se(void 0),d=he({onSuccess:t}),f=de({onSuccess:e=>{T(e.addresses),t()}}),p=i(()=>n.data?.interfaces??[],[n.data]),m=we({scheme:typeof window<`u`&&window.location.protocol===`https:`?`https`:`http`},{refetchInterval:3e4}),h=b({onSuccess:t}),_=y({onSuccess:t}),v=oe({onSuccess:t}),x=h.error?.message??_.error?.message??v.error?.message??null,S=h.isPending||_.isPending||v.isPending,[C,T]=s([]),[E,D]=s(!1);l(()=>{!E&&a.data&&(T(a.data.addresses),D(!0))},[a.data,E]);let O=a.data?.addresses??[],k=i(()=>{if(C.length!==O.length)return!0;let e=[...C].toSorted(),t=[...O].toSorted();return e.some((e,n)=>e!==t[n])},[C,O]);return r(V,{embedded:!0,children:[r(`div`,{className:`flex items-center justify-between gap-3 flex-wrap`,children:[c(`p`,{className:`text-xs text-foreground-subtle max-w-2xl`,children:`Pin which host interfaces exist, then tick what each address is used for. Add a custom address when this hub is reached at one it cannot detect.`}),c(`div`,{className:`flex items-center gap-2 shrink-0`,children:r(w,{size:`sm`,variant:`secondary`,onClick:()=>{n.refetch(),a.refetch(),m.refetch()},disabled:n.isLoading,children:[c(be,{className:`h-3 w-3 ${n.isLoading?`animate-spin`:``}`}),`Refresh`]})})]}),c(j,{headingId:`network-allowed-addresses`,title:`Allowed addresses`,actions:r(`div`,{className:`flex flex-wrap items-center gap-1`,children:[r(w,{size:`sm`,variant:`secondary`,onClick:()=>f.mutate(void 0),disabled:f.isPending||d.isPending,title:`Re-run the auto-detection heuristic + overwrite the current allowlist with the best matches.`,children:[c(pe,{className:`h-3 w-3`}),f.isPending?`Detecting…`:`Best match`]}),r(w,{size:`sm`,variant:`secondary`,onClick:()=>{T([])},disabled:C.length===0||d.isPending,children:[c(g,{className:`h-3 w-3`}),`Clear`]}),k&&c(w,{size:`sm`,variant:`secondary`,onClick:()=>{T(O)},disabled:d.isPending,children:`Discard`}),r(w,{size:`sm`,variant:`primary`,onClick:()=>{d.mutate({addresses:[...C]})},disabled:!k||d.isPending,children:[c(Oe,{className:`h-3 w-3`}),d.isPending?`Saving…`:`Save`]})]}),children:r(`div`,{className:u,children:[c(`p`,{className:`mb-3 text-xs text-foreground-subtle`,children:C.length===0?`Auto — every non-loopback / non-link-local interface participates.`:`${String(C.length)} pinned`}),c(at,{mode:`multiple`,interfaces:p,value:C,onChange:T,disabled:d.isPending,emptyMessage:n.isLoading?`Loading interfaces…`:`No interfaces detected.`})]})}),c(ut,{book:m.data,loading:m.isLoading,saving:S,error:x,onSetRole:e=>h.mutate({...e}),onAdd:(e,t)=>_.mutate({baseUrl:e,label:t}),onRemove:e=>v.mutate({baseUrl:e})})]})}n();var ft={mesh:`bg-violet-500/15 text-violet-700 dark:text-violet-300`,public:`bg-blue-500/15 text-blue-700 dark:text-blue-300`},X=5e3,Z=`mesh-network`,pt=3e4,mt={joined:!1,meshIp:``,magicDnsHostname:``,peerCount:0,endpoints:[],tenantName:``,magicDnsSuffix:``,userLogin:null,controlPlaneUrl:``,keyExpiry:null};function ht(){let e=A({capName:`mesh-network`},{refetchInterval:X}),t=P(void 0,{refetchInterval:X}),n=i(()=>W(t.data??[]),[t.data]),r=i(()=>(e.data??[]).map(e=>({addonId:e.addonId,displayName:n.get(e.addonId)??e.addonId,isActive:e.isActive})),[e.data,n]);return c(H,{embedded:!0,icon:L,title:`Mesh Networks`,subtitle:`Private VPN meshes that connect CamStack to your other devices (Tailscale, Headscale, …). Each provider can also expose this hub publicly via its ingress (Tailscale Funnel).`,groups:[{id:Z,label:`Mesh networks`,install:{capability:Z,label:`Add mesh`},entries:r.map(e=>({id:e.addonId,name:e.displayName,fallbackIcon:N,...We(e.isActive),render:()=>c(U,{addonId:e.addonId,displayName:e.displayName,capName:Z,isActive:e.isActive,badges:c(gt,{addonId:e.addonId}),primaryAction:c(_t,{addonId:e.addonId}),panels:[{id:`status`,title:`Status`,icon:L,content:c(vt,{addonId:e.addonId})}]})}))}],isLoading:e.isLoading,emptyTitle:`No mesh-network providers installed`,emptyDescription:`Install a mesh addon (e.g. @camstack/addon-tailscale) to join this hub into a private VPN mesh.`})}function Q(e){let{data:t,isLoading:n}=me({addonId:e},{refetchInterval:X,retry:!1}),r=t?Le.safeParse(t):null;return{status:r?.success?r.data:mt,isLoading:n}}function gt({addonId:e}){let{status:t}=Q(e);return r(a,{children:[c(I,{variant:t.joined?`success`:`default`,className:Ue,children:t.joined?`Joined`:`Not joined`}),t.joined&&r(I,{className:`text-[10px]`,children:[t.peerCount,` peer`,t.peerCount===1?``:`s`]})]})}function _t({addonId:t}){let n=o(),[i,l]=s(!1),[u,d]=s(null),f=h(),{status:p}=Q(t),m=e(()=>{n.invalidateQueries({queryKey:[[`meshNetwork`]]})},[n]),g=De(),_=ge({onSuccess:m}),v=ie({onSuccess:m}),y=e(async()=>{l(!0);let e={cancelled:!1};d(e);try{let n=await g.mutateAsync({addonId:t});if(e.cancelled)return;n.loginUrl&&window.open(n.loginUrl,`_blank`,`noopener,noreferrer`);let r=Date.now();for(;!e.cancelled&&(await Tt(2e3),!e.cancelled);){let e=await f.trpcClient.meshNetwork.getStatus.query({addonId:t}).catch(()=>null),n=e?Le.safeParse(e):null;if(n?.success&&n.data.joined){m();break}if(Date.now()-r>6e5)break}}finally{l(!1),d(null)}},[t,m,g,f]),b=e(()=>{u&&(u.cancelled=!0),l(!1),d(null)},[u]);return c(a,{children:p.joined?r(a,{children:[r(w,{size:`sm`,variant:`secondary`,disabled:_.isPending,onClick:()=>_.mutate({addonId:t}),children:[_.isPending?c(S,{className:`h-3 w-3 animate-spin`}):c(Be,{className:`h-3 w-3`}),`Disconnect`]}),r(w,{size:`sm`,variant:`ghost`,disabled:v.isPending,onClick:()=>v.mutate({addonId:t}),children:[v.isPending?c(S,{className:`h-3 w-3 animate-spin`}):c(ae,{className:`h-3 w-3`}),`Log out`]})]}):i?r(w,{size:`sm`,variant:`ghost`,onClick:b,children:[c(Te,{className:`h-3 w-3 mr-1`}),` Cancel`]}):r(a,{children:[r(w,{size:`sm`,variant:`primary`,onClick:()=>void y(),children:[c(Ke,{className:`h-3 w-3`}),`Connect`]}),r(w,{size:`sm`,variant:`ghost`,disabled:v.isPending,onClick:()=>v.mutate({addonId:t}),children:[v.isPending?c(S,{className:`h-3 w-3 animate-spin`}):c(ae,{className:`h-3 w-3`}),`Log out`]})]})})}function vt({addonId:e}){let{status:t}=Q(e),n=T({addonId:e},{refetchInterval:pt,retry:!1});return r(`div`,{className:`rounded-lg border border-border bg-surface`,children:[r(`div`,{className:`space-y-4 p-2 sm:p-4`,children:[t.error&&c(D,{message:t.error}),c(yt,{status:t}),t.endpoints.length>0&&r(`div`,{className:`space-y-1.5`,children:[c(`div`,{className:`text-[11px] font-semibold text-foreground-subtle uppercase tracking-wide`,children:`Endpoints`}),t.endpoints.map(e=>r(`div`,{className:`flex items-center gap-2 text-xs flex-wrap`,children:[r(`span`,{className:`text-[10px] rounded-full px-2 py-0.5 font-medium ${ft[e.scope]??`bg-foreground-subtle/15 text-foreground-subtle`}`,children:[e.scope===`public`?c(z,{className:`inline h-2.5 w-2.5 mr-0.5`}):null,e.label]}),r(`a`,{href:e.url,target:`_blank`,rel:`noopener noreferrer`,className:`text-emerald-700 dark:text-emerald-300 hover:underline break-all flex items-center gap-1`,children:[c(re,{className:`h-3 w-3 shrink-0`}),e.url]})]},e.id))]})]}),c(`div`,{className:`border-t border-border`,children:c(xt,{peers:n.data?.peers??[],loading:n.isLoading})})]})}function yt({status:e}){return c(`div`,{className:`grid grid-cols-1 sm:grid-cols-2 gap-x-6 gap-y-1.5`,children:[{label:`Tenant`,value:e.tenantName||`—`},{label:`User`,value:e.userLogin??`—`},{label:`Control plane`,value:e.controlPlaneUrl||`—`},{label:`Mesh IP`,value:e.meshIp||`—`,mono:!0},{label:`MagicDNS`,value:e.magicDnsHostname||`—`,mono:!0},{label:`DNS suffix`,value:e.magicDnsSuffix||`—`,mono:!0},{label:`Peers`,value:String(e.peerCount)},{label:`Key expiry`,value:e.keyExpiry?bt(e.keyExpiry):`—`}].map(e=>r(`div`,{className:`flex items-baseline justify-between gap-3 text-xs`,children:[c(`span`,{className:`text-foreground-subtle shrink-0`,children:e.label}),c(`span`,{className:`text-foreground truncate ${e.mono?`font-mono`:``}`,children:e.value})]},e.label))})}function bt(e){let t=e-Date.now();if(t<0)return`Expired ${wt(e)}`;let n=Math.floor(t/864e5);if(n>60)return new Date(e).toLocaleDateString();if(n>1)return`in ${n} days`;let r=Math.floor(t/36e5);return r>1?`in ${r}h`:`in ${Math.max(1,Math.floor(t/6e4))} min`}function xt({peers:t,loading:n}){let a=i(()=>t.map(e=>({...e,_id:e.id})),[t]),o=e(e=>{e&&navigator.clipboard?.writeText(e)},[]),s=i(()=>[{key:`hostname`,header:`Hostname`,render:e=>r(`div`,{className:`flex flex-col`,children:[r(`div`,{className:`flex items-center gap-1.5`,children:[c(`span`,{className:`font-medium text-foreground`,children:e.hostname||`(unknown)`}),e.isSelf&&c(I,{variant:`info`,children:`self`}),e.exitNodeOption&&c(I,{variant:`warning`,children:`exit-node`})]}),e.userLogin&&c(`div`,{className:`text-[10px] text-foreground-subtle truncate`,children:e.userLogin})]})},{key:`magicDns`,header:`MagicDNS`,render:e=>e.magicDns?r(`div`,{className:`flex items-center gap-1`,children:[c(`span`,{className:`font-mono text-xs truncate`,children:e.magicDns}),c(`button`,{onClick:()=>o(e.magicDns),className:`p-0.5 hover:bg-foreground-subtle/10 rounded`,"aria-label":`Copy MagicDNS`,children:c(v,{className:`h-3 w-3 text-foreground-subtle`})})]}):c(`span`,{className:`text-foreground-subtle`,children:`—`})},{key:`addresses`,header:`IP`,render:e=>{let t=e.addresses[0]??``;return t?r(`div`,{className:`flex items-center gap-1`,children:[c(`span`,{className:`font-mono text-xs`,children:t}),c(`button`,{onClick:()=>o(t),className:`p-0.5 hover:bg-foreground-subtle/10 rounded`,"aria-label":`Copy IP`,children:c(v,{className:`h-3 w-3 text-foreground-subtle`})})]}):c(`span`,{className:`text-foreground-subtle`,children:`—`})}},{key:`connection`,header:`Connection`,render:e=>r(`div`,{className:`flex items-center gap-1.5`,children:[c(I,{variant:St(e.connection),children:e.connection}),e.relay&&c(`span`,{className:`text-[10px] text-foreground-subtle font-mono`,children:e.relay})]})},{key:`routes`,header:`Routes`,render:e=>e.advertisedRoutes.length>0?c(`span`,{className:`text-[10px] font-mono text-foreground-subtle truncate`,children:e.advertisedRoutes.join(`, `)}):c(`span`,{className:`text-foreground-subtle`,children:`—`})},{key:`tx`,header:`TX / RX`,render:e=>r(`span`,{className:`text-[10px] text-foreground-subtle font-mono`,children:[`↑ `,Ct(e.txBytes),` · ↓ `,Ct(e.rxBytes)]})},{key:`lastSeen`,header:`Last seen`,render:e=>c(`span`,{className:`text-[10px] text-foreground-subtle`,children:e.lastSeenMs>0?wt(e.lastSeenMs):`—`})}],[o]);return n&&t.length===0?r(`div`,{className:`p-4 text-xs text-foreground-subtle`,children:[c(S,{className:`h-3 w-3 animate-spin inline mr-1`}),` Loading peers…`]}):t.length===0?c(`div`,{className:`p-4 text-xs text-foreground-subtle`,children:`No peers visible — your tailnet may be empty or the host is not joined.`}):c(`div`,{className:`p-4`,children:c(k,{columns:s,rows:a,rowKey:e=>e._id,minWidthPx:820})})}function St(e){return e===`direct`?`success`:e===`relay`?`warning`:`info`}function Ct(e){return e<1024?`${e}B`:e<1024*1024?`${(e/1024).toFixed(1)}KB`:e<1024*1024*1024?`${(e/1024/1024).toFixed(1)}MB`:`${(e/1024/1024/1024).toFixed(2)}GB`}function wt(e){let t=Date.now()-e;return t<6e4?`just now`:t<36e5?`${Math.round(t/6e4)}m ago`:t<864e5?`${Math.round(t/36e5)}h ago`:`${Math.round(t/864e5)}d ago`}function Tt(e){return new Promise(t=>setTimeout(t,e))}var Et=/-----BEGIN CERTIFICATE-----[\s\S]+?-----END CERTIFICATE-----/,Dt=/-----BEGIN ((?:RSA |EC |ENCRYPTED )?PRIVATE KEY)-----[\s\S]+?-----END \1-----/,Ot={certificate:`.pem,.crt,.cer`,privateKey:`.pem,.key`,ca:`.pem,.crt,.cer`};function $(e,t){if(t.trim()===``)return null;if(e===`privateKey`){let e=Dt.exec(t);return e===null?Et.test(t)?`This is a certificate, not a private key.`:`Not a PEM private key — expected -----BEGIN … PRIVATE KEY-----.`:e[1]===`ENCRYPTED PRIVATE KEY`?`The private key is password-protected. Export it without a passphrase.`:null}return Et.test(t)?e===`certificate`&&Dt.test(t)?`This file also contains a private key. Pick the certificate file, and the key in its own field.`:null:Dt.test(t)?`This is a private key, not a certificate.`:`Not a PEM certificate — expected -----BEGIN CERTIFICATE-----.`}function kt(e){return e.text()}n();function At({kind:e,label:n,value:i,onChange:a,rows:o=6}){let l=t(null),[u,d]=s(null),[f,p]=s(null),m=f??$(e,i),h=`pem-${e}`,g=async e=>{let t=e.target.files?.[0];if(e.target.value=``,t!==void 0){d(t.name);try{let e=await kt(t);p(null),a(e)}catch(e){p(`Could not read ${t.name}: ${e instanceof Error?e.message:String(e)}`)}}};return r(`div`,{className:`space-y-1`,children:[r(`div`,{className:`flex items-center justify-between gap-2`,children:[c(`label`,{htmlFor:h,className:`text-[11px] text-foreground-subtle`,children:n}),r(`div`,{className:`flex min-w-0 items-center gap-2`,children:[u!==null&&c(`span`,{className:`truncate font-mono text-[10px] text-foreground-subtle`,"data-testid":`${h}-file-name`,children:u}),c(`input`,{ref:l,type:`file`,accept:Ot[e],onChange:e=>void g(e),className:`hidden`,"data-testid":`${h}-file`}),r(w,{size:`sm`,variant:`outline`,type:`button`,onClick:()=>l.current?.click(),children:[c(Ge,{className:`mr-1 h-3 w-3`}),`Choose file`]})]})]}),c(`textarea`,{id:h,value:i,onChange:e=>{d(null),p(null),a(e.target.value)},rows:o,"aria-invalid":m!==null,className:`w-full rounded border border-border bg-surface px-2 py-1 font-mono text-[11px]`}),m!==null&&c(`p`,{className:`text-[11px] text-danger`,role:`alert`,children:m})]})}n();function jt(e){if(e===null||e===``)return`—`;let t=e.replace(/:/g,``);return t.length<=16?e:`${t.slice(0,8)}…${t.slice(-8)}`}function Mt(){let e=o(),t=ce(void 0,{refetchInterval:15e3}),n=te({onSuccess:()=>void e.invalidateQueries({queryKey:[[`localNetwork`]]})}),i=Ne({onSuccess:()=>void e.invalidateQueries({queryKey:[[`localNetwork`]]})}),a=O({onSuccess:()=>{p(!1),h(``),_(``),y(``),e.invalidateQueries({queryKey:[[`localNetwork`]]})}}),l=t.data,[f,p]=s(!1),[m,h]=s(``),[g,_]=s(``),[v,y]=s(``),b=()=>{if(!l?.caCertPem)return;let e=new Blob([l.caCertPem],{type:`application/x-pem-file`}),t=URL.createObjectURL(e),n=document.createElement(`a`);n.href=t,n.download=`camstack-ca.crt`,n.click(),URL.revokeObjectURL(t)},x=l?.mode===`uploaded`?`Uploaded`:l?.mode===`generated`?`Generated local CA`:`TLS disabled`;return c(V,{embedded:!0,children:r(`div`,{className:d,children:[c(He,{nodeId:`hub`,addonIds:[`local-network`],level:`global`,title:`LAN HTTP`,description:`Native apps on this Wi-Fi use http:// and skip the private certificate. The tunnel and this admin UI stay on HTTPS.`}),c(j,{headingId:`local-access-certificate`,title:`Certificate`,icon:E,children:r(`div`,{className:`${u} space-y-3`,children:[t.isError&&c(`p`,{className:`text-xs text-danger`,children:`Could not read TLS status.`}),l?.reissueError&&c(D,{variant:`warning`,title:`Certificate reissue`,message:l.reissueError}),l?.restartRequired&&c(D,{variant:`warning`,title:`Restart required`,message:`The HTTPS listener still serves the previous certificate. Restart the hub to apply it.`}),r(`dl`,{className:`grid grid-cols-1 gap-2 text-xs sm:grid-cols-2`,children:[r(`div`,{children:[c(`dt`,{className:`text-foreground-subtle`,children:`Mode`}),c(`dd`,{className:`font-medium`,children:x})]}),r(`div`,{children:[c(`dt`,{className:`text-foreground-subtle`,children:`Valid to`}),c(`dd`,{className:`font-mono`,children:l?.validTo?new Date(l.validTo).toLocaleString():`—`})]}),r(`div`,{children:[c(`dt`,{className:`text-foreground-subtle`,children:`Leaf SHA-256`}),c(`dd`,{className:`font-mono`,title:l?.leafFingerprintSha256??void 0,children:jt(l?.leafFingerprintSha256??null)})]}),r(`div`,{children:[c(`dt`,{className:`text-foreground-subtle`,children:`CA SHA-256`}),c(`dd`,{className:`font-mono`,title:l?.caFingerprintSha256??void 0,children:l?.mode===`uploaded`&&l.caFingerprintSha256===null?c(`span`,{className:`font-sans text-foreground-subtle`,children:`Issuer not included in the upload (not the local CA)`}):jt(l?.caFingerprintSha256??null)})]})]}),l&&l.sans.length>0&&c(`div`,{className:`flex flex-wrap gap-1`,children:l.sans.map(e=>c(`span`,{className:`rounded border border-border bg-surface px-1.5 py-0.5 font-mono text-[10px] text-foreground-subtle`,children:e},e))}),r(`div`,{className:`flex flex-wrap gap-2`,children:[c(ne,{label:`Regenerate`,title:`Issue a new leaf?`,description:`Issues a new leaf under the current local CA. Clients that trust the CA keep working. The HTTPS listener reloads.`,confirmLabel:`Regenerate`,disabled:l?.mode===`uploaded`||n.isPending,action:()=>n.mutateAsync({})}),c(w,{size:`sm`,variant:`outline`,onClick:()=>p(e=>!e),children:`Upload`}),l?.caCertPem&&c(w,{size:`sm`,variant:`outline`,onClick:b,children:`Download CA`}),l?.mode===`uploaded`&&c(ne,{label:`Revert to generated`,title:`Drop the uploaded certificate?`,description:`Deletes the uploaded pair and returns to the generated local CA.`,confirmLabel:`Revert`,confirmVariant:`danger`,disabled:i.isPending,action:()=>i.mutateAsync({})})]}),f&&r(`div`,{className:`space-y-2 border-t border-border pt-3`,children:[c(At,{kind:`certificate`,label:`Certificate or full chain (PEM)`,value:m,onChange:h}),c(At,{kind:`privateKey`,label:`Private key (PEM)`,value:g,onChange:_}),c(At,{kind:`ca`,label:`CA (PEM, optional)`,value:v,onChange:y,rows:4}),c(w,{size:`sm`,disabled:!m.trim()||!g.trim()||$(`certificate`,m)!==null||$(`privateKey`,g)!==null||$(`ca`,v)!==null||a.isPending,onClick:()=>a.mutate({certPem:m,keyPem:g,...v.trim()?{caPem:v}:{}}),children:`Save certificate`}),a.isError&&c(`p`,{className:`text-xs text-danger`,children:a.error instanceof Error?a.error.message:`Upload failed`})]})]})})]})})}n();function Nt({value:e,onChange:t}){let{data:n}=Ae({isCamera:!0,projection:`slim`}),a=i(()=>n===void 0?[{value:``,label:`Loading cameras…`}]:n.length===0?[{value:``,label:`No cameras`}]:[{value:``,label:`Pick a camera`},...n.toSorted((e,t)=>e.name.localeCompare(t.name)).map(e=>({value:String(e.id),label:`${e.name} (#${String(e.id)})`}))],[n]);return r(`label`,{className:`flex min-w-0 flex-col gap-1`,children:[c(`span`,{className:`text-[11px] text-foreground-subtle`,children:`Camera`}),c(Ee,{value:e===null?``:String(e),disabled:n===void 0||n.length===0,onChange:e=>t(e.target.value===``?null:Number(e.target.value)),options:a})]})}function Pt(){let t=h(),[n,i]=s(null);return c(V,{embedded:!0,children:r(`div`,{className:d,"data-testid":`network-diagnostics`,children:[c(x,{loadServers:e(async()=>t.trpcClient.turnProvider.getTurnServers.query(),[t])}),c(fe,{trpc:t.trpcClient,serverUrl:f(),deviceId:n,cameraField:c(Nt,{value:n,onChange:i})})]})})}function Ft(){let{t:e}=Re(),t=ze(`/system/network`,{t:e,isAdmin:!0})??{icon:z,title:e(`system.network`,`Network`)};return c(Ve,{icon:t.icon,title:t.title,defaultSection:`addresses`,sectionsLabel:`${t.title} sections`,sections:[{id:`local-access`,label:`Local access`,content:c(Mt,{})},{id:`addresses`,label:`Network Addresses`,content:c(dt,{})},{id:R.remoteAccess.tab,label:`Remote Access`,content:c(Je,{}),ownedParams:B},{id:R.mesh.tab,label:`Mesh Networks`,content:c(ht,{}),ownedParams:B},{id:R.turn.tab,label:`TURN Servers`,content:c(et,{}),ownedParams:B},{id:`diagnostics`,label:`Diagnostics`,content:c(Pt,{})}]})}export{Ft as NetworkPage};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{yt as e}from"./player-overlays-mP7nvLXZ.js";var t=e(`circle-slash`,[[`circle`,{cx:`12`,cy:`12`,r:`10`,key:`1mglay`}],[`line`,{x1:`9`,x2:`15`,y1:`15`,y2:`9`,key:`1dfufj`}]]);export{t};
|