livedesk 0.1.436 → 0.1.437
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/bin/livedesk.js +5 -1
- package/client/bin/livedesk-client.js +2 -0
- package/client/package.json +5 -5
- package/hub/src/remote-hub.js +11 -4
- package/hub/src/transport/udp-hub-transport.js +15 -9
- package/hub/src/transport/udp-protocol.js +68 -19
- package/package.json +6 -6
- package/web/dist/assets/{index-2-aflaqj.js → index-DxEkHN2-.js} +1 -1
- package/web/dist/index.html +1 -1
|
@@ -22,4 +22,4 @@ ${Me}`:x==="execute"?`Execute this request within the configured LiveDesk approv
|
|
|
22
22
|
${Me}`:Me,pn=m==="auto"?`Auto target selection is enabled. Inspect the allowed Clients and use only the most suitable target or targets for this request.
|
|
23
23
|
|
|
24
24
|
${Ln}`:Ln;if(!st){try{if(V==="custom"){const It=await lN(n,K,W);if(!It.ok)throw new Error(It.error||"Custom Agent policy could not be saved.")}const nt=await tN(n,{instruction:pn,deviceIds:mt,permissionMode:V,customPolicyId:K});if(!nt.ok||!nt.runId)throw new Error(nt.error||"Codex Agent run could not be started.");H(nt.approvals||[]),Ae({runId:nt.runId,stage:nt.stage||nt.status||"queued",message:nt.progressMessage||"Codex request queued on the Hub."}),R(nt.runId);return}catch(nt){try{const It=await eN(n,pn);It.ok&&It.plan&&(st={operation:It.plan.operation,query:It.plan.targetQuery})}catch{C(It=>[...It,{type:"error",id:jl("unsupported"),text:yN(nt)}])}}if(!st)return}try{const nt=await cN(n,{instruction:pn,deviceIds:mt,operation:st.operation,targetQuery:st.query});if(!nt.ok||!nt.batch||!nt.batchId)throw new Error(nt.error||"작업을 만들지 못했습니다.");const It={type:"task",id:jl("task"),batchId:nt.batchId,operation:st.operation,instruction:Me,states:Ld(nt.batch,a),status:nt.batch.status};he.current.add(nt.batchId),C(_n=>[..._n,It]),A(nt.batchId)}catch(nt){X(nt instanceof Error?nt.message:String(nt))}},Ke=async(ge,Oe)=>{ce(ge.approvalId);try{const ze=await rN(n,ge.runId,ge.approvalId,Oe);if(!ze.ok)throw new Error(ze.error||"Agent approval could not be applied.");await Be(ge.runId)}catch(ze){X(ze instanceof Error?ze.message:String(ze))}finally{ce("")}},Ve=async ge=>{ce(ge.approvalId);try{const Oe=await iN(n,ge.runId,ge.approvalId);if(!Oe.ok)throw new Error(Oe.error||"Agent approval could not be rejected.");await Be(ge.runId)}catch(Oe){X(Oe instanceof Error?Oe.message:String(Oe))}finally{ce("")}},rt=async(ge,Oe)=>{F(ge);try{if(Oe){const Me=await aN(n,Oe);for(const st of Me.taskBatches||[])se(st);R(st=>st===Oe?"":st),Ae(st=>st?.runId===Oe?null:st);return}const ze=await dN(n,ge);se(ze),A(Me=>Me===ge?"":Me)}catch(ze){X(ze instanceof Error?ze.message:String(ze))}finally{F("")}},vt=async()=>{if(!(J||!M&&!T)){I(!0);try{const ge=await sN(n);if(!ge.ok)throw new Error(ge.error||"모든 Agent 작업을 취소하지 못했습니다.");A(""),R(""),Ae(null),H([]),C(Oe=>Oe.map(ze=>ze.type==="task"&&["queued","running"].includes(ze.status)?{...ze,status:"cancelled",states:ze.states.map(Me=>["queued","running"].includes(Me.status)?{...Me,status:"cancelled",stage:"Cancelled",error:"Cancelled by user."}:Me)}:ze))}catch(ge){X(ge instanceof Error?ge.message:String(ge))}finally{I(!1)}}},q=async ge=>{F(ge);try{const Oe=await fN(n,ge);if(!Oe.ok||!Oe.batch||!Oe.batchId)throw new Error(Oe.error||"재시도하지 못했습니다.");De.current.delete(Oe.batchId),C(ze=>ze.map(Me=>Me.type==="task"&&Me.batchId===ge?{...Me,batchId:Oe.batchId,states:Ld(Oe.batch,a),status:Oe.batch.status}:Me)),A(Oe.batchId)}catch(Oe){X(Oe instanceof Error?Oe.message:String(Oe))}finally{F("")}},ye=ge=>{k(ge),fe(!1),window.requestAnimationFrame(()=>Ce.current?.focus())},ke=u.useMemo(()=>a.filter(Jd),[a]),$e=u.useMemo(()=>ke.filter(ge=>g.includes(ge.deviceId)),[ke,g]),ot=m==="auto"?`Auto · ${ke.length} available`:m==="all"?`All ${ke.length} computers`:$e.length===1?Zd($e[0]):`${$e.length} selected`,pt=m==="auto"?"LiveDesk chooses the best computer for each task.":m==="all"?"The Agent may coordinate every connected computer.":$e.length>0?$e.map(Zd).join(" · "):"Select one or more computers on the Wall.",St=m==="selected"&&$e.length===1?["Diagnose this computer","Analyze recent errors","Summarize running applications","Check disk and GPU capacity"]:m==="all"?["Compare all computer health","Find the busiest computer","Check disk capacity everywhere","Divide a task across computers"]:["Use the fastest computer","Find an idle computer","Choose the best GPU","Check all computer health"],Pe=m==="selected"?$e.length>0?"What should this computer do?":"Choose a computer or use Auto.":m==="all"?"Use all your computers together.":"What should LiveDesk handle?",Et=m==="auto"?"LiveDesk can inspect the available computers and choose the best target.":"Describe the outcome. LiveDesk will plan, request approval when needed, and show progress per computer.";return r.jsxs("section",{className:`agents-page ${d?"agents-dock-console":""}`,children:[d&&r.jsxs("header",{className:"agent-console-header",children:[r.jsxs("div",{className:"agent-console-title",children:[r.jsx("span",{className:"agent-console-mark",children:r.jsx(Ui,{size:16})}),r.jsxs("div",{children:[r.jsx("small",{children:"Live agent"}),r.jsx("strong",{children:"Agent Console"})]}),r.jsx("i",{children:"Ready"})]}),r.jsxs("section",{className:"agent-console-target","aria-label":"Agent target scope",children:[r.jsxs("div",{children:[r.jsx("span",{children:"Target"}),r.jsx("strong",{children:ot}),r.jsx("small",{children:pt})]}),r.jsxs("div",{className:"agent-console-target-modes",children:[r.jsxs("button",{className:m==="selected"?"active":"",onClick:()=>p("selected"),type:"button",children:[r.jsx(vr,{size:12})," Selected"]}),r.jsxs("button",{className:m==="all"?"active":"",onClick:()=>p("all"),type:"button",children:[r.jsx(nu,{size:12})," All"]}),r.jsxs("button",{type:"button",disabled:!0,title:"Saved Client groups are coming next.",children:[r.jsx(nu,{size:12})," Group"]}),r.jsxs("button",{className:m==="auto"?"active":"",onClick:()=>p("auto"),type:"button",children:[r.jsx(Ui,{size:12})," Auto"]})]})]}),r.jsx("div",{className:"agent-console-interaction-modes","aria-label":"Agent interaction mode",children:["auto","ask","plan","execute"].map(ge=>r.jsxs("button",{className:x===ge?"active":"",onClick:()=>b(ge),type:"button",children:[ge==="auto"?r.jsx(Ui,{size:12}):ge==="execute"?r.jsx(ha,{size:12}):null,ge[0].toUpperCase()+ge.slice(1)]},ge))})]}),r.jsxs("div",{className:"agents-workspace",children:[(M||T)&&r.jsxs("div",{className:"agents-page-status",children:[r.jsxs("span",{children:[Ge.length," Clients · ",gN(V)]}),r.jsx("button",{type:"button",className:"agents-stop-all",onClick:()=>{vt()},disabled:J,children:J?"Stopping…":"Stop all"})]}),ne&&r.jsx("div",{className:"agents-permission-modal-backdrop",role:"presentation",children:r.jsxs("section",{className:"agents-permission-modal",role:"dialog","aria-modal":"true","aria-labelledby":"full-access-title",children:[r.jsx("span",{className:"agents-permission-modal-icon",children:r.jsx(nf,{size:20})}),r.jsx("h3",{id:"full-access-title",children:"Confirm Full Access"}),r.jsx("p",{children:"Codex may change files, run commands, manage applications, and modify system state on the selected Clients. This run is still limited to the selected devices, the fixed policy, and the tool budget."}),r.jsxs("div",{className:"agents-permission-modal-actions",children:[r.jsx("button",{type:"button",onClick:()=>Y(null),children:"Cancel"}),r.jsx("button",{type:"button",className:"danger",onClick:()=>{const ge=ne;Y(null),re(!0),_e(ge.instruction,ge.targetIds,!0)},children:"Run with Full Access"})]})]})}),oe&&r.jsx("div",{className:"agents-error",role:"alert",children:oe}),r.jsx(zM,{items:j,onCancel:rt,onRetry:q,busyBatchId:B,approvals:Z,onApprove:Ke,onReject:Ve,busyApprovalId:pe,progress:z,emptyTitle:d?Pe:void 0,emptyBody:d?Et:void 0}),d&&j.length===0&&!z&&r.jsx("div",{className:"agent-console-suggestions","aria-label":"Suggested Agent commands",children:St.map(ge=>r.jsx("button",{type:"button",onClick:()=>ye(ge),children:ge},ge))}),r.jsxs("div",{className:"agents-composer-shell",children:[xe&&r.jsx(ZM,{devices:a,targetMode:m,selectedIds:g,permissionMode:V,customCategories:W,onSelectPrompt:ye,onClose:()=>fe(!1)}),r.jsx(kM,{ref:Ce,value:w,onChange:k,onSubmit:()=>{_e()},onOpenSuggestedTasks:()=>fe(ge=>!ge),suggestedOpen:xe,suggestedTaskCount:lt,disabled:!!M||!!T,placeholder:d?"Tell LiveDesk what outcome you want...":void 0,compact:d})]})]}),!d&&r.jsxs("aside",{className:"agents-target-sidebar","aria-label":"Agent targets and permissions",children:[r.jsx(JM,{devices:a,mode:m,selectedIds:g,onModeChange:p,onToggle:ge=>v(Oe=>Oe.includes(ge)?Oe.filter(ze=>ze!==ge):[...Oe,ge]),permissionMode:V,onPermissionModeChange:ge=>{L(ge),ge!=="full-access"&&re(!1)}}),V==="custom"&&r.jsxs("section",{className:"agents-custom-policy","aria-label":"Custom Agent permissions",children:[r.jsxs("div",{className:"agents-custom-policy-head",children:[r.jsxs("div",{children:[r.jsx("span",{className:"agents-section-label",children:"Custom policy"}),r.jsx("strong",{children:"Choose what Codex may do automatically."})]}),r.jsx("span",{children:"Denied actions never reach a device."})]}),r.jsx("div",{className:"agents-custom-policy-grid",children:jM.map(ge=>r.jsxs("label",{children:[r.jsx("span",{children:vN(ge)}),r.jsxs("select",{value:W[ge],onChange:Oe=>$(ze=>({...ze,[ge]:Oe.target.value})),children:[r.jsx("option",{value:"allow",children:"Allow automatically"}),r.jsx("option",{value:"ask",children:"Ask every time"}),r.jsx("option",{value:"deny",children:"Deny"})]})]},ge))})]})]})]})}function mw(n){return{type:n.type,deviceIds:n.deviceIds,monitorIndex:n.monitorIndex,label:n.label}}async function hw(n){try{return(await n.json()).error||`${n.status} ${n.statusText}`}catch{return`${n.status} ${n.statusText}`}}async function pw(n,a,i){const l=encodeURIComponent(JSON.stringify({kind:a.kind,fileName:a.fileName,mimeType:n.type||"application/octet-stream",durationMs:a.durationMs,target:mw(a.target)})),c=await fetch(`${Vi(i)}/api/captures?metadata=${l}`,{method:"POST",headers:{"Content-Type":n.type||"application/octet-stream"},body:n,cache:"no-store"});if(!c.ok)throw new Error(await hw(c));return await c.json()}async function bN(n,a){return await qe("/api/capture-sessions",{method:"POST",body:JSON.stringify({...n,target:mw(n.target)})},a)}async function xN(n,a,i,l){const c=await fetch(`${Vi(l)}/api/capture-sessions/${encodeURIComponent(n)}/chunks/${a}`,{method:"PUT",headers:{"Content-Type":i.type||"application/octet-stream"},body:i,cache:"no-store"});if(!c.ok)throw new Error(await hw(c));return await c.json()}async function wN(n,a,i){return await qe(`/api/capture-sessions/${encodeURIComponent(n)}/finalize`,{method:"POST",body:JSON.stringify({durationMs:a})},i)}async function pb(n,a){await qe(`/api/capture-sessions/${encodeURIComponent(n)}`,{method:"DELETE"},a)}async function SN(n){return await qe("/api/captures",void 0,n)}function gb(n,a){return`${Vi(a)}/api/captures/${encodeURIComponent(n)}/content`}async function CN(n,a){return await qe(`/api/captures/${encodeURIComponent(n)}`,{method:"DELETE"},a)}function kN({hubUrl:n,onToast:a}){const[i,l]=u.useState([]),[c,d]=u.useState(!0),m=u.useCallback(async()=>{d(!0);try{l((await SN(n)).captures||[])}catch(g){a(g instanceof Error?g.message:String(g))}finally{d(!1)}},[n,a]);u.useEffect(()=>{m()},[m]);const p=async g=>{const v=gb(g.id,n);if(navigator.share)try{await navigator.share({title:g.fileName,url:v});return}catch{}window.open(v,"_blank","noopener,noreferrer")};return r.jsxs("section",{className:"capture-gallery",children:[r.jsxs("header",{children:[r.jsxs("div",{children:[r.jsx("span",{className:"settings-eyebrow",children:"Capture Gallery"}),r.jsx("h2",{children:"Saved captures"})]}),r.jsxs("button",{className:"text-button",onClick:()=>{m()},type:"button",children:[r.jsx(_o,{size:15})," Refresh"]})]}),c?r.jsx("p",{className:"settings-help",children:"Loading captures..."}):i.length===0?r.jsx("p",{className:"settings-help",children:"No captures saved yet."}):r.jsx("div",{className:"capture-gallery-grid",children:i.map(g=>{const v=gb(g.id,n);return r.jsxs("article",{className:"capture-gallery-card",children:[g.mimeType.startsWith("image/")?r.jsx("img",{src:v,alt:g.target.label}):r.jsx("video",{src:v,controls:!0,preload:"metadata"}),r.jsxs("div",{className:"capture-gallery-card-body",children:[r.jsx("strong",{children:g.fileName}),r.jsxs("small",{children:[g.target.label," · ",new Date(g.createdAt).toLocaleString()," · ",Math.ceil(g.sizeBytes/1024)," KB"]}),r.jsxs("div",{children:[r.jsx("button",{className:"icon-button",title:"Open","aria-label":"Open",onClick:()=>window.open(v,"_blank","noopener,noreferrer"),type:"button",children:r.jsx(m0,{size:15})}),r.jsx("a",{className:"icon-button",title:"Download","aria-label":"Download",href:`${v}?download=1`,download:g.fileName,children:r.jsx(qh,{size:15})}),r.jsx("button",{className:"icon-button",title:"Share","aria-label":"Share",onClick:()=>{p(g)},type:"button",children:r.jsx(h0,{size:15})}),r.jsx("button",{className:"icon-button danger",title:"Delete","aria-label":"Delete",onClick:()=>{window.confirm(`Delete ${g.fileName}?`)&&CN(g.id,n).then(()=>m()).catch(x=>a(x instanceof Error?x.message:String(x)))},type:"button",children:r.jsx(Vh,{size:15})})]})]})]},g.id)})})]})}function _d(n){if(!n)return"";const a=Math.max(0,Math.floor((Date.now()-n)/1e3)),i=Math.floor(a/3600),l=Math.floor(a%3600/60),c=a%60;return i>0?`${String(i).padStart(2,"0")}:${String(l).padStart(2,"0")}:${String(c).padStart(2,"0")}`:`${String(l).padStart(2,"0")}:${String(c).padStart(2,"0")}`}function vb({controller:n,compact:a=!1,onFirstRollingUse:i,onError:l}){const{state:c,settings:d,videoSupported:m}=n,p=async b=>{try{await b()}catch(w){l?.(w instanceof Error?w.message:String(w))}},g=async()=>{if(d.rollingEnabled){n.setRollingEnabled(!1);return}(i?await i():window.confirm(`Keep the most recent 30 seconds in memory temporarily?
|
|
25
|
-
Nothing is saved until you press Save Last 30s.`))&&n.setRollingEnabled(!0)},v=c.recording?`Stop recording · ${_d(c.recordingStartedAt)}`:`Start recording · ${c.targetLabel}`,x=c.timelapse?`Stop timelapse · ${_d(c.timelapseStartedAt)}`:`Start timelapse · ${d.timelapseIntervalSeconds}s interval`;return r.jsxs("div",{className:`capture-toolbar ${a?"compact":""} ${c.recording?"recording-active":""}`,"aria-label":"Capture actions",children:[r.jsx("button",{className:"capture-action","aria-label":`Take screenshot · ${c.targetLabel}`,title:`Take screenshot · ${c.targetLabel}`,disabled:c.busy,onClick:()=>{p(n.takeScreenshot)},type:"button",children:r.jsx(p0,{size:17})}),r.jsx("button",{className:`capture-action ${c.recording?"active":""}`,"aria-label":v,title:v,disabled:!m||c.busy||c.timelapse,onClick:()=>{p(c.recording?n.stopRecording:n.startRecording)},type:"button",children:c.recording?r.jsxs(r.Fragment,{children:[r.jsx(ey,{size:14,fill:"currentColor"}),r.jsx("span",{children:_d(c.recordingStartedAt)})]}):r.jsx(Zb,{size:18})}),r.jsxs("button",{className:"capture-action","aria-label":c.bufferEnabled?`Save last 30 seconds · ${c.bufferSeconds}s ready`:"Enable Last 30s memory buffer",title:c.bufferEnabled?`Save last 30 seconds · ${c.bufferSeconds}s ready`:"Enable Last 30s memory buffer",disabled:!m||c.recording||c.timelapse||c.busy||c.bufferEnabled&&c.bufferSeconds<1,onClick:()=>c.bufferEnabled?void p(n.saveLast30Seconds):void g(),type:"button",children:[r.jsx(g0,{size:17}),r.jsx("small",{children:"30s"})]}),r.jsx("button",{className:`capture-action ${c.timelapse?"timelapse-active":""}`,"aria-label":x,title:x,disabled:!m||c.busy||c.recording,onClick:()=>{p(c.timelapse?n.stopTimelapse:n.startTimelapse)},type:"button",children:c.timelapse?r.jsxs(r.Fragment,{children:[r.jsx(ey,{size:14,fill:"currentColor"}),r.jsx("span",{children:_d(c.timelapseStartedAt)})]}):r.jsx(v0,{size:17})})]})}function gw({state:n}){return n==="ok"?r.jsx(Bn,{size:15,"aria-hidden":"true"}):r.jsx(ex,{size:15,"aria-hidden":"true"})}function yb({active:n,refreshing:a,onClick:i}){return r.jsx("button",{className:`remote-status-button ${n?"active":""}`,type:"button",title:"Check Remote Status","aria-label":"Check Remote Status","aria-pressed":n,"aria-busy":a,onClick:i,children:a?r.jsx("span",{className:"remote-status-spinner","aria-hidden":"true"}):r.jsx(y0,{size:18,"aria-hidden":"true"})})}function bb({active:n,refreshing:a,onClick:i}){return r.jsx("button",{className:`remote-status-button debug-status-button ${n?"active":""}`,type:"button",title:"Check Debug Status","aria-label":"Check Debug Status","aria-pressed":n,"aria-busy":a,onClick:i,children:a?r.jsx("span",{className:"remote-status-spinner","aria-hidden":"true"}):r.jsx(Gb,{size:18,"aria-hidden":"true"})})}function xb({active:n,refreshing:a,onClick:i}){return r.jsx("button",{className:`remote-status-button hub-status-button ${n?"active":""}`,type:"button",title:"Check Hub Status","aria-label":"Check Hub Status","aria-pressed":n,"aria-busy":a,onClick:i,children:a?r.jsx("span",{className:"remote-status-spinner","aria-hidden":"true"}):r.jsx(yr,{size:18,"aria-hidden":"true"})})}function jN({items:n,mode:a="remote"}){return r.jsx("span",{className:`remote-status-lines mode-${a}`,role:"status","aria-label":a==="debug"?"Debug status":"Remote status",children:n.map(i=>r.jsxs("span",{className:`remote-status-line state-${i.state}`,title:i.detail,children:[r.jsx(gw,{state:i.state}),r.jsxs("span",{children:[r.jsx("strong",{children:i.label}),r.jsx("small",{children:i.detail})]})]},i.id))})}function AN({items:n,collectedAt:a,onClose:i}){return r.jsxs("section",{className:"remote-status-dialog hub-status-dialog",role:"dialog","aria-modal":"false","aria-labelledby":"hub-status-dialog-title",children:[r.jsxs("header",{children:[r.jsx("span",{className:"remote-status-dialog-mark","aria-hidden":"true",children:r.jsx(yr,{size:19})}),r.jsxs("span",{children:[r.jsx("small",{children:"Hub measurements"}),r.jsx("h2",{id:"hub-status-dialog-title",children:"Hub Status"})]}),r.jsx("button",{type:"button",onClick:i,title:"Close Hub status","aria-label":"Close Hub status",children:r.jsx(ma,{size:17})})]}),r.jsx("p",{children:"LiveDesk collects these Hub and browser signals continuously. This check refreshed the latest snapshot instead of starting a one-time benchmark."}),r.jsx("div",{className:"remote-status-dialog-list",children:n.map(l=>r.jsxs("article",{className:`remote-status-dialog-row state-${l.state}`,children:[r.jsx(gw,{state:l.state}),r.jsxs("span",{children:[r.jsx("strong",{children:l.label}),r.jsx("small",{children:l.detail})]})]},l.id))}),r.jsxs("footer",{children:["Checked ",new Date(a).toLocaleTimeString()]})]})}const MN=/(nvenc|videotoolbox|qsv|quick\s*sync|amf|vaapi|v4l2m2m|mediafoundation|h264_mf)/i,NN=/(libx264|openh264|software|cpu)/i,Ut=Object.freeze({frameAgeWarningMs:2500,networkDelayWarningMs:120,receiveRatioWarning:.62,deliveryRatioWarning:.82,decoderOutputRatioWarning:.62,captureBudgetMultiplier:2.5,minimumCaptureBudgetMs:80,cpuWarningRatio:.82,memoryWarningRatio:.86,gpuWarningRatio:.92,gpuMemoryWarningRatio:.92,gpuTemperatureWarningC:85,networkUsageWarningRatio:.85,networkLatencyWarningMs:80,minimumClientStatusFreshMs:15e3,clientStatusFreshMultiplier:3,hubMinimumTotalMemoryBytes:8*1024**3,hubMinimumBrowserMemoryGb:4});function Rn(n){return n&&typeof n=="object"&&!Array.isArray(n)?n:{}}function vw(n,a){let i=n;for(const l of String(a).split(".")){if(!i||typeof i!="object")return;i=i[l]}return i}function As(n,...a){for(const i of a){const l=vw(n,i);if(l==null||l==="")continue;const c=Number(l);if(Number.isFinite(c))return c}}function Ol(n,...a){for(const i of a){const l=vw(n,i);if(typeof l=="string"&&l.trim())return l.trim()}return""}function El(n,...a){const i=As(n,...a);if(i!==void 0)return Math.max(0,Math.min(1,i>1?i/100:i))}function ut(n,a,i,l){return{id:n,state:a,label:i,detail:l}}function TN(n){const a=Date.parse(String(n||""));return Number.isFinite(a)&&a>0?a:void 0}function wb(n){return`${(Math.max(0,Number(n||0))/1024**3).toFixed(1)} GB`}function EN(n){const a=String(n||"").replace(/^::ffff:/,"").trim().toLowerCase();if(!a||a==="::1"||a==="localhost"||a==="127.0.0.1"||/^10\./.test(a)||/^192\.168\./.test(a))return!0;const i=a.match(/^172\.(\d{1,3})\./);return!!i&&Number(i[1])>=16&&Number(i[1])<=31||a.startsWith("fe80:")||a.startsWith("fc")||a.startsWith("fd")}function yw(n,a){const i=Number(n?.browserReceivedEpochMs);return Number.isFinite(i)&&i>0?Math.max(0,a-i):Number.POSITIVE_INFINITY}function RN(n){const a=String(n?.frameMode||n?.mode||n?.codec||"").toLowerCase(),i=[n?.hardwareEncoder,n?.platformProfile].filter(Boolean).join(" · ");return!n||!/h264/.test(a)?ut("encoder","warning","Video encoder unavailable","Waiting for an H.264 frame."):MN.test(i)?ut("encoder","ok","Hardware H.264 encoding",i):NN.test(i)?ut("encoder","warning","Software H.264 encoding",`${i||"CPU encoder"} may limit frame rate.`):ut("encoder","warning","H.264 encoder not identified",i||"The Client has not reported its active encoder profile.")}function DN({device:n,frame:a,receiveStat:i,paintStat:l,codec:c,selectedMonitorIndex:d=0,control:m=!1,controlInputReady:p=!1,controlBinding:g,clientHeartbeatMs:v=5e3,now:x=Date.now()}={}){const b=Rn(n),w=Rn(b.status),k=Rn(l?.lastInfo),j=[],C=yw(a,x);j.push(b.connected===!0&&C<=Ut.frameAgeWarningMs?ut("connection","ok","Video connection is current",`${Math.round(C)}ms since the latest frame.`):ut("connection","warning",b.connected===!0?"Video frame is stale":"Client is offline",b.connected===!0&&Number.isFinite(C)?`${Math.round(C)}ms since the latest frame.`:String(b.lastDisconnectReason||"No current Client session."))),j.push(RN(a));const M=Math.max(0,Number(a?.requestedFps||a?.effectiveFps||0)),A=Math.max(0,Number(i?.framesPerSecond||0)),T=As(a,"captureP95Ms","captureAverageMs","captureStageMs"),R=Math.max(0,Number(T||0)),B=a?.captureTimingAvailable===!0&&M>0&&Number(i?.sampleMs||0)>0&&T!==void 0,F=Math.max(Ut.minimumCaptureBudgetMs,M>0?1e3/M*Ut.captureBudgetMultiplier:0),V=R>F||M>0&&i?.sampleMs>0&&A<M*Ut.receiveRatioWarning;j.push(B?V?ut("capture","warning","Screen capture cannot keep target speed",`${R?`capture p95 ${Math.round(R)}ms`:"capture timing unavailable"} · ${A.toFixed(1)}/${M||"?"} fps`):ut("capture","ok","Screen capture speed is healthy",`${R?`capture p95 ${Math.round(R)}ms`:"no slow capture reported"}${M?` · ${A.toFixed(1)}/${M} fps`:""}`):ut("capture","warning","Screen capture data is still collecting","A current frame-rate and capture-time sample is required."));const L=As(a,"hubPacketTransitAgeMs"),K=Math.max(0,Number(L||0)),W=Math.max(0,Number(a?.agentSendGapMs||a?.sourceGapMs||0)),$=M>0?1e3/M:100,Z=!EN(b.remoteAddress),H=El(w,"network.usageRatio"),pe=As(w,"network.latencyMs"),ce=(H??0)>=Ut.networkUsageWarningRatio||(pe??0)>=Ut.networkLatencyWarningMs,J=L!==void 0&&Number(i?.sampleMs||0)>0,I=M>0&&Number(i?.sampleMs||0)>0&&A<M*Ut.deliveryRatioWarning,ne=K>Ut.networkDelayWarningMs||W>Math.max(Ut.networkDelayWarningMs,$*3)||I||C>Ut.frameAgeWarningMs||ce;j.push(J?ne||Z?ut("network","warning",Z?"External network route may add delay":ce?"Client network adapter needs attention":"Video delivery is uneven",[K?`${Math.round(K)}ms Hub-to-browser`:"",W?`${Math.round(W)}ms source gap`:"",I?`${A.toFixed(1)} of ${M} requested fps`:"",pe!==void 0?`${Math.round(pe)}ms Client network latency`:"",H!==void 0?`${Math.round(H*100)}% Client link use`:"",Z?String(b.remoteAddress||"external route"):""].filter(Boolean).join(" · ")||"Frame delivery is not current."):ut("network","ok","Network delivery is healthy",`${Math.round(K)}ms Hub-to-browser · the current video rate is on target.`):ut("network","warning","Network delivery data is still collecting","A calibrated Hub-to-browser transit sample is required."));const Y=El(w,"cpu.usageRatio","cpu.usedRatio","cpu.percent"),U=El(w,"memory.usedRatio","usedMemRatio"),re=El(w,"gpu.usageRatio"),oe=El(w,"gpu.memory.usedRatio"),X=As(w,"gpu.temperatureC","thermal.gpuC"),xe=Y!==void 0&&U!==void 0,fe=TN(b.lastStatusAt),z=fe===void 0?Number.POSITIVE_INFINITY:Math.max(0,x-fe),Ae=Math.max(Ut.minimumClientStatusFreshMs,Math.max(1e3,Number(v||0))*Ut.clientStatusFreshMultiplier),Ce=z<=Ae,De=(Y??0)>=Ut.cpuWarningRatio||(U??0)>=Ut.memoryWarningRatio||(re??0)>=Ut.gpuWarningRatio||(oe??0)>=Ut.gpuMemoryWarningRatio||(X??0)>=Ut.gpuTemperatureWarningC;j.push(xe?Ce?De?ut("computer","warning",(re??0)>=Ut.gpuWarningRatio||(oe??0)>=Ut.gpuMemoryWarningRatio||(X??0)>=Ut.gpuTemperatureWarningC?"Client GPU is under load":"Client computer is under load",[`CPU ${Math.round(Y*100)}%`,`memory ${Math.round(U*100)}%`,re!==void 0?`GPU ${Math.round(re*100)}%`:"",oe!==void 0?`GPU memory ${Math.round(oe*100)}%`:"",X!==void 0?`GPU ${Math.round(X)}C`:""].filter(Boolean).join(" · ")):ut("computer","ok","Client computer has working capacity",[`CPU ${Math.round(Y*100)}%`,`memory ${Math.round(U*100)}%`,re!==void 0?`GPU ${Math.round(re*100)}%`:""].filter(Boolean).join(" · ")):ut("computer","warning","Client performance snapshot is stale",Number.isFinite(z)?`The latest CPU and memory report is ${Math.round(z/1e3)} seconds old.`:"No current Client performance heartbeat has been received."):ut("computer","warning","Client performance data is unavailable","CPU and memory telemetry have not been reported yet."));const He=Number(a?.monitorIndex),he=Number.isInteger(He)&&He===Number(d),Ge=Rn(b.channels),lt=Rn(b.capabilities),se=Ge.input!==!1&<.remoteInput!==!1&<.accessibilityPermission!==!1,ve=lt.screenRecordingPermission!==!1,Te=Rn(g),Be=!m||String(a?.sessionId||"")&&String(a?.sessionId||"")===String(Te.sessionId||"")&&String(a?.commandId||"")&&String(a?.commandId||"")===String(Te.commandId||"")&&Number(a?.captureGeneration||0)>0&&Number(a?.captureGeneration||0)===Number(Te.captureGeneration||0)&&Number(a?.monitorIndex)===Number(Te.monitorIndex),_e=!m||p&&Be;j.push(he&&_e&&se&&ve?ut("display","ok",m?"Display and remote input are bound":"Display and remote input are available",`Display ${Number(d)+1} · generation ${Number(a?.captureGeneration||0)||"current"}.`):ut("display","warning",ve?se?m?"Remote input is not bound to this display":"Waiting for the selected display":"Remote input permission is missing":"Screen capture permission is missing",`Selected display ${Number(d)+1}; latest frame ${Number.isInteger(He)?He+1:"not available"}.`));const Ke=Rn(c),Ve=Array.isArray(Ke.canvases)?Rn(Ke.canvases[0]):{},rt=Math.max(0,Number(l?.decoderInputFps||0)),vt=Math.max(0,Number(l?.decoderOutputFps||0)),q=rt>0&&Number(l?.sampleMs||i?.sampleMs||0)>0,ye=rt>0?vt/rt:0,ke=Ol(Ve,"configuredAcceleration","requestedAcceleration")||Ol(k,"h264DecodeAcceleration"),$e=Ol(Ve,"softwareFallbackReason"),ot=!!$e||ke==="prefer-software";return j.push(q?ot||ye<Ut.decoderOutputRatioWarning?ut("decoder","warning",ot?"Browser is using software H.264 fallback":"Browser decoder output is slow",`${$e||String(k.decoderLabel||ke||"decoder")} · ${rt.toFixed(1)} in / ${vt.toFixed(1)} out fps.`):ut("decoder","ok",ke==="prefer-hardware"?"Browser requests hardware H.264":"Browser H.264 path is responsive",`${rt.toFixed(1)} in / ${vt.toFixed(1)} out fps. Chrome does not expose definitive hardware acceptance.`):ut("decoder","warning","Browser decoder data is still collecting","Decoder input and output samples are required before this path can be marked healthy.")),j}function jo(n){const a=String(n||"").trim();return a?a.length<=14?a:`${a.slice(0,6)}…${a.slice(-5)}`:"-"}function ca(n,a=1){const i=Number(n);return Number.isFinite(i)?i.toFixed(a):"-"}function LN({device:n,frame:a,receiveStat:i,paintStat:l,codec:c,selectedMonitorIndex:d=0,control:m=!1,controlInputReady:p=!1,controlBinding:g,now:v=Date.now()}={}){const x=Rn(n),b=Rn(g),w=Rn(l?.lastInfo),k=Rn(c),j=Array.isArray(k.canvases)?Rn(k.canvases[0]):{},C=[],M=Number(d),A=Number(a?.monitorIndex),T=Number.isInteger(A)&&A===M,R=Number(a?.captureGeneration||0),B=Number(b.captureGeneration||0),F=!m||T&&String(a?.sessionId||"")===String(b.sessionId||"")&&String(a?.commandId||"")===String(b.commandId||"")&&R>0&&R===B,V=Math.max(0,Number(a?.requestedFps||a?.effectiveFps||0)),L=Math.max(0,Number(i?.framesPerSecond||0)),K=Math.max(0,Number(l?.framesPerSecond||0)),W=Math.max(0,Number(l?.decoderInputFps||0)),$=Math.max(0,Number(l?.decoderOutputFps||0)),Z=Number(i?.sampleMs||0)>0&&Number(l?.sampleMs||0)>0,H=V>0&&Z&&Math.min(L,K||L,$||L)<V*Ut.deliveryRatioWarning,pe=Math.max(0,Number(a?.agentSendGapMs||a?.sourceGapMs||0)),ce=As(a,"hubPacketTransitAgeMs"),J=Math.max(0,Number(a?.droppedByAgent||0)),I=Math.max(0,Number(a?.hubIngressDropped||0)),ne=Math.max(0,Number(a?.hubClientDropped||0)),Y=J+I+ne,U=yw(a,v),re=Ol(j,"configuredAcceleration","requestedAcceleration")||Ol(w,"h264DecodeAcceleration"),oe=Ol(j,"softwareFallbackReason");C.push(a?ut("debug-frame","ok",`Frame #${Number(a.frameSeq||0)} · ${String(a.streamPurpose||(m?"control":"wall"))}`,`stream ${jo(a.streamId)} · ${a.width||0}x${a.height||0} · ${Math.round(Number(a.byteLength||0)/1024)} KB · age ${Number.isFinite(U)?Math.round(U):"-"}ms`):ut("debug-frame","warning","No current frame",`Device ${jo(x.deviceId)} has not produced a frame.`)),C.push(T?ut("debug-monitor","ok",`Monitor selected ${M+1} · frame ${A+1}`,`reported ${Number(a?.monitorCount||0)||"?"} displays · generation ${R||"-"}`):ut("debug-monitor","warning",`Monitor mismatch: selected ${M+1} · frame ${Number.isInteger(A)?A+1:"-"}`,`reported ${Number(a?.monitorCount||0)||"?"} displays · generation ${R||"-"}`)),C.push(F&&(!m||p)?ut("debug-binding","ok",m?"Control generation is bound":"Wall capture generation is current",`session ${jo(a?.sessionId)} · command ${jo(a?.commandId)} · generation ${R||"-"}`):ut("debug-binding","warning",m?"Control generation is not bound":"Wall capture generation is unavailable",`frame ${jo(a?.commandId)}/${R||"-"} · expected ${jo(b.commandId)}/${B||"-"} · input ${p?"ready":"waiting"}`)),C.push(Z&&!H?ut("debug-cadence","ok",`Cadence ${ca(L)} rx · ${ca(K)} paint · ${ca($)} decode`,`requested ${ca(V)} · decoder input ${ca(W)} fps · receive p95 ${ca(i?.p95IntervalMs)}ms`):ut("debug-cadence","warning",Z?"Frame cadence is below request":"Frame cadence sample is collecting",`requested ${ca(V)} · receive ${ca(L)} · paint ${ca(K)} · decode ${ca($)} fps`)),C.push(Y===0&&pe<=Ut.networkDelayWarningMs&&(ce===void 0||ce<=Ut.networkDelayWarningMs)?ut("debug-delivery","ok","Delivery counters are clear",`source gap ${Math.round(pe)}ms · Hub transit ${ce===void 0?"-":Math.round(ce)}ms · queue ${Math.round(Number(a?.agentQueueBeforePaceMs||0))}ms`):ut("debug-delivery","warning","Delivery counters show loss or delay",`Agent drop ${J} · Hub ingress ${I} · browser queue ${ne} · source gap ${Math.round(pe)}ms · transit ${ce===void 0?"-":Math.round(ce)}ms`));const X=As(a,"captureP95Ms");return C.push(a?.captureTimingAvailable===!0&&X!==void 0?ut("debug-capture","ok",`Capture p95 ${ca(X)}ms · avg ${ca(a?.captureAverageMs)}ms`,`stage ${ca(a?.captureStageMs)}ms · slow ${Number(a?.slowFrameCount||0)} · helper restart ${Number(a?.captureHelperRestarts||0)} · source skip ${Number(a?.captureSourceSkipped||0)}`):ut("debug-capture","warning","Capture timing sample is unavailable",`stage ${ca(a?.captureStageMs)}ms · helper restart ${Number(a?.captureHelperRestarts||0)} · source serial ${Number(a?.captureSourceSerial||0)}`)),C.push(!oe&&re!=="prefer-software"?ut("debug-decoder","ok",`Decoder ${re||"acceleration unknown"}`,`${String(a?.frameMode||a?.codec||"-")} · ${String(a?.hardwareEncoder||"-")} · ${String(a?.platformProfile||"-")}`):ut("debug-decoder","warning","Decoder software fallback is active",`${oe||re||"software"} · ${ca(W)} in / ${ca($)} out fps`)),C}function _N({status:n,deviceDiagnostics:a=[],codec:i,browser:l,frameStats:c}={}){const d=Rn(n),m=Rn(d.diagnostics),p=Rn(m.system),g=Rn(m.network),v=Rn(i),x=Rn(l),b=Array.isArray(a)?a:[],w=b.reduce((pe,ce)=>pe+(Array.isArray(ce)&&ce.some(J=>J?.state==="warning")?1:0),0),k=El(p,"cpuUsageRatio","processCpuRatio"),j=El(p,"memoryUsedRatio"),C=As(p,"totalMemoryBytes"),M=As(p,"cpuLogicalCores")??As(x,"hardwareConcurrency"),A=As(x,"deviceMemory"),T=(M??0)>0&&k!==void 0&&j!==void 0&&(C??0)>0,R=(C??0)>0&&C<Ut.hubMinimumTotalMemoryBytes,B=(A??0)>0&&A<Ut.hubMinimumBrowserMemoryGb,F=(M??0)>0&&M<4||(k??0)>=Ut.cpuWarningRatio||(j??0)>=Ut.memoryWarningRatio||R||B,V=Math.max(0,Number(v.activeSoftwareFallbackCount??v.softwareFallbackCount??0)),L=Math.max(0,Number(v.activeDecoderCount||0)),K=Math.max(0,Number(c?.bytesPerSecond||0)*8/1e6),W=As(g,"externalDeviceCount"),$=Ol(g,"route"),Z=W!==void 0&&!!$,H=(W??0)>0||$==="external";return[d.started&&!d.lastError?ut("hub-runtime","ok","Hub runtime is ready",`${d.connectedDeviceCount||0} Clients connected.`):ut("hub-runtime","warning","Hub runtime needs attention",String(d.lastError||"Hub is not ready.")),T?F?ut("hub-computer","warning",R?"Hub has limited system memory":B?"Browser reports limited memory capacity":"Hub computer may limit remote performance",[`${M||"?"} logical cores`,`${wb(C)} RAM`,A!==void 0?`${A} GB browser memory class`:"",`CPU ${k===void 0?"?":Math.round(k*100)}%`,`memory ${j===void 0?"?":Math.round(j*100)}%`].filter(Boolean).join(" · ")):ut("hub-computer","ok","Hub computer capacity is healthy",`${M||"?"} logical cores · ${wb(C)} RAM · CPU ${k===void 0?"not reported":`${Math.round(k*100)}%`}.`):ut("hub-computer","warning","Hub performance data is unavailable","Logical-core, CPU, used-memory, and total-memory telemetry are required."),L<=0?ut("hub-codec","warning","No active browser H.264 decoder to check","Open Wall or Control and wait for a video frame."):V>0?ut("hub-codec","warning","Some browser decoders use software fallback",`${L} active H.264 decoders · ${V} software fallbacks. Chrome does not expose definitive hardware acceptance.`):ut("hub-codec","ok","All active streams request hardware H.264",`${L} active decoders. Chrome hardware acceptance is inferred from health, not directly exposed.`),w>0?ut("hub-clients","warning",`${w} Clients need attention`,"Open the matching Wall slot lines for the exact cause."):ut("hub-clients","ok","Connected Clients report healthy remote paths",`${b.length} Client reports checked.`),ut("hub-network",!Z||H||K>120?"warning":"ok",Z?H?`${W} Clients use an external network route`:K>120?"Hub receive load is high":"Hub receive load is within range":"Hub network route data is unavailable",Z?`${$.toUpperCase()} route · ${K.toFixed(K>=10?0:1)} Mbps received by this browser.`:"The Hub has not reported LAN/external route classification yet.")]}async function IN(n,a={}){const i=n.filter(b=>b.canvas&&b.canvas.width>0&&b.canvas.height>0),l=Math.max(320,a.maxWidth||1920),c=Math.max(180,a.maxHeight||1080),d=i.length<=1?1:i.length<=4?2:i.length<=9?3:4,m=Math.max(1,Math.ceil(Math.max(1,i.length)/d)),p=Math.floor(l/d),g=Math.floor(c/m),v=document.createElement("canvas");v.width=Math.max(320,p*d),v.height=Math.max(180,g*m);const x=v.getContext("2d",{alpha:!1});if(!x)return v;x.fillStyle="#0b1220",x.fillRect(0,0,v.width,v.height),x.font="600 14px system-ui, sans-serif",x.textBaseline="bottom";for(let b=0;b<i.length;b+=1){const w=i[b],k=b%d,j=Math.floor(b/d),C=k*p,M=j*g,A=w.canvas,T=Math.min(p/A.width,g/A.height),R=Math.max(1,Math.floor(A.width*T)),B=Math.max(1,Math.floor(A.height*T)),F=C+Math.floor((p-R)/2),V=M+Math.floor((g-B)/2);x.drawImage(A,F,V,R,B),a.includeLabels!==!1&&(x.fillStyle="rgba(4, 10, 20, .74)",x.fillRect(C,M+g-28,p,28),x.fillStyle="#f8fafc",x.fillText(w.label,C+9,M+g-9))}return v}function zN(n){return n?.signature||"none"}function ON(n){return n?.label||"No capture target"}function Sb(n){return{...n,deviceIds:[...n.deviceIds],sourceCanvas:n.sourceCanvas||null}}function HN(){const n=new Date,a=i=>String(i).padStart(2,"0");return`${n.getFullYear()}${a(n.getMonth()+1)}${a(n.getDate())}_${a(n.getHours())}${a(n.getMinutes())}${a(n.getSeconds())}`}function FN(n){return n.replace(/[^a-zA-Z0-9._-]+/g,"-").replace(/^-+|-+$/g,"").slice(0,80)||"Wall"}function BN(n){return`LiveDesk_${n.type==="wall"||n.type==="selection"?"Wall":`${FN(n.label)}_Monitor-${Number(n.monitorIndex||0)+1}`}_${HN()}.png`}async function $N(n,a,i){const l=await new Promise((d,m)=>n.toBlob(p=>p?d(p):m(new Error("Screenshot could not be encoded.")),"image/png")),c=await pw(l,{kind:"screenshot",fileName:BN(a),target:a},i);try{await navigator.clipboard.write([new ClipboardItem({"image/png":l})])}catch{}return c.capture}function bw(){return["video/webm;codecs=vp9","video/webm;codecs=vp8","video/webm"].find(a=>typeof MediaRecorder<"u"&&MediaRecorder.isTypeSupported(a))||""}function PN(n,a){const i=new Date().toISOString().replace(/[-:]/g,"").replace(/\.\d{3}Z$/,"").replace("T","_"),l=a.label.replace(/[^a-zA-Z0-9._-]+/g,"-").slice(0,70)||"Wall";return n==="timelapse"?`LiveDesk_Timelapse_${l}_${i}.webm`:`LiveDesk_Recording_${l}_${i}.webm`}async function xw(n){if(typeof MediaRecorder>"u"||typeof n.canvas.captureStream!="function")throw new Error("This browser does not support video recording.");const a=bw();if(!a)throw new Error("This browser does not support WebM recording.");const i=n.kind||"recording",l=await bN({kind:i,fileName:PN(i,n.target),mimeType:a,target:n.target},n.hubUrl),c=n.canvas.captureStream(n.frameRequests?0:n.fps),d=new MediaRecorder(c,{mimeType:a,videoBitsPerSecond:i==="timelapse"?2e6:4e6}),m=performance.now();let p=0,g=!1,v=null,x=Promise.resolve();return d.ondataavailable=b=>{if(!b.data||b.data.size===0)return;const w=p++;x=x.then(()=>xN(l.sessionId,w,b.data,n.hubUrl).then(()=>{})).catch(k=>{v=v||(k instanceof Error?k:new Error(String(k))),n.onError?.(v)})},d.onerror=()=>{v=new Error("Video recording stopped unexpectedly."),n.onError?.(v)},d.start(1e3),{requestFrame:()=>{c.getVideoTracks()[0]?.requestFrame?.()},stop:async()=>{if(!g){if(g=!0,await new Promise(b=>{d.addEventListener("stop",()=>b(),{once:!0}),d.stop()}),c.getTracks().forEach(b=>b.stop()),await x,v)throw await pb(l.sessionId,n.hubUrl).catch(()=>{}),v;await wN(l.sessionId,Math.max(0,performance.now()-m),n.hubUrl)}},cancel:async()=>{g||(g=!0,d.stop(),c.getTracks().forEach(b=>b.stop()),await pb(l.sessionId,n.hubUrl).catch(()=>{}))}}}class UN{recorder=null;stream=null;segments=[];totalBytes=0;targetSignature="";startedAt=0;maxDurationMs=3e4;maxBytes=128*1024*1024;get seconds(){return!this.startedAt||this.segments.length===0?0:Math.min(30,Math.max(0,(performance.now()-this.segments[0].at)/1e3))}get active(){return this.recorder!==null}async start(a,i){if(this.recorder&&this.targetSignature===i.signature)return;if(this.stop(),typeof MediaRecorder>"u"||typeof a.captureStream!="function")throw new Error("This browser does not support Last 30s.");const l=bw();if(!l)throw new Error("This browser does not support WebM recording.");this.targetSignature=i.signature,this.stream=a.captureStream(20),this.recorder=new MediaRecorder(this.stream,{mimeType:l,videoBitsPerSecond:3e6}),this.startedAt=performance.now(),this.recorder.ondataavailable=c=>{if(!c.data.size)return;const d=performance.now();for(this.segments.push({blob:c.data,at:d}),this.totalBytes+=c.data.size;this.segments.length>1&&(d-this.segments[0].at>this.maxDurationMs||this.totalBytes>this.maxBytes);){const m=this.segments.shift();this.totalBytes-=m.blob.size}},this.recorder.start(1e3)}stop(){const a=this.recorder;this.recorder=null,a&&a.state!=="inactive"&&(a.ondataavailable=null,a.stop()),this.stream?.getTracks().forEach(i=>i.stop()),this.stream=null,this.segments=[],this.totalBytes=0,this.startedAt=0}async save(a,i){if(this.segments.length===0)throw new Error("The Last 30s buffer is not ready yet.");const l=new Blob(this.segments.map(m=>m.blob),{type:this.segments[0].blob.type||"video/webm"}),d=`LiveDesk_Last30s_${a.label.replace(/[^a-zA-Z0-9._-]+/g,"-").slice(0,70)||"Wall"}_${new Date().toISOString().replace(/[-:]/g,"").replace(/\.\d{3}Z$/,"").replace("T","_")}.webm`;return await pw(l,{kind:"last-30s",fileName:d,target:a,durationMs:Math.min(3e4,this.segments.length*1e3)},i)}}async function qN(n,a,i,l,c){const d=await xw({canvas:n,target:a,kind:"timelapse",fps:30,frameRequests:!0,hubUrl:l,onError:c});d.requestFrame?.();const m=window.setInterval(()=>d.requestFrame?.(),Math.max(5,i)*1e3);return{stop:async()=>{window.clearInterval(m),await d.stop()},cancel:async()=>{window.clearInterval(m),await d.cancel()}}}const ww="livedesk.captureSettings";function GN(){const n={rollingEnabled:!1,recordingQuality:"standard",timelapseIntervalSeconds:10,includeRemoteCursor:!0};try{const a=JSON.parse(localStorage.getItem(ww)||"null");return{...n,...a&&typeof a=="object"?a:{}}}catch{return n}}function VN(n){const[a,i]=u.useState(GN),[l,c]=u.useState(0),[d,m]=u.useState(0),[p,g]=u.useState(0),[v,x]=u.useState(!1),b=u.useRef(void 0),w=u.useRef(!1),k=u.useRef(""),j=u.useRef(null),C=u.useRef(null),M=u.useRef(null),A=u.useRef(null),T=u.useRef(new UN),R=n.sourceRegistry.resolveCurrentTarget(),B=zN(R),F=ON(R),V=typeof MediaRecorder<"u"&&typeof HTMLCanvasElement<"u"&&"captureStream"in HTMLCanvasElement.prototype;u.useEffect(()=>{if(localStorage.setItem(ww,JSON.stringify(a)),!w.current)return;const Y=JSON.stringify(a);Y!==k.current&&(k.current=Y,qe("/api/settings",{method:"PATCH",body:JSON.stringify({revision:b.current,filesAudio:{rollingBufferEnabled:a.rollingEnabled,recordingQuality:a.recordingQuality,timelapseIntervalSeconds:a.timelapseIntervalSeconds,includeRemoteCursor:a.includeRemoteCursor}})},n.hubUrl).then(U=>{b.current=Number(U.revision||b.current||0)}).catch(()=>{}))},[n.hubUrl,a]),u.useEffect(()=>{let Y=!0;return qe("/api/settings",void 0,n.hubUrl).then(U=>{if(!Y)return;const re=U.settings?.filesAudio||{},oe=typeof re.rollingBufferEnabled=="boolean"?re.rollingBufferEnabled:a.rollingEnabled,X=re.recordingQuality==="high"?"high":"standard",xe=[5,10,30,60].includes(Number(re.timelapseIntervalSeconds))?Number(re.timelapseIntervalSeconds):a.timelapseIntervalSeconds,fe=typeof re.includeRemoteCursor=="boolean"?re.includeRemoteCursor:a.includeRemoteCursor,z={rollingEnabled:oe,recordingQuality:X,timelapseIntervalSeconds:xe,includeRemoteCursor:fe};b.current=Number(U.revision||0),k.current=JSON.stringify(z),w.current=!0,i(z)}).catch(()=>{w.current=!0}),()=>{Y=!1}},[n.hubUrl]);const L=u.useCallback(async Y=>{if(Y.sourceCanvas&&Y.sourceCanvas.width>0&&Y.sourceCanvas.height>0)return{target:Sb(Y),canvas:Y.sourceCanvas,cleanup:()=>{}};const U=await n.sourceRegistry.composeWall(Y.deviceIds,{maxWidth:1920,maxHeight:1080,includeLabels:!0});let re=!1;const oe=window.setInterval(async()=>{if(re)return;const X=await n.sourceRegistry.composeWall(Y.deviceIds,{maxWidth:1920,maxHeight:1080,includeLabels:!0}).catch(()=>null);!X||re||((U.width!==X.width||U.height!==X.height)&&(U.width=X.width,U.height=X.height),U.getContext("2d")?.drawImage(X,0,0))},100);return{target:Sb(Y),canvas:U,cleanup:()=>{re=!0,window.clearInterval(oe)}}},[n.sourceRegistry]);u.useEffect(()=>{const Y=T.current;if(!a.rollingEnabled||!R||!V){Y.stop(),g(0);return}let U=!1,re=null;return(async()=>{try{if(re=await L(R),U)return;await Y.start(re.canvas,re.target);const X=window.setInterval(()=>g(Math.round(Y.seconds)),500),xe=()=>{window.clearInterval(X),re?.cleanup()};Y.__cleanup=xe}catch(X){U||n.onToast(X instanceof Error?X.message:String(X))}})(),()=>{U=!0,Y.stop(),Y.__cleanup?.(),Y.__cleanup=void 0,g(0)}},[L,n.onToast,a.rollingEnabled,B,V]);const K=u.useCallback(Y=>i(U=>({...U,rollingEnabled:Y})),[]),W=u.useCallback(Y=>i(U=>({...U,recordingQuality:Y})),[]),$=u.useCallback(Y=>i(U=>({...U,timelapseIntervalSeconds:Y})),[]),Z=u.useCallback(async()=>{const Y=n.sourceRegistry.resolveCurrentTarget();if(!Y)throw new Error("화면이 준비된 후 다시 시도하세요.");x(!0);let U=null;try{U=await L(Y),await $N(U.canvas,U.target,n.hubUrl),n.onToast("Screenshot saved")}finally{U?.cleanup(),x(!1)}},[L,n.hubUrl,n.onToast,n.sourceRegistry]),H=u.useCallback(async()=>{if(M.current)throw new Error("Stop the timelapse before recording.");const Y=n.sourceRegistry.resolveCurrentTarget();if(!Y)throw new Error("화면이 준비된 후 다시 시도하세요.");const U=await L(Y),re=await xw({canvas:U.canvas,target:U.target,fps:Y.type==="control"?30:15,hubUrl:n.hubUrl,onError:oe=>n.onToast(oe.message)});C.current=U.cleanup,j.current=re,c(Date.now()),n.onToast("Recording started")},[L,n.hubUrl,n.onToast,n.sourceRegistry]),pe=u.useCallback(async()=>{const Y=j.current;if(Y){x(!0);try{await Y.stop(),n.onToast("Recording saved")}finally{j.current=null,C.current?.(),C.current=null,c(0),x(!1)}}},[n.onToast]),ce=u.useCallback(async()=>{if(j.current||M.current)throw new Error("Last 30s is unavailable during a recording.");const Y=n.sourceRegistry.resolveCurrentTarget();if(!Y)throw new Error("화면이 준비된 후 다시 시도하세요.");x(!0);try{await T.current.save(Y,n.hubUrl),n.onToast("Last 30 seconds saved")}finally{x(!1)}},[n.hubUrl,n.onToast,n.sourceRegistry]),J=u.useCallback(async()=>{if(j.current)throw new Error("Stop the recording before starting a timelapse.");const Y=n.sourceRegistry.resolveCurrentTarget();if(!Y)throw new Error("화면이 준비된 후 다시 시도하세요.");const U=await L(Y),re=await qN(U.canvas,U.target,a.timelapseIntervalSeconds,n.hubUrl,oe=>n.onToast(oe.message));A.current=U.cleanup,M.current=re,m(Date.now()),n.onToast("Timelapse started")},[L,n.hubUrl,n.onToast,n.sourceRegistry,a.timelapseIntervalSeconds]),I=u.useCallback(async()=>{const Y=M.current;if(Y){x(!0);try{await Y.stop(),n.onToast("Timelapse saved")}finally{M.current=null,A.current?.(),A.current=null,m(0),x(!1)}}},[n.onToast]);return u.useEffect(()=>{const Y=U=>{!j.current&&!M.current||(U.preventDefault(),U.returnValue="")};return window.addEventListener("beforeunload",Y),()=>window.removeEventListener("beforeunload",Y)},[]),u.useEffect(()=>()=>{T.current.stop(),j.current?.cancel(),M.current?.cancel(),C.current?.(),A.current?.()},[]),{state:u.useMemo(()=>({recording:!!j.current,recordingStartedAt:l,timelapse:!!M.current,timelapseStartedAt:d,bufferSeconds:p,bufferEnabled:a.rollingEnabled,busy:v,targetLabel:F}),[p,v,l,a.rollingEnabled,F,d]),settings:a,videoSupported:V,targetKey:B,setRollingEnabled:K,setRecordingQuality:W,setTimelapseInterval:$,takeScreenshot:Z,startRecording:H,stopRecording:pe,saveLast30Seconds:ce,startTimelapse:J,stopTimelapse:I}}const WN=120,YN=999,Gc=5,Vc=30,KN=Number.POSITIVE_INFINITY,Sw="livedesk.remoteDirectory",Ph="livedesk.localHubAccessEnabled",XN="livedesk.streamSettings",QN=18,ZN="livedesk.controlStreamSettings",JN=8,Cw="livedesk.wallMonitorSelections",kw=2,uh=512*1024,Uh="".trim(),jw="".trim(),Cb="livedesk-adsense-script",dh=!!Uh&&!!jw,kb=typeof window<"u"&&new URLSearchParams(window.location.search).get("debug")==="1",eT=1e3/60,fh=1500,tT=650,nT=1800,aT=650,sT=2500,jb=1500,Ab=15e3,rT=6e4,Mb=240,iT=1024,lT=640,oT=360,Nb="Connected LiveDesk clients are updating in batches. Hub will restart after every target reconnects on the requested versions.",Id="LiveDesk is replacing the Hub and verifying the requested version.",zd=5*6e4,cT=5e3;function Ao({label:n,tooltip:a,disabled:i=!1,active:l=!1,danger:c=!1,pressed:d,onClick:m,children:p}){return r.jsx("span",{className:`manual-action-tooltip ${i?"disabled":""}`.trim(),"data-tooltip":a,children:r.jsx("button",{className:`manual-action ${l?"active":""} ${c?"danger":""}`.trim(),onClick:m,disabled:i,"aria-label":n,"aria-pressed":d,type:"button",children:p})})}function uT(n,a){if(a.deviceId!==n.deviceId)return!1;const i=Number(a.browserReceivedEpochMs);if(!Number.isFinite(i)||i<n.startedAtEpochMs)return!1;const l=Number(a.monitorIndex);if(Number.isFinite(l)&&Math.floor(l)!==n.monitorIndex)return!1;const c=String(a.frameMode||a.mode||"").trim().toLowerCase();return!c||c===n.frameMode}const Al={free:{name:"Free",price:"$0",limit:Gc,audience:"Personal trial",note:"5 devices with a standard ad banner."},ltd:{name:"Plus",price:"$79 LTD",ltd:"$79 LTD",limit:Vc,audience:"Creators and homelabs",note:"30 devices, no ads, personal use."},pro:{name:"Pro",price:"$199 LTD",ltd:"$199 LTD",limit:KN,audience:"Business and studios",note:"Commercial use and larger Client walls."}},dT={"mode3-h264-hw":30};function Tb(n,a){const i=m=>{const g=String(m||"").trim().replace(/^v/i,"").match(/^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?(?:\+[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?$/);if(!g)return null;const v=g[4]?g[4].split(".").map(x=>/^\d+$/.test(x)?x.length>1&&x.startsWith("0")?Number.NaN:Number(x):x):[];return v.some(x=>typeof x=="number"&&!Number.isFinite(x))?null:{core:[Number(g[1]),Number(g[2]),Number(g[3])],prerelease:v}},l=i(n),c=i(a);if(!l||!c)return!1;for(let m=0;m<3;m+=1){if(l.core[m]>c.core[m])return!0;if(l.core[m]<c.core[m])return!1}if(l.prerelease.length===0||c.prerelease.length===0)return l.prerelease.length===0;const d=Math.max(l.prerelease.length,c.prerelease.length);for(let m=0;m<d;m+=1){const p=l.prerelease[m],g=c.prerelease[m];if(p===void 0||g===void 0)return g===void 0;if(p!==g){if(typeof p=="number"&&typeof g=="number")return p>g;if(typeof p=="number")return!1;if(typeof g=="number"||p>g)return!0;if(p<g)return!1}}return!0}function qa(n,a){return`${n}:${a}`}const fT={frameMode:"mode3-h264-hw",h264DecodeAcceleration:"prefer-hardware",fps:dT["mode3-h264-hw"],maxWidth:1920,maxHeight:1080,atlasTileWidth:320,atlasTileHeight:180,atlasTileSizeMode:"auto",quality:75,autoStartLive:!0,showScreenStatus:!0,visibleOnlyLive:!0,adaptiveLoadShedding:!0,adaptiveSizeByWall:!0},mT={frameMode:"mode3-h264-hw",h264DecodeAcceleration:"prefer-hardware",fps:30,maxWidth:1920,maxHeight:1080,quality:75,showScreenStatus:!1};function mh(n,a){if(n.length===0)return 0;const i=[...n].sort((c,d)=>c-d),l=Math.max(0,Math.min(i.length-1,Math.ceil(i.length*a)-1));return i[l]}function Aw(n,a){n.push(a),n.length>Mb&&n.splice(0,n.length-Mb)}function Eb(n,a){n.sampleFrames=0,n.sampleStartedAt=a,n.intervalCount=0,n.intervalSumMs=0,n.minIntervalMs=Number.POSITIVE_INFINITY,n.maxIntervalMs=0,n.intervalSamplesMs.length=0,n.seqGapCount=0,n.maxSeqGap=0}function Rb(n,a,i){if(n.lastAt!==void 0){const l=Math.max(0,a-n.lastAt);n.intervalCount+=1,n.intervalSumMs+=l,n.minIntervalMs=Math.min(n.minIntervalMs,l),n.maxIntervalMs=Math.max(n.maxIntervalMs,l),Aw(n.intervalSamplesMs,l)}if(n.lastFrameSeq!==void 0){const l=i-n.lastFrameSeq;l!==1&&(n.seqGapCount+=1,n.maxSeqGap=Math.max(n.maxSeqGap,Math.abs(l)))}n.lastAt=a,n.lastFrameSeq=i}function Db(n){return!n||n.intervalCount<=0?"waiting":`${Math.round(n.averageIntervalMs)}ms avg / p95 ${Math.round(n.p95IntervalMs)}ms / max ${Math.round(n.maxIntervalMs)}ms`}function Lb(n){return!n||n.seqGapCount<=0?"none":`${n.seqGapCount} gaps / max ${n.maxSeqGap}`}function hT(n){return Number.isFinite(n)?String(n):"30+"}const Mw=[1,2,5,8,12,20,30],_b={large:{maxWidth:1280,maxHeight:720},medium:{maxWidth:960,maxHeight:540},list:{maxWidth:640,maxHeight:360}},fa={preserveConfiguredFpsDeviceCount:6,visibleRootMargin:"520px 0px 760px 0px",fallbackVisibleCounts:{large:12,medium:18,list:32},maxWallFramesPerSecond:Va.maxDirectFramesPerSecond,maxWallBytesPerSecond:80*1024*1024,maxFrameGapMs:280,lowLoadFrameRatio:.58,lowLoadByteRatio:.52,downshiftCooldownMs:1800,recoveryCooldownMs:14e3,minFps:5},Ib=2200,pT=8e3,gT=3500,vT=7e3,yT=2,bT=7e3,xT=2600,wT=4200,ST=750,CT=2,zb=4200;function Ob(n,a){return Math.min(3e4,n*2**Math.min(3,Math.max(0,a)))}function eu(n,a){const i=n.capabilities?.[a];return typeof i=="boolean"?i:/^(1|true|yes|on)$/i.test(String(i||"").trim())}function Ga(n){return n.deviceName||n.hostname||n.deviceId}function kT(){return"npx -y --prefer-online livedesk@latest client"}function jT(){return"npx -y --prefer-online livedesk@latest"}function AT(){try{return localStorage.getItem(Sw)||""}catch{return""}}function MT(){try{return localStorage.getItem(Ph)==="1"}catch{return!1}}function Mo(n){try{n?localStorage.setItem(Ph,"1"):localStorage.removeItem(Ph)}catch{}}function gr(n){return n&&typeof n=="object"&&!Array.isArray(n)?n:{}}function Jn(n){const a=gr(n.status),i=gr(a.screens),l=gr(n.capabilities),c=gr(n.latestLiveFrame),d=gr(n.activeLiveStream),p=[a.monitorCount,a.screenCount,i.count,l.monitorCount,l.screenCount,c.monitorCount,d.monitorCount,Array.isArray(i.displays)?i.displays.length:0,Array.isArray(a.displays)?a.displays.length:0,Array.isArray(a.monitors)?a.monitors.length:0].map(Number).filter(g=>Number.isFinite(g)&&g>0).map(g=>Math.max(1,Math.min(64,Math.floor(g))));return p.length>0?Math.max(...p):1}function Pi(n){const a=Number(n);return Number.isFinite(a)&&a>0?Math.floor(a):0}function Nw(n){const a=gr(n.status),l=[gr(a.screens).displays,a.displays,a.monitors];for(const c of l)if(!(!Array.isArray(c)||c.length===0))return c.map(d=>gr(d));return[]}function Tw(n,a){const i=Nw(n),l=i.find(g=>Number(g.index)===a)||i[a]||i[0]||{},c=Pi(l.width),d=Pi(l.height),m=[l.name,l.displayName,l.deviceName,l.id].find(g=>typeof g=="string"&&g.trim()),p=typeof m=="string"?m.trim():`Display ${a+1}`;return{index:a,name:p,width:c,height:d,primary:l.primary===!0||l.isPrimary===!0}}function NT(n){const a=Jn(n);return Array.from({length:a},(i,l)=>Tw(n,l))}function TT(n){const a=Nw(n);if(a.length===0)return"";const i=a.map((l,c)=>{const d=Number(l.index),m=[l.name,l.displayName,l.deviceName,l.id].find(g=>typeof g=="string"&&g.trim()),p=g=>{const v=Number(g);return Number.isFinite(v)?v:null};return[Number.isFinite(d)?Math.floor(d):c,typeof m=="string"?m.trim().toLowerCase():"",p(l.left),p(l.top),p(l.width),p(l.height),l.primary===!0||l.isPrimary===!0]}).sort((l,c)=>l[0]-c[0]);return JSON.stringify(i)}function ET(n){const a=[n.name||`Display ${n.index+1}`];return n.width>0&&n.height>0&&a.push(`${n.width}x${n.height}`),n.primary&&a.push("Primary"),a.join(" - ")}function Ew(n,a){const i=Tw(n,a);if(i.width>0&&i.height>0)return{width:i.width,height:i.height};const l=gr(n.status),c=Pi(l.screenWidth||l.width),d=Pi(l.screenHeight||l.height);return c>0&&d>0?{width:c,height:d}:null}function Rw(n){if(!n)return null;const a=Pi(n.sourceWidth)||Pi(n.width),i=Pi(n.sourceHeight)||Pi(n.height);return a>0&&i>0?{width:a,height:i}:null}function RT(n,a,i){const d=((typeof a?.monitorIndex=="number"?ea(a.monitorIndex,Math.max(1,a.monitorCount||1)):i)===i?Rw(a):null)||Ew(n,i);return d?Math.max(.25,Math.min(4,d.width/d.height)):16/9}const Od=16/9;function ea(n,a){const i=Math.max(1,Math.min(64,Math.floor(Number(a)||1))),l=Math.floor(Number(n));return Number.isFinite(l)?Math.max(0,Math.min(i-1,l)):0}function DT(){try{const n=localStorage.getItem(Cw);if(!n)return{selections:{},topologyFingerprints:{}};const a=JSON.parse(n),i=a&&typeof a=="object"&&!Array.isArray(a)?a.selections:void 0;if(Number(a?.version)!==kw||!i||typeof i!="object"||Array.isArray(i))return{selections:{},topologyFingerprints:{}};const l={},c={};for(const[d,m]of Object.entries(i)){const p=d.trim(),g=gr(m),v=g.monitorIndex,x=Math.floor(Number(v));if(!p||!Number.isFinite(x))continue;l[p]=Math.max(0,Math.min(63,x));const b=typeof g.topologyFingerprint=="string"?g.topologyFingerprint.trim():"";b&&(c[p]=b)}return{selections:l,topologyFingerprints:c}}catch{return{selections:{},topologyFingerprints:{}}}}function LT(n,a){try{const i=Object.fromEntries(Object.entries(n).map(([l,c])=>[l,{monitorIndex:c,topologyFingerprint:a[l]||""}]));localStorage.setItem(Cw,JSON.stringify({version:kw,selections:i}))}catch{}}function _T(){return{...fT}}function IT(n){try{localStorage.setItem(XN,JSON.stringify({...n,version:QN}))}catch{}}function zT(){return{...mT}}function OT(n){try{localStorage.setItem(ZN,JSON.stringify({...n,version:JN}))}catch{}}function hh(n,a){return n.length!==a.length?!1:n.every((i,l)=>i===a[l])}function ph(n){return[...new Set(n.filter(Boolean))].sort()}function HT(n){return fa.fallbackVisibleCounts[n]||fa.fallbackVisibleCounts.large}function FT(n,a,i){if(n.frameMode==="mode4-h264-atlas")return n.atlasTileSizeMode==="auto"?a==="large"?{maxWidth:640,maxHeight:360}:{maxWidth:320,maxHeight:180}:{maxWidth:n.atlasTileWidth,maxHeight:n.atlasTileHeight};if(!n.adaptiveSizeByWall)return{maxWidth:n.maxWidth,maxHeight:n.maxHeight};const l=_b[a]||_b.large;return{maxWidth:Math.min(n.maxWidth,l.maxWidth),maxHeight:Math.min(n.maxHeight,l.maxHeight)}}function BT(n,a=fa.minFps){const i=[...Mw].sort((c,d)=>c-d),l=Math.max(a,Math.floor(n/2));return[...i].reverse().find(c=>c<n&&c>=a)||l}function $T(n,a){return[...Mw].sort((l,c)=>l-c).find(l=>l>n&&l<=a)||a}function Wc(n){if(!Number.isFinite(n)||n<=0)return"0 B";const a=["B","KB","MB","GB","TB"];let i=n,l=0;for(;i>=1024&&l<a.length-1;)i/=1024,l+=1;const c=l===0||i>=100?0:1;return`${i.toFixed(c)} ${a[l]}`}function Hb(n){if(!Number.isFinite(n)||!n||n<=0)return"0GB";const a=1024**4,i=1024**3;return n>=a?`${Math.max(1,Math.round(n/a))}TB`:`${Math.max(1,Math.round(n/i))}GB`}function gh(n,a){if(!Number.isFinite(n)||!n||n<=0)return"not reported";const i=Math.max(0,n-Math.max(0,Number(a||0)));return`${Hb(i)} / ${Hb(n)}`}function PT(n){const a=new Uint8Array(n),i=32768;let l="";for(let c=0;c<a.length;c+=i){const d=a.subarray(c,c+i);l+=String.fromCharCode(...d)}return btoa(l)}function Fb(n){return Array.from(n).filter(a=>a.size>=0).map(a=>{const i=a.webkitRelativePath||a.name;return{file:a,relativePath:i,fingerprint:`${a.size}:${a.lastModified}`}})}function UT(n){return n===1?"middle":n===2?"right":"left"}function Bb(n,...a){for(const i of a){const l=n?.[i];if(typeof l=="string"&&l.trim())return l.trim()}return""}function $b(n){const a=Number(n);return Number.isInteger(a)&&a>=1&&a<=YN?a:0}const qT={cpu:"CPU",memory:"MEM",disk:"Disk",gpu:"GPU",vram:"VRAM",npu:"NPU",temp:"Temp",network:"Net"};function GT(n,a,i){if(n==="online")return"Online";if(n==="busy")return"Busy";if(n==="stale")return"Stale";if(n==="offline")return"Offline";if(i==="failed")return"Task failed";const l=n==="critical"?"critical":"warn",c=a.find(m=>m.state===l)||a.find(m=>m.state==="critical"||m.state==="warn");if(!c)return n==="critical"?"Issue":"Check";const d=qT[c.id]||c.label;return c.value&&c.value!=="-"?`${d} ${c.value}`:d}function df(n,a){const i=a.split(".");let l=n;for(const c of i){if(l==null)return;if(Array.isArray(l)){const d=Number(c);l=Number.isInteger(d)?l[d]:void 0;continue}if(typeof l=="object"){l=l[c];continue}return}return l}function on(n,...a){for(const i of a){const l=df(n,i),c=Number(l);if(Number.isFinite(c))return c}}function us(n,...a){for(const i of a){const l=df(n,i);if(typeof l=="string"&&l.trim())return l.trim();if(typeof l=="number"&&Number.isFinite(l))return String(l)}return""}function VT(n,...a){for(const i of a){const l=df(n,i);if(typeof l=="boolean")return l;if(/^(1|true|yes|on|online|running|ready)$/i.test(String(l||"").trim()))return!0;if(/^(0|false|no|off|offline|stopped|missing)$/i.test(String(l||"").trim()))return!1}}function Jr(n){const a=Number(n);if(Number.isFinite(a))return Math.max(0,Math.min(1,a>1?a/100:a))}function vh(n,a){if(!(typeof n!="number"||typeof a!="number"||!Number.isFinite(n)||!Number.isFinite(a)||n<=0))return Jr((n-Math.max(0,a))/n)}function No(n){return n===void 0?"-":`${Math.round(n*100)}%`}function yh(n){return n===void 0||!Number.isFinite(n)?"":n>=1e3?`${(n/1e3).toFixed(n>=1e4?0:1)}Gb/s`:`${Math.round(n)}Mb/s`}function Ml(n,a=.92,i=.78){return n===void 0?"unknown":n>=a?"critical":n>=i?"warn":"good"}function WT(n){return!Number.isFinite(n)||!n||n<=0?"-":n<3600?`${Math.round(n/60)}m`:n<86400?`${Math.round(n/3600)}h`:`${Math.round(n/86400)}d`}function Hd(n,a,i){const l=df(n,`tools.${a}`),c=us(n,`tools.${a}.status`,`tooling.${a}.status`,`${a}.status`).toLowerCase(),d=VT(n,`tools.${a}.running`,`tooling.${a}.running`,`${a}.running`),m=us(n,`tools.${a}.version`,`tooling.${a}.version`,`${a}.version`),p=us(n,`tools.${a}.port`,`tooling.${a}.port`,`${a}.port`);let g="unknown";return c==="busy"?g="busy":["online","running","ready","active"].includes(c)||d===!0?g="online":(["offline","stopped","missing","not-found"].includes(c)||d===!1||l===!1)&&(g="offline"),{id:a,label:i,status:g,detail:g==="offline"?"offline":m||(p?`:${p}`:g==="unknown"?"not reported":g)}}function Dw(n){const a=n.status||{},i=on(a,"cpu.cores","cpuCores","processorCount"),l=on(a,"loadavg.0","cpu.load1"),c=Jr(on(a,"cpu.usedRatio","cpu.usageRatio","cpu.ratio","cpu.usedPct","cpu.usagePct","cpu.percent")??(i&&l!==void 0?l/Math.max(1,i):void 0)),d=on(a,"memory.totalBytes","totalMem"),m=on(a,"memory.freeBytes","freeMem"),p=Jr(on(a,"memory.usedRatio","memory.usedPct","usedMemRatio"))??vh(d,m),g=on(a,"disk.totalBytes","rootDisk.totalBytes","storage.totalBytes"),v=on(a,"disk.freeBytes","rootDisk.freeBytes","storage.freeBytes"),x=Jr(on(a,"disk.usedRatio","disk.usedPct","rootDisk.usedRatio","storage.usedRatio"))??vh(g,v),b=on(a,"thermal.cpuC","temperatureC","cpu.temperatureC","gpu.temperatureC"),w=Jr(on(a,"gpu.usedRatio","gpu.usageRatio","gpu.usedPct","gpu.usagePct","gpu.percent","accelerators.gpu.usageRatio")),k=us(a,"gpu.name","gpu.model","accelerators.gpu.name"),j=on(a,"gpu.memory.totalBytes","gpu.totalMemoryBytes","gpu.vramTotalBytes"),C=on(a,"gpu.memory.freeBytes","gpu.freeMemoryBytes","gpu.vramFreeBytes"),M=Jr(on(a,"gpu.memory.usedRatio","gpu.vramUsedRatio"))??vh(j,C),A=on(a,"gpu.temperatureC","thermal.gpuC"),T=Jr(on(a,"npu.usedRatio","npu.usageRatio","npu.usedPct","npu.usagePct","npu.percent","accelerators.npu.usageRatio")),R=us(a,"npu.name","npu.model","accelerators.npu.name"),B=on(a,"npu.tops","npu.topsInt8","accelerators.npu.tops"),F=on(a,"network.rxMbps","network.receiveMbps","network.downMbps"),V=on(a,"network.txMbps","network.transmitMbps","network.upMbps"),L=on(a,"network.latencyMs","network.rttMs"),K=Jr(on(a,"network.usedRatio","network.usageRatio","network.saturationRatio")),W=us(a,"network.link","network.interface","network.name"),$=Math.max(b??Number.NEGATIVE_INFINITY,A??Number.NEGATIVE_INFINITY),Z=Number.isFinite($)?$:void 0,H=Z===void 0?void 0:Jr((Z-35)/60),pe=b!==void 0&&A!==void 0?`CPU ${Math.round(b)}° · GPU ${Math.round(A)}°`:b!==void 0?`CPU ${Math.round(b)}°`:A!==void 0?`GPU ${Math.round(A)}°`:"-",ce=us(a,"workload.role","role","lane")||(eu(n,"aiAssist")?"Agent worker":eu(n,"taskDispatch")?"Task worker":"Local machine"),J=us(n.latestTask,"status")||us(a,"workload.status")||"idle",I=us(n.latestTask,"title","instructionPreview")||us(a,"workload.title"),ne=us(n.latestTask,"resultSummary","error","instructionPreview")||us(a,"workload.summary"),Y=[Hd(a,"ollama","Ollama"),Hd(a,"lmStudio","LM Studio"),Hd(a,"comfyui","ComfyUI"),Hd(a,"stableDiffusion","Stable Diffusion")],U=Y.some(z=>z.status!=="unknown"),re=Date.parse(n.lastSeenAt||n.connectedAt||""),oe=Number.isFinite(re)?Date.now()-re:0;let X="online";n.connected?oe>5*6e4?X="stale":(b??0)>=86||(A??0)>=88||(c??0)>=.94||(w??0)>=.96||(M??0)>=.96||(T??0)>=.96||(p??0)>=.94||(x??0)>=.94||(L??0)>=200||J==="failed"?X="critical":(b??0)>=78||(A??0)>=80||(c??0)>=.82||(w??0)>=.86||(M??0)>=.88||(T??0)>=.86||(p??0)>=.82||(x??0)>=.86||(L??0)>=80?X="warn":(["queued","running","targeted"].includes(J)||Y.some(z=>z.status==="busy"))&&(X="busy"):X="offline";const xe=Y.filter(z=>z.status==="online"||z.status==="busy").length,fe=[{id:"cpu",label:"CPU",value:No(c),detail:i?`${i} cores`:l!==void 0?`load ${l.toFixed(1)}`:"not reported",ratio:c,state:Ml(c,.92,.78)},{id:"memory",label:"RAM",value:No(p),detail:gh(d,m),ratio:p,state:Ml(p,.92,.78)},{id:"disk",label:"DISK",value:No(x),detail:gh(g,v),ratio:x,state:Ml(x,.92,.82)},{id:"gpu",label:"GPU",value:No(w),detail:k||(j&&C!==void 0?`${Wc(C)} VRAM free`:A!==void 0?`${Math.round(A)}C`:"not reported"),ratio:w,state:A!==void 0&&A>=88?"critical":A!==void 0&&A>=80?"warn":Ml(w,.96,.86)},{id:"vram",label:"VRAM",value:No(M),detail:gh(j,C),ratio:M,state:Ml(M,.96,.88)},{id:"npu",label:"NPU",value:T!==void 0?No(T):B!==void 0?`${Math.round(B)} TOPS`:"-",detail:R||(B!==void 0?"neural engine":"not reported"),ratio:T,state:Ml(T,.96,.86)},{id:"temp",label:"TEMP",value:pe,detail:xe>0?`${xe} local tools`:`uptime ${WT(on(a,"uptimeSec"))}`,ratio:H,state:Z===void 0?"unknown":Z>=86?"critical":Z>=78?"warn":"good"},{id:"network",label:"NET",value:yh(F)||(L!==void 0?`${Math.round(L)}ms`:W||"-"),detail:yh(V)?`${yh(V)} up`:W||"not reported",ratio:K,state:L!==void 0&&L>=200?"critical":L!==void 0&&L>=80?"warn":Ml(K,.9,.75)}];return{state:X,label:GT(X,fe,J),role:ce,taskLabel:I||(J==="idle"?"idle":J),taskStatus:J,taskSummary:ne,tools:U?Y:Y.map(z=>({...z,detail:"not reported"})),metrics:fe}}const YT=[{id:"list",label:"List"},{id:"medium",label:"Small"},{id:"large",label:"Large"}],KT=[{id:"sync-server",label:"Hub",icon:yr,accent:"blue",tooltip:"hub"},{id:"wall",label:"Wall",icon:b0,accent:"blue",tooltip:"wall"},{id:"control",label:"Control",icon:Ch,accent:"red",tooltip:"control"},{id:"share-files",label:"Share Files",icon:Fd,accent:"green",tooltip:"share-files"},{id:"agents",label:"Agents",icon:kh,accent:"violet",tooltip:"agents"}],XT=[{id:"doc",label:"Doc",icon:Gd,accent:"amber"},{id:"support",label:"Support",icon:Yc,accent:"violet"},{id:"pricing",label:"Pricing",icon:Kc,accent:"green"}];function QT(n){return n.replaceAll("npx -y @livedesk/client","npx -y --prefer-online livedesk@latest client").replaceAll("npx @livedesk/client","npx -y --prefer-online livedesk@latest client").replaceAll("npx -y livedesk client","npx -y --prefer-online livedesk@latest client").replaceAll("npx livedesk client","npx -y --prefer-online livedesk@latest client").replaceAll("npx -y livedesk","npx -y --prefer-online livedesk@latest").replaceAll("npx livedesk","npx -y --prefer-online livedesk@latest")}function nn(n){return QT(n).replaceAll("Sync Server","Hub").replace(/\bRemoteHub\b/g,"Hub client endpoint").replace(/\bManagers\b/g,"Hubs").replace(/\bManager\b/g,"Hub").replace(/\bmanager\b/g,"Hub").replace(/\bMANAGER\b/g,"HUB").replace(/매니저/g,"Hub")}function Nl(n){return n.map(a=>nn(a))}function ua(n){return{languageName:n.languageName,languageLabel:n.languageLabel,eyebrow:n.eyebrow,title:nn(n.title),intro:nn(n.intro),sections:[{title:nn(n.managerTitle),body:nn(n.managerBody),points:Nl(n.managerPoints)},{title:nn(n.clientTitle),body:nn(n.clientBody),points:Nl(n.clientPoints)},{title:nn(n.syncTitle),body:nn(n.syncBody),points:Nl(n.syncPoints)},...n.agentTitle&&n.agentBody&&n.agentPoints?[{title:nn(n.agentTitle),body:nn(n.agentBody),points:Nl(n.agentPoints)}]:[],{title:nn(n.platformTitle),body:nn(n.platformBody),points:Nl(n.platformPoints)}],sync:{title:nn(n.syncTitle),body:nn(n.syncBody),points:Nl(n.syncPoints)},firewall:{title:nn(n.firewallTitle),body:nn(n.firewallBody),points:Nl(n.firewallPoints)},commands:{title:nn(n.commandsTitle),body:nn(n.commandsBody),manager:nn(n.managerCommandLabel),primary:nn(n.clientCommandLabel),slot:nn(n.slotCommandLabel),manual:nn(n.manualCommandLabel)}}}const Lo={ko:ua({languageName:"한국어",languageLabel:"문서 언어",eyebrow:"LiveDesk Manual",title:"한 줄로 여는 로컬 Screen Wall",intro:"LiveDesk는 매니저 PC 한 대에서 여러 작업 PC, AI 박스, 맥미니, 개발 장비를 한 화면에 모니터링하고 필요한 제어를 실행하는 npm-first 원격 작업 데크입니다.",managerTitle:"1. 매니저 실행",managerBody:"화면을 모아 볼 컴퓨터에서 LiveDesk 매니저를 실행합니다. 매니저는 UI와 로컬 서버를 같이 열고 브라우저를 http://127.0.0.1:5179 로 띄웁니다.",managerPoints:["명령은 npx -y livedesk 입니다.","매니저는 Screen Wall/API 포트 5179와 클라이언트 접속 포트 5197을 사용합니다.","기존 LiveDesk 포트가 남아 있으면 기본적으로 정리하고 새로 시작합니다. 필요하면 --no-clean 을 붙입니다."],clientTitle:"2. 워크스테이션 연결",clientBody:"모니터링할 PC에서는 Google 로그인 또는 Hub 화면의 6자리 PIN으로 연결합니다. Hub가 꺼져 있으면 실시간 온라인 알림을 기다리고 적응형 간격으로 다시 확인합니다.",clientPoints:["빈 자리에 자동 배치하려면 npx -y livedesk client 를 실행합니다.","자리 번호를 고정하려면 npx -y livedesk client 3 처럼 번호를 붙입니다.","브라우저 연결 페이지에서 Google 버튼 또는 6자리 PIN 입력 중 하나를 선택합니다."],syncTitle:"3. Sync Server와 자동 발견",syncBody:"Sync Server는 이 매니저를 현재 계정의 활성 Screen Wall로 Supabase에 등록합니다. 클라이언트는 같은 Google 계정으로 로그인한 뒤 그 기록을 읽고 접속 후보들을 순서대로 시도합니다.",syncPoints:["등록 정보에는 host instance, LAN/private 주소, public 후보 주소, pair token, 짧은 lease가 포함됩니다.","같은 LAN 후보를 먼저 쓰고, 필요하면 외부 주소 후보를 시도합니다.","매니저 위치, VPN, 방화벽, 공유기 설정이 바뀌었거나 개발 중 활성 매니저를 바꿀 때 Sync Server를 누릅니다.","이제 livedesk.pages.dev의 Chrome Local Network Access 허용은 기본 실행 경로가 아닙니다. npm 매니저 앱이 실제 서버입니다."],agentTitle:"4. Agent로 여러 장비 작업하기",agentBody:"Agents 화면은 Hub에서 실행되는 Codex 기반 Agent가 연결된 장비를 확인하고, 선택한 권한 범위 안에서 작업을 실행하는 공간입니다. 클라이언트 PC에 Codex 앱이나 Codex SDK를 설치할 필요는 없습니다.",agentPoints:["Suggested tasks에는 현재 72개의 읽기·운영 작업 템플릿이 있으며, 검색과 권한·플랫폼·capability 조건으로 실행 가능 항목을 안내합니다.","Ask, Safe Auto, Full Access, Custom 권한 모드를 선택할 수 있습니다. 위험한 변경 작업은 승인 카드와 장비 capability 검사를 거칩니다.","All connected 또는 Selected로 대상 장비를 제한하고, 결과는 장비별 상태 칩과 실제 RAM·디스크·GPU·프로세스 요약으로 확인합니다.","Agent 실행은 Hub의 격리된 runtime과 LiveDesk MCP 도구를 통해서만 장비에 전달되며, 모든 작업은 timeout·취소·tool limit·감사 로그 경계를 가집니다."],platformTitle:"5. 현재 플랫폼과 스트림 상태",platformBody:"Windows의 Wall, Control, 파일 전송, 원격 오디오와 RemoteFast 경로를 현재 기준으로 확인했습니다. macOS와 Linux는 같은 npm 명령 경로를 유지하면서 권한과 캡처 백엔드를 확장하고 있습니다.",platformPoints:["자동 Wall과 Control은 모든 Client에서 직접 H.264를 사용합니다. RGB565/LZO Atlas는 내부 진단 실험에만 남고 자동 전송 경로에는 사용되지 않습니다.","원격 오디오는 선택한 장비가 capability를 광고할 때 사용할 수 있으며, 브라우저는 queue·drop·resync·automatic restart 상태를 진단합니다.","macOS: 화면 캡처와 제어에는 Screen Recording/Accessibility 권한이 필요합니다. RemoteFast 로그는 필요한 상태 중심으로 제한됩니다.","Linux: linux-x64 runtime, X11/XTEST 입력과 PipeWire/PulseAudio 기본 출력 오디오 캡처를 지원합니다."],firewallTitle:"방화벽과 외부 장소 연결",firewallBody:"같은 LAN 안에서는 보통 공유기 포트포워딩이 필요 없습니다. 다른 장소의 PC를 붙이려면 클라이언트가 매니저의 클라이언트 접속 포트에 도달할 수 있어야 합니다.",firewallPoints:["클라이언트 접속 포트는 TCP 5197입니다.","클라이언트 쪽은 나가는 연결이므로 보통 인바운드 포트 개방이 필요 없습니다.","다른 네트워크에서 붙일 때는 매니저 PC 방화벽에서 TCP 5197 인바운드를 허용하고, 공유기에서는 TCP 5197을 매니저 PC로 전달합니다.","Screen Wall/API 포트 5179는 매니저 UI용입니다. 외부 브라우저에서 Wall을 직접 열 필요가 있을 때만 별도로 노출을 검토합니다.","공개 인터넷에 바로 열기보다 VPN, Tailscale, ZeroTier, 고정 IP allowlist를 우선합니다."],commandsTitle:"현재 사용 명령",commandsBody:"웹사이트를 찾아가 설치 파일을 받는 방식보다 터미널 한 줄로 모든 운영체제에서 거의 같은 흐름을 쓰는 것이 LiveDesk의 기본 방향입니다.",managerCommandLabel:"매니저 시작",clientCommandLabel:"워크스테이션 연결",slotCommandLabel:"3번 슬롯에 고정",manualCommandLabel:"고급 수동 연결"}),en:ua({languageName:"English",languageLabel:"Document language",eyebrow:"LiveDesk Manual",title:"Open a local Screen Wall with one command",intro:"LiveDesk is an npm-first remote work deck for monitoring many Clients, AI boxes, Mac minis, and dev machines from one manager PC.",managerTitle:"1. Run the manager",managerBody:"Run LiveDesk on the computer that will watch the screens. The manager starts the UI and local server together, then opens http://127.0.0.1:5179.",managerPoints:["The command is npx -y livedesk.","The manager uses port 5179 for the Screen Wall/API and port 5197 for client connections.","If an old LiveDesk process still owns those ports, startup cleans it by default. Add --no-clean when you want to keep it."],clientTitle:"2. Connect a Client",clientBody:"On each monitored computer, connect with Google sign-in or the 6-digit PIN shown on the Hub screen. If no manager is online yet, the client waits for a live online signal and falls back to adaptive retries.",clientPoints:["Run npx -y livedesk client for first available placement.","Run npx -y livedesk client 3 to pin the machine to slot 3.","The browser connection page lets you choose Google or a 6-digit PIN."],syncTitle:"3. Sync Server and discovery",syncBody:"Sync Server publishes this manager as the active Screen Wall for the signed-in account. Clients read that Supabase record after Google sign-in and try the endpoint candidates in order.",syncPoints:["The record includes host instance, LAN/private addresses, public endpoint candidates, pair token, and a short lease.","LAN candidates are preferred first, then public candidates if needed.","Press Sync Server after manager moves, VPN/firewall/router changes, or when this development machine should take over as active manager.","Chrome Local Network Access on livedesk.pages.dev is no longer the default runtime path. The npm manager app is the real server."],agentTitle:"4. Run multi-Client tasks with Agent",agentBody:"The Agents page runs a Codex-based Agent in the Hub to inspect connected Clients and perform actions within the selected permission scope. Client PCs do not need the Codex app or Codex SDK installed.",agentPoints:["Suggested tasks currently contains 72 read-only and operational templates with search plus permission, platform, and capability availability checks.","Choose Ask, Safe Auto, Full Access, or Custom. Risky changes go through approval cards and explicit client capability checks.","Limit targets with All connected or Selected. Results use compact per-device status chips and show real RAM, disk, GPU, and process summaries.","Runs stay inside the Hub-isolated runtime and LiveDesk MCP tools, with timeout, cancellation, tool-call limits, and audit-log boundaries."],platformTitle:"5. Current platform and stream state",platformBody:"Windows Wall, Control, file transfer, remote audio, and the RemoteFast path are verified against the current implementation. macOS and Linux keep the same npm command path while capture and permission backends expand.",platformPoints:["Automatic Wall and Control use direct H.264 for every Client. The RGB565/LZO Atlas remains only for internal diagnostic experiments and is never selected automatically.","Remote audio is available when the selected Client advertises the capability, with queue, drop, resync, and automatic-restart diagnostics in the browser.","macOS requires Screen Recording and Accessibility permissions for capture and control. RemoteFast logs focus on actionable state.","Linux includes the linux-x64 runtime, X11/XTEST input, and default-output audio capture through PipeWire or PulseAudio."],firewallTitle:"Firewall and remote locations",firewallBody:"On the same LAN, router forwarding is usually unnecessary. For another office, building, or country, clients must be able to reach the manager client port.",firewallPoints:["Client connection port: TCP 5197.","Client machines normally only need outbound access.","For remote networks, allow inbound TCP 5197 on the manager firewall and forward TCP 5197 from the router to the manager PC.","Screen Wall/API port 5179 is for the manager UI. Expose it only if a remote browser must open the Wall directly.","Prefer VPN, Tailscale, ZeroTier, or fixed-IP allowlists over public internet exposure."],commandsTitle:"Current commands",commandsBody:"The product direction is one terminal command across operating systems instead of finding a website, downloading, and installing manually.",managerCommandLabel:"Start manager",clientCommandLabel:"Connect Client",slotCommandLabel:"Pin to slot 3",manualCommandLabel:"Advanced manual connection"}),ja:ua({languageName:"日本語",languageLabel:"ドキュメント言語",eyebrow:"LiveDesk Manual",title:"1行でローカル Screen Wall を開く",intro:"LiveDesk は、複数の作業 PC、AI マシン、Mac mini、開発機を1台の manager PC から監視する npm-first のリモート作業デッキです。",managerTitle:"1. manager を実行",managerBody:"画面を集めて見るコンピューターで LiveDesk manager を実行します。UI とローカルサーバーが同時に起動し、http://127.0.0.1:5179 が開きます。",managerPoints:["コマンドは npx -y livedesk です。","Screen Wall/API は 5179、client 接続は 5197 を使います。","古い LiveDesk プロセスがポートを使っている場合は、基本的に自動で整理して起動します。"],clientTitle:"2. Client を接続",clientBody:"監視される PC で同じ Google アカウントにログインします。manager がまだ無い場合、client はオンライン通知を待ち、適応型の間隔で再確認します。",clientPoints:["空きスロットに入れるには npx -y livedesk client を実行します。","スロットを固定するには npx -y livedesk client 3 のように番号を付けます。","大量の PC でも再ログインなしで待ち続けます。"],syncTitle:"3. Sync Server と自動検出",syncBody:"Sync Server はこの manager を現在の Google アカウントの active Screen Wall として Supabase に登録します。",syncPoints:["host instance、LAN/private アドレス、public 候補、pair token、短い lease を登録します。","client は LAN 候補を優先し、必要なら public 候補も試します。","VPN、firewall、router、manager の場所が変わったら Sync Server を押します。","livedesk.pages.dev の Chrome Local Network Access は標準実行経路ではありません。npm manager アプリが実際のサーバーです。"],platformTitle:"4. 現在のプラットフォーム状態",platformBody:"Windows は live screen transfer まで確認済みです。macOS と Linux も同じ npm コマンド経路を目標に対応を広げています。",platformPoints:["Windows: 動作確認済み。","macOS: 画面取得と制御には Screen Recording/Accessibility 権限が必要です。","Linux: linux-x64 runtime、grim/gnome-screenshot/import/scrot capture、X11/XTEST input backend を追加済み。audio は未対応です。"],firewallTitle:"Firewall と外部拠点",firewallBody:"同じ LAN なら通常 router forwarding は不要です。外部拠点では client が manager の接続ポートへ到達できる必要があります。",firewallPoints:["client 接続ポートは TCP 5197 です。","client 側は通常 outbound だけで足ります。","外部 network では manager PC の firewall で TCP 5197 inbound を許可し、router から manager PC へ forward します。","5179 は manager UI 用なので、外部ブラウザで Wall を開く必要がある時だけ公開を検討します。","公開 internet より VPN、Tailscale、ZeroTier、IP allowlist を優先してください。"],commandsTitle:"現在のコマンド",commandsBody:"Web サイトから installer を探すより、OS をまたいで terminal 1行で動かす方向です。",managerCommandLabel:"manager 起動",clientCommandLabel:"Client 接続",slotCommandLabel:"slot 3 に固定",manualCommandLabel:"高度な手動接続"}),"zh-CN":ua({languageName:"简体中文",languageLabel:"文档语言",eyebrow:"LiveDesk Manual",title:"用一条命令打开本地 Screen Wall",intro:"LiveDesk 是 npm-first 的远程工作台,用一台 manager PC 监控多台工作站、AI 主机、Mac mini 和开发机器。",managerTitle:"1. 运行 manager",managerBody:"在负责查看屏幕的电脑上运行 LiveDesk manager。它会同时启动 UI 和本地服务器,并打开 http://127.0.0.1:5179。",managerPoints:["命令是 npx -y livedesk。","Screen Wall/API 使用 5179,client 连接使用 5197。","如果旧的 LiveDesk 进程占用端口,默认会清理后重新启动。"],clientTitle:"2. 连接 Client",clientBody:"在被监控的电脑上使用同一个 Google 账号登录。如果 manager 尚未在线,client 会等待上线通知,并以自适应间隔重新检查。",clientPoints:["自动放入空位: npx -y livedesk client。","固定到 3 号位置: npx -y livedesk client 3。","大量电脑也可以持续等待,不需要反复登录。"],syncTitle:"3. Sync Server 和自动发现",syncBody:"Sync Server 会把当前 manager 作为该账号的 active Screen Wall 写入 Supabase。client 登录后读取记录并依次尝试连接候选地址。",syncPoints:["记录包括 host instance、LAN/private 地址、public 候选、pair token 和短 lease。","优先尝试同一 LAN 地址,需要时再尝试外部地址。","manager 位置、VPN、防火墙或路由变化后请点击 Sync Server。","livedesk.pages.dev 的 Chrome Local Network Access 不再是默认运行方式。npm manager app 才是真正服务器。"],platformTitle:"4. 当前平台状态",platformBody:"Windows 已确认可传输实时画面。macOS 和 Linux 正按同一 npm 命令路径扩展支持。",platformPoints:["Windows: 已确认可用。","macOS: 画面捕获和控制需要 Screen Recording/Accessibility 权限。","Linux: 已加入 linux-x64 runtime、grim/gnome-screenshot/import/scrot 捕获、X11/XTEST 输入后端。音频尚未启用。"],firewallTitle:"防火墙和外部地点",firewallBody:"同一 LAN 通常不需要路由端口转发。不同地点连接时,client 必须能访问 manager 的 client 连接端口。",firewallPoints:["client 连接端口是 TCP 5197。","client 机器通常只需要出站访问。","跨网络时,在 manager 防火墙放行 TCP 5197 入站,并在路由器转发到 manager PC。","5179 是 manager UI 端口,仅在需要远程浏览器直接打开 Wall 时考虑暴露。","优先使用 VPN、Tailscale、ZeroTier 或固定 IP allowlist。"],commandsTitle:"当前命令",commandsBody:"产品方向是跨操作系统使用一条终端命令,而不是先找网站、下载、安装。",managerCommandLabel:"启动 manager",clientCommandLabel:"连接 Client",slotCommandLabel:"固定到 slot 3",manualCommandLabel:"高级手动连接"}),"zh-TW":ua({languageName:"繁體中文",languageLabel:"文件語言",eyebrow:"LiveDesk Manual",title:"用一行指令開啟本機 Screen Wall",intro:"LiveDesk 是 npm-first 的遠端工作台,用一台 manager PC 監看多台工作站、AI 主機、Mac mini 與開發機。",managerTitle:"1. 執行 manager",managerBody:"在負責看畫面的電腦上執行 LiveDesk manager。它會同時啟動 UI 與本機伺服器,並開啟 http://127.0.0.1:5179。",managerPoints:["指令是 npx -y livedesk。","Screen Wall/API 使用 5179,client 連線使用 5197。","若舊的 LiveDesk 程序占用連接埠,預設會清理後重新啟動。"],clientTitle:"2. 連接 Client",clientBody:"在被監看的電腦上使用同一個 Google 帳號登入。如果 manager 尚未上線,client 會等待上線通知,並以自適應間隔重新檢查。",clientPoints:["自動放入空位: npx -y livedesk client。","固定到 3 號位置: npx -y livedesk client 3。","大量電腦也能持續等待,不需要反覆登入。"],syncTitle:"3. Sync Server 與自動探索",syncBody:"Sync Server 會把目前 manager 登錄為此帳號的 active Screen Wall。client 登入後讀取 Supabase 記錄並依序嘗試連線候選地址。",syncPoints:["記錄包含 host instance、LAN/private 位址、public 候選、pair token 與短 lease。","優先嘗試 LAN 位址,需要時再嘗試外部位址。","manager 位置、VPN、防火牆或路由設定改變後請按 Sync Server。","livedesk.pages.dev 的 Chrome Local Network Access 不再是預設執行路徑。npm manager app 才是真正伺服器。"],platformTitle:"4. 目前平台狀態",platformBody:"Windows 已確認可傳輸即時畫面。macOS 與 Linux 也沿用相同 npm 指令路徑擴充支援。",platformPoints:["Windows: 已確認可用。","macOS: 畫面擷取與控制需要 Screen Recording/Accessibility 權限。","Linux: 已加入 linux-x64 runtime、grim/gnome-screenshot/import/scrot capture、X11/XTEST input backend。音訊尚未啟用。"],firewallTitle:"防火牆與外部地點",firewallBody:"同一 LAN 通常不需要路由器轉發。不同地點連線時,client 必須能到達 manager 的 client 連線埠。",firewallPoints:["client 連線埠是 TCP 5197。","client 電腦通常只需要對外連線。","跨網路時,請在 manager 防火牆允許 TCP 5197 inbound,並從路由器轉發到 manager PC。","5179 是 manager UI 連接埠,只在需要遠端瀏覽器直接打開 Wall 時才考慮公開。","優先使用 VPN、Tailscale、ZeroTier 或固定 IP allowlist。"],commandsTitle:"目前指令",commandsBody:"產品方向是跨作業系統用一行 terminal 指令,而不是先找網站、下載、安裝。",managerCommandLabel:"啟動 manager",clientCommandLabel:"連接 Client",slotCommandLabel:"固定到 slot 3",manualCommandLabel:"進階手動連線"}),es:ua({languageName:"Español",languageLabel:"Idioma del documento",eyebrow:"LiveDesk Manual",title:"Abre un Screen Wall local con un comando",intro:"LiveDesk es una consola npm-first para vigilar muchas estaciones, cajas de IA, Mac mini y equipos de desarrollo desde un PC manager.",managerTitle:"1. Ejecuta el manager",managerBody:"Ejecuta LiveDesk en el equipo que verá las pantallas. El manager inicia la UI y el servidor local juntos y abre http://127.0.0.1:5179.",managerPoints:["El comando es npx -y livedesk.","El manager usa 5179 para Screen Wall/API y 5197 para conexiones client.","Si un proceso antiguo ocupa los puertos, el arranque lo limpia por defecto."],clientTitle:"2. Conecta una Client",clientBody:"En cada equipo monitoreado, inicia sesión con la misma cuenta Google. Si el manager aún no está activo, el client espera una señal en tiempo real y vuelve a comprobar con intervalos adaptativos.",clientPoints:["Usa npx -y livedesk client para el primer slot libre.","Usa npx -y livedesk client 3 para fijar el equipo al slot 3.","Los clients siguen esperando para no repetir login en muchas máquinas."],syncTitle:"3. Sync Server y descubrimiento",syncBody:"Sync Server publica este manager como Screen Wall activo para la cuenta. Los clients leen el registro en Supabase y prueban los endpoints.",syncPoints:["Incluye host instance, direcciones LAN/private, candidatos public, pair token y lease corto.","Primero se prueban candidatos LAN, luego públicos si hace falta.","Pulsa Sync Server después de cambios de manager, VPN, firewall o router.","Chrome Local Network Access en livedesk.pages.dev ya no es el flujo principal. La app npm manager es el servidor real."],platformTitle:"4. Estado de plataformas",platformBody:"Windows ya fue verificado con transferencia de pantalla. macOS y Linux siguen el mismo camino npm.",platformPoints:["Windows: verificado.","macOS: captura y control requieren permisos Screen Recording/Accessibility.","Linux: runtime linux-x64, entrada X11/XTEST y audio de salida mediante PipeWire o PulseAudio."],firewallTitle:"Firewall y sedes remotas",firewallBody:"En la misma LAN normalmente no hace falta port forwarding. Para otra red, los clients deben llegar al puerto client del manager.",firewallPoints:["Puerto de conexión client: TCP 5197.","Los equipos client normalmente solo necesitan salida.","Para redes remotas, permite TCP 5197 inbound en el manager y reenvía TCP 5197 al PC manager.","El puerto 5179 es para la UI del manager; expónlo solo si un navegador remoto debe abrir el Wall.","Prefiere VPN, Tailscale, ZeroTier o allowlist de IP fijas."],commandsTitle:"Comandos actuales",commandsBody:"La dirección del producto es un comando de terminal común entre sistemas, no descargar e instalar desde una web.",managerCommandLabel:"Iniciar manager",clientCommandLabel:"Conectar Client",slotCommandLabel:"Fijar al slot 3",manualCommandLabel:"Conexión manual avanzada"}),fr:ua({languageName:"Français",languageLabel:"Langue du document",eyebrow:"LiveDesk Manual",title:"Ouvrir un Screen Wall local en une commande",intro:"LiveDesk est un deck npm-first pour surveiller plusieurs postes, machines IA, Mac mini et machines dev depuis un PC manager.",managerTitle:"1. Lancer le manager",managerBody:"Lancez LiveDesk sur l’ordinateur qui observe les écrans. Le manager démarre l’UI et le serveur local, puis ouvre http://127.0.0.1:5179.",managerPoints:["La commande est npx -y livedesk.","Le manager utilise 5179 pour Screen Wall/API et 5197 pour les connexions client.","Si un ancien processus utilise les ports, le démarrage le nettoie par défaut."],clientTitle:"2. Connecter une Client",clientBody:"Sur chaque ordinateur surveillé, connectez-vous avec le même compte Google. Si le manager n’est pas encore actif, le client réessaie toutes les 5 secondes.",clientPoints:["Utilisez npx -y livedesk client pour le premier slot libre.","Utilisez npx -y livedesk client 3 pour fixer la machine au slot 3.","Les clients continuent d’attendre pour éviter les reconnexions répétées."],syncTitle:"3. Sync Server et découverte",syncBody:"Sync Server publie ce manager comme Screen Wall actif du compte. Les clients lisent l’enregistrement Supabase et testent les endpoints.",syncPoints:["Il contient host instance, adresses LAN/private, candidats public, pair token et lease court.","Les candidats LAN sont essayés en premier, puis les candidats publics si besoin.","Cliquez Sync Server après un changement de manager, VPN, firewall ou routeur.","Chrome Local Network Access sur livedesk.pages.dev n’est plus le chemin par défaut. L’app npm manager est le vrai serveur."],platformTitle:"4. État des plateformes",platformBody:"Windows est vérifié avec transfert d’écran live. macOS et Linux suivent le même chemin npm.",platformPoints:["Windows: vérifié.","macOS: capture et contrôle nécessitent Screen Recording/Accessibility.","Linux: runtime linux-x64, entrée X11/XTEST et audio de sortie via PipeWire ou PulseAudio."],firewallTitle:"Firewall et sites distants",firewallBody:"Sur le même LAN, le port forwarding est rarement nécessaire. Depuis un autre réseau, les clients doivent atteindre le port client du manager.",firewallPoints:["Port de connexion client: TCP 5197.","Les machines client ont normalement seulement besoin de sortie réseau.","Pour un réseau distant, autorisez TCP 5197 entrant sur le manager et redirigez TCP 5197 vers le PC manager.","Le port 5179 sert à l’UI manager; exposez-le seulement si un navigateur distant doit ouvrir le Wall.","Préférez VPN, Tailscale, ZeroTier ou allowlist IP fixes."],commandsTitle:"Commandes actuelles",commandsBody:"La direction produit est une commande terminal commune aux OS, plutôt qu’un téléchargement manuel depuis un site.",managerCommandLabel:"Démarrer manager",clientCommandLabel:"Connecter Client",slotCommandLabel:"Fixer au slot 3",manualCommandLabel:"Connexion manuelle avancée"}),de:ua({languageName:"Deutsch",languageLabel:"Dokumentsprache",eyebrow:"LiveDesk Manual",title:"Lokale Screen Wall mit einem Befehl öffnen",intro:"LiveDesk ist ein npm-first Arbeitsdeck, um viele Clients, AI-Boxen, Mac minis und Dev-Rechner von einem Manager-PC zu überwachen.",managerTitle:"1. Manager starten",managerBody:"Starte LiveDesk auf dem Computer, der die Screens sieht. Der Manager startet UI und lokalen Server zusammen und öffnet http://127.0.0.1:5179.",managerPoints:["Der Befehl ist npx -y livedesk.","Der Manager nutzt 5179 für Screen Wall/API und 5197 für Client-Verbindungen.","Wenn alte LiveDesk-Prozesse Ports belegen, werden sie beim Start standardmäßig bereinigt."],clientTitle:"2. Client verbinden",clientBody:"Melde dich auf jedem überwachten Computer mit demselben Google-Konto an. Wenn kein Manager online ist, wartet der Client auf ein Live-Signal und prüft mit adaptiven Intervallen erneut.",clientPoints:["npx -y livedesk client nutzt den ersten freien Slot.","npx -y livedesk client 3 fixiert den Rechner auf Slot 3.","Clients warten weiter, damit große Setups kein wiederholtes Login brauchen."],syncTitle:"3. Sync Server und Discovery",syncBody:"Sync Server veröffentlicht diesen Manager als aktive Screen Wall des Kontos. Clients lesen den Supabase-Eintrag und testen Endpoints der Reihe nach.",syncPoints:["Enthält host instance, LAN/private Adressen, public Kandidaten, pair token und kurze lease.","LAN-Kandidaten werden zuerst versucht, danach public Kandidaten.","Nach Änderungen an Manager, VPN, Firewall oder Router Sync Server drücken.","Chrome Local Network Access auf livedesk.pages.dev ist nicht mehr der Standardpfad. Die npm Manager App ist der echte Server."],platformTitle:"4. Plattformstatus",platformBody:"Windows wurde mit Live-Screen-Transfer geprüft. macOS und Linux folgen demselben npm-Pfad.",platformPoints:["Windows: verifiziert.","macOS: Capture und Control brauchen Screen Recording/Accessibility Rechte.","Linux: linux-x64 Runtime, X11/XTEST Eingabe und Ausgangsaudio über PipeWire oder PulseAudio."],firewallTitle:"Firewall und entfernte Standorte",firewallBody:"Im gleichen LAN ist Router-Forwarding meist unnötig. Aus anderen Netzwerken müssen Clients den Client-Port des Managers erreichen.",firewallPoints:["Client-Verbindungsport: TCP 5197.","Client-Rechner brauchen normalerweise nur ausgehenden Zugriff.","Für entfernte Netzwerke TCP 5197 inbound am Manager erlauben und vom Router zum Manager-PC forwarden.","Port 5179 ist für die Manager-UI; nur öffnen, wenn ein Remote-Browser die Wall direkt öffnen muss.","VPN, Tailscale, ZeroTier oder feste IP-Allowlists bevorzugen."],commandsTitle:"Aktuelle Befehle",commandsBody:"Die Produktlinie ist ein Terminalbefehl über Betriebssysteme hinweg, statt Website suchen, herunterladen und installieren.",managerCommandLabel:"Manager starten",clientCommandLabel:"Client verbinden",slotCommandLabel:"An Slot 3 fixieren",manualCommandLabel:"Erweiterte manuelle Verbindung"}),"pt-BR":ua({languageName:"Português",languageLabel:"Idioma do documento",eyebrow:"LiveDesk Manual",title:"Abra um Screen Wall local com um comando",intro:"LiveDesk é um deck npm-first para monitorar várias estações, caixas de IA, Mac minis e máquinas dev a partir de um PC manager.",managerTitle:"1. Execute o manager",managerBody:"Rode LiveDesk no computador que verá as telas. O manager inicia a UI e o servidor local juntos e abre http://127.0.0.1:5179.",managerPoints:["O comando é npx -y livedesk.","O manager usa 5179 para Screen Wall/API e 5197 para conexões client.","Se um processo antigo ocupa as portas, a inicialização limpa por padrão."],clientTitle:"2. Conecte uma Client",clientBody:"Em cada computador monitorado, entre com a mesma conta Google. Se o manager ainda não estiver online, o client aguarda um sinal em tempo real e verifica novamente em intervalos adaptativos.",clientPoints:["Use npx -y livedesk client para o primeiro slot livre.","Use npx -y livedesk client 3 para fixar no slot 3.","Clients continuam tentando para evitar login repetido em muitos PCs."],syncTitle:"3. Sync Server e descoberta",syncBody:"Sync Server publica este manager como Screen Wall ativo da conta. Clients leem o registro no Supabase e testam endpoints.",syncPoints:["Inclui host instance, endereços LAN/private, candidatos public, pair token e lease curto.","Candidatos LAN vêm primeiro; public depois se necessário.","Clique Sync Server após mudanças de manager, VPN, firewall ou roteador.","Chrome Local Network Access em livedesk.pages.dev não é mais o caminho padrão. O app npm manager é o servidor real."],platformTitle:"4. Estado das plataformas",platformBody:"Windows foi verificado com transferência de tela. macOS e Linux seguem o mesmo caminho npm.",platformPoints:["Windows: verificado.","macOS: captura e controle precisam de permissões Screen Recording/Accessibility.","Linux: runtime linux-x64, captura grim/gnome-screenshot/import/scrot e entrada X11/XTEST adicionados. Áudio ainda desativado."],firewallTitle:"Firewall e locais remotos",firewallBody:"Na mesma LAN normalmente não é preciso port forwarding. Em outra rede, clients precisam alcançar a porta client do manager.",firewallPoints:["Porta client: TCP 5197.","Máquinas client normalmente só precisam de saída.","Para redes remotas, permita TCP 5197 inbound no manager e encaminhe TCP 5197 para o PC manager.","A porta 5179 é para a UI do manager; exponha apenas se um navegador remoto precisar abrir o Wall.","Prefira VPN, Tailscale, ZeroTier ou allowlist de IP fixo."],commandsTitle:"Comandos atuais",commandsBody:"A direção do produto é um comando de terminal comum entre sistemas, não baixar e instalar manualmente.",managerCommandLabel:"Iniciar manager",clientCommandLabel:"Conectar Client",slotCommandLabel:"Fixar no slot 3",manualCommandLabel:"Conexão manual avançada"}),it:ua({languageName:"Italiano",languageLabel:"Lingua documento",eyebrow:"LiveDesk Manual",title:"Apri uno Screen Wall locale con un comando",intro:"LiveDesk è un deck npm-first per monitorare Client, box AI, Mac mini e macchine dev da un solo PC manager.",managerTitle:"1. Avvia il manager",managerBody:"Esegui LiveDesk sul computer che guarderà gli schermi. Il manager avvia UI e server locale e apre http://127.0.0.1:5179.",managerPoints:["Il comando è npx -y livedesk.","Il manager usa 5179 per Screen Wall/API e 5197 per connessioni client.","Se vecchi processi occupano le porte, l’avvio le pulisce di default."],clientTitle:"2. Connetti una Client",clientBody:"Su ogni computer monitorato, accedi con lo stesso account Google. Se il manager non è online, il client riprova ogni 5 secondi.",clientPoints:["npx -y livedesk client usa il primo slot libero.","npx -y livedesk client 3 fissa la macchina allo slot 3.","I client continuano a provare per evitare login ripetuti."],syncTitle:"3. Sync Server e discovery",syncBody:"Sync Server pubblica questo manager come Screen Wall attivo dell’account. I client leggono Supabase e provano gli endpoint.",syncPoints:["Include host instance, indirizzi LAN/private, candidati public, pair token e lease breve.","Prima LAN, poi public se necessario.","Premi Sync Server dopo cambi di manager, VPN, firewall o router.","Chrome Local Network Access su livedesk.pages.dev non è più il percorso principale. L’app npm manager è il server reale."],platformTitle:"4. Stato piattaforme",platformBody:"Windows è verificato con trasferimento schermo live. macOS e Linux seguono lo stesso percorso npm.",platformPoints:["Windows: verificato.","macOS: capture e control richiedono permessi Screen Recording/Accessibility.","Linux: aggiunti runtime linux-x64, capture grim/gnome-screenshot/import/scrot e input X11/XTEST. Audio disattivato."],firewallTitle:"Firewall e sedi remote",firewallBody:"Nella stessa LAN di solito non serve port forwarding. Da altre reti, i client devono raggiungere la porta client del manager.",firewallPoints:["Porta client: TCP 5197.","I client normalmente richiedono solo uscita.","Per reti remote, abilita TCP 5197 inbound sul manager e inoltra TCP 5197 al PC manager.","La porta 5179 è per la UI manager; esponila solo se un browser remoto deve aprire il Wall.","Preferisci VPN, Tailscale, ZeroTier o IP allowlist."],commandsTitle:"Comandi attuali",commandsBody:"La direzione è un comando terminale comune tra sistemi operativi, invece di download e install manuale.",managerCommandLabel:"Avvia manager",clientCommandLabel:"Connetti Client",slotCommandLabel:"Fissa allo slot 3",manualCommandLabel:"Connessione manuale avanzata"}),id:ua({languageName:"Bahasa Indonesia",languageLabel:"Bahasa dokumen",eyebrow:"LiveDesk Manual",title:"Buka Screen Wall lokal dengan satu perintah",intro:"LiveDesk adalah deck npm-first untuk memantau banyak Client, AI box, Mac mini, dan mesin dev dari satu PC manager.",managerTitle:"1. Jalankan manager",managerBody:"Jalankan LiveDesk di komputer yang melihat layar. Manager membuka UI dan server lokal bersama, lalu membuka http://127.0.0.1:5179.",managerPoints:["Perintahnya npx -y livedesk.","Manager memakai 5179 untuk Screen Wall/API dan 5197 untuk koneksi client.","Jika proses lama memakai port itu, startup membersihkannya secara default."],clientTitle:"2. Hubungkan Client",clientBody:"Di komputer yang dipantau, masuk dengan akun Google yang sama. Jika manager belum online, client tetap mencoba tiap 5 detik.",clientPoints:["npx -y livedesk client memakai slot kosong pertama.","npx -y livedesk client 3 mengunci mesin ke slot 3.","Client terus mencoba agar banyak PC tidak perlu login berulang."],syncTitle:"3. Sync Server dan discovery",syncBody:"Sync Server menerbitkan manager ini sebagai Screen Wall aktif untuk akun. Client membaca Supabase dan mencoba endpoint.",syncPoints:["Berisi host instance, alamat LAN/private, kandidat public, pair token, dan lease pendek.","Kandidat LAN dicoba lebih dulu, lalu public jika perlu.","Tekan Sync Server setelah perubahan manager, VPN, firewall, atau router.","Chrome Local Network Access di livedesk.pages.dev bukan jalur utama lagi. App npm manager adalah server sebenarnya."],platformTitle:"4. Status platform",platformBody:"Windows sudah diverifikasi dengan live screen transfer. macOS dan Linux mengikuti jalur npm yang sama.",platformPoints:["Windows: terverifikasi.","macOS: capture dan control perlu izin Screen Recording/Accessibility.","Linux: runtime linux-x64, capture grim/gnome-screenshot/import/scrot, dan input X11/XTEST sudah ditambahkan. Audio belum aktif."],firewallTitle:"Firewall dan lokasi luar",firewallBody:"Di LAN yang sama biasanya tidak perlu port forwarding. Dari jaringan lain, client harus mencapai port client manager.",firewallPoints:["Port client: TCP 5197.","Mesin client biasanya hanya perlu akses keluar.","Untuk jaringan luar, izinkan TCP 5197 inbound di manager dan forward TCP 5197 ke PC manager.","Port 5179 untuk UI manager; buka hanya jika browser luar harus membuka Wall.","Utamakan VPN, Tailscale, ZeroTier, atau IP allowlist."],commandsTitle:"Perintah saat ini",commandsBody:"Arah produk adalah satu perintah terminal lintas OS, bukan download dan instal manual.",managerCommandLabel:"Mulai manager",clientCommandLabel:"Hubungkan Client",slotCommandLabel:"Kunci ke slot 3",manualCommandLabel:"Koneksi manual lanjutan"}),vi:ua({languageName:"Tiếng Việt",languageLabel:"Ngôn ngữ tài liệu",eyebrow:"LiveDesk Manual",title:"Mở Screen Wall cục bộ bằng một lệnh",intro:"LiveDesk là deck npm-first để giám sát nhiều Client, máy AI, Mac mini và máy dev từ một PC manager.",managerTitle:"1. Chạy manager",managerBody:"Chạy LiveDesk trên máy dùng để xem màn hình. Manager khởi động UI và server cục bộ cùng lúc, rồi mở http://127.0.0.1:5179.",managerPoints:["Lệnh là npx -y livedesk.","Manager dùng 5179 cho Screen Wall/API và 5197 cho kết nối client.","Nếu tiến trình cũ giữ port, startup mặc định sẽ dọn và khởi động lại."],clientTitle:"2. Kết nối Client",clientBody:"Trên mỗi máy được giám sát, đăng nhập cùng tài khoản Google. Nếu manager chưa online, client tiếp tục thử mỗi 5 giây.",clientPoints:["npx -y livedesk client dùng slot trống đầu tiên.","npx -y livedesk client 3 ghim máy vào slot 3.","Client tiếp tục chờ để nhiều máy không cần đăng nhập lại."],syncTitle:"3. Sync Server và tự phát hiện",syncBody:"Sync Server đăng manager này là Screen Wall đang hoạt động của tài khoản. Client đọc Supabase và thử các endpoint.",syncPoints:["Bao gồm host instance, địa chỉ LAN/private, public candidate, pair token và lease ngắn.","Ưu tiên LAN trước, sau đó thử public nếu cần.","Bấm Sync Server sau thay đổi manager, VPN, firewall hoặc router.","Chrome Local Network Access trên livedesk.pages.dev không còn là đường chạy chính. App npm manager mới là server thật."],platformTitle:"4. Trạng thái nền tảng",platformBody:"Windows đã được kiểm chứng với truyền màn hình live. macOS và Linux đi theo cùng đường npm.",platformPoints:["Windows: đã kiểm chứng.","macOS: capture và control cần quyền Screen Recording/Accessibility.","Linux: đã thêm runtime linux-x64, capture grim/gnome-screenshot/import/scrot và input X11/XTEST. Audio chưa bật."],firewallTitle:"Firewall và địa điểm ngoài",firewallBody:"Trong cùng LAN thường không cần port forwarding. Từ mạng khác, client phải truy cập được port client của manager.",firewallPoints:["Port client: TCP 5197.","Máy client thường chỉ cần kết nối outbound.","Với mạng ngoài, cho phép TCP 5197 inbound trên manager và forward TCP 5197 tới PC manager.","Port 5179 dành cho UI manager; chỉ mở nếu trình duyệt từ xa cần mở Wall trực tiếp.","Ưu tiên VPN, Tailscale, ZeroTier hoặc allowlist IP cố định."],commandsTitle:"Lệnh hiện tại",commandsBody:"Hướng sản phẩm là một lệnh terminal dùng chung giữa các OS, thay vì tải và cài thủ công.",managerCommandLabel:"Khởi động manager",clientCommandLabel:"Kết nối Client",slotCommandLabel:"Ghim vào slot 3",manualCommandLabel:"Kết nối thủ công nâng cao"}),th:ua({languageName:"ไทย",languageLabel:"ภาษาของเอกสาร",eyebrow:"LiveDesk Manual",title:"เปิด Screen Wall ภายในด้วยคำสั่งเดียว",intro:"LiveDesk คือ deck แบบ npm-first สำหรับดู Client, กล่อง AI, Mac mini และเครื่อง dev หลายเครื่องจาก manager PC เครื่องเดียว",managerTitle:"1. รัน manager",managerBody:"รัน LiveDesk บนเครื่องที่ใช้ดูหน้าจอ Manager จะเปิด UI และ local server พร้อมกัน แล้วเปิด http://127.0.0.1:5179",managerPoints:["คำสั่งคือ npx -y livedesk","Manager ใช้ 5179 สำหรับ Screen Wall/API และ 5197 สำหรับ client connection","ถ้า process เก่ายังใช้ port อยู่ startup จะล้างให้โดย default"],clientTitle:"2. เชื่อม Client",clientBody:"บนเครื่องที่ต้อง monitor ให้ login ด้วย Google account เดียวกัน ถ้า manager ยังไม่ออนไลน์ client จะลองใหม่ทุก 5 วินาที",clientPoints:["npx -y livedesk client จะใช้ slot ว่างแรก","npx -y livedesk client 3 จะตรึงเครื่องไว้ที่ slot 3","Client จะรอต่อเนื่องเพื่อไม่ต้อง login ซ้ำเมื่อมีหลายเครื่อง"],syncTitle:"3. Sync Server และ discovery",syncBody:"Sync Server จะ publish manager นี้เป็น active Screen Wall ของ account แล้ว client อ่านข้อมูลจาก Supabase และลอง endpoint ต่างๆ",syncPoints:["มี host instance, LAN/private address, public candidate, pair token และ short lease","ลอง LAN ก่อน แล้วค่อยลอง public ถ้าจำเป็น","กด Sync Server หลังเปลี่ยน manager, VPN, firewall หรือ router","Chrome Local Network Access บน livedesk.pages.dev ไม่ใช่ runtime หลักแล้ว app npm manager คือ server จริง"],platformTitle:"4. สถานะ platform",platformBody:"Windows ตรวจสอบแล้วว่าส่งภาพ live ได้ macOS และ Linux ใช้เส้นทาง npm เดียวกัน",platformPoints:["Windows: verified","macOS: capture/control ต้องใช้สิทธิ์ Screen Recording/Accessibility","Linux: เพิ่ม linux-x64 runtime, grim/gnome-screenshot/import/scrot capture และ X11/XTEST input แล้ว audio ยังปิดอยู่"],firewallTitle:"Firewall และเครื่องต่างสถานที่",firewallBody:"ใน LAN เดียวกันปกติไม่ต้อง port forwarding ถ้าอยู่คนละ network client ต้องเข้าถึง client port ของ manager ได้",firewallPoints:["Client port คือ TCP 5197","เครื่อง client ปกติต้องการ outbound เท่านั้น","ถ้าอยู่นอก network ให้เปิด TCP 5197 inbound ที่ manager และ forward TCP 5197 ไปยัง manager PC","Port 5179 เป็น UI ของ manager เปิดเฉพาะเมื่อ browser ระยะไกลต้องเปิด Wall โดยตรง","ควรใช้ VPN, Tailscale, ZeroTier หรือ IP allowlist ก่อนเปิดสู่ public internet"],commandsTitle:"คำสั่งปัจจุบัน",commandsBody:"ทิศทาง product คือคำสั่ง terminal เดียวข้าม OS ไม่ใช่ download/install จากเว็บไซต์",managerCommandLabel:"เริ่ม manager",clientCommandLabel:"เชื่อม Client",slotCommandLabel:"ตรึง slot 3",manualCommandLabel:"manual connection ขั้นสูง"}),hi:ua({languageName:"हिन्दी",languageLabel:"दस्तावेज़ भाषा",eyebrow:"LiveDesk Manual",title:"एक कमांड से local Screen Wall खोलें",intro:"LiveDesk एक npm-first remote work deck है जिससे एक manager PC से कई Client, AI box, Mac mini और dev machines देखे जा सकते हैं।",managerTitle:"1. manager चलाएं",managerBody:"जिस computer से screens देखनी हैं उस पर LiveDesk manager चलाएं। यह UI और local server साथ में शुरू करता है और http://127.0.0.1:5179 खोलता है।",managerPoints:["कमांड है npx -y livedesk।","Manager Screen Wall/API के लिए 5179 और client connections के लिए 5197 इस्तेमाल करता है।","अगर पुराने LiveDesk ports बचे हों तो startup default रूप से साफ करता है।"],clientTitle:"2. Client जोड़ें",clientBody:"हर monitored computer पर उसी Google account से sign in करें। Manager online न हो तब भी client बंद नहीं होता और हर 5 सेकंड retry करता है।",clientPoints:["npx -y livedesk client पहला खाली slot लेता है।","npx -y livedesk client 3 machine को slot 3 पर pin करता है।","बहुत सी machines हों तब भी बार-बार login की जरूरत नहीं रहती।"],syncTitle:"3. Sync Server और discovery",syncBody:"Sync Server इस manager को account का active Screen Wall बनाकर Supabase में publish करता है। Clients record पढ़कर endpoints try करते हैं।",syncPoints:["इसमें host instance, LAN/private addresses, public candidates, pair token और short lease होता है।","LAN candidates पहले try होते हैं, जरूरत हो तो public candidates।","Manager, VPN, firewall या router बदलने के बाद Sync Server दबाएं।","livedesk.pages.dev का Chrome Local Network Access अब default runtime नहीं है। npm manager app ही real server है।"],platformTitle:"4. Platform status",platformBody:"Windows live screen transfer के साथ verified है। macOS और Linux भी इसी npm path पर आगे बढ़ रहे हैं।",platformPoints:["Windows: verified.","macOS: capture/control के लिए Screen Recording/Accessibility permissions चाहिए।","Linux: linux-x64 runtime, X11/XTEST input और PipeWire/PulseAudio output audio capture उपलब्ध हैं।"],firewallTitle:"Firewall और remote locations",firewallBody:"Same LAN में आम तौर पर router forwarding जरूरी नहीं। दूसरे network से clients को manager client port तक पहुंचना होगा।",firewallPoints:["Client connection port: TCP 5197.","Client machines को आम तौर पर सिर्फ outbound access चाहिए।","Remote network के लिए manager firewall में TCP 5197 inbound allow करें और router से TCP 5197 manager PC पर forward करें।","5179 manager UI port है; इसे तभी expose करें जब remote browser को Wall खोलना हो।","Public internet से पहले VPN, Tailscale, ZeroTier या fixed IP allowlist बेहतर है।"],commandsTitle:"Current commands",commandsBody:"Product direction है OS के बीच एक terminal command, website से download/install नहीं।",managerCommandLabel:"Start manager",clientCommandLabel:"Connect Client",slotCommandLabel:"Pin to slot 3",manualCommandLabel:"Advanced manual connection"}),ar:ua({languageName:"العربية",languageLabel:"لغة المستند",eyebrow:"LiveDesk Manual",title:"افتح Screen Wall محلي بأمر واحد",intro:"LiveDesk هو deck بنمط npm-first لمراقبة عدة Clients وأجهزة AI وMac mini وأجهزة تطوير من كمبيوتر manager واحد.",managerTitle:"1. شغل manager",managerBody:"شغل LiveDesk على الكمبيوتر الذي سيعرض الشاشات. يبدأ manager الواجهة والخادم المحلي معا ويفتح http://127.0.0.1:5179.",managerPoints:["الأمر هو npx -y livedesk.","يستخدم manager المنفذ 5179 للواجهة/API والمنفذ 5197 لاتصالات client.","إذا بقيت عملية LiveDesk قديمة على المنافذ، يتم تنظيفها افتراضيا عند التشغيل."],clientTitle:"2. وصل Client",clientBody:"على كل كمبيوتر تتم مراقبته، سجل الدخول بنفس حساب Google. إذا لم يكن manager متاحا، يبقى client يعمل ويحاول كل 5 ثوان.",clientPoints:["npx -y livedesk client يختار أول slot فارغ.","npx -y livedesk client 3 يثبت الجهاز في slot 3.","clients تستمر بالمحاولة لتجنب تكرار تسجيل الدخول عند كثرة الأجهزة."],syncTitle:"3. Sync Server والاكتشاف",syncBody:"Sync Server ينشر هذا manager كـ active Screen Wall للحساب. يقرأ clients سجل Supabase ويجربون endpoints بالترتيب.",syncPoints:["السجل يتضمن host instance وعناوين LAN/private وpublic candidates وpair token وlease قصير.","يتم تفضيل LAN أولا، ثم public عند الحاجة.","اضغط Sync Server بعد تغيير manager أو VPN أو firewall أو router.","Chrome Local Network Access على livedesk.pages.dev لم يعد مسار التشغيل الافتراضي. تطبيق npm manager هو الخادم الحقيقي."],platformTitle:"4. حالة المنصات",platformBody:"Windows تم التحقق منه مع نقل شاشة live. macOS وLinux يتبعان نفس مسار npm.",platformPoints:["Windows: تم التحقق.","macOS: capture/control يحتاجان أذونات Screen Recording/Accessibility.","Linux: تمت إضافة linux-x64 runtime وgrim/gnome-screenshot/import/scrot capture وX11/XTEST input. الصوت ما زال معطلا."],firewallTitle:"Firewall والمواقع البعيدة",firewallBody:"داخل نفس LAN غالبا لا تحتاج port forwarding. من شبكة أخرى يجب أن يصل client إلى منفذ manager client.",firewallPoints:["منفذ client: TCP 5197.","أجهزة client تحتاج عادة اتصال outbound فقط.","للشبكات البعيدة، اسمح TCP 5197 inbound على manager ووجه TCP 5197 من router إلى manager PC.","المنفذ 5179 مخصص لواجهة manager؛ اكشفه فقط إذا كان متصفح بعيد يحتاج فتح Wall مباشرة.","يفضل VPN أو Tailscale أو ZeroTier أو fixed IP allowlist بدل التعريض العام."],commandsTitle:"الأوامر الحالية",commandsBody:"اتجاه المنتج هو أمر terminal واحد عبر الأنظمة، وليس تحميل وتثبيت يدوي من موقع.",managerCommandLabel:"Start manager",clientCommandLabel:"Connect Client",slotCommandLabel:"Pin to slot 3",manualCommandLabel:"Advanced manual connection"}),tr:ua({languageName:"Türkçe",languageLabel:"Belge dili",eyebrow:"LiveDesk Manual",title:"Yerel Screen Wall tek komutla açılsın",intro:"LiveDesk, tek bir manager PC üzerinden birçok Client, AI box, Mac mini ve dev machine izlemek için npm-first bir çalışma deckidir.",managerTitle:"1. Manager çalıştır",managerBody:"Ekranları izleyecek bilgisayarda LiveDesk manager çalıştır. Manager UI ve local server birlikte başlar ve http://127.0.0.1:5179 açılır.",managerPoints:["Komut: npx -y livedesk.","Manager Screen Wall/API için 5179, client bağlantıları için 5197 kullanır.","Eski LiveDesk process portları tutuyorsa başlangıç varsayılan olarak temizler."],clientTitle:"2. Client bağla",clientBody:"İzlenecek bilgisayarda aynı Google hesabıyla giriş yap. Manager henüz online değilse client kapanmaz ve her 5 saniyede tekrar dener.",clientPoints:["npx -y livedesk client ilk boş slota yerleşir.","npx -y livedesk client 3 makineyi slot 3’e sabitler.","Çok makinede tekrar tekrar login gerekmesin diye client beklemeye devam eder."],syncTitle:"3. Sync Server ve discovery",syncBody:"Sync Server bu managerı hesabın active Screen Wall kaydı olarak Supabase’e yazar. Clients bu kaydı okuyup endpoint adaylarını dener.",syncPoints:["Host instance, LAN/private adresler, public adaylar, pair token ve kısa lease içerir.","Önce LAN adayları, gerekirse public adaylar denenir.","Manager, VPN, firewall veya router değişince Sync Server’a bas.","livedesk.pages.dev üzerindeki Chrome Local Network Access artık default runtime yolu değil. Gerçek server npm manager app."],platformTitle:"4. Platform durumu",platformBody:"Windows live screen transfer ile doğrulandı. macOS ve Linux aynı npm yolunu takip ediyor.",platformPoints:["Windows: doğrulandı.","macOS: capture/control için Screen Recording/Accessibility izinleri gerekir.","Linux: linux-x64 runtime, grim/gnome-screenshot/import/scrot capture ve X11/XTEST input backend eklendi. Audio kapalı."],firewallTitle:"Firewall ve uzak konumlar",firewallBody:"Aynı LAN içinde genelde router forwarding gerekmez. Başka ağdan clients manager client portuna ulaşabilmelidir.",firewallPoints:["Client port: TCP 5197.","Client makineler normalde sadece outbound erişim ister.","Uzak ağ için manager firewall üzerinde TCP 5197 inbound aç ve routerdan TCP 5197yi manager PCye forward et.","5179 manager UI portudur; sadece uzak browser Wall açacaksa expose et.","Public internet yerine VPN, Tailscale, ZeroTier veya sabit IP allowlist tercih et."],commandsTitle:"Güncel komutlar",commandsBody:"Ürün yönü, webden indirip kurmak yerine işletim sistemleri arasında ortak tek terminal komutu.",managerCommandLabel:"Manager başlat",clientCommandLabel:"Client bağla",slotCommandLabel:"Slot 3’e sabitle",manualCommandLabel:"Gelişmiş manuel bağlantı"})},ZT=Object.entries(Lo).map(([n,a])=>({id:n,label:a.languageName}));function JT(){if(typeof navigator>"u")return"en";const n=navigator.languages?.length?navigator.languages:[navigator.language];for(const a of n){const i=a.toLowerCase();if(Lo[a])return a;if(i.startsWith("zh-tw")||i.startsWith("zh-hk"))return"zh-TW";if(i.startsWith("pt"))return"pt-BR";const l=i.split("-")[0];if(Lo[l])return l}return"en"}function Pb({index:n}){const a=n+1;return r.jsx("article",{className:"device-tile placeholder-tile","aria-label":`Empty slot ${a}`,children:r.jsxs("div",{className:"tile-screen","aria-hidden":"true",children:[r.jsx("span",{className:"slot-grid","aria-hidden":"true"}),r.jsxs("span",{className:"empty-screen",children:[r.jsx(ha,{size:24}),r.jsxs("strong",{children:["Slot ",String(a).padStart(3,"0")]})]})]})})}const eE=["cpu","gpu","memory","vram","network","disk","temp"];function tE({device:n,slotNumber:a,selected:i,updateIndicator:l,onSelect:c,onOpenControl:d}){const m=Dw(n),p=new Map(m.metrics.map(v=>[v.id,v])),g=l?.label||m.label;return r.jsxs("div",{className:`wall-device-list-row state-${m.state} ${i?"selected":""}`,role:"row","aria-selected":i,tabIndex:0,onClick:c,onDoubleClick:v=>{v.preventDefault(),d()},onKeyDown:v=>{(v.key==="Enter"||v.key===" ")&&(v.preventDefault(),c())},children:[r.jsxs("span",{className:"wall-device-name",role:"gridcell",children:[r.jsx("span",{className:"wall-device-icon","aria-hidden":"true",children:r.jsx(ha,{size:17})}),r.jsxs("span",{children:[r.jsx("strong",{children:Ga(n)}),r.jsxs("small",{children:["Slot ",String(a).padStart(3,"0"),n.platform?` · ${n.platform}`:"",n.arch?` · ${n.arch}`:""]})]}),i&&r.jsx(vr,{size:16,strokeWidth:3,"aria-label":"Selected"})]}),r.jsxs("span",{className:`wall-device-list-status state-${m.state} ${l?`update-${l.state}`:""}`,role:"gridcell",title:l?.error||g,children:[r.jsx("i",{"aria-hidden":"true"}),r.jsx("strong",{children:g})]}),eE.map(v=>{const x=p.get(v);return r.jsxs("span",{className:`wall-device-list-metric state-${x?.state||"unknown"}`,role:"gridcell",title:x?`${x.label}: ${x.value} · ${x.detail}`:"Not reported",children:[r.jsx("strong",{children:x?.value||"-"}),r.jsx("small",{children:x?.detail||"not reported"}),x?.ratio!==void 0&&r.jsx("i",{style:{width:`${Math.round(x.ratio*100)}%`},"aria-hidden":"true"})]},v)})]})}function nE(n){if(!n)return!1;const a=String(n.platform||"").trim().toLowerCase();return ei(a)&&n.capabilities?.screenRecordingPermission===!1}function ei(n){const a=String(n||"").trim().toLowerCase();return a==="macos"||a==="darwin"||a==="mac"||a==="macosx"}function aE(n){const a=String(n||"").trim().toLowerCase();return a==="linux"||a.startsWith("linux ")}function Ub(n){if(!n)return!1;const a=String(n.capabilities?.liveCaptureConcurrency||"").trim().toLowerCase();return a==="exclusive"?!0:a==="parallel"?!1:ei(n.platform)}function bh({device:n,frame:a,selected:i,controlEnabled:l,showStatus:c,monitorIndex:d,monitorCount:m,onSelect:p,onFocusDevice:g,onOpenControl:v,onCycleMonitor:x,onCanvasReady:b,sendInput:w,updateIndicator:k,presentationReady:j,showPresentationSpinner:C=!1,remoteDiagnostics:M,diagnosticsMode:A="remote"}){const T=u.useRef(null),R=u.useRef(null),B=u.useRef(0),F=u.useRef(null),V=Dw(n),L=["cpu","gpu","memory","vram"].map(z=>V.metrics.find(Ae=>Ae.id===z)).filter(z=>!!z),K=ea(d,m),W=m>1,$=j??!!a,Z=nE(n)&&!$,H=C&&n.connected&&!$&&!Z,pe=RT(n,a,K),ce=pe>=Od?100:pe/Od*100,J=pe>=Od?Od/pe*100:100,I={"--screen-aspect":pe.toFixed(5),"--screen-content-width":`${Math.max(1,Math.min(100,ce)).toFixed(3)}%`,"--screen-content-height":`${Math.max(1,Math.min(100,J)).toFixed(3)}%`},ne=u.useCallback(z=>{T.current=z,b(n.deviceId,z)},[n.deviceId,b]),Y=z=>{const Ae=z.currentTarget.getBoundingClientRect();return I1(z.clientX,z.clientY,Ae)},U=z=>({shiftKey:z.shiftKey,ctrlKey:z.ctrlKey,altKey:z.altKey,metaKey:z.metaKey}),re=u.useCallback(()=>{F.current!==null&&(window.cancelAnimationFrame(F.current),F.current=null)},[]),oe=u.useCallback(()=>{const z=R.current;if(!z)return;R.current=null;const Ae=Number(z.input.normalizedX),Ce=Number(z.input.normalizedY);if(!Number.isFinite(Ae)||!Number.isFinite(Ce))return;w(n.deviceId,{...z.input,normalizedX:Ae,normalizedY:Ce})&&(B.current=performance.now())},[n.deviceId,w]),X=u.useCallback(()=>{F.current===null&&(F.current=window.requestAnimationFrame(()=>{F.current=null,oe()}))},[oe]);u.useEffect(()=>()=>{re(),R.current=null},[re]);const xe=(z,Ae)=>{if(!l)return;const Ce={type:Ae,monitorIndex:K,...Y(z),button:UT(z.button),...U(z)};if(Ae==="pointermove"){const De=performance.now();R.current={input:Ce},De-B.current>=eT&&(re(),oe()),X();return}R.current&&(re(),oe()),w(n.deviceId,Ce)},fe=u.useCallback(z=>{const Ae=document.activeElement;Ae instanceof HTMLElement&&z.contains(Ae)&&Ae.classList.contains("monitor-cycle-button")&&Ae.blur()},[]);return r.jsx("article",{className:`device-tile ${i?"selected":""} ${n.connected?"online":"offline"}`,"data-live-device-id":n.deviceId,style:I,children:r.jsxs("div",{className:`tile-screen ${$?"has-frame":"is-empty"}`,role:"button",tabIndex:l?-1:0,"aria-pressed":i,onClick:z=>{z.target instanceof Element&&z.target.closest(".monitor-cycle")||l||p()},onKeyDown:z=>{z.target instanceof Element&&z.target.closest(".monitor-cycle")||l||(z.key==="Enter"||z.key===" ")&&(z.preventDefault(),p())},onDoubleClick:z=>{z.target instanceof Element&&z.target.closest(".monitor-cycle")||(z.preventDefault(),v())},onPointerLeave:z=>fe(z.currentTarget),children:[r.jsx("span",{className:`screen-content ${$?"has-frame":"is-empty"}`,children:r.jsx("canvas",{ref:ne,className:`remote-canvas ${$?"has-frame":"is-empty"}`,tabIndex:l?0:-1,onPointerDown:z=>{l&&(z.preventDefault(),z.stopPropagation(),g(),z.currentTarget.focus(),z.currentTarget.setPointerCapture(z.pointerId),xe(z,"pointerdown"))},onPointerMove:z=>{l&&z.preventDefault(),xe(z,"pointermove")},onPointerLeave:z=>{l&&(xe(z,"pointermove"),re(),oe())},onPointerUp:z=>{l&&(z.preventDefault(),z.stopPropagation(),xe(z,"pointerup"),z.currentTarget.hasPointerCapture(z.pointerId)&&z.currentTarget.releasePointerCapture(z.pointerId))},onPointerCancel:z=>{z.currentTarget.hasPointerCapture(z.pointerId)&&z.currentTarget.releasePointerCapture(z.pointerId)},onWheel:z=>{l&&(z.preventDefault(),w(n.deviceId,{type:"wheel",monitorIndex:K,...Y(z),deltaX:z.deltaX,deltaY:z.deltaY,...U(z)}))},onKeyDown:z=>{l&&(z.preventDefault(),z.stopPropagation(),w(n.deviceId,{type:"keydown",monitorIndex:K,key:z.key,code:z.code,keyCode:z.keyCode||z.which||0,location:z.location,text:z.key.length===1?z.key:"",repeat:z.repeat,...U(z)}))},onKeyUp:z=>{l&&(z.preventDefault(),z.stopPropagation(),w(n.deviceId,{type:"keyup",monitorIndex:K,key:z.key,code:z.code,keyCode:z.keyCode||z.which||0,location:z.location,text:z.key.length===1?z.key:"",repeat:!1,...U(z)}))},onContextMenu:z=>l&&z.preventDefault()})}),!$&&r.jsx("span",{className:"empty-screen",children:r.jsx(ha,{size:26})}),r.jsx("span",{className:"wall-device-title",title:Ga(n),children:Ga(n)}),H&&r.jsxs("span",{className:"wall-presentation-loading",role:"status","aria-live":"polite",children:[r.jsx("span",{className:"wall-presentation-spinner","aria-hidden":"true"}),r.jsx("strong",{children:"Preparing video"})]}),Z&&r.jsxs("span",{className:"wall-presentation-loading permission-required",role:"status","aria-live":"polite",children:[r.jsx(Dn,{size:24,"aria-hidden":"true"}),r.jsx("strong",{children:"Screen Recording permission required"}),r.jsx("small",{children:"Allow the terminal app on this Mac, then restart LiveDesk."})]}),k&&r.jsxs("span",{className:`wall-update-status state-${k.state}`,role:"status","aria-live":"polite",title:k.error||k.label,children:[k.state==="updated"?r.jsx(Bn,{size:20,"aria-hidden":"true"}):k.state==="failed"?r.jsx(ma,{size:22,"aria-hidden":"true"}):k.state==="offline"?r.jsx(tf,{size:22,"aria-hidden":"true"}):r.jsx("span",{className:"wall-update-spinner","aria-hidden":"true"}),r.jsx("strong",{children:k.label}),k.error?r.jsx("small",{children:k.error}):null]}),c&&r.jsxs(r.Fragment,{children:[r.jsx("span",{className:`tile-health-pill state-${V.state}`,children:V.label}),r.jsx("span",{className:"tile-metrics","aria-label":`${Ga(n)} status`,children:L.map(z=>r.jsxs("span",{className:`tile-metric state-${z.state}`,children:[r.jsx("b",{children:z.label}),r.jsx("em",{children:z.value}),z.ratio!==void 0&&r.jsx("i",{style:{width:`${Math.round(z.ratio*100)}%`}})]},z.id))})]}),M&&r.jsx(jN,{items:M,mode:A}),W&&r.jsxs("span",{className:"monitor-cycle","aria-label":`${Ga(n)} display ${K+1} of ${m}`,onPointerDown:z=>z.stopPropagation(),onClick:z=>z.stopPropagation(),onDoubleClick:z=>z.stopPropagation(),children:[r.jsx("button",{className:"monitor-cycle-button previous",onClick:z=>{z.preventDefault(),z.stopPropagation(),x(-1),z.currentTarget.blur()},title:"Previous display",type:"button",children:r.jsx(tx,{size:18})}),r.jsxs("span",{className:"monitor-cycle-count",children:["Screen ",K+1,"/",m]}),r.jsx("button",{className:"monitor-cycle-button next",onClick:z=>{z.preventDefault(),z.stopPropagation(),x(1),z.currentTarget.blur()},title:"Next display",type:"button",children:r.jsx(Xh,{size:18})})]}),i&&r.jsx("span",{className:"device-selection-check","aria-hidden":"true",children:r.jsx(vr,{size:23,strokeWidth:3.5})})]})})}function sE({visible:n}){return u.useEffect(()=>{if(!n||!dh)return;if(!document.getElementById(Cb)){const i=document.createElement("script");i.id=Cb,i.async=!0,i.crossOrigin="anonymous",i.src=`https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=${encodeURIComponent(Uh)}`,document.head.appendChild(i)}const a=window.setTimeout(()=>{try{window.adsbygoogle=window.adsbygoogle||[],window.adsbygoogle.push({})}catch{}},120);return()=>window.clearTimeout(a)},[n]),n?r.jsxs("aside",{className:`free-ad-banner ${dh?"adsense-ready":"house-ad"}`,"aria-label":"Advertisement",children:[r.jsx("span",{className:"ad-label",children:"Advertisement"}),dh?r.jsx("ins",{className:"adsbygoogle livedesk-ad-unit",style:{display:"block"},"data-ad-client":Uh,"data-ad-slot":jw,"data-ad-format":"auto","data-full-width-responsive":"true"}):r.jsxs("div",{className:"house-ad-copy",children:[r.jsx("strong",{children:"Sponsored placement"}),r.jsx("span",{children:"Local AI gear, mini PCs, GPUs, NAS, and developer tools."})]})]}):null}function rE(){const{session:n,loading:a}=f1(),{target:i,error:l,refresh:c}=h1(n),d=u.useMemo(()=>{if(!i?.active)return null;const y=i.expires_at?Date.parse(i.expires_at):Number.POSITIVE_INFINITY;return Number.isFinite(y)&&y<=Date.now()?null:i},[i]),m=u.useMemo(()=>hy(d),[d]),p=u.useMemo(()=>m.length>0?[...new Set([...m,hn])]:[hn],[m]),[g,v]=u.useState(MT),x=u.useMemo(()=>p.some(y=>!!Qc(y)),[p]),b=!x||g,{status:w,devices:k,error:j,refresh:C,hubBaseUrl:M}=m1(p,b),{entitlement:A,licensed:T,loading:R,error:B,refresh:F}=p1(n),[V,L]=u.useState([]),[K,W]=u.useState(""),[$,Z]=u.useState(""),[H,pe]=u.useState(""),[ce,J]=u.useState(""),[I,ne]=u.useState(""),[Y,U]=u.useState("idle"),[re,oe]=u.useState(!1),[X,xe]=u.useState(null),[fe,z]=u.useState(""),Ae=u.useRef("");Ae.current=ce;const[Ce,De]=u.useState(!1),[He,he]=u.useState(!1),Ge=u.useRef(""),lt=u.useRef(new Map),se=u.useRef(0),[ve,Te]=u.useState(""),[Be,_e]=u.useState(""),[Ke,Ve]=u.useState(!1),rt=u.useRef(null),vt=u.useRef(null);u.useEffect(()=>{ve==="auth"&&n?.access_token&&Te("")},[ve,n?.access_token]);const[q,ye]=u.useState(""),[ke,$e]=u.useState(null),ot=u.useRef(null),pt=u.useRef(0),St=u.useCallback((y,N,_)=>{const O=String(y||"").trim();if(ye(O),ot.current!==null&&(window.clearTimeout(ot.current),ot.current=null),!O){$e(null);return}const D=++pt.current;if($e({id:D,message:O,tone:N}),!_)return;const ae=window.setTimeout(()=>{$e(de=>de?.id===D?null:de),ot.current===ae&&(ot.current=null)},5e3);ot.current=ae},[]),Pe=u.useCallback((y,N="info")=>{St(y,N,!0)},[St]),Et=u.useCallback(y=>{St(y,"progress",!1)},[St]);u.useEffect(()=>()=>{ot.current!==null&&window.clearTimeout(ot.current)},[]);const[ge,Oe]=u.useState(()=>typeof window<"u"&&window.location.pathname.startsWith("/support")?"support":"wall");u.useEffect(()=>{const y=()=>{Oe(window.location.pathname.startsWith("/support")?"support":N=>N==="support"?"wall":N)};return window.addEventListener("popstate",y),()=>window.removeEventListener("popstate",y)},[]);const[ze,Me]=u.useState("large"),[st,mt]=u.useState(!1),[Ee,Ln]=u.useState(!1),[pn,nt]=u.useState(!1),[It,_n]=u.useState(!1),[Ya,Aa]=u.useState(Date.now),ta=u.useRef(null);u.useEffect(()=>()=>{ta.current!==null&&window.clearTimeout(ta.current)},[]);const[Ka,ri]=u.useState(JT),[pa,fs]=u.useState(!1),[Ts,Ma]=u.useState(!1),[P,Xe]=u.useState(!1),[ht,cn]=u.useState(AT),[Ze]=u.useState(_T),[an]=u.useState(zT),[Na]=u.useState(DT),[ee,Ne]=u.useState(Na.selections),We=u.useRef({...Na.topologyFingerprints}),dt=u.useRef({...Na.selections}),[et,At]=u.useState(null),[tt,Tt]=u.useState([]),[Lt,Jt]=u.useState(Ze.fps),[gn,Ht]=u.useState(""),[vn,Vt]=u.useState(0),[wr,Sr]=u.useState(0),[yn,en]=u.useState(()=>new Set),kn=u.useRef(new Map),ga=u.useRef(new Map),Ta=u.useRef(new Map),ii=u.useRef(new Map),jn=u.useRef(new Map),Vs=u.useRef(new Map),An=u.useRef(new Set),qn=u.useRef(new Map),na=u.useRef(""),Hl=u.useRef(""),li=u.useRef(null),Ws=u.useRef(null),Xa=u.useRef(new Map),Fl=u.useRef(null),aa=u.useRef(0),oi=u.useRef(new Map),[Wi,Bl]=u.useState({}),Es=u.useRef(new Map),[Yi,Ho]=u.useState({}),[Cr,Qa]=u.useState(!1),$l=u.useRef(null),Za=u.useRef(null),sa=u.useRef(0),kr=u.useRef(""),Ys=u.useRef(new Map),ms=u.useRef(new Map),Ea=u.useRef(performance.now()),Ks=u.useRef(new Map),Rs=u.useRef(new Map),Ra=u.useRef(new Map),Ja=u.useRef(new Map),Ki=u.useRef(new Map),jr=u.useRef(0),Xi=u.useRef(0),Xs=u.useRef(0),Da=u.useRef(0),Qi=u.useRef(0),ci=u.useRef(new Map),In=u.useRef(new Map),hs=u.useCallback((y,N)=>{const _=qa(y,N),O=performance.now();Ys.current.delete(_),ms.current.delete(_),Ks.current.delete(_),Ki.current.delete(_),In.current.delete(_),Es.current.delete(_),oi.current.delete(_),Ho(D=>{if(!Object.prototype.hasOwnProperty.call(D,_))return D;const ae={...D};return delete ae[_],ae}),Bl(D=>{if(!Object.prototype.hasOwnProperty.call(D,_))return D;const ae={...D};return delete ae[_],ae}),y==="control"?(Rs.current.delete(_),Ra.current.set(_,O)):Ja.current.set(_,O)},[]),[ps,Ds]=u.useState(null),[Pl,ui]=u.useState(!1),[ff,ra]=u.useState([]),[La,Ul]=u.useState("all"),[gs,Fo]=u.useState([]),[uu,du]=u.useState(!1),[ql,Gl]=u.useState(!0),Bo=u.useRef(null),fu=u.useRef(null),mf=u.useRef(null),hf=u.useRef(null),$o=u.useRef(!1),Po=u.useRef(!1),Uo=u.useRef(!1),Zi=u.useRef(0),Qs=u.useRef(0),Vl=ge==="settings",qo=ge==="pricing",mu=ge==="doc",hu=ge==="support",Ji=ge==="share-files",Go=ge==="agents",Ar=!Vl&&!qo&&!mu&&!hu&&!Ji&&!Go,ct=ge==="control";u.useEffect(()=>{Ar||(mt(!1),Ln(!1),nt(!1))},[Ar]);const Ct=Ze.frameMode==="mode4-h264-atlas",va=I||an.frameMode,di=va!=="mode3-h264-hw"?Math.min(30,an.fps):an.fps,pf=k.find(y=>y.deviceId===$)?.platform,pu=aE(pf)?{maxWidth:1280,maxHeight:720}:{maxWidth:3840,maxHeight:2160},fi={maxWidth:Math.min(va!=="mode3-h264-hw"?1920:3840,pu.maxWidth,an.maxWidth),maxHeight:Math.min(va!=="mode3-h264-hw"?1080:2160,pu.maxHeight,an.maxHeight)};u.useEffect(()=>{if(w?.started){if(!n?.access_token){qe("/api/remote/license",{method:"DELETE"},M).catch(()=>{});return}qe("/api/remote/license/sync",{method:"POST",headers:{Authorization:`Bearer ${n.access_token}`}},M).catch(y=>{console.warn("[LiveDesk] Hub license sync failed",y)})}},[A?.updated_at,M,n?.access_token,w?.started]),u.useEffect(()=>{!n?.access_token||!w?.started||w.runtimeRole!=="hub"||!w.deviceId||(async()=>{try{const{data:y,error:N}=await gt.rpc("register_livedesk_device",{p_device_id:w.deviceId,p_device_name:w.deviceName||w.deviceId,p_role:"hub",p_assigned_hub_id:null,p_platform:navigator.platform||navigator.userAgent,p_os_version:navigator.userAgent,p_app_version:w.managerVersion||w.managerPackage||""}),_=Array.isArray(y)?y[0]:y;(N||_?.ok===!1)&&console.warn("[LiveDesk] Hub device role registration failed",N||_?.reason||"unknown-error")}catch(y){console.warn("[LiveDesk] Hub device role registration failed",y)}})()},[n?.access_token,w?.deviceId,w?.deviceName,w?.managerPackage,w?.managerVersion,w?.runtimeRole,w?.started]),u.useEffect(()=>{!n?.access_token||!n.refresh_token||!w?.started||w.runtimeRole!=="hub"||Uo.current||(Uo.current=!0,qe("/api/auth/session",{method:"POST",body:JSON.stringify({accessToken:n.access_token,refreshToken:n.refresh_token,expiresAt:n.expires_at,user:{id:n.user.id,email:n.user.email}})},M||hn).then(y=>{y.hostTarget?.ok===!1&&console.warn("[LiveDesk] Hub host target publish is waiting for a valid Hub role:",y.hostTarget.error||"unknown-error"),C()}).catch(y=>{console.warn("[LiveDesk] Hub session sync failed:",y)}).finally(()=>{Uo.current=!1}))},[M,C,n?.access_token,n?.expires_at,n?.refresh_token,n?.user?.email,n?.user?.id,w?.runtimeRole,w?.started]);const at=u.useMemo(()=>k.filter(y=>y.connected),[k]),gu=La==="all"?at.length:gs.filter(y=>at.some(N=>N.deviceId===y)).length,gf=u.useMemo(()=>at.reduce((y,N)=>Math.max(y,$b(N.slotNumber)),0),[at]),Vo=Math.max(WN,at.length,gf),Mr=u.useMemo(()=>{const y=Array(Vo).fill(null),N=[];for(const _ of at){const O=$b(_.slotNumber);O>0&&O<=Vo&&!y[O-1]?y[O-1]=_:N.push(_)}for(const _ of N){const O=y.findIndex(D=>!D);O<0?y.push(_):y[O]=_}return y},[at]),vu=u.useMemo(()=>Mr.flatMap((y,N)=>y?[{device:y,slotNumber:N+1}]:[]),[Mr]),el=u.useMemo(()=>at.map(y=>y.deviceId),[at]),es=u.useMemo(()=>new Set(V),[V]),ya=u.useMemo(()=>at.filter(y=>es.has(y.deviceId)),[at,es]),Nr=u.useMemo(()=>k.find(y=>y.deviceId===K)||ya[0]||null,[k,K,ya]),zn=u.useMemo(()=>k.find(y=>y.deviceId===$)||null,[$,k]),vs=zn?ea(ee[zn.deviceId],Jn(zn)):0,Fe=ct?zn:Nr,Wo=u.useMemo(()=>new Set(gs),[gs]),mi=u.useMemo(()=>La==="all"?at.map(y=>y.deviceId):at.filter(y=>Wo.has(y.deviceId)).map(y=>y.deviceId),[at,Wo,La]),vf=Fe?eu(Fe,"control"):!1,Yo=ei(Fe?.platform)&&(Fe?.capabilities?.controlPermission===!1||Fe?.capabilities?.accessibilityPermission===!1),tl=Fe?eu(Fe,"audio")||eu(Fe,"remoteAudio"):!1,Ko=(Array.isArray(Fe?.capabilities?.agentTools)?Fe.capabilities.agentTools.map(String):[]).includes("system.power"),Gt=T?A?.plan==="pro"?"pro":"ltd":"free",hi=Al[Gt],Wl=hi.limit,Ls=u.useMemo(()=>{if(!Number.isFinite(Wl))return new Set(at.map(N=>N.deviceId));const y=Fe?[Fe,...at.filter(N=>N.deviceId!==Fe.deviceId)]:at;return new Set(y.slice(0,Wl).map(N=>N.deviceId))},[Wl,at,Fe]),yu=!Fe||Ls.has(Fe.deviceId),bu=Gt==="free",Wt=yu,Tr=Gt==="ltd"?`Plus includes up to ${Vc} Clients. Upgrade to Pro for commercial use and larger walls.`:`Free includes up to ${Gc} Clients. Upgrade to Plus for ${Vc} devices and no ads.`,xu=Gt==="free",ys=Wt&&!!Fe&&vf,Xo=Wt&&!!Fe&&tl,Yl=u.useMemo(()=>Mr.filter(y=>!!y?.connected).slice(0,HT(ze)).map(y=>y.deviceId),[Mr,ze]),Kl=u.useMemo(()=>ze==="list"?[]:Ct?el:Ze.visibleOnlyLive?tt.length>0?tt:Yl:el,[Yl,Ct,tt,Ze.visibleOnlyLive,el,ze]),ts=u.useMemo(()=>ph(Kl.filter(y=>Ls.has(y))),[Ls,Kl]),Zs=u.useMemo(()=>ts.filter(y=>y!==gn),[ts,gn]),Xl=u.useMemo(()=>new Map(at.map(y=>[y.deviceId,y.platform])),[at]),Kt=z1(Ct,ts.length),bn=u.useMemo(()=>Ct?Zs.filter(y=>Pc({mode4Enabled:Ct,directMode4WallEnabled:Kt,isMac:ei(Xl.get(y))})==="atlas"):[],[Kt,Ct,Xl,Zs]),ia=u.useMemo(()=>Zs.filter(y=>Pc({mode4Enabled:Ct,directMode4WallEnabled:Kt,isMac:ei(Xl.get(y))})==="direct"),[Kt,Ct,Xl,Zs]);u.useEffect(()=>{const y=new Set(ts);en(N=>{let _=!1;const O=new Set;for(const D of N)y.has(D)?O.add(D):_=!0;return _?O:N})},[ts]);const wu=u.useMemo(()=>{const y=D=>at.find(ae=>ae.deviceId===D)||null,N=D=>ea(ee[D],y(D)?Jn(y(D)):0),_=(D,ae=N(D))=>D===H&&(Y==="connecting"||Y==="active"||Y==="warm")?jn.current.get(D)||ga.current.get(D)||null:Ct?Xa.current.get(D)||kn.current.get(D)||null:kn.current.get(D)||null,O=(D,ae=N(D))=>{const de=y(D);return`${de?Ga(de):D} · Monitor ${ae+1}`};return{resolveCurrentTarget(){if(ct&&zn){const je=vs;return{type:"control",deviceIds:[zn.deviceId],monitorIndex:je,label:`${Ga(zn)} · Monitor ${je+1}`,sourceCanvas:_(zn.deviceId,je),signature:`control:${zn.deviceId}:${je}:${an.frameMode}`}}const D=ya.map(je=>je.deviceId).filter(je=>Zs.includes(je)),ae=Nr&&Zs.includes(Nr.deviceId)?Nr.deviceId:"",de=D.length>0?D:ae?[ae]:Zs,Re=D.length===0&&!ae;if(de.length===0)return null;if(de.length===1){const je=de[0],Ie=N(je);return{type:Re?"wall":D.length>1?"selection":"device",deviceIds:de,monitorIndex:Ie,label:Re?"Wall":O(je,Ie),sourceCanvas:Re?null:_(je,Ie),signature:`${Re?"wall":"device"}:${je}:${Ie}:${Ct?"atlas":"direct"}`}}return{type:D.length>1?"selection":"wall",deviceIds:de,label:D.length>1?`${de.length} selected Clients`:"Wall",sourceCanvas:null,signature:`${D.length>1?"selection":"wall"}:${de.join(",")}:${ze}`}},getDeviceCanvas:_,composeWall(D,ae={}){return IN(D.map(de=>{const Re=N(de);return{deviceId:de,label:O(de,Re),canvas:_(de,Re)}}),ae)}}},[at,zn,H,Y,an.frameMode,kn,Ct,ee,vs,ya,ct,Zs,Nr,ze]),Qo=VN({hubUrl:M,sourceRegistry:wu,onToast:Pe}),Er=u.useMemo(()=>Wt&&H&&Ls.has(H)&&(Y==="connecting"||Y==="active"||Y==="warm")?[H]:[],[H,Y,Wt,Ls]),Zo=u.useMemo(()=>ph([...ts,...Er]),[Er,ts]),ns=u.useMemo(()=>FT(Ze,ze),[Ze,ze]),_a=O1({mode4Enabled:Ct,directMode4WallEnabled:Kt,adaptiveFps:Lt,directDeviceCount:ia.length,adaptive:Ze.adaptiveLoadShedding}),nl=H1({deviceCount:bn.length,targetFps:Lt,adaptive:Ze.adaptiveLoadShedding});Ct&&bn.length>0;const un=u.useCallback((y,N,_)=>{if(y.width<=0||y.height<=0)return!1;const O=_?Math.min(1,_.maxWidth/y.width,_.maxHeight/y.height):1,D=Math.max(1,Math.round(y.width*O)),ae=Math.max(1,Math.round(y.height*O));(N.width!==D||N.height!==ae)&&(N.width=D,N.height=ae);const de=N.getContext("2d",{alpha:!1});return de?(de.imageSmoothingEnabled=!0,de.imageSmoothingQuality="high",de.drawImage(y,0,0,y.width,y.height,0,0,N.width,N.height),!0):!1},[]),Ia=u.useCallback(y=>{y&&en(N=>{if(N.has(y))return N;const _=new Set(N);return _.add(y),_})},[]),al=u.useCallback(y=>{y&&en(N=>{if(!N.has(y))return N;const _=new Set(N);return _.delete(y),_})},[]),za=u.useCallback((y,N)=>{if(!N||N.width<=0||N.height<=0)return!1;const _=document.createElement("canvas");return _.width=N.width,_.height=N.height,un(N,_)?(Ta.current.set(y,_),Ia(y),!0):!1},[Ia,un]),ba=u.useCallback(y=>{const N=ii.current.get(y);N!==void 0&&(window.cancelAnimationFrame(N),ii.current.delete(y))},[]),Js=u.useCallback(y=>{ba(y),Ta.current.delete(y)},[ba]),sl=u.useCallback((y,N)=>{ba(y);const _=()=>{const O=Ta.current.get(y),D=kn.current.get(y)===N||Xa.current.get(y)===N;if(!O||!D){ii.current.delete(y);return}un(O,N),ii.current.set(y,window.requestAnimationFrame(_))};_()},[un,ba]);u.useEffect(()=>()=>{for(const y of ii.current.values())window.cancelAnimationFrame(y);ii.current.clear()},[]);const Gn=u.useCallback((y,N=!1)=>{if(!An.current.has(y))return!1;const _=performance.now(),O=qn.current.get(y)||0,D=Math.max(1,Math.min(Va.controlWallReuseMaxFps,di)),ae=di>D?Math.max(0,1e3/D-2):0;if(!N&&_-O<ae)return!1;const de=jn.current.get(y)||ga.current.get(y),Re=kn.current.get(y)||Xa.current.get(y);return!de||!Re||!un(de,Re,ns)?!1:(qn.current.set(y,_),Ia(y),Js(y),!0)},[di,ns,Ia,un,Js]);u.useEffect(()=>{na.current=gn},[gn]);const Jo=u.useCallback((y,N)=>{const _=kn.current.get(y);if(_&&_!==N&&(ba(y),za(y,_),So(_)),N){kn.current.set(y,N);const O=Ta.current.get(y);O&&un(O,N)&&(Ia(y),sl(y,N)),Gn(y,!0);return}kn.current.delete(y),Ta.current.has(y)||al(y)},[al,sl,Ia,un,Gn,za,ba]),Su=u.useCallback((y,N)=>{const _=ga.current.get(y);if(_&&_!==N&&So(_),N){ga.current.set(y,N);const O=H===y&&re?jn.current.get(y):null;O&&un(O,N)&&y===$&&Qa(!0);return}ga.current.delete(y)},[$,H,re,un]),rl=u.useCallback((y,N,_)=>{const O=performance.now(),D=qa(y,_.deviceId);let ae=_;if(y==="control"&&_.visualChanged){const Re=w1(_.deviceId),je=ci.current.get(_.deviceId)||0,Ie=Re?O-Re.sentAt:Number.POSITIVE_INFINITY;Re&&Re.inputSeq>je&&Ie<=jb&&(ae={..._,inputFeedbackSeq:Re.inputSeq,inputFeedbackMs:Math.max(0,Ie),inputFeedbackAt:O},ci.current.set(_.deviceId,Re.inputSeq))}ms.current.set(D,O);const de=oi.current.get(D)||{totalFrames:0,totalChangedFrames:0,sampleFrames:0,sampleChangedFrames:0,sampleStartedAt:O,intervalCount:0,intervalSumMs:0,minIntervalMs:Number.POSITIVE_INFINITY,maxIntervalMs:0,intervalSamplesMs:[],seqGapCount:0,maxSeqGap:0,sampleDecoderInputBase:0,sampleDecoderOutputBase:0};y==="control"&&ae.inputFeedbackMs===void 0&&de.lastInfo?.inputFeedbackMs!==void 0&&de.lastInfo.inputFeedbackAt!==void 0&&O-de.lastInfo.inputFeedbackAt<=jb&&(ae={...ae,inputFeedbackSeq:de.lastInfo.inputFeedbackSeq,inputFeedbackMs:de.lastInfo.inputFeedbackMs,inputFeedbackAt:de.lastInfo.inputFeedbackAt}),de.totalFrames+=1,de.sampleFrames+=1,Rb(de,O,ae.frameSeq),ae.visualChanged&&(de.totalChangedFrames+=1,de.sampleChangedFrames+=1),de.lastInfo=ae,oi.current.set(D,de)},[]),Ql=u.useCallback((y,N)=>{rl("wall",y,N),y.kind==="live"&&(Ia(y.deviceId),Js(y.deviceId));const _=re&&H===y.deviceId;na.current!==y.deviceId&&!_&&(An.current.delete(y.deviceId),qn.current.delete(y.deviceId))},[H,re,Ia,rl,Js]),Cu=u.useCallback((y,N)=>{rl("control",y,N),z(ae=>ae&&"");const _=jn.current.get(y.deviceId),O=ga.current.get(y.deviceId);_&&_.width>0&&_.height>0&&(O&&un(_,O),Vs.current.set(y.deviceId,{...y,payload:new Uint8Array(0),byteLength:0}),An.current.add(y.deviceId),y.deviceId===H&&(oe(!0),U(ae=>ae==="connecting"?"active":ae)),Gn(y.deviceId));const D=$l.current;!D||!uT(D,y)||Qa(!0)},[H,un,Gn,rl]);u.useEffect(()=>{const y=window.setInterval(()=>{const N=performance.now(),_={};for(const[O,D]of oi.current.entries()){const ae=Math.max(1,N-D.sampleStartedAt);_[O]={totalFrames:D.totalFrames,totalChangedFrames:D.totalChangedFrames,framesPerSecond:D.sampleFrames*1e3/ae,changedFramesPerSecond:D.sampleChangedFrames*1e3/ae,averageIntervalMs:D.intervalCount>0?D.intervalSumMs/D.intervalCount:0,minIntervalMs:Number.isFinite(D.minIntervalMs)?D.minIntervalMs:0,maxIntervalMs:D.maxIntervalMs,p95IntervalMs:mh(D.intervalSamplesMs,.95),intervalCount:D.intervalCount,seqGapCount:D.seqGapCount,maxSeqGap:D.maxSeqGap,decoderInputFps:Math.max(0,(Number(D.lastInfo?.decoderInputCount||0)-D.sampleDecoderInputBase)*1e3/ae),decoderOutputFps:Math.max(0,(Number(D.lastInfo?.decoderOutputCount||0)-D.sampleDecoderOutputBase)*1e3/ae),sampleMs:ae,lastInfo:D.lastInfo},D.sampleDecoderInputBase=Number(D.lastInfo?.decoderInputCount||0),D.sampleDecoderOutputBase=Number(D.lastInfo?.decoderOutputCount||0),D.sampleChangedFrames=0,Eb(D,N)}Bl(_)},1e3);return()=>window.clearInterval(y)},[]);const pi=u.useCallback((y,N)=>{const _=performance.now(),O=qa(y,N.deviceId);Ys.current.set(O,_),In.current.delete(O),y==="control"?Rs.current.delete(O):Ja.current.delete(O);const D=Es.current.get(O)||{totalFrames:0,sampleFrames:0,sampleStartedAt:_,intervalCount:0,intervalSumMs:0,minIntervalMs:Number.POSITIVE_INFINITY,maxIntervalMs:0,intervalSamplesMs:[],seqGapCount:0,maxSeqGap:0,sampleBytes:0,sampleH264Frames:0,sampleTinyFrames:0,sampleRepeatedFrames:0,sourceSendIntervalsMs:[]};if(D.totalFrames+=1,D.sampleFrames+=1,D.sampleBytes+=Math.max(0,Number(N.byteLength||0)),String(N.frameMode||N.mode||N.codec||N.mimeType||"").toLowerCase().includes("h264")){D.sampleH264Frames+=1,Number(N.byteLength||0)<=iT&&(D.sampleTinyFrames+=1);const je=String(N.contentHash||"");je&&D.lastContentHash===je&&(D.sampleRepeatedFrames+=1),D.lastContentHash=je}const Re=Number(N.agentSendGapMs||0);Re>0&&Re<1e3&&Aw(D.sourceSendIntervalsMs,Re),D.lastFrame=N,Rb(D,_,N.frameSeq),Es.current.set(O,D)},[]);u.useEffect(()=>{const y=window.setInterval(()=>{const N=performance.now(),_={};for(const[O,D]of Es.current.entries()){const ae=Math.max(1,N-D.sampleStartedAt);_[O]={totalFrames:D.totalFrames,framesPerSecond:D.sampleFrames*1e3/ae,averageIntervalMs:D.intervalCount>0?D.intervalSumMs/D.intervalCount:0,minIntervalMs:Number.isFinite(D.minIntervalMs)?D.minIntervalMs:0,maxIntervalMs:D.maxIntervalMs,p95IntervalMs:mh(D.intervalSamplesMs,.95),intervalCount:D.intervalCount,seqGapCount:D.seqGapCount,maxSeqGap:D.maxSeqGap,averageFrameBytes:D.sampleFrames>0?D.sampleBytes/D.sampleFrames:0,tinyFrameRatio:D.sampleH264Frames>0?D.sampleTinyFrames/D.sampleH264Frames:0,repeatedFrameRatio:D.sampleH264Frames>0?D.sampleRepeatedFrames/D.sampleH264Frames:0,sourceSendAverageMs:D.sourceSendIntervalsMs.length>0?D.sourceSendIntervalsMs.reduce((de,Re)=>de+Re,0)/D.sourceSendIntervalsMs.length:0,sourceSendP95Ms:mh(D.sourceSendIntervalsMs,.95),sourceSendSampleCount:D.sourceSendIntervalsMs.length,sampleMs:ae,lastFrame:D.lastFrame},D.sampleBytes=0,D.sampleH264Frames=0,D.sampleTinyFrames=0,D.sampleRepeatedFrames=0,D.sourceSendIntervalsMs.length=0,Eb(D,N)}Ho(_)},1e3);return()=>window.clearInterval(y)},[]);const ec=u.useCallback(y=>{pi("wall",y);const N=kn.current.get(y.deviceId);if(!N)return;const _=F1(Ct,Ze.h264DecodeAcceleration);Qm(N,y,()=>kn.current.get(y.deviceId)!==N,Ql,_.acceleration,_.latencyMode).catch(()=>{})},[Ql,Ct,pi,Ze.h264DecodeAcceleration]),tc=u.useCallback(y=>{pi("control",y);let N=jn.current.get(y.deviceId);N||(N=document.createElement("canvas"),N.setAttribute("aria-hidden","true"),jn.current.set(y.deviceId,N));const _=N,O=Ae.current;Qm(_,y,()=>jn.current.get(y.deviceId)!==_,Cu,an.h264DecodeAcceleration,"interactive").catch(D=>{Ae.current!==O||jn.current.get(y.deviceId)!==_||H!==y.deviceId||z(D instanceof Error?D.message:String(D||"Control H.264 decode failed"))})},[H,an.h264DecodeAcceleration,Cu,pi]),nc=u.useCallback(y=>{const N=Ws.current;N&&N!==y&&So(N),Ws.current=y,jr.current=0,Xi.current=0,Xs.current=0,Da.current=0,Qi.current=0,aa.current=0},[]),On=u.useCallback(()=>{const y=new Set,N=Ws.current,_=Fl.current;if(!N||!_||N.width<=0||N.height<=0||aa.current!==_.layoutVersion)return y;for(const O of _.tiles){const D=Xa.current.get(O.deviceId);if(!D||O.width<=0||O.height<=0)continue;const ae=Math.max(1,Math.round(O.width)),de=Math.max(1,Math.round(O.height)),Re=lT,je=oT;(D.width!==Re||D.height!==je)&&(D.width=Re,D.height=je);const Ie=D.getContext("2d",{alpha:!1});if(Ie){Ie.imageSmoothingEnabled=!0,Ie.imageSmoothingQuality="high";try{Ie.drawImage(N,Math.round(O.x),Math.round(O.y),ae,de,0,0,Re,je),y.add(O.deviceId)}catch{}}}return y},[]),Zl=u.useCallback((y,N)=>{if(N){Xa.current.set(y,N);const O=Ta.current.get(y);O&&un(O,N)&&(Ia(y),sl(y,N)),window.requestAnimationFrame(()=>{On().has(y)&&Js(y)}),window.requestAnimationFrame(()=>Gn(y,!0));return}const _=Xa.current.get(y);ba(y),za(y,_),Xa.current.delete(y),Ta.current.has(y)||al(y)},[al,sl,Ia,On,un,Gn,za,Js,ba]),xa=u.useCallback((y,N)=>{Xs.current=performance.now(),aa.current=Number(y.layoutVersion||0),On();const _=Fl.current?.tiles||[],O=_.length>0?Math.round(y.byteLength/_.length):y.byteLength;for(const D of _){const ae={...y,deviceId:D.deviceId,width:D.width,height:D.height,sourceWidth:D.width,sourceHeight:D.height,byteLength:O};Ql(ae,{...N,deviceId:D.deviceId,width:D.width,height:D.height,byteLength:O}),na.current===D.deviceId&&Gn(D.deviceId,!0)}},[Ql,On,Gn]),gi=u.useCallback(y=>{const N=performance.now();jr.current<=0&&(jr.current=N),Xi.current=N;const _=Ws.current;if(!_)return;const O=Fl.current?.tiles||[],D=O.length>0?Math.round(y.byteLength/O.length):y.byteLength;for(const ae of O)pi("wall",{...y,deviceId:ae.deviceId,width:ae.width,height:ae.height,sourceWidth:ae.width,sourceHeight:ae.height,byteLength:D});Qm(_,y,()=>Ws.current!==_,xa).catch(()=>{})},[xa,pi]),ac=u.useMemo(()=>({fps:_a,maxWidth:ns.maxWidth,maxHeight:ns.maxHeight,quality:Ze.quality,mode:Ct?"mode3-h264-hw":Ze.frameMode,frameMode:Ct?"mode3-h264-hw":Ze.frameMode,streamPurpose:"wall",monitorSelections:ee,onFrame:ec}),[_a,ec,ns,Ct,ee,Ze]),il=u.useCallback(y=>{if(y.deviceId!==H||y.streamPurpose!=="control"||y.monitorIndex!==vs)return;const N=at.find(_=>_.deviceId===y.deviceId);!N?.sessionId||N.sessionId!==y.sessionId||(xe(y),U(_=>_==="connecting"?"active":_))},[at,H,vs]);u.useEffect(()=>{xe(null)},[H,ce,va,vs]);const Rr=ce?`${ce}-monitor-${vs}`:"",sc=u.useMemo(()=>({fps:di,maxWidth:fi.maxWidth,maxHeight:fi.maxHeight,quality:an.quality,mode:va,frameMode:va,streamPurpose:"control",forceRestart:Rr!=="",forceRestartToken:Rr,restartReason:Rr?"control-session-or-monitor-change":"",monitorSelections:ee,onFrame:tc,onStreamReady:il}),[Rr,an.quality,tc,di,va,fi.maxHeight,fi.maxWidth,il,ee]),{frames:er,state:vi,error:ll,stats:wa,restartLive:Jl}=My(ia,Ze.autoStartLive&&ia.length>0,M,ac),{frame:xn,layout:_s,state:as,stats:ku,refreshInputs:ol}=C1(bn,Ct&&Ze.autoStartLive&&bn.length>0,M,{fps:Va.atlasOutputFps,inputFps:nl,width:1920,height:1080,tileWidth:ns.maxWidth,tileHeight:ns.maxHeight,captureDeviceIds:bn,monitorSelections:ee,onFrame:gi});u.useEffect(()=>{Fl.current=_s,_s&&window.requestAnimationFrame(On)},[_s,On]),u.useEffect(()=>{if(!Ct||as!=="open"){jr.current=0,Xi.current=0,Xs.current=0;return}const y=window.setInterval(()=>{const N=Ws.current,_=performance.now();if(!N||document.visibilityState!=="visible"||_-Ea.current<fh)return;const O=Xi.current,D=jr.current;if(O<=0||D<=0||_-O>500)return;const ae=Xs.current||D,de=_-ae;if(de>=nT&&_-Qi.current>=sT){Bc(N)&&(Qi.current=_,Da.current=_);return}de>=tT&&_-Da.current>=aT&&i1(N)&&(Da.current=_)},250);return()=>window.clearInterval(y)},[as,Ct]);const{frames:bs,state:cl,error:yi,stats:rc,restartLive:ul,reconnectLive:Sa}=My(Er,Er.length>0,M,sc);u.useEffect(()=>{const y=dt.current,N=new Set([...Object.keys(y),...Object.keys(ee)]);for(const _ of N){const O=Math.max(0,Math.floor(Number(y[_])||0)),D=Math.max(0,Math.floor(Number(ee[_])||0));O!==D&&(hs("wall",_),hs("control",_),Bc(kn.current.get(_)||null),Bc(jn.current.get(_)||null))}dt.current={...ee}},[ee,hs]);const xs=u.useMemo(()=>{const y=(_,O)=>{const D=at.find(de=>de.deviceId===_),ae=ea(ee[_],D?Jn(D):1);return!Number.isFinite(Number(O.monitorIndex))||Number(O.monitorIndex)===ae},N=new Map;for(const[_,O]of er.entries())y(_,O)&&N.set(_,O);if(Ct&&xn&&bn.length>0){const _=new Map((_s?.tiles||[]).map(D=>[D.deviceId,D])),O=Math.max(1,_.size);for(const D of bn){const ae=_.get(D);ae&&N.set(D,{...xn,deviceId:D,width:ae.width,height:ae.height,sourceWidth:ae.width,sourceHeight:ae.height,byteLength:Math.round(xn.byteLength/O)})}}for(const[_,O]of bs.entries())y(_,O)&&N.set(_,O);for(const _ of Kl){if(!An.current.has(_)||N.has(_))continue;const O=Vs.current.get(_);O&&y(_,O)&&N.set(_,O)}return N},[xn,_s,at,bs,bn,Ct,ee,er,Kl,wr]),tr=ct&&Er.length>0?cl:bn.length>0?as:vi,Dr=ct&&Er.length>0?rc:bn.length>0?ku:wa;u.useCallback((y,N="browser-stale")=>{const _=Jl(y,N),O=ul(y,N);return _||O},[ul,Jl]),u.useEffect(()=>{Ct&&bn.length>0&&!ct&&ol()},[bn.length,Ct,ol,ct]);const nr=u.useCallback((y,N,_)=>{const O=N==="control"?jn.current.get(y)||null:kn.current.get(y)||null;if(!O||!Bc(O))return!1;const D=performance.now();return Ki.current.set(qa(N,y),D),!0},[]),Vn=u.useCallback(y=>{if(va!=="mode3-h264-hw"||!H||I)return!1;const N=at.find(Re=>Re.deviceId===H)||zn,_=String(N?.platform||"").toLowerCase();if(!["win32","windows","win32nt"].includes(_))return!1;const O="mode5-lzo-delta",D=qa("control",H),ae=performance.now();Ra.current.set(D,ae),Ys.current.delete(D),ms.current.delete(D),Ks.current.delete(D),Rs.current.delete(D),In.current.delete(D),Bc(jn.current.get(H)||null),Sa([H],"control-decode-fallback"),ne(O),J(`control-${O}-fallback-${H}-${Date.now()}`),oe(!1),Qa(!1);const de=y==="no first frame"||y==="control-first-frame-timeout";return z(Re=>Re||(de?`Control frame transport timeout; retrying Control with Mode 5 (${y})`:`H.264 decode failed; retrying Control with Mode 5 (${y})`)),U("connecting"),!0},[at,zn,H,I,va,Sa]);u.useEffect(()=>{const y=ct?zn:null;if(y){$l.current={deviceId:y.deviceId,monitorIndex:vs,frameMode:va,startedAtEpochMs:Date.now()},Qa(H===y.deviceId&&re);return}$l.current=null,Qa(!1)},[zn?.sessionId,$,H,re,va,vs,ct]);const Hn=u.useCallback(async y=>{const N=k.find(ae=>ae.deviceId===y);Pc({mode4Enabled:Ct,directMode4WallEnabled:Kt,isMac:ei(N?.platform)});const _=ea(ee[y],N?Jn(N):1),O={fps:Ct?_a:Lt,maxWidth:ns.maxWidth,maxHeight:ns.maxHeight,quality:Ze.quality,mode:Ct?"mode3-h264-hw":Ze.frameMode,frameMode:Ct?"mode3-h264-hw":Ze.frameMode,streamPurpose:"wall",monitorIndex:_},D=await qe(`/api/remote/devices/${encodeURIComponent(y)}/live/start`,{method:"POST",body:JSON.stringify(O)},M);if(D?.ok===!1)throw new Error(D.error||"wall-stream-start-failed");return D},[Lt,nl,k,Kt,_a,ns.maxHeight,ns.maxWidth,M,Ct,ee,Ze.frameMode,Ze.quality]),ss=u.useCallback(async y=>{const N=k.find(O=>O.deviceId===y);Pc({mode4Enabled:Ct,directMode4WallEnabled:Kt,isMac:ei(N?.platform)});const _=await qe(`/api/remote/devices/${encodeURIComponent(y)}/live/stop`,{method:"POST",body:JSON.stringify({streamPurpose:"wall",reason:"control-active"})},M);if(_?.ok===!1)throw new Error(_.error||"wall-stream-stop-failed");return _},[k,Kt,M,Ct]),Wn=u.useCallback(async(y,N="control-session-stop")=>{const _=await qe(`/api/remote/devices/${encodeURIComponent(y)}/live/stop`,{method:"POST",body:JSON.stringify({streamPurpose:"control",reason:N})},M);if(_?.ok===!1)throw new Error(_.error||"control-stream-stop-failed");return _},[M]),Yn=u.useCallback(y=>{const N=jn.current.get(y);N&&(Ta.current.has(y)||kn.current.has(y)||Xa.current.has(y)||za(y,N),So(N),jn.current.delete(y));const _=ga.current.get(y);_&&So(_),Vs.current.delete(y),An.current.delete(y),qn.current.delete(y)},[za]);u.useEffect(()=>{const y=$&&at.find(D=>D.deviceId===$)||null;if(!ct||!Wt||!y){kr.current&&(++sa.current,kr.current="");return}if(H===y.deviceId&&(Y==="active"||Y==="warm"||Y==="connecting")){U(D=>D==="warm"?"active":D);return}if(kr.current===y.deviceId)return;const N=++sa.current,_=H;kr.current=y.deviceId,(async()=>{if(_){if(U("stopping"),await Wn(_,"control-session-switch").catch(()=>{}),N!==sa.current)return;const de=na.current;if(de&&de!==y.deviceId)try{await Hn(de)}catch(Re){Pe(Re instanceof Error?Re.message:String(Re))}finally{na.current===de&&(na.current="",Ht(""))}if(N!==sa.current)return;Yn(_)}if(N!==sa.current)return;if(Ub(y)){try{await ss(y.deviceId)}catch(de){N===sa.current&&(kr.current="",U("error"),Pe(de instanceof Error?de.message:String(de)));return}if(N!==sa.current){await Hn(y.deviceId).catch(()=>{});return}na.current=y.deviceId,Ht(y.deviceId),An.current.add(y.deviceId)}const D=qa("control",y.deviceId),ae=`control-session-${N}-${y.deviceId}`;Ra.current.set(D,performance.now()),Ys.current.delete(D),ms.current.delete(D),In.current.delete(D),Rs.current.delete(D),ne(""),z(""),J(ae),pe(y.deviceId),oe(!1),U("connecting"),sa.current===N&&kr.current===y.deviceId&&(kr.current="")})()},[at,$,H,Y,Wt,Yn,Pe,ct,Hn,Wn,ss]),u.useEffect(()=>{if(!H||at.some(_=>_.deviceId===H))return;const y=H,N=++sa.current;U("stopping"),Wn(y,"control-device-disconnected").catch(()=>{}).finally(()=>{N===sa.current&&(Yn(y),oe(!1),J(""),ne(""),z(""),pe(_=>_===y?"":_),U("idle"))})},[at,H,Yn,Wn]);const ws=Ub(at.find(y=>y.deviceId===H));u.useEffect(()=>{if(Za.current!==null&&(window.clearTimeout(Za.current),Za.current=null),!H||Y==="idle"||Y==="stopping"||Y==="error"||ct||ge==="wall"){H&&!ct&&ge==="wall"&&U(_=>_==="warm"||_==="active"?"active":_);return}U(_=>_==="idle"||_==="stopping"?_:"warm");const y=H,N=++sa.current;return Za.current=window.setTimeout(()=>{Za.current=null,U("stopping"),Wn(y,"control-session-warm-timeout").catch(()=>{}).finally(()=>{N===sa.current&&(Yn(y),oe(!1),J(""),ne(""),z(""),pe(_=>_===y?"":_),U("idle"))})},rT),()=>{Za.current!==null&&(window.clearTimeout(Za.current),Za.current=null)}},[ge,H,Y,Yn,ct,Wn]),u.useEffect(()=>()=>{Za.current!==null&&window.clearTimeout(Za.current)},[]),u.useEffect(()=>{const N=H&&(ws&&(Y==="connecting"||Y==="active"||Y==="warm"||Y==="stopping")||re&&(Y==="active"||Y==="warm"))?H:"";if(Hl.current=N,li.current)return;const _=async()=>{for(;;){const D=Hl.current,ae=na.current;if(ae===D)return;if(ae){try{await Hn(ae)}catch(de){Pe(de instanceof Error?de.message:String(de))}finally{na.current===ae&&(na.current="",Ht(""))}continue}if(!D)return;try{await ss(D)}catch(de){Hl.current=na.current,Pe(de instanceof Error?de.message:String(de));return}So(kn.current.get(D)||null),na.current=D,Ht(D),An.current.add(D)}};let O;O=_().finally(()=>{li.current===O&&(li.current=null),na.current!==Hl.current&&Vt(D=>D+1)}),li.current=O},[H,re,Y,ws,Ct,Pe,Hn,ss,vn]);const ar=H&&at.find(y=>y.deviceId===H)||null,bi=!!(ct&&Ce&&Y==="active"&&ar?.sessionId&&X?.deviceId===ar.deviceId&&X.sessionId===ar.sessionId&&X.streamPurpose==="control"&&X.commandId&&X.captureGeneration>0&&X.monitorIndex===vs),{state:dl,sendInput:ic,stats:Ft,error:Bt}=k1(ys&&bi,M),yt=u.useCallback((y,N)=>!bi||!ar||!X||y!==ar.deviceId?!1:ic(y,{...N,monitorIndex:X.monitorIndex,controlSessionId:ar.sessionId,controlCommandId:X.commandId,captureGeneration:X.captureGeneration}),[bi,ar,X,ic]),Ca=bi&&dl==="open",{state:Oa,lastFrame:eo,stats:Xt,error:lc}=L1(Fe?.deviceId||"",Xo&&He,M),fl=u.useRef(new Map);u.useEffect(()=>{const y=[["hub",j],["registry",l],["license",B],["wall-stream",ll],["control-stream",yi],["control-decode",fe],["remote-input",Bt],["remote-audio",lc]];for(const[N,_]of y){const O=String(_||"").trim();if(N==="hub"&&et?.requiresHubRestart&&et.stage==="updating"){fl.current.delete(N);continue}if(!O){fl.current.delete(N);continue}fl.current.get(N)!==O&&(fl.current.set(N,O),Pe(O))}},[lc,fe,yi,j,Bt,B,l,Pe,ll,et]),u.useEffect(()=>{if(!ct||!Ce||!ys||!Ca||!Fe)return;const y=(O,D)=>{const ae=O.target,de=ae?.tagName?.toLowerCase();ae?.isContentEditable||de==="input"||de==="textarea"||de==="select"||(O.preventDefault(),O.stopPropagation(),yt(Fe.deviceId,{type:D,monitorIndex:ea(ee[Fe.deviceId],Jn(Fe)),key:O.key,code:O.code,keyCode:O.keyCode||O.which||0,location:O.location,text:O.key.length===1?O.key:"",repeat:D==="keydown"?O.repeat:!1,shiftKey:O.shiftKey,ctrlKey:O.ctrlKey,altKey:O.altKey,metaKey:O.metaKey}))},N=O=>y(O,"keydown"),_=O=>y(O,"keyup");return window.addEventListener("keydown",N),window.addEventListener("keyup",_),()=>{window.removeEventListener("keydown",N),window.removeEventListener("keyup",_)}},[ys,Ca,Ce,ee,Fe,yt,ct]),u.useEffect(()=>{const y=()=>{document.visibilityState==="visible"&&(Ea.current=performance.now())};return document.addEventListener("visibilitychange",y),window.addEventListener("pageshow",y),()=>{document.removeEventListener("visibilitychange",y),window.removeEventListener("pageshow",y)}},[]),u.useEffect(()=>{if(!H||Y!=="connecting"&&Y!=="active"&&Y!=="warm"||cl!=="open")return;const y=H,N=qa("control",y),_=window.setInterval(()=>{const O=performance.now();if(document.visibilityState!=="visible"||O-Ea.current<fh)return;const D=Ys.current.get(N)||0,ae=ms.current.get(N)||0,de=Ks.current.get(N)||0,Re=Ra.current.get(N)||O;Ra.current.has(N)||Ra.current.set(N,Re);const je=D<=0,Ie=je?O-Re>pT:O-D>Ib,wt=D>0&&(ae<=0||O-ae>Ib);if(!Ie&&wt){const $s=Ki.current.get(N)||0;O-$s>=zb&&nr(y,"control","decoder-paint-stale");return}if(!Ie)return;const sn=Rs.current.get(N)||0;if(sn>0){if(O-sn<vT)return;Rs.current.delete(N)}const Tn=In.current.get(N)||0;if(Tn>=yT){if(Vn(je?"no first frame":"stale stream"))return;oe(!1),Qa(!1),U("error");return}if(O-de<Ob(gT,Tn))return;ul([y],je?"control-first-frame-timeout":"control-receive-stale")&&(Ks.current.set(N,O),Rs.current.set(N,O),Ra.current.set(N,O),In.current.set(N,Tn+1))},650);return()=>window.clearInterval(_)},[cl,H,Y,Vn,nr,ul]),u.useEffect(()=>{!fe||va!=="mode3-h264-hw"||Vn(fe)||(oe(!1),Qa(!1),U("error"))},[fe,va,Vn]),u.useEffect(()=>{!ct||!H||!yi||U(y=>y==="error"?y:"error")},[yi,H,ct]),u.useEffect(()=>{if(vi!=="open"||!Ze.autoStartLive||ia.length===0)return;const y=window.setInterval(()=>{const N=performance.now();if(document.visibilityState!=="visible"||N-Ea.current<fh)return;const _=1e3/Math.max(1,_a||Ze.fps||1),O=Math.max(xT,Math.ceil(_*4)),D=[];for(const ae of ia){if(D.length>=CT)break;const de=qa("wall",ae),Re=Ys.current.get(de)||0,je=Re<=0,Ie=Ja.current.get(de)||N;if(Ja.current.has(de)||Ja.current.set(de,Ie),je&&N-Ie<=bT)continue;const wt=Ks.current.get(de)||0,sn=ms.current.get(de)||0,Tn=je||N-Re>O,Cs=!je&&(sn<=0||N-sn>O);if(!Tn&&Cs){const Pr=Ki.current.get(de)||0;N-Pr>=zb&&nr(ae,"wall","decoder-paint-stale");continue}if(!Tn)continue;const $s=In.current.get(de)||0;N-wt>=Ob(wT,$s)&&D.push(ae)}if(D.length>0&&Jl(D,D.some(ae=>(Ys.current.get(qa("wall",ae))||0)<=0)?"wall-first-frame-timeout":"wall-frame-stale"))for(const ae of D){const de=qa("wall",ae);Ks.current.set(de,N),Ja.current.set(de,N),Ys.current.delete(de),ms.current.delete(de),In.current.set(de,(In.current.get(de)||0)+1)}},ST);return()=>window.clearInterval(y)},[_a,ia,nr,Jl,Ze.autoStartLive,Ze.fps,vi]),u.useEffect(()=>{Jt(Ze.fps),Zi.current=Date.now(),Qs.current=0},[Ze.fps,Ze.frameMode,Ze.adaptiveLoadShedding]),u.useEffect(()=>{XC({h264DecodeAcceleration:Ze.h264DecodeAcceleration})},[Ze.h264DecodeAcceleration]),u.useEffect(()=>{if(!Wt||ct||ze==="list"||!Ze.visibleOnlyLive){Tt([]);return}const y=Bo.current;if(!y||typeof IntersectionObserver>"u"){Tt(Yl);return}const N=Array.from(y.querySelectorAll("[data-live-device-id]"));if(N.length===0){Tt([]);return}const _=new Map,O=()=>{const ae=ph(N.map(de=>String(de.dataset.liveDeviceId||"")).filter(de=>(_.get(de)||0)>0));Tt(de=>hh(de,ae)?de:ae)},D=new IntersectionObserver(ae=>{for(const de of ae){const Re=String(de.target.dataset.liveDeviceId||"");Re&&_.set(Re,de.isIntersecting?de.intersectionRatio||.01:0)}O()},{root:null,rootMargin:fa.visibleRootMargin,threshold:[0,.01,.2,.6,1]});return N.forEach(ae=>D.observe(ae)),O(),()=>D.disconnect()},[Yl,Wt,ct,Mr,Ze.visibleOnlyLive,ze]),u.useEffect(()=>{if(!Ze.adaptiveLoadShedding||!Ze.autoStartLive||ia.length===0)return;const y=Ze.fps;if(ia.length<=fa.preserveConfiguredFpsDeviceCount){Lt!==y&&(Jt(y),Zi.current=Date.now(),Qs.current=0);return}if(vi!=="open"||wa.sampleMs<500)return;const N=Date.now(),_=ia.length*Lt,O=wa.framesPerSecond,D=wa.bytesPerSecond,ae=Math.max(0,...ia.map(wt=>Yi[qa("wall",wt)]?.maxIntervalMs||0)),de=Math.max(0,...ia.map(wt=>Wi[qa("wall",wt)]?.maxIntervalMs||0)),Re=_>8&&O>0&&O<_*.72;if(O>fa.maxWallFramesPerSecond||D>fa.maxWallBytesPerSecond||ae>fa.maxFrameGapMs||de>fa.maxFrameGapMs||Re){Qs.current=0;const wt=fa.minFps;if(N-Zi.current>=fa.downshiftCooldownMs&&Lt>wt){const sn=Math.max(wt,BT(Lt,wt));sn<Lt&&(Jt(sn),Zi.current=N)}return}const Ie=O<fa.maxWallFramesPerSecond*fa.lowLoadFrameRatio&&D<fa.maxWallBytesPerSecond*fa.lowLoadByteRatio;if(!Ie||Lt>=y){Ie||(Qs.current=0);return}if(!Qs.current){Qs.current=N;return}if(N-Qs.current>=fa.recoveryCooldownMs){const wt=Math.min(y,$T(Lt,y));wt>Lt&&(Jt(wt),Zi.current=N,Qs.current=0)}},[Lt,ia.length,ia,Wi,Yi,vi,wa.bytesPerSecond,wa.framesPerSecond,wa.lastUpdatedAt,wa.sampleMs,Ze.adaptiveLoadShedding,Ze.autoStartLive,Ze.frameMode,Ze.fps]),u.useEffect(()=>{const y=new URLSearchParams(window.location.search).get("checkout");if(y==="success"){Pe("Checkout returned. Waiting for Dodo confirmation."),F();const N=window.setInterval(()=>{F()},4e3),_=window.setTimeout(()=>{window.clearInterval(N)},45e3);return()=>{window.clearInterval(N),window.clearTimeout(_)}}y==="cancelled"&&Pe("Checkout cancelled.")},[F]),u.useEffect(()=>{!Wt&&Ce&&De(!1),!Wt&&He&&he(!1)},[He,Ce,Wt]),u.useEffect(()=>{const y=Fe?.deviceId||"",N=Ge.current;if(Ge.current=y,!N||N===y)return;const _=++se.current,O=y?`audio-${Date.now()}-${Math.random().toString(36).slice(2,8)}`:"";y&&He&<.current.set(y,O);let D=!0;return(async()=>{try{const de=lt.current.get(N);if(lt.current.delete(N),await qe(`/api/remote/devices/${encodeURIComponent(N)}/audio/stop`,{method:"POST",body:JSON.stringify(de?{streamId:de}:{})},M),!D||_!==se.current||!He||!y||!Wt||!tl)return;const Re=await qe(`/api/remote/devices/${encodeURIComponent(y)}/audio/start`,{method:"POST",body:JSON.stringify({streamId:O,mimeType:"audio/pcm;format=f32le",sampleRate:48e3,channels:2})},M);if(Re.ok===!1)throw new Error(Re.error||"Remote audio start failed.")}catch(de){D&&(lt.current.delete(y),he(!1),Pe(de instanceof Error?de.message:String(de)))}})(),()=>{D=!1}},[He,Wt,M,tl,Fe?.deviceId]),u.useEffect(()=>{const y=at.map(O=>O.deviceId),N=new Set(y),_=new Map(at.map(O=>[O.deviceId,O]));for(const O of at){const D=TT(O);D&&(We.current[O.deviceId]=D)}L(O=>{const D=O.filter(ae=>N.has(ae));return hh(O,D)?O:D}),W(O=>O&&N.has(O)?O:""),Z(O=>O&&N.has(O)?O:""),Ne(O=>{let D=!1;const ae={};for(const[de,Re]of Object.entries(O)){const je=_.get(de);if(!je){ae[de]=Re;continue}const Ie=ea(Re,Jn(je));ae[de]=Ie,Ie!==Re&&(D=!0)}return D?ae:O}),Fo(O=>{const D=O.filter(ae=>N.has(ae));return hh(O,D)?O:D})},[at,Vo]),u.useEffect(()=>{ra([]),Gl(!1)},[Gn]),u.useEffect(()=>{!Ji||La!=="selected"||gs.length>0||ya.length===0||Fo(ya.map(y=>y.deviceId))},[ya,gs.length,La,Ji]),u.useEffect(()=>{try{localStorage.setItem(Sw,ht)}catch{}},[ht]),u.useEffect(()=>{IT(Ze)},[Ze]),u.useEffect(()=>{OT(an)},[an]),u.useEffect(()=>{LT(ee,We.current)},[at,ee]);const sr=u.useCallback(async()=>{const y=new Set(p);y.add(hn);try{const ae=await qe("/api/remote/status",void 0,hn);hy({endpoint:ae.agentEndpoint,endpoint_candidates:[...ae.agentEndpointCandidates||[],...ae.hostTargetEndpointCandidates||[]]}).forEach(de=>y.add(de))}catch{}const N=[...y].sort((ae,de)=>{const Re=je=>{const Ie=Qc(je);return Ie==="local"?0:Ie==="loopback"?1:2};return Re(ae)-Re(de)||ae.localeCompare(de)});let _=!1,O="",D=!1;try{for(const ae of N){const de=Qc(ae);try{if(await qe("/api/health",void 0,ae),de==="local")return v(!0),Mo(!0),ae;de==="loopback"&&(_=!0,O=ae)}catch{de==="local"&&(D=!0)}}return _?(D&&Pe("LiveDesk Hub is reachable on this PC, but not through the LAN address yet. Restart LiveDesk Hub to bind 5179 on the network."),v(!0),Mo(!0),O||hn):(v(!1),Mo(!1),"")}catch{return v(!1),Mo(!1),""}},[p]),rs=async()=>{if(Te("auth"),Ma(!1),await sr(),window.liveDesk?.auth?.signInWithGoogle){const _=await window.liveDesk.auth.signInWithGoogle();_?.ok===!1&&(Pe(_.error||"LiveDesk could not open the system browser for Google sign-in."),Te(""));return}const{data:y,error:N}=await gt.auth.signInWithOAuth({provider:"google",options:{redirectTo:`${window.location.origin}/callback`}});N&&(Pe(N.message),Te(""))},oc=async()=>{Te("auth"),Ma(!1),await gt.auth.signOut(),(await window.liveDesk?.auth?.persistSession?.(null))?.runtimeCleared===!1?Pe("Signed out locally. The LiveDesk runtime could not be reached to clear its session."):window.liveDesk?.auth?.persistSession||await qe("/api/auth/session",{method:"DELETE"},hn).catch(()=>{}),Te("")};u.useEffect(()=>{if(!Ts)return;const y=_=>{_.target instanceof Element&&(_.target.closest(".account-popover, .rail-account")||Ma(!1))},N=_=>{_.key==="Escape"&&Ma(!1)};return window.addEventListener("pointerdown",y),window.addEventListener("keydown",N),()=>{window.removeEventListener("pointerdown",y),window.removeEventListener("keydown",N)}},[Ts]);const xi=u.useCallback(async(y={})=>{const N=y.silent===!0,_=y.takeover===!0,O=y.requestLocalAccess===!0;if(!n?.user){N||Pe("Sign in before publishing this host.","error");return}if(!Po.current){Po.current=!0,N||(Te("host"),Et("Hub · 1/4 · Checking the local runtime…"));try{const ae=(O?await sr():"")||M||hn;if(!(w||await qe("/api/remote/status",void 0,ae)).started)throw new Error("Start LiveDesk on this computer before syncing the server.");N||Et("Hub · 2/4 · Sending the signed-in session to the Hub…");const Re=await qe("/api/auth/session",{method:"POST",body:JSON.stringify({accessToken:n.access_token,refreshToken:n.refresh_token,expiresAt:n.expires_at,user:{id:n.user.id,email:n.user.email}})},ae);if(!Re.ok||!Re.authenticated)throw new Error(Re.error||"hub-session-sync-failed");N||Et("Hub · 3/4 · Claiming the Hub role and publishing its endpoint…");const je=await qe("/api/hub/sync",{method:"POST",body:JSON.stringify({takeover:_})},ae);if(!je.ok)throw new Error(je.error||"host-target-failed");if(!N){Et("Hub · 4/4 · Verifying the endpoint and lease renewal…");const Ie=await qe("/api/hub/status",void 0,ae);if(!Ie.hostTargetActive)throw new Error(Ie.hostTargetLease?.lastError||"host-target-not-active-after-sync");const wt=Ie.hostTargetEndpoint||Ie.agentEndpoint||"endpoint ready",sn=Number(Ie.connectedDeviceCount||0),Tn=sn>0?`${sn} client${sn===1?"":"s"} connected.`:"No clients connected yet; reconnect the laptop Client.";Pe(`Hub ready · ${wt} · lease auto-renewal active. ${Tn}`,"success"),(O||x)&&(v(!0),Mo(!0)),C()}c()}catch(D){if(!N){const ae=D instanceof Error?D.message:String(D);ae.includes("local network access")&&(v(!1),Mo(!1)),Pe(`Hub setup failed · ${ae}`,"error")}}finally{Po.current=!1,N||Te("")}}},[M,x,C,c,sr,n,Pe,Et,w]),Lr=u.useCallback((y=!0)=>{ve!=="host"&&(Ve(!1),y&&window.requestAnimationFrame(()=>vt.current?.focus()))},[ve]),_r=u.useCallback(()=>{ve!=="host"&&(vt.current=document.activeElement instanceof HTMLElement?document.activeElement:null,Ma(!1),Ve(!0))},[ve]),Is=u.useCallback(()=>{Ve(!1),xi({takeover:!0,requestLocalAccess:!0})},[xi]);u.useEffect(()=>{if(!Ke)return;const y=window.requestAnimationFrame(()=>rt.current?.focus()),N=O=>{O.target instanceof Element&&O.target.closest(".client-hub-switch-popover, .hub-sync-trigger")||Lr(!1)},_=O=>{O.key==="Escape"&&Lr()};return window.addEventListener("pointerdown",N),window.addEventListener("keydown",_),()=>{window.cancelAnimationFrame(y),window.removeEventListener("pointerdown",N),window.removeEventListener("keydown",_)}},[Lr,Ke]);const rr=u.useCallback(async()=>{if(!(!w?.update?.canApply||ve==="update")){Te("update");try{const y=await qe("/api/update/apply",{method:"POST",body:JSON.stringify({})},M||hn);if(!y.ok)throw new Error(y.error||"LiveDesk update could not be started.");const N=[...new Set((y.targets||[]).map(D=>String(D.deviceId||"").trim()).filter(Boolean))],_=!!(y.managerUpdateAvailable||y.clientPackageUpdateAvailable),O=y.state==="hub-restart-requested"?Date.now():void 0;(N.length>0||_)&&At({operationId:String(y.operationId||`wall-update-${Date.now()}`),targetIds:N,stage:y.state==="clients-updated"?"updated":"updating",requiresHubRestart:_,initialHostInstanceId:String(w.hostInstanceId||""),targetManagerVersion:String(y.latestVersion||w.update?.latestVersion||""),targetClientVersion:String(y.latestClientVersion||w.update?.latestClientVersion||""),restartRequestedAt:O,restartDeadlineAt:O?O+zd:void 0}),Et(y.state==="hub-restart-requested"?Id:Nb),C()}catch(y){Pe(y instanceof Error?y.message:String(y),"error")}finally{Te("")}}},[ve,M,C,Pe,Et,w]);u.useEffect(()=>{const y=et,N=w?.update;if(!y||y.stage!=="updating"||!N)return;const _=N.restartResult?.operationId===y.operationId?N.restartResult:null;if(_?.outcome==="restored"){const je=_.error||`The requested Hub ${_.targetVersion||y.targetManagerVersion||"version"} failed verification.`;At(Ie=>Ie?.operationId===y.operationId?{...Ie,stage:"restored",error:je}:Ie),Pe(`LiveDesk update did not complete. Hub ${_.fallbackVersion||_.appVersion||"the previous version"} was restored. ${je}`,"error");return}if(_?.outcome==="failed"){const je=_.error||"No verified LiveDesk Hub replacement could be started.";At(Ie=>Ie?.operationId===y.operationId?{...Ie,stage:"failed",error:je}:Ie),Pe(`LiveDesk Hub update failed. ${je}`,"error");return}if(N.operationId===y.operationId&&N.state==="failed"){const je=N.error||"LiveDesk update failed before the Hub replacement completed.";At(Ie=>Ie?.operationId===y.operationId?{...Ie,stage:"failed",error:je}:Ie),Pe(je,"error");return}if(N.operationId===y.operationId){if(N.state==="hub-restart-requested"&&!y.restartDeadlineAt){const je=Date.now();At(Ie=>Ie?.operationId===y.operationId?{...Ie,restartRequestedAt:je,restartDeadlineAt:je+zd}:Ie),Et(Id)}!y.requiresHubRestart&&N.state==="clients-updated"&&(At(je=>je?.operationId===y.operationId?{...je,stage:"updated"}:je),Pe("LiveDesk clients updated and reconnected successfully.","success"));return}if(!y.requiresHubRestart)return;const O=w.started===!0&&(!y.initialHostInstanceId||w.hostInstanceId&&w.hostInstanceId!==y.initialHostInstanceId),D=!!N.checkedAt,ae=Tb(N.currentVersion,y.targetManagerVersion),de=!y.targetClientVersion||Tb(N.currentClientVersion,y.targetClientVersion),Re=D&&N.state==="idle"&&N.updateAvailable===!1&&ae&&de;O&&Re&&(At(je=>je?.operationId===y.operationId?{...je,stage:"updated"}:je),Pe(`LiveDesk Hub ${N.currentVersion||_?.appVersion||y.targetManagerVersion} restarted and passed version verification.`,"success"))},[Pe,Et,w,et]),u.useEffect(()=>{const y=et;if(!y||y.stage!=="updating"||!y.requiresHubRestart||y.restartDeadlineAt)return;const N=()=>{const O=Date.now();At(D=>D?.operationId===y.operationId&&D.stage==="updating"&&!D.restartDeadlineAt?{...D,restartRequestedAt:O,restartDeadlineAt:O+zd}:D),Et(Id)};if(w?.update?.state==="hub-restart-requested"){N();return}if(!j)return;const _=window.setTimeout(N,cT);return()=>window.clearTimeout(_)},[j,Et,w?.update?.state,et]),u.useEffect(()=>{const y=et;if(!y||y.stage!=="updating"||!y.requiresHubRestart||!y.restartDeadlineAt)return;const N=Math.max(0,y.restartDeadlineAt-Date.now()),_=y.operationId,O=window.setTimeout(()=>{const D=`LiveDesk Hub did not return and verify ${y.targetManagerVersion||"the requested version"} within ${Math.round(zd/6e4)} minutes. Check the Hub terminal or restart log, then run livedesk@latest manually if the Hub is still offline.`;At(ae=>ae?.operationId===_&&ae.stage==="updating"?{...ae,stage:"failed",error:D}:ae),Pe(D,"error")},N);return()=>window.clearTimeout(O)},[Pe,et]),u.useEffect(()=>{if(!et||et.stage!=="updated")return;const y=et.operationId,N=window.setTimeout(()=>{At(_=>_?.operationId===y?null:_)},8e3);return()=>window.clearTimeout(N)},[et]),u.useEffect(()=>{if(q!==Nb&&q!==Id)return;const y=w?.update;if(String(y?.state||"")==="failed"){Pe(y?.error||"LiveDesk update failed.","error");return}et?.stage==="updated"&&Pe("")},[q,Pe,w?.update?.error,w?.update?.state,et?.stage]);const zs=u.useCallback(async()=>{if(!ve){Te("pin");try{const N=await qe("/api/remote/pairing-pin",{method:"POST"},M||hn);if(!N.ok)throw new Error(N.error||"pin-rotation-failed");await C(),Pe("New PIN published.")}catch(y){Pe(y instanceof Error?y.message:String(y))}finally{Te("")}}},[ve,M,C]),ml=u.useCallback(y=>{y&&(W(y),L(N=>N.includes(y)?N:[...N,y]))},[]),hl=u.useCallback(y=>{if(!y)return;const N=es.has(y);W(_=>N?_===y?"":_:y),L(_=>N?_.filter(O=>O!==y):_.includes(y)?_:[..._,y])},[es]),Ss=u.useCallback(()=>{L([]),W("")},[un]),pl=u.useCallback(()=>{L(at.map(y=>y.deviceId)),W("")},[at]),Os=u.useCallback((y,N)=>{const _=at.find(D=>D.deviceId===y);if(!_)return;const O=Jn(_);O<=1||(Ne(D=>{const ae=ea(D[y],O),de=(ae+N+O)%O;return de===ae?D:{...D,[y]:de}}),hs("wall",y),hs("control",y),en(D=>{if(!D.has(y))return D;const ae=new Set(D);return ae.delete(y),ae}),Vs.current.delete(y),An.current.delete(y),ct&&(oe(!1),U("connecting"),Qa(!1),Z(y)))},[at,hs,ct]);u.useEffect(()=>{const y=N=>{if(ct&&Ce&&ys)return;const _=N.target,O=_?.tagName?.toLowerCase();_?.isContentEditable||O==="input"||O==="textarea"||O==="select"||((N.ctrlKey||N.metaKey)&&N.key.toLowerCase()==="a"&&(N.preventDefault(),pl()),N.key==="Escape"&&Ss())};return window.addEventListener("keydown",y),()=>window.removeEventListener("keydown",y)},[ys,Ss,Ce,pl,ct]);const Mn=(y,N)=>{const _=at.find(de=>de.deviceId===y),O=_?Jn(_):1,D=ea(ee[y],O),ae=ea(N,O);D!==ae&&(Ne(de=>({...de,[y]:ae})),en(de=>{if(!de.has(y))return de;const Re=new Set(de);return Re.delete(y),Re}),Vs.current.delete(y),An.current.delete(y),hs("wall",y),hs("control",y)),ct?(D!==ae&&(oe(!1),U("connecting")),Qa(!1),Z(y)):ml(y)},ir=async y=>{if(Qa(!1),Z(y),Oe("control"),!Ls.has(y)){De(!1),Pe(Tr);return}const N=at.find(O=>O.deviceId===y);if(ei(N?.platform)&&(N?.capabilities?.controlPermission===!1||N?.capabilities?.accessibilityPermission===!1)){De(!1),Pe("Mac remote control needs Accessibility permission. Allow it for the terminal app that starts LiveDesk, then restart the Mac client.","error");return}De(!0),Pe(`Opening remote control${N?` for ${Ga(N)}`:""}.`,"success")},ju=async()=>{if(!Fe){Pe("Select a Client before playing audio.");return}if(!Wt){Pe(Tr);return}if(!tl){Pe("Selected Client does not report remote audio support yet.");return}if(Be)return;const y=!He,N=y?`audio-${Date.now()}-${Math.random().toString(36).slice(2,8)}`:lt.current.get(Fe.deviceId);y&&N?lt.current.set(Fe.deviceId,N):y||lt.current.delete(Fe.deviceId),_e("audio"),he(y);try{await qe(`/api/remote/devices/${encodeURIComponent(Fe.deviceId)}/audio/${y?"start":"stop"}`,{method:"POST",body:JSON.stringify({...N?{streamId:N}:{},mimeType:"audio/pcm;format=f32le",sampleRate:48e3,channels:2})},M),Pe(`${y?"Audio started":"Audio stopped"} for ${Ga(Fe)}.`,"success")}catch(_){y&<.current.delete(Fe.deviceId),he(!1),Pe(_ instanceof Error?_.message:String(_),"error")}finally{_e("")}},bf=y=>{const N=y instanceof Error?y.message:String(y);return/remote-actions-disabled|allowAgent-blocked-by-settings/i.test(N)?"Remote actions are off. Turn on Settings > Agent > Enable AI Agent first.":/power-actions-disabled/i.test(N)?"Power actions are off. Turn on Settings > Agent > Allow power actions first.":/agent-tool-capability-unavailable|unsupported-agent-operation/i.test(N)?"This Client needs a newer LiveDesk Client for power actions.":/device-not-connected|404 device-not-found/i.test(N)?"The Client disconnected before the action could be sent.":N},cc=async y=>{if(!Fe||Be)return;const N=Fe,O={sleep:"Sleep",restart:"Restart",shutdown:"Shut down"}[y];if(window.confirm(`${O} ${Ga(N)}? Unsaved work on that computer may be lost.`)){_e(y),Pe(`Sending ${O.toLowerCase()} request to ${Ga(N)}...`);try{const D=await qe(`/api/remote/devices/${encodeURIComponent(N.deviceId)}/quick-actions`,{method:"POST",body:JSON.stringify({action:y})},M);if(!D.ok)throw new Error(D.error||"The remote action could not be sent.");Pe(`${O} request sent to ${Ga(N)}.`,"success"),C()}catch(D){Pe(bf(D),"error")}finally{_e("")}}},Au=async(y,N,_="files")=>{const O=y.filter(Re=>Re.file.size>=0);if(O.length===0)return{files:0,bytes:0};if(!Wt)throw new Error(Tr);if(N.length===0)throw new Error("Select at least one connected Client.");const D=crypto.randomUUID(),ae=O.reduce((Re,je)=>Re+je.file.size,0);let de=0;Ds({id:D,status:"reading",progress:1,message:`Preparing ${O.length} ${_}`});try{for(let Re=0;Re<O.length;Re+=1){const je=O[Re],Ie=je.file,wt=Math.max(1,Math.ceil(Ie.size/uh));for(let sn=0;sn<wt;sn+=1){const Tn=sn*uh,Cs=Math.min(Ie.size,Tn+uh),$s=Ie.size===0?new ArrayBuffer(0):await Ie.slice(Tn,Cs).arrayBuffer(),Pr=sn===wt-1;Ds({id:D,status:"sending",progress:Math.max(2,Math.min(98,Math.round(de/Math.max(1,ae)*96))),message:`${Re+1}/${O.length} ${je.relativePath}`});const Ps=await qe("/api/remote/files/chunk",{method:"POST",body:JSON.stringify({transferId:D,deviceIds:N,remoteDirectory:ht.trim(),name:Ie.name,relativePath:je.relativePath,size:Ie.size,type:Ie.type,lastModified:Ie.lastModified,offset:Tn,totalBytes:Ie.size,final:Pr,dataBase64:PT($s)})},M);if(!Ps.ok||Ps.queued!==N.length)throw new Error(Ps.error||`file-chunk-not-queued-${Ps.queued}/${N.length}`);de+=$s.byteLength,await new Promise(xf=>window.setTimeout(xf,0))}}return Ds({id:D,status:"queued",progress:100,message:`Sent ${O.length} file${O.length===1?"":"s"} to ${N.length} Client${N.length===1?"":"s"}`}),window.setTimeout(()=>{Ds(Re=>Re?.id===D&&Re.status==="queued"?null:Re)},4500),{files:O.length,bytes:ae}}catch(Re){throw Ds({id:D,status:"failed",progress:100,message:Re instanceof Error?Re.message:String(Re)}),Re}},tn=async y=>{const N=ya.length>0?ya.map(_=>_.deviceId):Fe?[Fe.deviceId]:[];try{await Au(Fb(y),N)}catch(_){Pe(_ instanceof Error?_.message:String(_))}},to=async y=>{try{await Au(Fb(y),mi)}catch(N){Pe(N instanceof Error?N.message:String(N))}};u.useEffect(()=>{$o.current=!1},[]);const gl=y=>{y.preventDefault(),y.stopPropagation(),ui(!1),tn(y.dataTransfer.files)},Ir=async(y="ltd")=>{if(!n){Pe("Sign in with Google before checkout.");return}if(T&&y===Gt){Pe("LiveDesk license is already active.");return}if(T&&Gt==="pro"){Pe("LiveDesk Pro is already active.");return}Te("checkout");try{const{data:N,error:_}=await gt.functions.invoke("dodo-checkout",{body:{plan:y}});if(_)throw _;if(!N?.checkout_url)throw new Error("Checkout function did not return a checkout URL.");window.location.assign(N.checkout_url)}catch(N){Pe(N instanceof Error?N.message:String(N)),Te("")}},wi=kT(),zr=jT(),lr=Zo.filter(y=>xs.has(y)).length,uc=tr==="open"&&Dr.sampleMs>0?`${Dr.framesPerSecond.toFixed(1)} fps`:tr,Le=Fe?ct?bs.get(Fe.deviceId):xs.get(Fe.deviceId):void 0,no=Fe?ea(ee[Fe.deviceId],Jn(Fe)):0,dc=Fe?NT(Fe):[],Or=typeof Le?.monitorIndex=="number"?ea(Le.monitorIndex,Math.max(1,Le.monitorCount||1)):no,Si=(Le&&Or===no?Rw(Le):null)||(Fe?Ew(Fe,no):null);Si&&`${Si.width}${Si.height}`;const vl=Fe?qa(ct?"control":"wall",Fe.deviceId):"",Qt=vl?Yi[vl]:void 0,Kn=vl?Wi[vl]:void 0,xt=Kn?.lastInfo,ao=Qt&&Qt.sampleMs>0?`${Qt.framesPerSecond.toFixed(1)} fps`:"waiting";Qt&&`${Qt.totalFrames}${ao}`,Kn&&Kn.sampleMs>0&&`${Kn.framesPerSecond.toFixed(1)}`,Kn&&Kn.sampleMs>0&&`${Kn.changedFramesPerSecond.toFixed(1)}`,xt&&(`${xt.frameSeq}${xt.decoderLabel}${xt.queueSize}${xt.renderQueueSize}${xt.droppedForLatency}${xt.decoderFlushCount}${xt.decoderTimestampFallbacks}${xt.decoderInputCount}${xt.decoderOutputCount}`,xt.visualChanged),xt&&`${Math.round(xt.queuedForMs)}${Math.round(xt.decodedForMs)}${Math.round(xt.scheduledForMs)}${xt.drawMs.toFixed(1)}`,xt?.targetPaintIntervalMs&&`${xt.targetPaintIntervalMs.toFixed(1)}${(1e3/xt.targetPaintIntervalMs).toFixed(1)}`,Kn&&`${Kn.decoderInputFps.toFixed(1)}${Kn.decoderOutputFps.toFixed(1)}`,xt?.canvasPresentation,xt&&`${xt.interactiveBufferWaitCount}${Math.round(xt.interactiveBufferWaitMs)}${xt.interactiveQueueDrainCount}`,Qt&&(Le?.frameMode==="mode5-lzo-delta"?`${Wc(Qt.averageFrameBytes)}${Le.deltaVisualTileCount||0}${Le.deltaRefreshTileCount||0}${Le.deltaTotalTiles||0}`:`${Wc(Qt.averageFrameBytes)}${Math.round(Qt.tinyFrameRatio*100)}${Math.round(Qt.repeatedFrameRatio*100)}`),Qt&&Qt.sourceSendSampleCount>0&&`${Qt.sourceSendAverageMs.toFixed(1)}${Qt.sourceSendP95Ms.toFixed(1)}`,Db(Qt),Db(Kn),Qt&&Kn&&`${Lb(Qt)}${Lb(Kn)}`,Le?Le.frameMode||Le.mode||Le.codec||Le.mimeType:ct?an.frameMode:Ze.frameMode,ct?an.frameMode:Ze.frameMode,Le?`${Le.width}${Le.height}${Wc(Le.byteLength)}`:ct?`${fi.maxWidth}${fi.maxHeight}`:`${Ze.maxWidth}${Ze.maxHeight}`,Le&&`${Le.isKeyFrame?"key":String(Le.chunkType||"delta")}`,Le&&Le.monitorCount&&((Le.monitorIndex??0)+1,`${Le.monitorCount}`),Le&&[`src ${Math.round(Le.sourceGapMs||0)}ms`,`gap ${Math.round(Le.agentSendGapMs||0)}ms`,`tx ${Math.round(Le.agentSendDurationMs||0)}ms`,`pace ${Math.round(Le.agentPaceMs||0)}ms`,`queue ${Math.round(Le.agentQueueBeforePaceMs||0)}ms`,`age ${Math.round(Le.agentFrameAgeMs||0)}ms`,Le.hubPacketTransitAgeMs!==void 0?`sock ${Math.max(0,Math.round(Le.hubPacketTransitAgeMs))}ms`:"",Le.droppedByAgent?`drop ${Le.droppedByAgent}`:"",Le.hubIngressDropped?`ingress-drop ${Le.hubIngressDropped}`:"",Le.hubClientDropped?`ws-drop ${Le.hubClientDropped}`:"",Le.captureStageMs?`capture ${Math.round(Le.captureStageMs)}ms`:"",Le.requestedFps?`req ${Math.round(Le.requestedFps)}fps`:"",Le.effectiveFps?`eff ${Math.round(Le.effectiveFps)}fps`:"",Le.captureAverageMs?`avg ${Math.round(Le.captureAverageMs)}ms`:"",Le.captureP95Ms?`p95 ${Math.round(Le.captureP95Ms)}ms`:"",Le.slowFrameCount?`slow ${Math.round(Le.slowFrameCount)}`:"",Le.captureHelperRestarts?`helper ${Math.round(Le.captureHelperRestarts)}`:"",Le.convertMs?`rgb565 ${Math.round(Le.convertMs)}ms`:"",Le.compressMs?`lzo ${Math.round(Le.compressMs)}ms`:"",Le.captureSourceSerial?`cap #${Le.captureSourceSerial}`:"",Le.captureSourceSkipped?`cap skip ${Le.captureSourceSkipped}`:"",Le.frameMode==="mode5-lzo-delta"?`tiles ${Le.deltaVisualTileCount||0}+${Le.deltaRefreshTileCount||0}/${Le.deltaTotalTiles||0}@${Le.deltaTileSize||0}`:"",Le.platformProfile||Le.hardwareEncoder||"-",Le.atlasComposeMs?`atlas ${Le.atlasComposeMs.toFixed(1)}ms`:"",Le.atlasPoolStarved?`pool ${Le.atlasPoolStarved}`:"",Le.atlasTileCount?`tiles ${Le.atlasReadyTiles||0}/${Le.atlasTileCount}`:"",Le.atlasInputRevision?`input #${Le.atlasInputRevision}`:"",Le.atlasSourceMaxAgeMs!==void 0?`input-age ${Math.round(Le.atlasSourceMaxAgeMs)}ms`:"",xt?.inputFeedbackMs!==void 0&&xt.inputFeedbackAt!==void 0&&performance.now()-xt.inputFeedbackAt<=Ab?`feedback ${Math.round(xt.inputFeedbackMs)}ms`:"",Le.contentHash?`enc ${Le.contentHash.slice(0,6)}`:"",xt?.visualHash?`pix ${xt.visualHash.slice(0,6)}`:""].filter(Boolean).join(" / "),dl==="open"&&[`${Ft.sentPerSecond.toFixed(1)} hz`,Ft.lastInputSeq>0?`rtt ${Math.round(Ft.lastRoundTripMs)}ms`:"",Ft.lastInputSeq>0?`apply ${Ft.lastAgentApplyMs.toFixed(1)}ms`:"",Ft.droppedPerSecond>0?`drop ${Ft.droppedPerSecond.toFixed(1)}/s`:"",Ft.bufferedAmount>0?`buf ${Wc(Ft.bufferedAmount)}`:"",Ft.lastInputType||"ready"].filter(Boolean).join(" / "),xt?.inputFeedbackMs!==void 0&&xt.inputFeedbackAt!==void 0&&performance.now()-xt.inputFeedbackAt<=Ab&&`${Math.round(xt.inputFeedbackMs)}${xt.inputFeedbackSeq||0}`,He&&[Oa,`context ${Xt.audioContextState}`,`queue ${Math.round(Xt.queuedMs)}ms`,`last ${Math.round(Xt.lastPacketAgeMs)}ms`,`accepted ${Math.round(Xt.lastAcceptedAgeMs)}ms`,`process ${Math.round(Xt.localProcessingAgeMs)}ms`,`remote ${Math.round(Xt.browserAgeMs)}/${Math.round(Xt.captureAgeMs)}ms`,`heartbeat ${Math.round(Xt.captureStatusAgeMs)}ms`,`capture ${Xt.captureAlive&&Xt.helperAlive?"alive":"down"}`,`drop ${Xt.drops}`,`resync ${Xt.resyncs}`,`restart ${Xt.automaticRestarts}`,Xt.pendingDecodes>0?`decode ${Xt.pendingDecodes}`:"",eo?`#${eo.frameSeq}`:"waiting"].filter(Boolean).join(" / "),Le&&`${Le.frameSeq}${ix(Le.receivedAt||Le.capturedAt)}`;const Hr=Math.max(0,Dr.bytesPerSecond*8/1e6),Fr=u.useCallback(()=>{_n(!0),Aa(Date.now()),C(),ta.current!==null&&window.clearTimeout(ta.current),ta.current=window.setTimeout(()=>{ta.current=null,Aa(Date.now()),_n(!1)},650)},[C]),or=u.useCallback(()=>{if(st){mt(!1);return}Ln(!1),mt(!0),Fr()},[Fr,st]),cr=u.useCallback(()=>{if(Ee){Ln(!1);return}mt(!1),Ln(!0),Fr()},[Ee,Fr]),fc=u.useCallback(()=>{if(pn){nt(!1);return}nt(!0),Fr()},[pn,Fr]),Ci=new Map,Hs=new Map;if(st||Ee||pn)for(const y of at){const N=y.deviceId,_=ct&&H===N,D=qa(_?"control":"wall",N),ae=_?bs.get(N):xs.get(N),de=_?jn.current.get(N)||ga.current.get(N)||null:kn.current.get(N)||(Ct?Ws.current:null),Re={device:y,frame:ae,receiveStat:Yi[D],paintStat:Wi[D],codec:de?Cy(de):{},selectedMonitorIndex:ea(ee[N],Jn(y)),control:_,controlInputReady:_&&Ca,controlBinding:_&&X?.deviceId===N?X:void 0,clientHeartbeatMs:Number(w?.heartbeatMs||5e3),now:Date.now()};(st||pn)&&Ci.set(N,DN(Re)),Ee&&Hs.set(N,LN(Re))}const yl=pn?_N({status:w||{},deviceDiagnostics:[...Ci.values()],codec:Cy(),browser:{hardwareConcurrency:navigator.hardwareConcurrency,deviceMemory:Number(navigator.deviceMemory||0)},frameStats:Dr}):[],bl=xn?.atlasReadyTiles??lr,Br=xn?.atlasTileCount??lr,so=bl===Br?`${Br} tiles`:`${bl}/${Br} tiles`,Nu=bn.length>0;ct?`${uc}${Hr.toFixed(Hr>=10?0:1)}`:Nu?`${so}${uc}${Hr.toFixed(Hr>=10?0:1)}`:`${lr}${uc}${Hr.toFixed(Hr>=10?0:1)}`;const Fs=ya.length,Tu=ct?Fe?Ga(Fe):"No Client":Fs===0?"No Client":Fs>1?`${Fs} selected`:Fe?Ga(Fe):"No Client",ur=ct?[Fe?.deviceId].filter(y=>!!y):ya.map(y=>y.deviceId),Nn=Lo[Ka]||Lo.en,ki=Nn.sync||Lo.en.sync,dr=a||R?"checking":T?Gt==="pro"?"Pro LTD active":`Plus LTD ${at.length}/${Vc}`:bu?`Free ${at.length}/${Gc}`:`Upgrade ${at.length}/${Gc}`,xl=T?Gt==="pro"?"Pro active":"Upgrade to Pro LTD":n?ve==="checkout"?"Opening checkout":Gt==="ltd"?"Buy Pro LTD":"Buy Plus LTD":ve==="auth"?"Opening Google":"Sign in to buy",mc=Gt==="pro"||ve==="checkout"||ve==="auth"||a||R,ro=n?.user?.user_metadata,fr=Bb(ro,"avatar_url","picture"),Bs=Bb(ro,"full_name","name")||n?.user?.email||"Google",hc=Bs.slice(0,1).toUpperCase(),Eu=w?.started?"Local app ready":"Start LiveDesk",ji=i?.active?"Published":n?"Not published":"Sign in required",io=/^\d{6}$/.test(w?.pairingPin||"")?w?.pairingPin:"------",lo=y=>{if(typeof window<"u"&&(y==="support"?window.history.pushState({},"","/support"):ge==="support"&&window.history.pushState({},"","/")),y==="control"){const N=zn||Nr;if(N&&(Qa(!1),Z(N.deviceId)),Oe(y),!Wt){De(!1),Pe(Tr);return}De(!0);return}y==="wall"&&Sr(N=>N+1),Oe(y),De(!1)},mr=ve==="update"||et?.stage==="updating"||["dispatching","waiting-for-clients","hub-restart-requested"].includes(w?.update?.state||""),pc=!!(w?.update?.updateAvailable&&w.update.canApply||mr||et?.error||!w&&et),dn=wC(Ka),la=u.useCallback(y=>{if(!et?.targetIds.includes(y))return;const N=w?.update?.operationId===et.operationId?w.update.targets?.find(_=>_.deviceId===y):void 0;switch(N?.state){case"completed":return{state:"updated",label:"Updated"};case"failed":return{state:"failed",label:"Update failed",error:N.error};case"pending-offline":return{state:"offline",label:"Waiting for reconnect"};case"queued":return et.stage==="updating"?{state:"queued",label:"Queued"}:void 0;case"waiting":return et.stage==="updating"?N.candidateSince?{state:"updating",label:"Verifying restart..."}:{state:"updating",label:"Restarting..."}:void 0;default:return et.stage==="updated"?{state:"updated",label:"Updated"}:et.stage==="updating"?{state:"updating",label:"Verifying..."}:void 0}},[w?.update,et]);return r.jsxs("main",{className:"app-shell",children:[r.jsxs("aside",{className:"tool-rail","aria-label":"LiveDesk tools",children:[r.jsx("div",{className:"rail-brand",title:"LiveDesk",children:r.jsx(lx,{size:25})}),r.jsx("nav",{className:"rail-tools",children:KT.map(y=>{const N=y.icon,_=Vd(y.tooltip,Ka);return r.jsxs("button",{className:`rail-tool ${y.id==="sync-server"?"sync-server hub-sync-trigger":""} ${ge===y.id?"active":""} accent-${y.accent}`,onClick:()=>y.id==="sync-server"?_r():lo(y.id),"data-tooltip":y.id==="sync-server"&&Ke?void 0:Mh(y.label,y.tooltip,Ka),"aria-label":`${y.label}. ${_}`,type:"button",disabled:y.id==="sync-server"&&ve==="host","aria-busy":y.id==="sync-server"&&ve==="host","aria-expanded":y.id==="sync-server"?Ke:void 0,"aria-haspopup":y.id==="sync-server"?"dialog":void 0,"aria-controls":y.id==="sync-server"&&Ke?"hub-sync-popover":void 0,children:[y.id==="sync-server"&&ve==="host"?r.jsx("span",{className:"sync-server-spinner","aria-hidden":"true"}):r.jsx(N,{size:19}),r.jsx("span",{children:y.id==="sync-server"&&ve==="host"?"Syncing…":y.label})]},y.id)})}),r.jsxs("div",{className:"rail-footer",children:[XT.map(y=>{const N=y.icon;return r.jsxs("button",{className:`rail-tool compact ${ge===y.id?"active":""} accent-${y.accent}`,onClick:()=>lo(y.id),title:y.label,type:"button",children:[r.jsx(N,{size:18}),r.jsx("span",{children:y.label})]},y.id)}),r.jsxs("button",{className:`rail-tool compact local-settings ${Vl?"active":""}`,onClick:()=>{Oe("settings"),typeof window<"u"&&window.history.pushState({},"","/"),Ma(!1),De(!1),fs(!1)},title:"Settings",type:"button",children:[r.jsx(Gh,{size:18}),r.jsx("span",{children:"Settings"})]}),r.jsxs("button",{className:`rail-account ${n?"signed-in":""}`,onClick:()=>{Ma(y=>!y)},disabled:a||ve==="auth","aria-expanded":Ts,title:n?`Account: ${n.user?.email||Bs}`:"Sign in with Google",type:"button",children:[n?fr?r.jsx("img",{src:fr,alt:Bs,referrerPolicy:"no-referrer"}):r.jsx("span",{className:"avatar-fallback",children:hc}):r.jsx(_l,{size:18}),r.jsx("span",{children:n?"Account":"Google"})]}),Ts&&r.jsxs("section",{className:"account-popover",role:"dialog","aria-label":"LiveDesk account",children:[r.jsxs("div",{className:"account-popover-head",children:[r.jsx("div",{className:"account-popover-avatar",children:n&&fr?r.jsx("img",{src:fr,alt:Bs,referrerPolicy:"no-referrer"}):n?r.jsx("span",{children:hc}):r.jsx(_l,{size:18})}),r.jsxs("div",{children:[r.jsx("strong",{children:n?Bs:"Account"}),r.jsx("p",{children:n?.user?.email||"Not signed in"})]})]}),r.jsxs("dl",{className:"account-popover-facts",children:[r.jsx("dt",{children:"User"}),r.jsx("dd",{children:n?.user?.email||"Not signed in"}),r.jsx("dt",{children:"Network"}),r.jsx("dd",{children:Eu}),r.jsx("dt",{children:"Access"}),r.jsx("dd",{children:ji}),r.jsx("dt",{children:"Plan"}),r.jsx("dd",{children:dr}),r.jsx("dt",{children:"Ads"}),r.jsx("dd",{children:Gt==="free"?"Standard banner":"Removed"})]}),n?r.jsxs("button",{className:"account-popover-action",onClick:oc,disabled:ve==="auth",type:"button",children:[r.jsx(Vb,{size:16}),"Logout"]}):r.jsxs("button",{className:"account-popover-action primary",onClick:rs,disabled:ve==="auth",type:"button",children:[r.jsx(_l,{size:16}),"Sign in with Google"]})]})]})]}),Ke&&r.jsxs("section",{id:"hub-sync-popover",className:"client-hub-switch-popover hub-sync-popover",role:"dialog","aria-modal":"false","aria-labelledby":"hub-sync-title","aria-describedby":"hub-sync-description hub-sync-notice",children:[r.jsxs("header",{className:"client-hub-switch-head",children:[r.jsx("span",{className:"client-hub-switch-mark","aria-hidden":"true",children:r.jsx(yr,{size:19})}),r.jsx("span",{className:"client-hub-switch-eyebrow",children:dn.eyebrow}),r.jsx("button",{className:"client-hub-switch-close",type:"button",onClick:()=>Lr(),"aria-label":dn.closeLabel,children:r.jsx(ma,{size:17})})]}),r.jsx("h2",{id:"hub-sync-title",children:dn.title}),r.jsx("p",{id:"hub-sync-description",children:dn.description}),r.jsxs("p",{id:"hub-sync-notice",className:"client-hub-switch-notice",children:[r.jsx(Dn,{size:16,"aria-hidden":"true"}),r.jsx("span",{children:dn.restartNotice})]}),r.jsxs("div",{className:"client-hub-switch-actions",children:[r.jsx("button",{type:"button",onClick:()=>Lr(),children:dn.cancel}),r.jsxs("button",{ref:rt,className:"primary",type:"button",onClick:Is,disabled:ve==="host",children:[r.jsx(yr,{size:16}),dn.confirm]})]})]}),r.jsxs("section",{className:`desk-stage ${Ar&&!pa?"dock-open":""} ${ct?"control-stage":""} ${Go?"agents-stage":""}`,children:[r.jsxs("header",{className:"topbar",children:[r.jsxs("div",{className:"brand",children:[r.jsx("span",{className:"brand-mark",children:r.jsx(Il,{size:18})}),r.jsxs("div",{className:"brand-copy",children:[r.jsx("h1",{children:"LiveDesk"}),r.jsx("p",{children:"Control wall for local machines"})]}),pc?r.jsxs("button",{className:"topbar-update-button",onClick:()=>{rr()},title:et?.error?`LiveDesk Hub update failed: ${et.error}`:`Update LiveDesk to ${w?.update?.latestVersion||et?.targetManagerVersion||"latest"}`,"aria-label":et?.error?`LiveDesk Hub update failed: ${et.error}`:mr?"LiveDesk update in progress":`Update LiveDesk to ${w?.update?.latestVersion||et?.targetManagerVersion||"latest"}`,type:"button",disabled:mr||!w?.update?.canApply,"aria-busy":mr,children:[mr?r.jsx("span",{className:"topbar-update-spinner","aria-hidden":"true"}):r.jsx(qh,{size:19,"aria-hidden":"true"}),mr&&w?.update?.targetCount?r.jsxs("span",{className:"topbar-update-count","aria-hidden":"true",children:[w.update.completedCount||0,"/",w.update.targetCount]}):null]}):null]}),r.jsxs("div",{className:"topbar-tray",children:[Ji&&!ct&&r.jsxs("div",{className:"file-share-top-route","aria-label":"File distribution route",children:[r.jsxs("div",{children:[r.jsx(Rl,{size:17}),r.jsxs("span",{children:[r.jsx("small",{children:"Source"}),r.jsx("strong",{children:"LiveDesk Hub"})]})]}),r.jsx(Kh,{size:15}),r.jsx(x0,{size:16}),r.jsxs("div",{children:[r.jsx("span",{className:"file-share-top-route-laptops",children:"▣ ▣ ▣"}),r.jsxs("span",{children:[r.jsx("small",{children:"Destination"}),r.jsxs("strong",{children:[gu," Client",gu===1?"":"s"]}),r.jsx("em",{children:ht.trim()||"Desktop/LiveDeskFiles"})]})]})]}),Ar&&!ct&&r.jsxs("div",{className:"top-actions",children:[r.jsx(xb,{active:pn,refreshing:pn&&It,onClick:fc}),r.jsx(yb,{active:st,refreshing:st&&It,onClick:or}),kb&&r.jsx(bb,{active:Ee,refreshing:Ee&&It,onClick:cr}),r.jsx("span",{className:"top-actions-divider","aria-hidden":"true"}),r.jsx(vb,{controller:Qo,onError:Pe}),r.jsx("span",{className:"top-actions-divider","aria-hidden":"true"}),r.jsx("div",{className:"segmented-control","aria-label":"Wall view",children:YT.map(y=>r.jsx("button",{className:ze===y.id?"active":"",onClick:()=>Me(y.id),type:"button",children:y.label},y.id))})]}),ct&&r.jsxs("div",{className:"top-actions",children:[r.jsx(xb,{active:pn,refreshing:pn&&It,onClick:fc}),r.jsx(yb,{active:st,refreshing:st&&It,onClick:or}),kb&&r.jsx(bb,{active:Ee,refreshing:Ee&&It,onClick:cr}),r.jsx("span",{className:"top-actions-divider","aria-hidden":"true"}),r.jsx(vb,{controller:Qo,onError:Pe})]})]})]}),pn&&Ar&&r.jsx(AN,{items:yl,collectedAt:Ya,onClose:()=>nt(!1)}),mu?r.jsxs("section",{className:"doc-page",lang:Ka,dir:Ka==="ar"?"rtl":"ltr",children:[r.jsxs("section",{className:"doc-hero",children:[r.jsxs("div",{className:"doc-hero-copy",children:[r.jsxs("span",{className:"pricing-eyebrow",children:[r.jsx(Gd,{size:16}),Nn.eyebrow]}),r.jsx("h2",{children:Nn.title}),r.jsx("p",{children:Nn.intro})]}),r.jsxs("label",{className:"doc-language-picker",children:[r.jsxs("span",{children:[r.jsx(w0,{size:17}),Nn.languageLabel]}),r.jsx("select",{value:Ka,onChange:y=>ri(y.target.value),children:ZT.map(y=>r.jsx("option",{value:y.id,children:y.label},y.id))})]})]}),r.jsx("section",{className:"doc-grid",children:Nn.sections.map((y,N)=>{const O=[yr,kh,ty,Dn,tf][N]||Gd;return r.jsxs("article",{className:"doc-card",children:[r.jsx("div",{className:"doc-card-icon",children:r.jsx(O,{size:19})}),r.jsxs("div",{children:[r.jsx("h3",{children:y.title}),r.jsx("p",{children:y.body}),r.jsx("ul",{children:y.points.map(D=>r.jsx("li",{children:D},D))})]})]},y.title)})}),ki&&r.jsxs("section",{className:"doc-sync",children:[r.jsxs("div",{className:"doc-sync-head",children:[r.jsx(ty,{size:20}),r.jsxs("div",{children:[r.jsx("h3",{children:ki.title}),r.jsx("p",{children:ki.body})]})]}),r.jsx("ul",{children:ki.points.map(y=>r.jsx("li",{children:y},y))})]}),r.jsxs("section",{className:"doc-firewall",children:[r.jsxs("div",{className:"doc-firewall-head",children:[r.jsx(Dn,{size:20}),r.jsxs("div",{children:[r.jsx("h3",{children:Nn.firewall.title}),r.jsx("p",{children:Nn.firewall.body})]})]}),r.jsx("ul",{children:Nn.firewall.points.map(y=>r.jsx("li",{children:y},y))})]}),r.jsxs("section",{className:"doc-command-panel",children:[r.jsxs("div",{className:"doc-command-head",children:[r.jsx(kh,{size:20}),r.jsxs("div",{children:[r.jsx("h3",{children:Nn.commands.title}),r.jsx("p",{children:Nn.commands.body})]})]}),r.jsxs("div",{className:"doc-command-list",children:[Nn.commands.manager&&r.jsxs("div",{className:"doc-command-row",children:[r.jsx("span",{children:Nn.commands.manager}),r.jsx("code",{children:zr}),r.jsx("button",{className:"icon-button",onClick:()=>Bd(zr),title:"Copy command",type:"button",children:r.jsx(Fm,{size:16})})]}),r.jsxs("div",{className:"doc-command-row",children:[r.jsx("span",{children:Nn.commands.primary}),r.jsx("code",{children:wi}),r.jsx("button",{className:"icon-button",onClick:()=>Bd(wi),title:"Copy command",type:"button",children:r.jsx(Fm,{size:16})})]}),r.jsxs("div",{className:"doc-command-row",children:[r.jsx("span",{children:Nn.commands.slot}),r.jsx("code",{children:`${wi} 3`}),r.jsx("button",{className:"icon-button",onClick:()=>Bd(`${wi} 3`),title:"Copy command",type:"button",children:r.jsx(Fm,{size:16})})]})]})]})]}):Go?r.jsx(hb,{hubUrl:M,devices:k}):Ji?r.jsx(CM,{hubUrl:M,devices:at,localFeatureAccess:Wt,upgradeNotice:Tr,remoteDirectory:ht,onRemoteDirectoryChange:cn,targetMode:La,selectedTargetIds:gs,onTargetModeChange:Ul,onTargetToggle:y=>Fo(N=>N.includes(y)?N.filter(_=>_!==y):[...N,y]),filePickerRef:mf,folderPickerRef:hf,onBrowserFileImport:y=>{to(y)},onBrowserFolderImport:y=>{to(y)},onNotice:Pe,onTransferChange:y=>{if(!y){Ds(null);return}const N=y.state==="failed"||y.state==="partially-failed"||y.state==="cancelled"?"failed":y.state==="completed"?"queued":y.state==="sending"?"sending":"reading";Ds({id:y.id,status:N,progress:y.progress,message:y.message}),["completed","partially-failed","failed","cancelled"].includes(y.state)&&window.setTimeout(()=>Ds(_=>_?.id===y.id?null:_),4500)}}):hu?r.jsx(ax,{session:n,currentProductKey:"livedesk",appVersion:"",onSignIn:rs}):Vl?r.jsx($1,{hubUrl:M,runtimeRole:w?.runtimeRole||"hub",runtimeDeviceId:w?.deviceId||"",sessionAccessToken:n?.access_token||"",pin:r.jsxs(P1,{children:[r.jsx("div",{className:"settings-section-grid",children:r.jsxs("section",{className:"settings-section-card settings-pairing-card",children:[r.jsxs("div",{className:"settings-section-heading",children:[r.jsxs("div",{children:[r.jsx("span",{children:"Pair a Client"}),r.jsx("h3",{children:"Connect another local PC"})]}),r.jsx(ha,{size:20})]}),r.jsxs("div",{className:"settings-pin-display",children:[r.jsx("span",{children:"Hub PIN"}),r.jsx("strong",{children:io}),r.jsxs("button",{className:"text-button ghost compact",onClick:zs,disabled:!w?.started||ve==="pin",type:"button",children:[r.jsx(ti,{size:15}),ve==="pin"?"Creating":"New PIN"]})]})]})}),r.jsxs("section",{className:"settings-section-card",children:[r.jsxs("div",{className:"settings-section-heading",children:[r.jsxs("div",{children:[r.jsx("span",{children:"File transfer"}),r.jsx("h3",{children:"Client destination folder"})]}),r.jsx(To,{size:20})]}),r.jsxs("label",{className:"settings-field",children:[r.jsx("span",{children:"Remote folder"}),r.jsx("input",{value:ht,onChange:y=>cn(y.target.value),placeholder:"Desktop/LiveDeskFiles"})]}),r.jsx("p",{className:"settings-help",children:"Blank uses Desktop/LiveDeskFiles on each Client."})]})]}),agent:r.jsx(q1,{hubUrl:M}),captureGallery:r.jsx(kN,{hubUrl:M,onToast:Pe})}):qo?r.jsxs("section",{className:"pricing-page",children:[r.jsxs("header",{className:"pricing-header",children:[r.jsxs("div",{children:[r.jsxs("span",{className:"pricing-eyebrow",children:[r.jsx(Sh,{size:15})," LiveDesk plans"]}),r.jsx("h2",{children:"Choose the plan that fits your wall"}),r.jsx("p",{children:"Start free with a local screen wall. Upgrade when you need ad-free monitoring, more Clients, or commercial controls."})]}),r.jsx("button",{className:"pricing-close",onClick:()=>lo("wall"),title:"Close pricing",type:"button",children:r.jsx(ma,{size:18})})]}),r.jsxs("div",{className:"pricing-status","aria-live":"polite",children:[r.jsx("span",{children:"Current plan"}),r.jsx("strong",{children:hi.name}),r.jsxs("small",{children:[at.length,"/",hT(Wl)," connected devices"]})]}),r.jsxs("div",{className:"pricing-cards","aria-label":"LiveDesk plans",children:[r.jsxs("article",{className:`pricing-plan-card ${Gt==="free"?"current":""}`,children:[r.jsxs("div",{className:"pricing-plan-topline",children:[r.jsx("span",{className:"pricing-plan-badge free",children:"Free"}),r.jsx("strong",{children:"$0"})]}),r.jsx("p",{className:"pricing-plan-audience",children:Al.free.audience}),r.jsx("h3",{children:"Monitor your local wall"}),r.jsx("p",{children:Al.free.note}),r.jsxs("ul",{children:[r.jsxs("li",{children:[r.jsx(Bn,{size:16})," Up to ",Gc," devices"]}),r.jsxs("li",{children:[r.jsx(Bn,{size:16})," Live view and local control"]}),r.jsxs("li",{children:[r.jsx(Bn,{size:16})," Standard ad-supported banner"]})]}),r.jsx("span",{className:"pricing-current-label",children:Gt==="free"?"Current plan":"Included for everyone"})]}),r.jsxs("article",{className:`pricing-plan-card featured ${Gt==="ltd"?"current":""}`,children:[r.jsxs("div",{className:"pricing-plan-topline",children:[r.jsxs("span",{className:"pricing-plan-badge plus",children:[r.jsx(S0,{size:14})," Plus"]}),r.jsx("strong",{children:"$5"})]}),r.jsx("p",{className:"pricing-plan-audience",children:Al.ltd.audience}),r.jsx("h3",{children:"Ad-free monitoring"}),r.jsx("p",{children:"More room for personal AI Clients without the standard banner."}),r.jsxs("ul",{children:[r.jsxs("li",{children:[r.jsx(Bn,{size:16})," Up to ",Vc," devices"]}),r.jsxs("li",{children:[r.jsx(Bn,{size:16})," No standard ad banner"]}),r.jsxs("li",{children:[r.jsx(Bn,{size:16})," ",Al.ltd.ltd]})]}),r.jsxs("button",{className:"pricing-plan-button",onClick:n?()=>Ir("ltd"):rs,disabled:Gt!=="free"||ve==="checkout"||ve==="auth"||a||R,type:"button",children:[r.jsx(Kc,{size:16}),Gt==="free"?n?"Choose Plus":"Sign in for Plus":"Plus active"]})]}),r.jsxs("article",{className:`pricing-plan-card pro ${Gt==="pro"?"current":""}`,children:[r.jsxs("div",{className:"pricing-plan-topline",children:[r.jsxs("span",{className:"pricing-plan-badge pro",children:[r.jsx(Dn,{size:14})," Pro"]}),r.jsx("strong",{children:"$15"})]}),r.jsx("p",{className:"pricing-plan-audience",children:Al.pro.audience}),r.jsx("h3",{children:"For serious operations"}),r.jsx("p",{children:"Commercial-ready monitoring for larger AI labs, studios, and teams."}),r.jsxs("ul",{children:[r.jsxs("li",{children:[r.jsx(Bn,{size:16})," 30+ Client walls"]}),r.jsxs("li",{children:[r.jsx(Bn,{size:16})," Commercial use included"]}),r.jsxs("li",{children:[r.jsx(Bn,{size:16})," ",Al.pro.ltd]})]}),r.jsxs("button",{className:"pricing-plan-button",onClick:n?()=>Ir("pro"):rs,disabled:Gt==="pro"||ve==="checkout"||ve==="auth"||a||R,type:"button",children:[r.jsx(Kc,{size:16}),Gt==="pro"?"Pro active":n?"Choose Pro":"Sign in for Pro"]})]})]}),r.jsxs("section",{className:"pricing-includes",children:[r.jsxs("div",{children:[r.jsx(ha,{size:17}),r.jsx("span",{children:"Screen wall"}),r.jsx("strong",{children:"120+ slots"})]}),r.jsxs("div",{children:[r.jsx(Ch,{size:17}),r.jsx("span",{children:"Focused control"}),r.jsx("strong",{children:"Local-first"})]}),r.jsxs("div",{children:[r.jsx(ny,{size:17}),r.jsx("span",{children:"Remote audio"}),r.jsx("strong",{children:"Available"})]}),r.jsxs("div",{children:[r.jsx(To,{size:17}),r.jsx("span",{children:"File transfer"}),r.jsx("strong",{children:"Included"})]})]}),r.jsxs("p",{className:"pricing-note",children:[r.jsx(Bn,{size:15})," Commercial use requires Pro. Plus LTD is for personal, creator, and homelab use."]}),r.jsxs("button",{className:"pricing-primary-cta",onClick:n?()=>Ir(Gt==="ltd"?"pro":"ltd"):rs,disabled:mc,type:"button",children:[r.jsx(Kc,{size:18}),xl]})]}):r.jsx(r.Fragment,{children:r.jsxs("section",{className:`monitor-layout ${pa?"dock-collapsed":""}`,children:[r.jsxs("section",{ref:Bo,className:`screen-wall-panel ${Pl?"drop-active":""}`,onClick:y=>{y.currentTarget===y.target&&Ss()},onDragEnter:y=>{y.preventDefault(),ui(!0)},onDragOver:y=>{y.preventDefault(),y.dataTransfer.dropEffect="copy"},onDragLeave:y=>{y.currentTarget===y.target&&ui(!1)},onDrop:gl,children:[bn.length>0&&r.jsx("canvas",{ref:nc,className:"atlas-source-canvas",width:_s?.width||1920,height:_s?.height||1080,"aria-hidden":"true"}),ct?r.jsx("section",{className:"control-focus",children:r.jsxs("div",{className:"control-wall",children:[Fe&&dc.length>1&&r.jsx("div",{className:"control-monitor-tabs","aria-label":"Select monitor",children:dc.map(y=>r.jsxs("button",{className:y.index===no?"active":"",onClick:()=>Mn(Fe.deviceId,y.index),title:ET(y),type:"button",children:[r.jsx("span",{children:y.name||`Display ${y.index+1}`}),y.width>0&&y.height>0&&r.jsxs("small",{children:[y.width,"x",y.height]})]},y.index))}),Fe?r.jsx(bh,{device:Fe,frame:Le,selected:!1,controlEnabled:ys&&Ce&&Ca,showStatus:an.showScreenStatus,monitorIndex:ea(ee[Fe.deviceId],Jn(Fe)),monitorCount:Jn(Fe),onSelect:()=>{},onFocusDevice:()=>Z(Fe.deviceId),onOpenControl:()=>ir(Fe.deviceId),onCycleMonitor:y=>Os(Fe.deviceId,y),onCanvasReady:Su,sendInput:yt,presentationReady:Cr,remoteDiagnostics:Ee?Hs.get(Fe.deviceId):st?Ci.get(Fe.deviceId):void 0,diagnosticsMode:Ee?"debug":"remote"},Fe.deviceId):r.jsxs("div",{className:"control-empty",children:[r.jsx(ha,{size:30}),r.jsx("strong",{children:"No Client"}),r.jsx("span",{children:"Open a Client from the Wall."})]}),Fe&&Yo&&r.jsxs("div",{className:"control-connection-overlay",role:"alert",children:[r.jsx(Dn,{size:22,"aria-hidden":"true"}),r.jsx("strong",{children:"Accessibility permission required"}),r.jsx("small",{children:"On the Mac, allow Accessibility for the terminal app that starts LiveDesk, then restart the Mac client."})]}),Fe&&!Yo&&!Cr&&r.jsxs("div",{className:"control-connection-overlay",role:"status","aria-live":"polite",children:[Y!=="error"&&r.jsx("span",{className:"control-connection-spinner","aria-hidden":"true"}),r.jsx("strong",{children:Y==="error"?"Connection failed":"Connecting"}),r.jsx("small",{children:Y==="error"?"See the notification for details. Reopen Control to retry.":yi||fe?"Control stream is recovering. See the notification for details.":tr==="open"?"Preparing the first control frame":"Opening the Control stream"})]})]})}):ze==="list"?r.jsxs("div",{className:"wall-device-list",role:"grid","aria-label":"Computer status list",children:[r.jsxs("div",{className:"wall-device-list-head",role:"row",children:[r.jsx("span",{role:"columnheader",children:"Computer"}),r.jsx("span",{role:"columnheader",children:"Status"}),r.jsx("span",{role:"columnheader",children:"CPU"}),r.jsx("span",{role:"columnheader",children:"GPU"}),r.jsx("span",{role:"columnheader",children:"RAM"}),r.jsx("span",{role:"columnheader",children:"VRAM"}),r.jsx("span",{role:"columnheader",children:"Network"}),r.jsx("span",{role:"columnheader",children:"HDD"}),r.jsx("span",{role:"columnheader",children:"Temp"})]}),vu.length>0?vu.map(({device:y,slotNumber:N})=>r.jsx(tE,{device:y,slotNumber:N,selected:es.has(y.deviceId),updateIndicator:la(y.deviceId),onSelect:()=>hl(y.deviceId),onOpenControl:()=>ir(y.deviceId)},y.deviceId)):r.jsxs("div",{className:"wall-device-list-empty",children:[r.jsx(ha,{size:24,"aria-hidden":"true"}),r.jsx("strong",{children:"No connected computers"})]})]}):Ct?r.jsx("div",{className:`screen-wall atlas-slot-wall size-${ze}`,onClick:y=>{y.currentTarget===y.target&&Ss()},children:Mr.map((y,N)=>y?r.jsx(bh,{device:y,frame:xs.get(y.deviceId),selected:es.has(y.deviceId),controlEnabled:!1,showStatus:Ze.showScreenStatus,monitorIndex:ea(ee[y.deviceId],Jn(y)),monitorCount:Jn(y),onSelect:()=>hl(y.deviceId),onFocusDevice:()=>ml(y.deviceId),onOpenControl:()=>ir(y.deviceId),onCycleMonitor:_=>Os(y.deviceId,_),onCanvasReady:Pc({mode4Enabled:Ct,directMode4WallEnabled:Kt,isMac:ei(y.platform)})==="direct"?Jo:Zl,sendInput:yt,updateIndicator:la(y.deviceId),presentationReady:Ze.autoStartLive&&ts.includes(y.deviceId)?yn.has(y.deviceId):void 0,showPresentationSpinner:Ze.autoStartLive&&ts.includes(y.deviceId),remoteDiagnostics:Ee?Hs.get(y.deviceId):st?Ci.get(y.deviceId):void 0,diagnosticsMode:Ee?"debug":"remote"},y.deviceId):r.jsx(Pb,{index:N},`slot-${N}`))}):r.jsx("div",{className:`screen-wall size-${ze}`,onClick:y=>{y.currentTarget===y.target&&Ss()},children:Mr.map((y,N)=>y?r.jsx(bh,{device:y,frame:xs.get(y.deviceId),selected:es.has(y.deviceId),controlEnabled:ys&&Ce&&Ca&&Fe?.deviceId===y.deviceId,showStatus:Ze.showScreenStatus,monitorIndex:ea(ee[y.deviceId],Jn(y)),monitorCount:Jn(y),onSelect:()=>hl(y.deviceId),onFocusDevice:()=>ml(y.deviceId),onOpenControl:()=>ir(y.deviceId),onCycleMonitor:_=>Os(y.deviceId,_),onCanvasReady:Jo,sendInput:yt,updateIndicator:la(y.deviceId),presentationReady:Ze.autoStartLive&&ts.includes(y.deviceId)?yn.has(y.deviceId):void 0,showPresentationSpinner:Ze.autoStartLive&&ts.includes(y.deviceId),remoteDiagnostics:Ee?Hs.get(y.deviceId):st?Ci.get(y.deviceId):void 0,diagnosticsMode:Ee?"debug":"remote"},y.deviceId):r.jsx(Pb,{index:N},`slot-${N}`))}),r.jsx(sE,{visible:xu&&!ct})]}),!Vl&&r.jsxs("aside",{className:`right-dock actions-mode ${pa?"collapsed":""}`,"aria-label":"LiveDesk tools",children:[r.jsx("button",{className:"dock-toggle",onClick:()=>fs(y=>!y),"aria-label":pa?"Show control panel":"Hide control panel",title:pa?"Show control panel":"Hide control panel",type:"button",children:pa?r.jsx(tx,{size:18}):r.jsx(Xh,{size:18})}),!pa&&r.jsxs(r.Fragment,{children:[r.jsxs("section",{className:"action-dock",children:[r.jsx("input",{ref:fu,className:"hidden-file-input",type:"file",multiple:!0,onChange:y=>{y.currentTarget.files&&tn(y.currentTarget.files),y.currentTarget.value=""}}),r.jsxs("div",{className:"manual-action-bar",role:"toolbar","aria-label":"Manual Client controls",children:[r.jsx(Ao,{label:"Control",tooltip:Fe?Yo?"Allow Accessibility for the terminal app that starts LiveDesk, then restart the Mac client.":ys?Ca?"Keyboard and mouse control is ready":"Waiting for the current Control capture session":"Remote control is not available on this Client":"Select a Client to use Control",onClick:()=>{if(Fe){if(ct&&Ce){De(!1),Pe("Remote control paused. Screen viewing stays on.");return}ir(Fe.deviceId)}},disabled:!ys||!!Be,active:Ce,pressed:ct&&Ce,children:r.jsx(Ch,{size:17})}),r.jsx(Ao,{label:"Audio",tooltip:Fe?tl?He?"Stop hearing this Client":"Hear this Client":"Audio is not available on this Client":"Select a Client to use Audio",onClick:()=>{ju()},disabled:!Xo||!!Be,active:He,pressed:He,children:Be==="audio"?r.jsx(ni,{className:"spin",size:17}):r.jsx(ny,{size:17})}),r.jsx(Ao,{label:"Send files",tooltip:ya.length===0&&!ct?"Select one or more Clients to send files":Fe?"Choose files and send them to the selected Client":"Select a Client to send files",onClick:()=>{Pe(`Choose files to send to ${Tu}.`),fu.current?.click()},disabled:!!Be||!Wt||(ct?!Fe:ya.length===0),children:r.jsx(To,{size:17})}),r.jsx(Ao,{label:"Restart",tooltip:Fe?Ko?"Restart Client":"Restart is not available on this Client":"Select a Client to restart",onClick:()=>{cc("restart")},disabled:!Fe||!Wt||!!Be||!Ko,children:Be==="restart"?r.jsx(ni,{className:"spin",size:17}):r.jsx(Wh,{size:17})}),r.jsx(Ao,{label:"Sleep",tooltip:Fe?"Put Client to sleep":"Select a Client to put to sleep",onClick:()=>{cc("sleep")},disabled:!Fe||!Wt||!!Be,children:Be==="sleep"?r.jsx(ni,{className:"spin",size:17}):r.jsx(C0,{size:17})}),r.jsx(Ao,{label:"Shut down",tooltip:Fe?"Shut down Client":"Select a Client to shut down",onClick:()=>{cc("shutdown")},disabled:!Fe||!Wt||!!Be,danger:!0,children:Be==="shutdown"?r.jsx(ni,{className:"spin",size:17}):r.jsx(k0,{size:17})})]})]}),r.jsx(hb,{hubUrl:M,devices:k,variant:"dock",selectedDeviceIds:ur})]})]})]})})]}),ps&&r.jsxs("aside",{className:`transfer-bar state-${ps.status}`,"aria-live":"polite",children:[r.jsx("span",{className:"transfer-bar-icon","aria-hidden":"true",children:ps.status==="queued"?r.jsx(Bn,{size:18}):ps.status==="failed"?r.jsx(ma,{size:18}):r.jsx(Qb,{size:18})}),r.jsxs("div",{className:"transfer-bar-copy",children:[r.jsx("p",{children:ps.message}),r.jsx("div",{className:"transfer-bar-track",children:r.jsx("span",{style:{width:`${Math.max(0,Math.min(100,ps.progress))}%`}})})]}),r.jsxs("strong",{children:[Math.max(0,Math.min(100,ps.progress)),"%"]})]}),ke&&r.jsxs("aside",{className:`livedesk-toast tone-${ke.tone}`,role:ke.tone==="error"?"alert":"status","aria-live":ke.tone==="error"?"assertive":"polite","aria-busy":ke.tone==="progress",children:[ke.tone==="progress"&&r.jsx("span",{className:"livedesk-toast-spinner","aria-hidden":"true"}),r.jsx("span",{children:ke.message}),r.jsx("button",{className:"livedesk-toast-close",onClick:()=>Pe(""),type:"button","aria-label":"Dismiss notification",title:"Dismiss notification",children:r.jsx(ma,{size:15})})]})]})}function iE(){if(typeof window>"u")return[];const n=window.location.origin;return window.location.port==="5179"?[n]:["http://127.0.0.1:5179"]}function lE(){const[a,i]=u.useState(null);return u.useEffect(()=>{let l=!1,c=!1;const d=async()=>{for(const g of iE())try{const v=await fetch(`${g}/api/runtime`,{cache:"no-store"});if(!v.ok)continue;const b=(await v.json()).role==="hub"?"hub":"client";c=!0,l||i(b);return}catch{}};d();const m=window.setInterval(()=>{d()},1e3),p=window.setTimeout(()=>{!l&&!c&&i("client")},2500);return()=>{l=!0,window.clearInterval(m),window.clearTimeout(p)}},[null]),a?a==="client"?r.jsx(RC,{}):r.jsx(rE,{}):r.jsxs("main",{className:"runtime-role-boot","aria-busy":"true","aria-label":"Starting LiveDesk",children:[r.jsx("span",{className:"runtime-role-boot-mark","aria-hidden":"true"}),r.jsx("strong",{children:"Starting LiveDesk"}),r.jsx("p",{children:"Loading this computer's assigned role..."})]})}D0.createRoot(document.getElementById("root")).render(r.jsx(u.StrictMode,{children:r.jsx(lE,{})}));
|
|
25
|
+
Nothing is saved until you press Save Last 30s.`))&&n.setRollingEnabled(!0)},v=c.recording?`Stop recording · ${_d(c.recordingStartedAt)}`:`Start recording · ${c.targetLabel}`,x=c.timelapse?`Stop timelapse · ${_d(c.timelapseStartedAt)}`:`Start timelapse · ${d.timelapseIntervalSeconds}s interval`;return r.jsxs("div",{className:`capture-toolbar ${a?"compact":""} ${c.recording?"recording-active":""}`,"aria-label":"Capture actions",children:[r.jsx("button",{className:"capture-action","aria-label":`Take screenshot · ${c.targetLabel}`,title:`Take screenshot · ${c.targetLabel}`,disabled:c.busy,onClick:()=>{p(n.takeScreenshot)},type:"button",children:r.jsx(p0,{size:17})}),r.jsx("button",{className:`capture-action ${c.recording?"active":""}`,"aria-label":v,title:v,disabled:!m||c.busy||c.timelapse,onClick:()=>{p(c.recording?n.stopRecording:n.startRecording)},type:"button",children:c.recording?r.jsxs(r.Fragment,{children:[r.jsx(ey,{size:14,fill:"currentColor"}),r.jsx("span",{children:_d(c.recordingStartedAt)})]}):r.jsx(Zb,{size:18})}),r.jsxs("button",{className:"capture-action","aria-label":c.bufferEnabled?`Save last 30 seconds · ${c.bufferSeconds}s ready`:"Enable Last 30s memory buffer",title:c.bufferEnabled?`Save last 30 seconds · ${c.bufferSeconds}s ready`:"Enable Last 30s memory buffer",disabled:!m||c.recording||c.timelapse||c.busy||c.bufferEnabled&&c.bufferSeconds<1,onClick:()=>c.bufferEnabled?void p(n.saveLast30Seconds):void g(),type:"button",children:[r.jsx(g0,{size:17}),r.jsx("small",{children:"30s"})]}),r.jsx("button",{className:`capture-action ${c.timelapse?"timelapse-active":""}`,"aria-label":x,title:x,disabled:!m||c.busy||c.recording,onClick:()=>{p(c.timelapse?n.stopTimelapse:n.startTimelapse)},type:"button",children:c.timelapse?r.jsxs(r.Fragment,{children:[r.jsx(ey,{size:14,fill:"currentColor"}),r.jsx("span",{children:_d(c.timelapseStartedAt)})]}):r.jsx(v0,{size:17})})]})}function gw({state:n}){return n==="ok"?r.jsx(Bn,{size:15,"aria-hidden":"true"}):r.jsx(ex,{size:15,"aria-hidden":"true"})}function yb({active:n,refreshing:a,onClick:i}){return r.jsx("button",{className:`remote-status-button ${n?"active":""}`,type:"button",title:"Check Remote Status","aria-label":"Check Remote Status","aria-pressed":n,"aria-busy":a,onClick:i,children:a?r.jsx("span",{className:"remote-status-spinner","aria-hidden":"true"}):r.jsx(y0,{size:18,"aria-hidden":"true"})})}function bb({active:n,refreshing:a,onClick:i}){return r.jsx("button",{className:`remote-status-button debug-status-button ${n?"active":""}`,type:"button",title:"Check Debug Status","aria-label":"Check Debug Status","aria-pressed":n,"aria-busy":a,onClick:i,children:a?r.jsx("span",{className:"remote-status-spinner","aria-hidden":"true"}):r.jsx(Gb,{size:18,"aria-hidden":"true"})})}function xb({active:n,refreshing:a,onClick:i}){return r.jsx("button",{className:`remote-status-button hub-status-button ${n?"active":""}`,type:"button",title:"Check Hub Status","aria-label":"Check Hub Status","aria-pressed":n,"aria-busy":a,onClick:i,children:a?r.jsx("span",{className:"remote-status-spinner","aria-hidden":"true"}):r.jsx(yr,{size:18,"aria-hidden":"true"})})}function jN({items:n,mode:a="remote"}){return r.jsx("span",{className:`remote-status-lines mode-${a}`,role:"status","aria-label":a==="debug"?"Debug status":"Remote status",children:n.map(i=>r.jsxs("span",{className:`remote-status-line state-${i.state}`,title:i.detail,children:[r.jsx(gw,{state:i.state}),r.jsxs("span",{children:[r.jsx("strong",{children:i.label}),r.jsx("small",{children:i.detail})]})]},i.id))})}function AN({items:n,collectedAt:a,onClose:i}){return r.jsxs("section",{className:"remote-status-dialog hub-status-dialog",role:"dialog","aria-modal":"false","aria-labelledby":"hub-status-dialog-title",children:[r.jsxs("header",{children:[r.jsx("span",{className:"remote-status-dialog-mark","aria-hidden":"true",children:r.jsx(yr,{size:19})}),r.jsxs("span",{children:[r.jsx("small",{children:"Hub measurements"}),r.jsx("h2",{id:"hub-status-dialog-title",children:"Hub Status"})]}),r.jsx("button",{type:"button",onClick:i,title:"Close Hub status","aria-label":"Close Hub status",children:r.jsx(ma,{size:17})})]}),r.jsx("p",{children:"LiveDesk collects these Hub and browser signals continuously. This check refreshed the latest snapshot instead of starting a one-time benchmark."}),r.jsx("div",{className:"remote-status-dialog-list",children:n.map(l=>r.jsxs("article",{className:`remote-status-dialog-row state-${l.state}`,children:[r.jsx(gw,{state:l.state}),r.jsxs("span",{children:[r.jsx("strong",{children:l.label}),r.jsx("small",{children:l.detail})]})]},l.id))}),r.jsxs("footer",{children:["Checked ",new Date(a).toLocaleTimeString()]})]})}const MN=/(nvenc|videotoolbox|qsv|quick\s*sync|amf|vaapi|v4l2m2m|mediafoundation|h264_mf)/i,NN=/(libx264|openh264|software|cpu)/i,Ut=Object.freeze({frameAgeWarningMs:2500,networkDelayWarningMs:120,receiveRatioWarning:.62,deliveryRatioWarning:.82,decoderOutputRatioWarning:.62,captureBudgetMultiplier:2.5,minimumCaptureBudgetMs:80,cpuWarningRatio:.82,memoryWarningRatio:.86,gpuWarningRatio:.92,gpuMemoryWarningRatio:.92,gpuTemperatureWarningC:85,networkUsageWarningRatio:.85,networkLatencyWarningMs:80,minimumClientStatusFreshMs:15e3,clientStatusFreshMultiplier:3,hubMinimumTotalMemoryBytes:8*1024**3,hubMinimumBrowserMemoryGb:4});function Rn(n){return n&&typeof n=="object"&&!Array.isArray(n)?n:{}}function vw(n,a){let i=n;for(const l of String(a).split(".")){if(!i||typeof i!="object")return;i=i[l]}return i}function As(n,...a){for(const i of a){const l=vw(n,i);if(l==null||l==="")continue;const c=Number(l);if(Number.isFinite(c))return c}}function Ol(n,...a){for(const i of a){const l=vw(n,i);if(typeof l=="string"&&l.trim())return l.trim()}return""}function El(n,...a){const i=As(n,...a);if(i!==void 0)return Math.max(0,Math.min(1,i>1?i/100:i))}function ut(n,a,i,l){return{id:n,state:a,label:i,detail:l}}function TN(n){const a=Date.parse(String(n||""));return Number.isFinite(a)&&a>0?a:void 0}function wb(n){return`${(Math.max(0,Number(n||0))/1024**3).toFixed(1)} GB`}function EN(n){const a=String(n||"").replace(/^::ffff:/,"").trim().toLowerCase();if(!a||a==="::1"||a==="localhost"||a==="127.0.0.1"||/^10\./.test(a)||/^192\.168\./.test(a))return!0;const i=a.match(/^172\.(\d{1,3})\./);return!!i&&Number(i[1])>=16&&Number(i[1])<=31||a.startsWith("fe80:")||a.startsWith("fc")||a.startsWith("fd")}function yw(n,a){const i=Number(n?.browserReceivedEpochMs);return Number.isFinite(i)&&i>0?Math.max(0,a-i):Number.POSITIVE_INFINITY}function RN(n){const a=String(n?.frameMode||n?.mode||n?.codec||"").toLowerCase(),i=[n?.hardwareEncoder,n?.platformProfile].filter(Boolean).join(" · ");return!n||!/h264/.test(a)?ut("encoder","warning","Video encoder unavailable","Waiting for an H.264 frame."):MN.test(i)?ut("encoder","ok","Hardware H.264 encoding",i):NN.test(i)?ut("encoder","warning","Software H.264 encoding",`${i||"CPU encoder"} may limit frame rate.`):ut("encoder","warning","H.264 encoder not identified",i||"The Client has not reported its active encoder profile.")}function DN({device:n,frame:a,receiveStat:i,paintStat:l,codec:c,selectedMonitorIndex:d=0,control:m=!1,controlInputReady:p=!1,controlBinding:g,clientHeartbeatMs:v=5e3,now:x=Date.now()}={}){const b=Rn(n),w=Rn(b.status),k=Rn(l?.lastInfo),j=[],C=yw(a,x);j.push(b.connected===!0&&C<=Ut.frameAgeWarningMs?ut("connection","ok","Video connection is current",`${Math.round(C)}ms since the latest frame.`):ut("connection","warning",b.connected===!0?"Video frame is stale":"Client is offline",b.connected===!0&&Number.isFinite(C)?`${Math.round(C)}ms since the latest frame.`:String(b.lastDisconnectReason||"No current Client session."))),j.push(RN(a));const M=Math.max(0,Number(a?.requestedFps||a?.effectiveFps||0)),A=Math.max(0,Number(i?.framesPerSecond||0)),T=As(a,"captureP95Ms","captureAverageMs","captureStageMs"),R=Math.max(0,Number(T||0)),B=a?.captureTimingAvailable===!0&&M>0&&Number(i?.sampleMs||0)>0&&T!==void 0,F=Math.max(Ut.minimumCaptureBudgetMs,M>0?1e3/M*Ut.captureBudgetMultiplier:0),V=R>F||M>0&&i?.sampleMs>0&&A<M*Ut.receiveRatioWarning;j.push(B?V?ut("capture","warning","Screen capture cannot keep target speed",`${R?`capture p95 ${Math.round(R)}ms`:"capture timing unavailable"} · ${A.toFixed(1)}/${M||"?"} fps`):ut("capture","ok","Screen capture speed is healthy",`${R?`capture p95 ${Math.round(R)}ms`:"no slow capture reported"}${M?` · ${A.toFixed(1)}/${M} fps`:""}`):ut("capture","warning","Screen capture data is still collecting","A current frame-rate and capture-time sample is required."));const L=As(a,"hubPacketTransitAgeMs"),K=Math.max(0,Number(L||0)),W=Math.max(0,Number(a?.agentSendGapMs||a?.sourceGapMs||0)),$=M>0?1e3/M:100,Z=!EN(b.remoteAddress),H=El(w,"network.usageRatio"),pe=As(w,"network.latencyMs"),ce=(H??0)>=Ut.networkUsageWarningRatio||(pe??0)>=Ut.networkLatencyWarningMs,J=L!==void 0&&Number(i?.sampleMs||0)>0,I=M>0&&Number(i?.sampleMs||0)>0&&A<M*Ut.deliveryRatioWarning,ne=K>Ut.networkDelayWarningMs||W>Math.max(Ut.networkDelayWarningMs,$*3)||I||C>Ut.frameAgeWarningMs||ce;j.push(J?ne||Z?ut("network","warning",Z?"External network route may add delay":ce?"Client network adapter needs attention":"Video delivery is uneven",[K?`${Math.round(K)}ms Hub-to-browser`:"",W?`${Math.round(W)}ms source gap`:"",I?`${A.toFixed(1)} of ${M} requested fps`:"",pe!==void 0?`${Math.round(pe)}ms Client network latency`:"",H!==void 0?`${Math.round(H*100)}% Client link use`:"",Z?String(b.remoteAddress||"external route"):""].filter(Boolean).join(" · ")||"Frame delivery is not current."):ut("network","ok","Network delivery is healthy",`${Math.round(K)}ms Hub-to-browser · the current video rate is on target.`):ut("network","warning","Network delivery data is still collecting","A calibrated Hub-to-browser transit sample is required."));const Y=El(w,"cpu.usageRatio","cpu.usedRatio","cpu.percent"),U=El(w,"memory.usedRatio","usedMemRatio"),re=El(w,"gpu.usageRatio"),oe=El(w,"gpu.memory.usedRatio"),X=As(w,"gpu.temperatureC","thermal.gpuC"),xe=Y!==void 0&&U!==void 0,fe=TN(b.lastStatusAt),z=fe===void 0?Number.POSITIVE_INFINITY:Math.max(0,x-fe),Ae=Math.max(Ut.minimumClientStatusFreshMs,Math.max(1e3,Number(v||0))*Ut.clientStatusFreshMultiplier),Ce=z<=Ae,De=(Y??0)>=Ut.cpuWarningRatio||(U??0)>=Ut.memoryWarningRatio||(re??0)>=Ut.gpuWarningRatio||(oe??0)>=Ut.gpuMemoryWarningRatio||(X??0)>=Ut.gpuTemperatureWarningC;j.push(xe?Ce?De?ut("computer","warning",(re??0)>=Ut.gpuWarningRatio||(oe??0)>=Ut.gpuMemoryWarningRatio||(X??0)>=Ut.gpuTemperatureWarningC?"Client GPU is under load":"Client computer is under load",[`CPU ${Math.round(Y*100)}%`,`memory ${Math.round(U*100)}%`,re!==void 0?`GPU ${Math.round(re*100)}%`:"",oe!==void 0?`GPU memory ${Math.round(oe*100)}%`:"",X!==void 0?`GPU ${Math.round(X)}C`:""].filter(Boolean).join(" · ")):ut("computer","ok","Client computer has working capacity",[`CPU ${Math.round(Y*100)}%`,`memory ${Math.round(U*100)}%`,re!==void 0?`GPU ${Math.round(re*100)}%`:""].filter(Boolean).join(" · ")):ut("computer","warning","Client performance snapshot is stale",Number.isFinite(z)?`The latest CPU and memory report is ${Math.round(z/1e3)} seconds old.`:"No current Client performance heartbeat has been received."):ut("computer","warning","Client performance data is unavailable","CPU and memory telemetry have not been reported yet."));const He=Number(a?.monitorIndex),he=Number.isInteger(He)&&He===Number(d),Ge=Rn(b.channels),lt=Rn(b.capabilities),se=Ge.input!==!1&<.remoteInput!==!1&<.accessibilityPermission!==!1,ve=lt.screenRecordingPermission!==!1,Te=Rn(g),Be=!m||String(a?.sessionId||"")&&String(a?.sessionId||"")===String(Te.sessionId||"")&&String(a?.commandId||"")&&String(a?.commandId||"")===String(Te.commandId||"")&&Number(a?.captureGeneration||0)>0&&Number(a?.captureGeneration||0)===Number(Te.captureGeneration||0)&&Number(a?.monitorIndex)===Number(Te.monitorIndex),_e=!m||p&&Be;j.push(he&&_e&&se&&ve?ut("display","ok",m?"Display and remote input are bound":"Display and remote input are available",`Display ${Number(d)+1} · generation ${Number(a?.captureGeneration||0)||"current"}.`):ut("display","warning",ve?se?m?"Remote input is not bound to this display":"Waiting for the selected display":"Remote input permission is missing":"Screen capture permission is missing",`Selected display ${Number(d)+1}; latest frame ${Number.isInteger(He)?He+1:"not available"}.`));const Ke=Rn(c),Ve=Array.isArray(Ke.canvases)?Rn(Ke.canvases[0]):{},rt=Math.max(0,Number(l?.decoderInputFps||0)),vt=Math.max(0,Number(l?.decoderOutputFps||0)),q=rt>0&&Number(l?.sampleMs||i?.sampleMs||0)>0,ye=rt>0?vt/rt:0,ke=Ol(Ve,"configuredAcceleration","requestedAcceleration")||Ol(k,"h264DecodeAcceleration"),$e=Ol(Ve,"softwareFallbackReason"),ot=!!$e||ke==="prefer-software";return j.push(q?ot||ye<Ut.decoderOutputRatioWarning?ut("decoder","warning",ot?"Browser is using software H.264 fallback":"Browser decoder output is slow",`${$e||String(k.decoderLabel||ke||"decoder")} · ${rt.toFixed(1)} in / ${vt.toFixed(1)} out fps.`):ut("decoder","ok",ke==="prefer-hardware"?"Browser requests hardware H.264":"Browser H.264 path is responsive",`${rt.toFixed(1)} in / ${vt.toFixed(1)} out fps. Chrome does not expose definitive hardware acceptance.`):ut("decoder","warning","Browser decoder data is still collecting","Decoder input and output samples are required before this path can be marked healthy.")),j}function jo(n){const a=String(n||"").trim();return a?a.length<=14?a:`${a.slice(0,6)}…${a.slice(-5)}`:"-"}function ca(n,a=1){const i=Number(n);return Number.isFinite(i)?i.toFixed(a):"-"}function LN({device:n,frame:a,receiveStat:i,paintStat:l,codec:c,selectedMonitorIndex:d=0,control:m=!1,controlInputReady:p=!1,controlBinding:g,now:v=Date.now()}={}){const x=Rn(n),b=Rn(g),w=Rn(l?.lastInfo),k=Rn(c),j=Array.isArray(k.canvases)?Rn(k.canvases[0]):{},C=[],M=Number(d),A=Number(a?.monitorIndex),T=Number.isInteger(A)&&A===M,R=Number(a?.captureGeneration||0),B=Number(b.captureGeneration||0),F=!m||T&&String(a?.sessionId||"")===String(b.sessionId||"")&&String(a?.commandId||"")===String(b.commandId||"")&&R>0&&R===B,V=Math.max(0,Number(a?.requestedFps||a?.effectiveFps||0)),L=Math.max(0,Number(i?.framesPerSecond||0)),K=Math.max(0,Number(l?.framesPerSecond||0)),W=Math.max(0,Number(l?.decoderInputFps||0)),$=Math.max(0,Number(l?.decoderOutputFps||0)),Z=Number(i?.sampleMs||0)>0&&Number(l?.sampleMs||0)>0,H=V>0&&Z&&Math.min(L,K||L,$||L)<V*Ut.deliveryRatioWarning,pe=Math.max(0,Number(a?.agentSendGapMs||a?.sourceGapMs||0)),ce=As(a,"hubPacketTransitAgeMs"),J=Math.max(0,Number(a?.droppedByAgent||0)),I=Math.max(0,Number(a?.hubIngressDropped||0)),ne=Math.max(0,Number(a?.hubClientDropped||0)),Y=J+I+ne,U=yw(a,v),re=Ol(j,"configuredAcceleration","requestedAcceleration")||Ol(w,"h264DecodeAcceleration"),oe=Ol(j,"softwareFallbackReason");C.push(a?ut("debug-frame","ok",`Frame #${Number(a.frameSeq||0)} · ${String(a.streamPurpose||(m?"control":"wall"))}`,`stream ${jo(a.streamId)} · ${a.width||0}x${a.height||0} · ${Math.round(Number(a.byteLength||0)/1024)} KB · age ${Number.isFinite(U)?Math.round(U):"-"}ms`):ut("debug-frame","warning","No current frame",`Device ${jo(x.deviceId)} has not produced a frame.`)),C.push(T?ut("debug-monitor","ok",`Monitor selected ${M+1} · frame ${A+1}`,`reported ${Number(a?.monitorCount||0)||"?"} displays · generation ${R||"-"}`):ut("debug-monitor","warning",`Monitor mismatch: selected ${M+1} · frame ${Number.isInteger(A)?A+1:"-"}`,`reported ${Number(a?.monitorCount||0)||"?"} displays · generation ${R||"-"}`)),C.push(F&&(!m||p)?ut("debug-binding","ok",m?"Control generation is bound":"Wall capture generation is current",`session ${jo(a?.sessionId)} · command ${jo(a?.commandId)} · generation ${R||"-"}`):ut("debug-binding","warning",m?"Control generation is not bound":"Wall capture generation is unavailable",`frame ${jo(a?.commandId)}/${R||"-"} · expected ${jo(b.commandId)}/${B||"-"} · input ${p?"ready":"waiting"}`)),C.push(Z&&!H?ut("debug-cadence","ok",`Cadence ${ca(L)} rx · ${ca(K)} paint · ${ca($)} decode`,`requested ${ca(V)} · decoder input ${ca(W)} fps · receive p95 ${ca(i?.p95IntervalMs)}ms`):ut("debug-cadence","warning",Z?"Frame cadence is below request":"Frame cadence sample is collecting",`requested ${ca(V)} · receive ${ca(L)} · paint ${ca(K)} · decode ${ca($)} fps`)),C.push(Y===0&&pe<=Ut.networkDelayWarningMs&&(ce===void 0||ce<=Ut.networkDelayWarningMs)?ut("debug-delivery","ok","Delivery counters are clear",`source gap ${Math.round(pe)}ms · Hub transit ${ce===void 0?"-":Math.round(ce)}ms · queue ${Math.round(Number(a?.agentQueueBeforePaceMs||0))}ms`):ut("debug-delivery","warning","Delivery counters show loss or delay",`Agent drop ${J} · Hub ingress ${I} · browser queue ${ne} · source gap ${Math.round(pe)}ms · transit ${ce===void 0?"-":Math.round(ce)}ms`));const X=As(a,"captureP95Ms");return C.push(a?.captureTimingAvailable===!0&&X!==void 0?ut("debug-capture","ok",`Capture p95 ${ca(X)}ms · avg ${ca(a?.captureAverageMs)}ms`,`stage ${ca(a?.captureStageMs)}ms · slow ${Number(a?.slowFrameCount||0)} · helper restart ${Number(a?.captureHelperRestarts||0)} · source skip ${Number(a?.captureSourceSkipped||0)}`):ut("debug-capture","warning","Capture timing sample is unavailable",`stage ${ca(a?.captureStageMs)}ms · helper restart ${Number(a?.captureHelperRestarts||0)} · source serial ${Number(a?.captureSourceSerial||0)}`)),C.push(!oe&&re!=="prefer-software"?ut("debug-decoder","ok",`Decoder ${re||"acceleration unknown"}`,`${String(a?.frameMode||a?.codec||"-")} · ${String(a?.hardwareEncoder||"-")} · ${String(a?.platformProfile||"-")}`):ut("debug-decoder","warning","Decoder software fallback is active",`${oe||re||"software"} · ${ca(W)} in / ${ca($)} out fps`)),C}function _N({status:n,deviceDiagnostics:a=[],codec:i,browser:l,frameStats:c}={}){const d=Rn(n),m=Rn(d.diagnostics),p=Rn(m.system),g=Rn(m.network),v=Rn(i),x=Rn(l),b=Array.isArray(a)?a:[],w=b.reduce((pe,ce)=>pe+(Array.isArray(ce)&&ce.some(J=>J?.state==="warning")?1:0),0),k=El(p,"cpuUsageRatio","processCpuRatio"),j=El(p,"memoryUsedRatio"),C=As(p,"totalMemoryBytes"),M=As(p,"cpuLogicalCores")??As(x,"hardwareConcurrency"),A=As(x,"deviceMemory"),T=(M??0)>0&&k!==void 0&&j!==void 0&&(C??0)>0,R=(C??0)>0&&C<Ut.hubMinimumTotalMemoryBytes,B=(A??0)>0&&A<Ut.hubMinimumBrowserMemoryGb,F=(M??0)>0&&M<4||(k??0)>=Ut.cpuWarningRatio||(j??0)>=Ut.memoryWarningRatio||R||B,V=Math.max(0,Number(v.activeSoftwareFallbackCount??v.softwareFallbackCount??0)),L=Math.max(0,Number(v.activeDecoderCount||0)),K=Math.max(0,Number(c?.bytesPerSecond||0)*8/1e6),W=As(g,"externalDeviceCount"),$=Ol(g,"route"),Z=W!==void 0&&!!$,H=(W??0)>0||$==="external";return[d.started&&!d.lastError?ut("hub-runtime","ok","Hub runtime is ready",`${d.connectedDeviceCount||0} Clients connected.`):ut("hub-runtime","warning","Hub runtime needs attention",String(d.lastError||"Hub is not ready.")),T?F?ut("hub-computer","warning",R?"Hub has limited system memory":B?"Browser reports limited memory capacity":"Hub computer may limit remote performance",[`${M||"?"} logical cores`,`${wb(C)} RAM`,A!==void 0?`${A} GB browser memory class`:"",`CPU ${k===void 0?"?":Math.round(k*100)}%`,`memory ${j===void 0?"?":Math.round(j*100)}%`].filter(Boolean).join(" · ")):ut("hub-computer","ok","Hub computer capacity is healthy",`${M||"?"} logical cores · ${wb(C)} RAM · CPU ${k===void 0?"not reported":`${Math.round(k*100)}%`}.`):ut("hub-computer","warning","Hub performance data is unavailable","Logical-core, CPU, used-memory, and total-memory telemetry are required."),L<=0?ut("hub-codec","warning","No active browser H.264 decoder to check","Open Wall or Control and wait for a video frame."):V>0?ut("hub-codec","warning","Some browser decoders use software fallback",`${L} active H.264 decoders · ${V} software fallbacks. Chrome does not expose definitive hardware acceptance.`):ut("hub-codec","ok","All active streams request hardware H.264",`${L} active decoders. Chrome hardware acceptance is inferred from health, not directly exposed.`),w>0?ut("hub-clients","warning",`${w} Clients need attention`,"Open the matching Wall slot lines for the exact cause."):ut("hub-clients","ok","Connected Clients report healthy remote paths",`${b.length} Client reports checked.`),ut("hub-network",!Z||H||K>120?"warning":"ok",Z?H?`${W} Clients use an external network route`:K>120?"Hub receive load is high":"Hub receive load is within range":"Hub network route data is unavailable",Z?`${$.toUpperCase()} route · ${K.toFixed(K>=10?0:1)} Mbps received by this browser.`:"The Hub has not reported LAN/external route classification yet.")]}async function IN(n,a={}){const i=n.filter(b=>b.canvas&&b.canvas.width>0&&b.canvas.height>0),l=Math.max(320,a.maxWidth||1920),c=Math.max(180,a.maxHeight||1080),d=i.length<=1?1:i.length<=4?2:i.length<=9?3:4,m=Math.max(1,Math.ceil(Math.max(1,i.length)/d)),p=Math.floor(l/d),g=Math.floor(c/m),v=document.createElement("canvas");v.width=Math.max(320,p*d),v.height=Math.max(180,g*m);const x=v.getContext("2d",{alpha:!1});if(!x)return v;x.fillStyle="#0b1220",x.fillRect(0,0,v.width,v.height),x.font="600 14px system-ui, sans-serif",x.textBaseline="bottom";for(let b=0;b<i.length;b+=1){const w=i[b],k=b%d,j=Math.floor(b/d),C=k*p,M=j*g,A=w.canvas,T=Math.min(p/A.width,g/A.height),R=Math.max(1,Math.floor(A.width*T)),B=Math.max(1,Math.floor(A.height*T)),F=C+Math.floor((p-R)/2),V=M+Math.floor((g-B)/2);x.drawImage(A,F,V,R,B),a.includeLabels!==!1&&(x.fillStyle="rgba(4, 10, 20, .74)",x.fillRect(C,M+g-28,p,28),x.fillStyle="#f8fafc",x.fillText(w.label,C+9,M+g-9))}return v}function zN(n){return n?.signature||"none"}function ON(n){return n?.label||"No capture target"}function Sb(n){return{...n,deviceIds:[...n.deviceIds],sourceCanvas:n.sourceCanvas||null}}function HN(){const n=new Date,a=i=>String(i).padStart(2,"0");return`${n.getFullYear()}${a(n.getMonth()+1)}${a(n.getDate())}_${a(n.getHours())}${a(n.getMinutes())}${a(n.getSeconds())}`}function FN(n){return n.replace(/[^a-zA-Z0-9._-]+/g,"-").replace(/^-+|-+$/g,"").slice(0,80)||"Wall"}function BN(n){return`LiveDesk_${n.type==="wall"||n.type==="selection"?"Wall":`${FN(n.label)}_Monitor-${Number(n.monitorIndex||0)+1}`}_${HN()}.png`}async function $N(n,a,i){const l=await new Promise((d,m)=>n.toBlob(p=>p?d(p):m(new Error("Screenshot could not be encoded.")),"image/png")),c=await pw(l,{kind:"screenshot",fileName:BN(a),target:a},i);try{await navigator.clipboard.write([new ClipboardItem({"image/png":l})])}catch{}return c.capture}function bw(){return["video/webm;codecs=vp9","video/webm;codecs=vp8","video/webm"].find(a=>typeof MediaRecorder<"u"&&MediaRecorder.isTypeSupported(a))||""}function PN(n,a){const i=new Date().toISOString().replace(/[-:]/g,"").replace(/\.\d{3}Z$/,"").replace("T","_"),l=a.label.replace(/[^a-zA-Z0-9._-]+/g,"-").slice(0,70)||"Wall";return n==="timelapse"?`LiveDesk_Timelapse_${l}_${i}.webm`:`LiveDesk_Recording_${l}_${i}.webm`}async function xw(n){if(typeof MediaRecorder>"u"||typeof n.canvas.captureStream!="function")throw new Error("This browser does not support video recording.");const a=bw();if(!a)throw new Error("This browser does not support WebM recording.");const i=n.kind||"recording",l=await bN({kind:i,fileName:PN(i,n.target),mimeType:a,target:n.target},n.hubUrl),c=n.canvas.captureStream(n.frameRequests?0:n.fps),d=new MediaRecorder(c,{mimeType:a,videoBitsPerSecond:i==="timelapse"?2e6:4e6}),m=performance.now();let p=0,g=!1,v=null,x=Promise.resolve();return d.ondataavailable=b=>{if(!b.data||b.data.size===0)return;const w=p++;x=x.then(()=>xN(l.sessionId,w,b.data,n.hubUrl).then(()=>{})).catch(k=>{v=v||(k instanceof Error?k:new Error(String(k))),n.onError?.(v)})},d.onerror=()=>{v=new Error("Video recording stopped unexpectedly."),n.onError?.(v)},d.start(1e3),{requestFrame:()=>{c.getVideoTracks()[0]?.requestFrame?.()},stop:async()=>{if(!g){if(g=!0,await new Promise(b=>{d.addEventListener("stop",()=>b(),{once:!0}),d.stop()}),c.getTracks().forEach(b=>b.stop()),await x,v)throw await pb(l.sessionId,n.hubUrl).catch(()=>{}),v;await wN(l.sessionId,Math.max(0,performance.now()-m),n.hubUrl)}},cancel:async()=>{g||(g=!0,d.stop(),c.getTracks().forEach(b=>b.stop()),await pb(l.sessionId,n.hubUrl).catch(()=>{}))}}}class UN{recorder=null;stream=null;segments=[];totalBytes=0;targetSignature="";startedAt=0;maxDurationMs=3e4;maxBytes=128*1024*1024;get seconds(){return!this.startedAt||this.segments.length===0?0:Math.min(30,Math.max(0,(performance.now()-this.segments[0].at)/1e3))}get active(){return this.recorder!==null}async start(a,i){if(this.recorder&&this.targetSignature===i.signature)return;if(this.stop(),typeof MediaRecorder>"u"||typeof a.captureStream!="function")throw new Error("This browser does not support Last 30s.");const l=bw();if(!l)throw new Error("This browser does not support WebM recording.");this.targetSignature=i.signature,this.stream=a.captureStream(20),this.recorder=new MediaRecorder(this.stream,{mimeType:l,videoBitsPerSecond:3e6}),this.startedAt=performance.now(),this.recorder.ondataavailable=c=>{if(!c.data.size)return;const d=performance.now();for(this.segments.push({blob:c.data,at:d}),this.totalBytes+=c.data.size;this.segments.length>1&&(d-this.segments[0].at>this.maxDurationMs||this.totalBytes>this.maxBytes);){const m=this.segments.shift();this.totalBytes-=m.blob.size}},this.recorder.start(1e3)}stop(){const a=this.recorder;this.recorder=null,a&&a.state!=="inactive"&&(a.ondataavailable=null,a.stop()),this.stream?.getTracks().forEach(i=>i.stop()),this.stream=null,this.segments=[],this.totalBytes=0,this.startedAt=0}async save(a,i){if(this.segments.length===0)throw new Error("The Last 30s buffer is not ready yet.");const l=new Blob(this.segments.map(m=>m.blob),{type:this.segments[0].blob.type||"video/webm"}),d=`LiveDesk_Last30s_${a.label.replace(/[^a-zA-Z0-9._-]+/g,"-").slice(0,70)||"Wall"}_${new Date().toISOString().replace(/[-:]/g,"").replace(/\.\d{3}Z$/,"").replace("T","_")}.webm`;return await pw(l,{kind:"last-30s",fileName:d,target:a,durationMs:Math.min(3e4,this.segments.length*1e3)},i)}}async function qN(n,a,i,l,c){const d=await xw({canvas:n,target:a,kind:"timelapse",fps:30,frameRequests:!0,hubUrl:l,onError:c});d.requestFrame?.();const m=window.setInterval(()=>d.requestFrame?.(),Math.max(5,i)*1e3);return{stop:async()=>{window.clearInterval(m),await d.stop()},cancel:async()=>{window.clearInterval(m),await d.cancel()}}}const ww="livedesk.captureSettings";function GN(){const n={rollingEnabled:!1,recordingQuality:"standard",timelapseIntervalSeconds:10,includeRemoteCursor:!0};try{const a=JSON.parse(localStorage.getItem(ww)||"null");return{...n,...a&&typeof a=="object"?a:{}}}catch{return n}}function VN(n){const[a,i]=u.useState(GN),[l,c]=u.useState(0),[d,m]=u.useState(0),[p,g]=u.useState(0),[v,x]=u.useState(!1),b=u.useRef(void 0),w=u.useRef(!1),k=u.useRef(""),j=u.useRef(null),C=u.useRef(null),M=u.useRef(null),A=u.useRef(null),T=u.useRef(new UN),R=n.sourceRegistry.resolveCurrentTarget(),B=zN(R),F=ON(R),V=typeof MediaRecorder<"u"&&typeof HTMLCanvasElement<"u"&&"captureStream"in HTMLCanvasElement.prototype;u.useEffect(()=>{if(localStorage.setItem(ww,JSON.stringify(a)),!w.current)return;const Y=JSON.stringify(a);Y!==k.current&&(k.current=Y,qe("/api/settings",{method:"PATCH",body:JSON.stringify({revision:b.current,filesAudio:{rollingBufferEnabled:a.rollingEnabled,recordingQuality:a.recordingQuality,timelapseIntervalSeconds:a.timelapseIntervalSeconds,includeRemoteCursor:a.includeRemoteCursor}})},n.hubUrl).then(U=>{b.current=Number(U.revision||b.current||0)}).catch(()=>{}))},[n.hubUrl,a]),u.useEffect(()=>{let Y=!0;return qe("/api/settings",void 0,n.hubUrl).then(U=>{if(!Y)return;const re=U.settings?.filesAudio||{},oe=typeof re.rollingBufferEnabled=="boolean"?re.rollingBufferEnabled:a.rollingEnabled,X=re.recordingQuality==="high"?"high":"standard",xe=[5,10,30,60].includes(Number(re.timelapseIntervalSeconds))?Number(re.timelapseIntervalSeconds):a.timelapseIntervalSeconds,fe=typeof re.includeRemoteCursor=="boolean"?re.includeRemoteCursor:a.includeRemoteCursor,z={rollingEnabled:oe,recordingQuality:X,timelapseIntervalSeconds:xe,includeRemoteCursor:fe};b.current=Number(U.revision||0),k.current=JSON.stringify(z),w.current=!0,i(z)}).catch(()=>{w.current=!0}),()=>{Y=!1}},[n.hubUrl]);const L=u.useCallback(async Y=>{if(Y.sourceCanvas&&Y.sourceCanvas.width>0&&Y.sourceCanvas.height>0)return{target:Sb(Y),canvas:Y.sourceCanvas,cleanup:()=>{}};const U=await n.sourceRegistry.composeWall(Y.deviceIds,{maxWidth:1920,maxHeight:1080,includeLabels:!0});let re=!1;const oe=window.setInterval(async()=>{if(re)return;const X=await n.sourceRegistry.composeWall(Y.deviceIds,{maxWidth:1920,maxHeight:1080,includeLabels:!0}).catch(()=>null);!X||re||((U.width!==X.width||U.height!==X.height)&&(U.width=X.width,U.height=X.height),U.getContext("2d")?.drawImage(X,0,0))},100);return{target:Sb(Y),canvas:U,cleanup:()=>{re=!0,window.clearInterval(oe)}}},[n.sourceRegistry]);u.useEffect(()=>{const Y=T.current;if(!a.rollingEnabled||!R||!V){Y.stop(),g(0);return}let U=!1,re=null;return(async()=>{try{if(re=await L(R),U)return;await Y.start(re.canvas,re.target);const X=window.setInterval(()=>g(Math.round(Y.seconds)),500),xe=()=>{window.clearInterval(X),re?.cleanup()};Y.__cleanup=xe}catch(X){U||n.onToast(X instanceof Error?X.message:String(X))}})(),()=>{U=!0,Y.stop(),Y.__cleanup?.(),Y.__cleanup=void 0,g(0)}},[L,n.onToast,a.rollingEnabled,B,V]);const K=u.useCallback(Y=>i(U=>({...U,rollingEnabled:Y})),[]),W=u.useCallback(Y=>i(U=>({...U,recordingQuality:Y})),[]),$=u.useCallback(Y=>i(U=>({...U,timelapseIntervalSeconds:Y})),[]),Z=u.useCallback(async()=>{const Y=n.sourceRegistry.resolveCurrentTarget();if(!Y)throw new Error("화면이 준비된 후 다시 시도하세요.");x(!0);let U=null;try{U=await L(Y),await $N(U.canvas,U.target,n.hubUrl),n.onToast("Screenshot saved")}finally{U?.cleanup(),x(!1)}},[L,n.hubUrl,n.onToast,n.sourceRegistry]),H=u.useCallback(async()=>{if(M.current)throw new Error("Stop the timelapse before recording.");const Y=n.sourceRegistry.resolveCurrentTarget();if(!Y)throw new Error("화면이 준비된 후 다시 시도하세요.");const U=await L(Y),re=await xw({canvas:U.canvas,target:U.target,fps:Y.type==="control"?30:15,hubUrl:n.hubUrl,onError:oe=>n.onToast(oe.message)});C.current=U.cleanup,j.current=re,c(Date.now()),n.onToast("Recording started")},[L,n.hubUrl,n.onToast,n.sourceRegistry]),pe=u.useCallback(async()=>{const Y=j.current;if(Y){x(!0);try{await Y.stop(),n.onToast("Recording saved")}finally{j.current=null,C.current?.(),C.current=null,c(0),x(!1)}}},[n.onToast]),ce=u.useCallback(async()=>{if(j.current||M.current)throw new Error("Last 30s is unavailable during a recording.");const Y=n.sourceRegistry.resolveCurrentTarget();if(!Y)throw new Error("화면이 준비된 후 다시 시도하세요.");x(!0);try{await T.current.save(Y,n.hubUrl),n.onToast("Last 30 seconds saved")}finally{x(!1)}},[n.hubUrl,n.onToast,n.sourceRegistry]),J=u.useCallback(async()=>{if(j.current)throw new Error("Stop the recording before starting a timelapse.");const Y=n.sourceRegistry.resolveCurrentTarget();if(!Y)throw new Error("화면이 준비된 후 다시 시도하세요.");const U=await L(Y),re=await qN(U.canvas,U.target,a.timelapseIntervalSeconds,n.hubUrl,oe=>n.onToast(oe.message));A.current=U.cleanup,M.current=re,m(Date.now()),n.onToast("Timelapse started")},[L,n.hubUrl,n.onToast,n.sourceRegistry,a.timelapseIntervalSeconds]),I=u.useCallback(async()=>{const Y=M.current;if(Y){x(!0);try{await Y.stop(),n.onToast("Timelapse saved")}finally{M.current=null,A.current?.(),A.current=null,m(0),x(!1)}}},[n.onToast]);return u.useEffect(()=>{const Y=U=>{!j.current&&!M.current||(U.preventDefault(),U.returnValue="")};return window.addEventListener("beforeunload",Y),()=>window.removeEventListener("beforeunload",Y)},[]),u.useEffect(()=>()=>{T.current.stop(),j.current?.cancel(),M.current?.cancel(),C.current?.(),A.current?.()},[]),{state:u.useMemo(()=>({recording:!!j.current,recordingStartedAt:l,timelapse:!!M.current,timelapseStartedAt:d,bufferSeconds:p,bufferEnabled:a.rollingEnabled,busy:v,targetLabel:F}),[p,v,l,a.rollingEnabled,F,d]),settings:a,videoSupported:V,targetKey:B,setRollingEnabled:K,setRecordingQuality:W,setTimelapseInterval:$,takeScreenshot:Z,startRecording:H,stopRecording:pe,saveLast30Seconds:ce,startTimelapse:J,stopTimelapse:I}}const WN=120,YN=999,Gc=5,Vc=30,KN=Number.POSITIVE_INFINITY,Sw="livedesk.remoteDirectory",Ph="livedesk.localHubAccessEnabled",XN="livedesk.streamSettings",QN=18,ZN="livedesk.controlStreamSettings",JN=8,Cw="livedesk.wallMonitorSelections",kw=2,uh=512*1024,Uh="".trim(),jw="".trim(),Cb="livedesk-adsense-script",dh=!!Uh&&!!jw,kb=typeof window<"u"&&new URLSearchParams(window.location.search).get("debug")==="1",eT=1e3/60,fh=1500,tT=650,nT=1800,aT=650,sT=2500,jb=1500,Ab=15e3,rT=6e4,Mb=240,iT=1024,lT=640,oT=360,Nb="Connected LiveDesk clients are updating in batches. Hub will restart after every target reconnects on the requested versions.",Id="LiveDesk is replacing the Hub and verifying the requested version.",zd=5*6e4,cT=5e3;function Ao({label:n,tooltip:a,disabled:i=!1,active:l=!1,danger:c=!1,pressed:d,onClick:m,children:p}){return r.jsx("span",{className:`manual-action-tooltip ${i?"disabled":""}`.trim(),"data-tooltip":a,children:r.jsx("button",{className:`manual-action ${l?"active":""} ${c?"danger":""}`.trim(),onClick:m,disabled:i,"aria-label":n,"aria-pressed":d,type:"button",children:p})})}function uT(n,a){if(a.deviceId!==n.deviceId)return!1;const i=Number(a.browserReceivedEpochMs);if(!Number.isFinite(i)||i<n.startedAtEpochMs)return!1;const l=Number(a.monitorIndex);if(Number.isFinite(l)&&Math.floor(l)!==n.monitorIndex)return!1;const c=String(a.frameMode||a.mode||"").trim().toLowerCase();return!c||c===n.frameMode}const Al={free:{name:"Free",price:"$0",limit:Gc,audience:"Personal trial",note:"5 devices with a standard ad banner."},ltd:{name:"Plus",price:"$79 LTD",ltd:"$79 LTD",limit:Vc,audience:"Creators and homelabs",note:"30 devices, no ads, personal use."},pro:{name:"Pro",price:"$199 LTD",ltd:"$199 LTD",limit:KN,audience:"Business and studios",note:"Commercial use and larger Client walls."}},dT={"mode3-h264-hw":30};function Tb(n,a){const i=m=>{const g=String(m||"").trim().replace(/^v/i,"").match(/^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?(?:\+[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?$/);if(!g)return null;const v=g[4]?g[4].split(".").map(x=>/^\d+$/.test(x)?x.length>1&&x.startsWith("0")?Number.NaN:Number(x):x):[];return v.some(x=>typeof x=="number"&&!Number.isFinite(x))?null:{core:[Number(g[1]),Number(g[2]),Number(g[3])],prerelease:v}},l=i(n),c=i(a);if(!l||!c)return!1;for(let m=0;m<3;m+=1){if(l.core[m]>c.core[m])return!0;if(l.core[m]<c.core[m])return!1}if(l.prerelease.length===0||c.prerelease.length===0)return l.prerelease.length===0;const d=Math.max(l.prerelease.length,c.prerelease.length);for(let m=0;m<d;m+=1){const p=l.prerelease[m],g=c.prerelease[m];if(p===void 0||g===void 0)return g===void 0;if(p!==g){if(typeof p=="number"&&typeof g=="number")return p>g;if(typeof p=="number")return!1;if(typeof g=="number"||p>g)return!0;if(p<g)return!1}}return!0}function qa(n,a){return`${n}:${a}`}const fT={frameMode:"mode3-h264-hw",h264DecodeAcceleration:"prefer-hardware",fps:dT["mode3-h264-hw"],maxWidth:1920,maxHeight:1080,atlasTileWidth:320,atlasTileHeight:180,atlasTileSizeMode:"auto",quality:75,autoStartLive:!0,showScreenStatus:!0,visibleOnlyLive:!0,adaptiveLoadShedding:!0,adaptiveSizeByWall:!0},mT={frameMode:"mode3-h264-hw",h264DecodeAcceleration:"prefer-hardware",fps:30,maxWidth:1920,maxHeight:1080,quality:75,showScreenStatus:!1};function mh(n,a){if(n.length===0)return 0;const i=[...n].sort((c,d)=>c-d),l=Math.max(0,Math.min(i.length-1,Math.ceil(i.length*a)-1));return i[l]}function Aw(n,a){n.push(a),n.length>Mb&&n.splice(0,n.length-Mb)}function Eb(n,a){n.sampleFrames=0,n.sampleStartedAt=a,n.intervalCount=0,n.intervalSumMs=0,n.minIntervalMs=Number.POSITIVE_INFINITY,n.maxIntervalMs=0,n.intervalSamplesMs.length=0,n.seqGapCount=0,n.maxSeqGap=0}function Rb(n,a,i){if(n.lastAt!==void 0){const l=Math.max(0,a-n.lastAt);n.intervalCount+=1,n.intervalSumMs+=l,n.minIntervalMs=Math.min(n.minIntervalMs,l),n.maxIntervalMs=Math.max(n.maxIntervalMs,l),Aw(n.intervalSamplesMs,l)}if(n.lastFrameSeq!==void 0){const l=i-n.lastFrameSeq;l!==1&&(n.seqGapCount+=1,n.maxSeqGap=Math.max(n.maxSeqGap,Math.abs(l)))}n.lastAt=a,n.lastFrameSeq=i}function Db(n){return!n||n.intervalCount<=0?"waiting":`${Math.round(n.averageIntervalMs)}ms avg / p95 ${Math.round(n.p95IntervalMs)}ms / max ${Math.round(n.maxIntervalMs)}ms`}function Lb(n){return!n||n.seqGapCount<=0?"none":`${n.seqGapCount} gaps / max ${n.maxSeqGap}`}function hT(n){return Number.isFinite(n)?String(n):"30+"}const Mw=[1,2,5,8,12,20,30],_b={large:{maxWidth:1280,maxHeight:720},medium:{maxWidth:960,maxHeight:540},list:{maxWidth:640,maxHeight:360}},fa={preserveConfiguredFpsDeviceCount:6,visibleRootMargin:"520px 0px 760px 0px",fallbackVisibleCounts:{large:12,medium:18,list:32},maxWallFramesPerSecond:Va.maxDirectFramesPerSecond,maxWallBytesPerSecond:80*1024*1024,maxFrameGapMs:280,lowLoadFrameRatio:.58,lowLoadByteRatio:.52,downshiftCooldownMs:1800,recoveryCooldownMs:14e3,minFps:5},Ib=2200,pT=8e3,gT=3500,vT=7e3,yT=2,bT=7e3,xT=2600,wT=4200,ST=750,CT=2,zb=4200;function Ob(n,a){return Math.min(3e4,n*2**Math.min(3,Math.max(0,a)))}function eu(n,a){const i=n.capabilities?.[a];return typeof i=="boolean"?i:/^(1|true|yes|on)$/i.test(String(i||"").trim())}function Ga(n){return n.deviceName||n.hostname||n.deviceId}function kT(){return"npx -y --prefer-online livedesk@latest client"}function jT(){return"npx -y --prefer-online livedesk@latest"}function AT(){try{return localStorage.getItem(Sw)||""}catch{return""}}function MT(){try{return localStorage.getItem(Ph)==="1"}catch{return!1}}function Mo(n){try{n?localStorage.setItem(Ph,"1"):localStorage.removeItem(Ph)}catch{}}function gr(n){return n&&typeof n=="object"&&!Array.isArray(n)?n:{}}function Jn(n){const a=gr(n.status),i=gr(a.screens),l=gr(n.capabilities),c=gr(n.latestLiveFrame),d=gr(n.activeLiveStream),p=[a.monitorCount,a.screenCount,i.count,l.monitorCount,l.screenCount,c.monitorCount,d.monitorCount,Array.isArray(i.displays)?i.displays.length:0,Array.isArray(a.displays)?a.displays.length:0,Array.isArray(a.monitors)?a.monitors.length:0].map(Number).filter(g=>Number.isFinite(g)&&g>0).map(g=>Math.max(1,Math.min(64,Math.floor(g))));return p.length>0?Math.max(...p):1}function Pi(n){const a=Number(n);return Number.isFinite(a)&&a>0?Math.floor(a):0}function Nw(n){const a=gr(n.status),l=[gr(a.screens).displays,a.displays,a.monitors];for(const c of l)if(!(!Array.isArray(c)||c.length===0))return c.map(d=>gr(d));return[]}function Tw(n,a){const i=Nw(n),l=i.find(g=>Number(g.index)===a)||i[a]||i[0]||{},c=Pi(l.width),d=Pi(l.height),m=[l.name,l.displayName,l.deviceName,l.id].find(g=>typeof g=="string"&&g.trim()),p=typeof m=="string"?m.trim():`Display ${a+1}`;return{index:a,name:p,width:c,height:d,primary:l.primary===!0||l.isPrimary===!0}}function NT(n){const a=Jn(n);return Array.from({length:a},(i,l)=>Tw(n,l))}function TT(n){const a=Nw(n);if(a.length===0)return"";const i=a.map((l,c)=>{const d=Number(l.index),m=[l.name,l.displayName,l.deviceName,l.id].find(g=>typeof g=="string"&&g.trim()),p=g=>{const v=Number(g);return Number.isFinite(v)?v:null};return[Number.isFinite(d)?Math.floor(d):c,typeof m=="string"?m.trim().toLowerCase():"",p(l.left),p(l.top),p(l.width),p(l.height),l.primary===!0||l.isPrimary===!0]}).sort((l,c)=>l[0]-c[0]);return JSON.stringify(i)}function ET(n){const a=[n.name||`Display ${n.index+1}`];return n.width>0&&n.height>0&&a.push(`${n.width}x${n.height}`),n.primary&&a.push("Primary"),a.join(" - ")}function Ew(n,a){const i=Tw(n,a);if(i.width>0&&i.height>0)return{width:i.width,height:i.height};const l=gr(n.status),c=Pi(l.screenWidth||l.width),d=Pi(l.screenHeight||l.height);return c>0&&d>0?{width:c,height:d}:null}function Rw(n){if(!n)return null;const a=Pi(n.sourceWidth)||Pi(n.width),i=Pi(n.sourceHeight)||Pi(n.height);return a>0&&i>0?{width:a,height:i}:null}function RT(n,a,i){const d=((typeof a?.monitorIndex=="number"?ea(a.monitorIndex,Math.max(1,a.monitorCount||1)):i)===i?Rw(a):null)||Ew(n,i);return d?Math.max(.25,Math.min(4,d.width/d.height)):16/9}const Od=16/9;function ea(n,a){const i=Math.max(1,Math.min(64,Math.floor(Number(a)||1))),l=Math.floor(Number(n));return Number.isFinite(l)?Math.max(0,Math.min(i-1,l)):0}function DT(){try{const n=localStorage.getItem(Cw);if(!n)return{selections:{},topologyFingerprints:{}};const a=JSON.parse(n),i=a&&typeof a=="object"&&!Array.isArray(a)?a.selections:void 0;if(Number(a?.version)!==kw||!i||typeof i!="object"||Array.isArray(i))return{selections:{},topologyFingerprints:{}};const l={},c={};for(const[d,m]of Object.entries(i)){const p=d.trim(),g=gr(m),v=g.monitorIndex,x=Math.floor(Number(v));if(!p||!Number.isFinite(x))continue;l[p]=Math.max(0,Math.min(63,x));const b=typeof g.topologyFingerprint=="string"?g.topologyFingerprint.trim():"";b&&(c[p]=b)}return{selections:l,topologyFingerprints:c}}catch{return{selections:{},topologyFingerprints:{}}}}function LT(n,a){try{const i=Object.fromEntries(Object.entries(n).map(([l,c])=>[l,{monitorIndex:c,topologyFingerprint:a[l]||""}]));localStorage.setItem(Cw,JSON.stringify({version:kw,selections:i}))}catch{}}function _T(){return{...fT}}function IT(n){try{localStorage.setItem(XN,JSON.stringify({...n,version:QN}))}catch{}}function zT(){return{...mT}}function OT(n){try{localStorage.setItem(ZN,JSON.stringify({...n,version:JN}))}catch{}}function hh(n,a){return n.length!==a.length?!1:n.every((i,l)=>i===a[l])}function ph(n){return[...new Set(n.filter(Boolean))].sort()}function HT(n){return fa.fallbackVisibleCounts[n]||fa.fallbackVisibleCounts.large}function FT(n,a,i){if(n.frameMode==="mode4-h264-atlas")return n.atlasTileSizeMode==="auto"?a==="large"?{maxWidth:640,maxHeight:360}:{maxWidth:320,maxHeight:180}:{maxWidth:n.atlasTileWidth,maxHeight:n.atlasTileHeight};if(!n.adaptiveSizeByWall)return{maxWidth:n.maxWidth,maxHeight:n.maxHeight};const l=_b[a]||_b.large;return{maxWidth:Math.min(n.maxWidth,l.maxWidth),maxHeight:Math.min(n.maxHeight,l.maxHeight)}}function BT(n,a=fa.minFps){const i=[...Mw].sort((c,d)=>c-d),l=Math.max(a,Math.floor(n/2));return[...i].reverse().find(c=>c<n&&c>=a)||l}function $T(n,a){return[...Mw].sort((l,c)=>l-c).find(l=>l>n&&l<=a)||a}function Wc(n){if(!Number.isFinite(n)||n<=0)return"0 B";const a=["B","KB","MB","GB","TB"];let i=n,l=0;for(;i>=1024&&l<a.length-1;)i/=1024,l+=1;const c=l===0||i>=100?0:1;return`${i.toFixed(c)} ${a[l]}`}function Hb(n){if(!Number.isFinite(n)||!n||n<=0)return"0GB";const a=1024**4,i=1024**3;return n>=a?`${Math.max(1,Math.round(n/a))}TB`:`${Math.max(1,Math.round(n/i))}GB`}function gh(n,a){if(!Number.isFinite(n)||!n||n<=0)return"not reported";const i=Math.max(0,n-Math.max(0,Number(a||0)));return`${Hb(i)} / ${Hb(n)}`}function PT(n){const a=new Uint8Array(n),i=32768;let l="";for(let c=0;c<a.length;c+=i){const d=a.subarray(c,c+i);l+=String.fromCharCode(...d)}return btoa(l)}function Fb(n){return Array.from(n).filter(a=>a.size>=0).map(a=>{const i=a.webkitRelativePath||a.name;return{file:a,relativePath:i,fingerprint:`${a.size}:${a.lastModified}`}})}function UT(n){return n===1?"middle":n===2?"right":"left"}function Bb(n,...a){for(const i of a){const l=n?.[i];if(typeof l=="string"&&l.trim())return l.trim()}return""}function $b(n){const a=Number(n);return Number.isInteger(a)&&a>=1&&a<=YN?a:0}const qT={cpu:"CPU",memory:"MEM",disk:"Disk",gpu:"GPU",vram:"VRAM",npu:"NPU",temp:"Temp",network:"Net"};function GT(n,a,i){if(n==="online")return"Online";if(n==="busy")return"Busy";if(n==="stale")return"Stale";if(n==="offline")return"Offline";if(i==="failed")return"Task failed";const l=n==="critical"?"critical":"warn",c=a.find(m=>m.state===l)||a.find(m=>m.state==="critical"||m.state==="warn");if(!c)return n==="critical"?"Issue":"Check";const d=qT[c.id]||c.label;return c.value&&c.value!=="-"?`${d} ${c.value}`:d}function df(n,a){const i=a.split(".");let l=n;for(const c of i){if(l==null)return;if(Array.isArray(l)){const d=Number(c);l=Number.isInteger(d)?l[d]:void 0;continue}if(typeof l=="object"){l=l[c];continue}return}return l}function on(n,...a){for(const i of a){const l=df(n,i),c=Number(l);if(Number.isFinite(c))return c}}function us(n,...a){for(const i of a){const l=df(n,i);if(typeof l=="string"&&l.trim())return l.trim();if(typeof l=="number"&&Number.isFinite(l))return String(l)}return""}function VT(n,...a){for(const i of a){const l=df(n,i);if(typeof l=="boolean")return l;if(/^(1|true|yes|on|online|running|ready)$/i.test(String(l||"").trim()))return!0;if(/^(0|false|no|off|offline|stopped|missing)$/i.test(String(l||"").trim()))return!1}}function Jr(n){const a=Number(n);if(Number.isFinite(a))return Math.max(0,Math.min(1,a>1?a/100:a))}function vh(n,a){if(!(typeof n!="number"||typeof a!="number"||!Number.isFinite(n)||!Number.isFinite(a)||n<=0))return Jr((n-Math.max(0,a))/n)}function No(n){return n===void 0?"-":`${Math.round(n*100)}%`}function yh(n){return n===void 0||!Number.isFinite(n)?"":n>=1e3?`${(n/1e3).toFixed(n>=1e4?0:1)}Gb/s`:`${Math.round(n)}Mb/s`}function Ml(n,a=.92,i=.78){return n===void 0?"unknown":n>=a?"critical":n>=i?"warn":"good"}function WT(n){return!Number.isFinite(n)||!n||n<=0?"-":n<3600?`${Math.round(n/60)}m`:n<86400?`${Math.round(n/3600)}h`:`${Math.round(n/86400)}d`}function Hd(n,a,i){const l=df(n,`tools.${a}`),c=us(n,`tools.${a}.status`,`tooling.${a}.status`,`${a}.status`).toLowerCase(),d=VT(n,`tools.${a}.running`,`tooling.${a}.running`,`${a}.running`),m=us(n,`tools.${a}.version`,`tooling.${a}.version`,`${a}.version`),p=us(n,`tools.${a}.port`,`tooling.${a}.port`,`${a}.port`);let g="unknown";return c==="busy"?g="busy":["online","running","ready","active"].includes(c)||d===!0?g="online":(["offline","stopped","missing","not-found"].includes(c)||d===!1||l===!1)&&(g="offline"),{id:a,label:i,status:g,detail:g==="offline"?"offline":m||(p?`:${p}`:g==="unknown"?"not reported":g)}}function Dw(n){const a=n.status||{},i=on(a,"cpu.cores","cpuCores","processorCount"),l=on(a,"loadavg.0","cpu.load1"),c=Jr(on(a,"cpu.usedRatio","cpu.usageRatio","cpu.ratio","cpu.usedPct","cpu.usagePct","cpu.percent")??(i&&l!==void 0?l/Math.max(1,i):void 0)),d=on(a,"memory.totalBytes","totalMem"),m=on(a,"memory.freeBytes","freeMem"),p=Jr(on(a,"memory.usedRatio","memory.usedPct","usedMemRatio"))??vh(d,m),g=on(a,"disk.totalBytes","rootDisk.totalBytes","storage.totalBytes"),v=on(a,"disk.freeBytes","rootDisk.freeBytes","storage.freeBytes"),x=Jr(on(a,"disk.usedRatio","disk.usedPct","rootDisk.usedRatio","storage.usedRatio"))??vh(g,v),b=on(a,"thermal.cpuC","temperatureC","cpu.temperatureC","gpu.temperatureC"),w=Jr(on(a,"gpu.usedRatio","gpu.usageRatio","gpu.usedPct","gpu.usagePct","gpu.percent","accelerators.gpu.usageRatio")),k=us(a,"gpu.name","gpu.model","accelerators.gpu.name"),j=on(a,"gpu.memory.totalBytes","gpu.totalMemoryBytes","gpu.vramTotalBytes"),C=on(a,"gpu.memory.freeBytes","gpu.freeMemoryBytes","gpu.vramFreeBytes"),M=Jr(on(a,"gpu.memory.usedRatio","gpu.vramUsedRatio"))??vh(j,C),A=on(a,"gpu.temperatureC","thermal.gpuC"),T=Jr(on(a,"npu.usedRatio","npu.usageRatio","npu.usedPct","npu.usagePct","npu.percent","accelerators.npu.usageRatio")),R=us(a,"npu.name","npu.model","accelerators.npu.name"),B=on(a,"npu.tops","npu.topsInt8","accelerators.npu.tops"),F=on(a,"network.rxMbps","network.receiveMbps","network.downMbps"),V=on(a,"network.txMbps","network.transmitMbps","network.upMbps"),L=on(a,"network.latencyMs","network.rttMs"),K=Jr(on(a,"network.usedRatio","network.usageRatio","network.saturationRatio")),W=us(a,"network.link","network.interface","network.name"),$=Math.max(b??Number.NEGATIVE_INFINITY,A??Number.NEGATIVE_INFINITY),Z=Number.isFinite($)?$:void 0,H=Z===void 0?void 0:Jr((Z-35)/60),pe=b!==void 0&&A!==void 0?`CPU ${Math.round(b)}° · GPU ${Math.round(A)}°`:b!==void 0?`CPU ${Math.round(b)}°`:A!==void 0?`GPU ${Math.round(A)}°`:"-",ce=us(a,"workload.role","role","lane")||(eu(n,"aiAssist")?"Agent worker":eu(n,"taskDispatch")?"Task worker":"Local machine"),J=us(n.latestTask,"status")||us(a,"workload.status")||"idle",I=us(n.latestTask,"title","instructionPreview")||us(a,"workload.title"),ne=us(n.latestTask,"resultSummary","error","instructionPreview")||us(a,"workload.summary"),Y=[Hd(a,"ollama","Ollama"),Hd(a,"lmStudio","LM Studio"),Hd(a,"comfyui","ComfyUI"),Hd(a,"stableDiffusion","Stable Diffusion")],U=Y.some(z=>z.status!=="unknown"),re=Date.parse(n.lastSeenAt||n.connectedAt||""),oe=Number.isFinite(re)?Date.now()-re:0;let X="online";n.connected?oe>5*6e4?X="stale":(b??0)>=86||(A??0)>=88||(c??0)>=.94||(w??0)>=.96||(M??0)>=.96||(T??0)>=.96||(p??0)>=.94||(x??0)>=.94||(L??0)>=200||J==="failed"?X="critical":(b??0)>=78||(A??0)>=80||(c??0)>=.82||(w??0)>=.86||(M??0)>=.88||(T??0)>=.86||(p??0)>=.82||(x??0)>=.86||(L??0)>=80?X="warn":(["queued","running","targeted"].includes(J)||Y.some(z=>z.status==="busy"))&&(X="busy"):X="offline";const xe=Y.filter(z=>z.status==="online"||z.status==="busy").length,fe=[{id:"cpu",label:"CPU",value:No(c),detail:i?`${i} cores`:l!==void 0?`load ${l.toFixed(1)}`:"not reported",ratio:c,state:Ml(c,.92,.78)},{id:"memory",label:"RAM",value:No(p),detail:gh(d,m),ratio:p,state:Ml(p,.92,.78)},{id:"disk",label:"DISK",value:No(x),detail:gh(g,v),ratio:x,state:Ml(x,.92,.82)},{id:"gpu",label:"GPU",value:No(w),detail:k||(j&&C!==void 0?`${Wc(C)} VRAM free`:A!==void 0?`${Math.round(A)}C`:"not reported"),ratio:w,state:A!==void 0&&A>=88?"critical":A!==void 0&&A>=80?"warn":Ml(w,.96,.86)},{id:"vram",label:"VRAM",value:No(M),detail:gh(j,C),ratio:M,state:Ml(M,.96,.88)},{id:"npu",label:"NPU",value:T!==void 0?No(T):B!==void 0?`${Math.round(B)} TOPS`:"-",detail:R||(B!==void 0?"neural engine":"not reported"),ratio:T,state:Ml(T,.96,.86)},{id:"temp",label:"TEMP",value:pe,detail:xe>0?`${xe} local tools`:`uptime ${WT(on(a,"uptimeSec"))}`,ratio:H,state:Z===void 0?"unknown":Z>=86?"critical":Z>=78?"warn":"good"},{id:"network",label:"NET",value:yh(F)||(L!==void 0?`${Math.round(L)}ms`:W||"-"),detail:yh(V)?`${yh(V)} up`:W||"not reported",ratio:K,state:L!==void 0&&L>=200?"critical":L!==void 0&&L>=80?"warn":Ml(K,.9,.75)}];return{state:X,label:GT(X,fe,J),role:ce,taskLabel:I||(J==="idle"?"idle":J),taskStatus:J,taskSummary:ne,tools:U?Y:Y.map(z=>({...z,detail:"not reported"})),metrics:fe}}const YT=[{id:"list",label:"List"},{id:"medium",label:"Small"},{id:"large",label:"Large"}],KT=[{id:"sync-server",label:"Hub",icon:yr,accent:"blue",tooltip:"hub"},{id:"wall",label:"Wall",icon:b0,accent:"blue",tooltip:"wall"},{id:"control",label:"Control",icon:Ch,accent:"red",tooltip:"control"},{id:"share-files",label:"Share Files",icon:Fd,accent:"green",tooltip:"share-files"},{id:"agents",label:"Agents",icon:kh,accent:"violet",tooltip:"agents"}],XT=[{id:"doc",label:"Doc",icon:Gd,accent:"amber"},{id:"support",label:"Support",icon:Yc,accent:"violet"},{id:"pricing",label:"Pricing",icon:Kc,accent:"green"}];function QT(n){return n.replaceAll("npx -y @livedesk/client","npx -y --prefer-online livedesk@latest client").replaceAll("npx @livedesk/client","npx -y --prefer-online livedesk@latest client").replaceAll("npx -y livedesk client","npx -y --prefer-online livedesk@latest client").replaceAll("npx livedesk client","npx -y --prefer-online livedesk@latest client").replaceAll("npx -y livedesk","npx -y --prefer-online livedesk@latest").replaceAll("npx livedesk","npx -y --prefer-online livedesk@latest")}function nn(n){return QT(n).replaceAll("Sync Server","Hub").replace(/\bRemoteHub\b/g,"Hub client endpoint").replace(/\bManagers\b/g,"Hubs").replace(/\bManager\b/g,"Hub").replace(/\bmanager\b/g,"Hub").replace(/\bMANAGER\b/g,"HUB").replace(/매니저/g,"Hub")}function Nl(n){return n.map(a=>nn(a))}function ua(n){return{languageName:n.languageName,languageLabel:n.languageLabel,eyebrow:n.eyebrow,title:nn(n.title),intro:nn(n.intro),sections:[{title:nn(n.managerTitle),body:nn(n.managerBody),points:Nl(n.managerPoints)},{title:nn(n.clientTitle),body:nn(n.clientBody),points:Nl(n.clientPoints)},{title:nn(n.syncTitle),body:nn(n.syncBody),points:Nl(n.syncPoints)},...n.agentTitle&&n.agentBody&&n.agentPoints?[{title:nn(n.agentTitle),body:nn(n.agentBody),points:Nl(n.agentPoints)}]:[],{title:nn(n.platformTitle),body:nn(n.platformBody),points:Nl(n.platformPoints)}],sync:{title:nn(n.syncTitle),body:nn(n.syncBody),points:Nl(n.syncPoints)},firewall:{title:nn(n.firewallTitle),body:nn(n.firewallBody),points:Nl(n.firewallPoints)},commands:{title:nn(n.commandsTitle),body:nn(n.commandsBody),manager:nn(n.managerCommandLabel),primary:nn(n.clientCommandLabel),slot:nn(n.slotCommandLabel),manual:nn(n.manualCommandLabel)}}}const Lo={ko:ua({languageName:"한국어",languageLabel:"문서 언어",eyebrow:"LiveDesk Manual",title:"한 줄로 여는 로컬 Screen Wall",intro:"LiveDesk는 매니저 PC 한 대에서 여러 작업 PC, AI 박스, 맥미니, 개발 장비를 한 화면에 모니터링하고 필요한 제어를 실행하는 npm-first 원격 작업 데크입니다.",managerTitle:"1. 매니저 실행",managerBody:"화면을 모아 볼 컴퓨터에서 LiveDesk 매니저를 실행합니다. 매니저는 UI와 로컬 서버를 같이 열고 브라우저를 http://127.0.0.1:5179 로 띄웁니다.",managerPoints:["명령은 npx -y livedesk 입니다.","매니저는 Screen Wall/API 포트 5179와 클라이언트 접속 포트 5197을 사용합니다.","기존 LiveDesk 포트가 남아 있으면 기본적으로 정리하고 새로 시작합니다. 필요하면 --no-clean 을 붙입니다."],clientTitle:"2. 워크스테이션 연결",clientBody:"모니터링할 PC에서는 Google 로그인 또는 Hub 화면의 6자리 PIN으로 연결합니다. Hub가 꺼져 있으면 실시간 온라인 알림을 기다리고 적응형 간격으로 다시 확인합니다.",clientPoints:["빈 자리에 자동 배치하려면 npx -y livedesk client 를 실행합니다.","자리 번호를 고정하려면 npx -y livedesk client 3 처럼 번호를 붙입니다.","브라우저 연결 페이지에서 Google 버튼 또는 6자리 PIN 입력 중 하나를 선택합니다."],syncTitle:"3. Sync Server와 자동 발견",syncBody:"Sync Server는 이 매니저를 현재 계정의 활성 Screen Wall로 Supabase에 등록합니다. 클라이언트는 같은 Google 계정으로 로그인한 뒤 그 기록을 읽고 접속 후보들을 순서대로 시도합니다.",syncPoints:["등록 정보에는 host instance, LAN/private 주소, public 후보 주소, pair token, 짧은 lease가 포함됩니다.","같은 LAN 후보를 먼저 쓰고, 필요하면 외부 주소 후보를 시도합니다.","매니저 위치, VPN, 방화벽, 공유기 설정이 바뀌었거나 개발 중 활성 매니저를 바꿀 때 Sync Server를 누릅니다.","이제 livedesk.pages.dev의 Chrome Local Network Access 허용은 기본 실행 경로가 아닙니다. npm 매니저 앱이 실제 서버입니다."],agentTitle:"4. Agent로 여러 장비 작업하기",agentBody:"Agents 화면은 Hub에서 실행되는 Codex 기반 Agent가 연결된 장비를 확인하고, 선택한 권한 범위 안에서 작업을 실행하는 공간입니다. 클라이언트 PC에 Codex 앱이나 Codex SDK를 설치할 필요는 없습니다.",agentPoints:["Suggested tasks에는 현재 72개의 읽기·운영 작업 템플릿이 있으며, 검색과 권한·플랫폼·capability 조건으로 실행 가능 항목을 안내합니다.","Ask, Safe Auto, Full Access, Custom 권한 모드를 선택할 수 있습니다. 위험한 변경 작업은 승인 카드와 장비 capability 검사를 거칩니다.","All connected 또는 Selected로 대상 장비를 제한하고, 결과는 장비별 상태 칩과 실제 RAM·디스크·GPU·프로세스 요약으로 확인합니다.","Agent 실행은 Hub의 격리된 runtime과 LiveDesk MCP 도구를 통해서만 장비에 전달되며, 모든 작업은 timeout·취소·tool limit·감사 로그 경계를 가집니다."],platformTitle:"5. 현재 플랫폼과 스트림 상태",platformBody:"Windows의 Wall, Control, 파일 전송, 원격 오디오와 RemoteFast 경로를 현재 기준으로 확인했습니다. macOS와 Linux는 같은 npm 명령 경로를 유지하면서 권한과 캡처 백엔드를 확장하고 있습니다.",platformPoints:["자동 Wall과 Control은 모든 Client에서 직접 H.264를 사용합니다. RGB565/LZO Atlas는 내부 진단 실험에만 남고 자동 전송 경로에는 사용되지 않습니다.","원격 오디오는 선택한 장비가 capability를 광고할 때 사용할 수 있으며, 브라우저는 queue·drop·resync·automatic restart 상태를 진단합니다.","macOS: 화면 캡처와 제어에는 Screen Recording/Accessibility 권한이 필요합니다. RemoteFast 로그는 필요한 상태 중심으로 제한됩니다.","Linux: linux-x64 runtime, X11/XTEST 입력과 PipeWire/PulseAudio 기본 출력 오디오 캡처를 지원합니다."],firewallTitle:"방화벽과 외부 장소 연결",firewallBody:"같은 LAN 안에서는 보통 공유기 포트포워딩이 필요 없습니다. 다른 장소의 PC를 붙이려면 클라이언트가 매니저의 클라이언트 접속 포트에 도달할 수 있어야 합니다.",firewallPoints:["클라이언트 접속 포트는 TCP 5197입니다.","클라이언트 쪽은 나가는 연결이므로 보통 인바운드 포트 개방이 필요 없습니다.","다른 네트워크에서 붙일 때는 매니저 PC 방화벽에서 TCP 5197 인바운드를 허용하고, 공유기에서는 TCP 5197을 매니저 PC로 전달합니다.","Screen Wall/API 포트 5179는 매니저 UI용입니다. 외부 브라우저에서 Wall을 직접 열 필요가 있을 때만 별도로 노출을 검토합니다.","공개 인터넷에 바로 열기보다 VPN, Tailscale, ZeroTier, 고정 IP allowlist를 우선합니다."],commandsTitle:"현재 사용 명령",commandsBody:"웹사이트를 찾아가 설치 파일을 받는 방식보다 터미널 한 줄로 모든 운영체제에서 거의 같은 흐름을 쓰는 것이 LiveDesk의 기본 방향입니다.",managerCommandLabel:"매니저 시작",clientCommandLabel:"워크스테이션 연결",slotCommandLabel:"3번 슬롯에 고정",manualCommandLabel:"고급 수동 연결"}),en:ua({languageName:"English",languageLabel:"Document language",eyebrow:"LiveDesk Manual",title:"Open a local Screen Wall with one command",intro:"LiveDesk is an npm-first remote work deck for monitoring many Clients, AI boxes, Mac minis, and dev machines from one manager PC.",managerTitle:"1. Run the manager",managerBody:"Run LiveDesk on the computer that will watch the screens. The manager starts the UI and local server together, then opens http://127.0.0.1:5179.",managerPoints:["The command is npx -y livedesk.","The manager uses port 5179 for the Screen Wall/API and port 5197 for client connections.","If an old LiveDesk process still owns those ports, startup cleans it by default. Add --no-clean when you want to keep it."],clientTitle:"2. Connect a Client",clientBody:"On each monitored computer, connect with Google sign-in or the 6-digit PIN shown on the Hub screen. If no manager is online yet, the client waits for a live online signal and falls back to adaptive retries.",clientPoints:["Run npx -y livedesk client for first available placement.","Run npx -y livedesk client 3 to pin the machine to slot 3.","The browser connection page lets you choose Google or a 6-digit PIN."],syncTitle:"3. Sync Server and discovery",syncBody:"Sync Server publishes this manager as the active Screen Wall for the signed-in account. Clients read that Supabase record after Google sign-in and try the endpoint candidates in order.",syncPoints:["The record includes host instance, LAN/private addresses, public endpoint candidates, pair token, and a short lease.","LAN candidates are preferred first, then public candidates if needed.","Press Sync Server after manager moves, VPN/firewall/router changes, or when this development machine should take over as active manager.","Chrome Local Network Access on livedesk.pages.dev is no longer the default runtime path. The npm manager app is the real server."],agentTitle:"4. Run multi-Client tasks with Agent",agentBody:"The Agents page runs a Codex-based Agent in the Hub to inspect connected Clients and perform actions within the selected permission scope. Client PCs do not need the Codex app or Codex SDK installed.",agentPoints:["Suggested tasks currently contains 72 read-only and operational templates with search plus permission, platform, and capability availability checks.","Choose Ask, Safe Auto, Full Access, or Custom. Risky changes go through approval cards and explicit client capability checks.","Limit targets with All connected or Selected. Results use compact per-device status chips and show real RAM, disk, GPU, and process summaries.","Runs stay inside the Hub-isolated runtime and LiveDesk MCP tools, with timeout, cancellation, tool-call limits, and audit-log boundaries."],platformTitle:"5. Current platform and stream state",platformBody:"Windows Wall, Control, file transfer, remote audio, and the RemoteFast path are verified against the current implementation. macOS and Linux keep the same npm command path while capture and permission backends expand.",platformPoints:["Automatic Wall and Control use direct H.264 for every Client. The RGB565/LZO Atlas remains only for internal diagnostic experiments and is never selected automatically.","Remote audio is available when the selected Client advertises the capability, with queue, drop, resync, and automatic-restart diagnostics in the browser.","macOS requires Screen Recording and Accessibility permissions for capture and control. RemoteFast logs focus on actionable state.","Linux includes the linux-x64 runtime, X11/XTEST input, and default-output audio capture through PipeWire or PulseAudio."],firewallTitle:"Firewall and remote locations",firewallBody:"On the same LAN, router forwarding is usually unnecessary. For another office, building, or country, clients must be able to reach the manager client port.",firewallPoints:["Client connection port: TCP 5197.","Client machines normally only need outbound access.","For remote networks, allow inbound TCP 5197 on the manager firewall and forward TCP 5197 from the router to the manager PC.","Screen Wall/API port 5179 is for the manager UI. Expose it only if a remote browser must open the Wall directly.","Prefer VPN, Tailscale, ZeroTier, or fixed-IP allowlists over public internet exposure."],commandsTitle:"Current commands",commandsBody:"The product direction is one terminal command across operating systems instead of finding a website, downloading, and installing manually.",managerCommandLabel:"Start manager",clientCommandLabel:"Connect Client",slotCommandLabel:"Pin to slot 3",manualCommandLabel:"Advanced manual connection"}),ja:ua({languageName:"日本語",languageLabel:"ドキュメント言語",eyebrow:"LiveDesk Manual",title:"1行でローカル Screen Wall を開く",intro:"LiveDesk は、複数の作業 PC、AI マシン、Mac mini、開発機を1台の manager PC から監視する npm-first のリモート作業デッキです。",managerTitle:"1. manager を実行",managerBody:"画面を集めて見るコンピューターで LiveDesk manager を実行します。UI とローカルサーバーが同時に起動し、http://127.0.0.1:5179 が開きます。",managerPoints:["コマンドは npx -y livedesk です。","Screen Wall/API は 5179、client 接続は 5197 を使います。","古い LiveDesk プロセスがポートを使っている場合は、基本的に自動で整理して起動します。"],clientTitle:"2. Client を接続",clientBody:"監視される PC で同じ Google アカウントにログインします。manager がまだ無い場合、client はオンライン通知を待ち、適応型の間隔で再確認します。",clientPoints:["空きスロットに入れるには npx -y livedesk client を実行します。","スロットを固定するには npx -y livedesk client 3 のように番号を付けます。","大量の PC でも再ログインなしで待ち続けます。"],syncTitle:"3. Sync Server と自動検出",syncBody:"Sync Server はこの manager を現在の Google アカウントの active Screen Wall として Supabase に登録します。",syncPoints:["host instance、LAN/private アドレス、public 候補、pair token、短い lease を登録します。","client は LAN 候補を優先し、必要なら public 候補も試します。","VPN、firewall、router、manager の場所が変わったら Sync Server を押します。","livedesk.pages.dev の Chrome Local Network Access は標準実行経路ではありません。npm manager アプリが実際のサーバーです。"],platformTitle:"4. 現在のプラットフォーム状態",platformBody:"Windows は live screen transfer まで確認済みです。macOS と Linux も同じ npm コマンド経路を目標に対応を広げています。",platformPoints:["Windows: 動作確認済み。","macOS: 画面取得と制御には Screen Recording/Accessibility 権限が必要です。","Linux: linux-x64 runtime、grim/gnome-screenshot/import/scrot capture、X11/XTEST input backend を追加済み。audio は未対応です。"],firewallTitle:"Firewall と外部拠点",firewallBody:"同じ LAN なら通常 router forwarding は不要です。外部拠点では client が manager の接続ポートへ到達できる必要があります。",firewallPoints:["client 接続ポートは TCP 5197 です。","client 側は通常 outbound だけで足ります。","外部 network では manager PC の firewall で TCP 5197 inbound を許可し、router から manager PC へ forward します。","5179 は manager UI 用なので、外部ブラウザで Wall を開く必要がある時だけ公開を検討します。","公開 internet より VPN、Tailscale、ZeroTier、IP allowlist を優先してください。"],commandsTitle:"現在のコマンド",commandsBody:"Web サイトから installer を探すより、OS をまたいで terminal 1行で動かす方向です。",managerCommandLabel:"manager 起動",clientCommandLabel:"Client 接続",slotCommandLabel:"slot 3 に固定",manualCommandLabel:"高度な手動接続"}),"zh-CN":ua({languageName:"简体中文",languageLabel:"文档语言",eyebrow:"LiveDesk Manual",title:"用一条命令打开本地 Screen Wall",intro:"LiveDesk 是 npm-first 的远程工作台,用一台 manager PC 监控多台工作站、AI 主机、Mac mini 和开发机器。",managerTitle:"1. 运行 manager",managerBody:"在负责查看屏幕的电脑上运行 LiveDesk manager。它会同时启动 UI 和本地服务器,并打开 http://127.0.0.1:5179。",managerPoints:["命令是 npx -y livedesk。","Screen Wall/API 使用 5179,client 连接使用 5197。","如果旧的 LiveDesk 进程占用端口,默认会清理后重新启动。"],clientTitle:"2. 连接 Client",clientBody:"在被监控的电脑上使用同一个 Google 账号登录。如果 manager 尚未在线,client 会等待上线通知,并以自适应间隔重新检查。",clientPoints:["自动放入空位: npx -y livedesk client。","固定到 3 号位置: npx -y livedesk client 3。","大量电脑也可以持续等待,不需要反复登录。"],syncTitle:"3. Sync Server 和自动发现",syncBody:"Sync Server 会把当前 manager 作为该账号的 active Screen Wall 写入 Supabase。client 登录后读取记录并依次尝试连接候选地址。",syncPoints:["记录包括 host instance、LAN/private 地址、public 候选、pair token 和短 lease。","优先尝试同一 LAN 地址,需要时再尝试外部地址。","manager 位置、VPN、防火墙或路由变化后请点击 Sync Server。","livedesk.pages.dev 的 Chrome Local Network Access 不再是默认运行方式。npm manager app 才是真正服务器。"],platformTitle:"4. 当前平台状态",platformBody:"Windows 已确认可传输实时画面。macOS 和 Linux 正按同一 npm 命令路径扩展支持。",platformPoints:["Windows: 已确认可用。","macOS: 画面捕获和控制需要 Screen Recording/Accessibility 权限。","Linux: 已加入 linux-x64 runtime、grim/gnome-screenshot/import/scrot 捕获、X11/XTEST 输入后端。音频尚未启用。"],firewallTitle:"防火墙和外部地点",firewallBody:"同一 LAN 通常不需要路由端口转发。不同地点连接时,client 必须能访问 manager 的 client 连接端口。",firewallPoints:["client 连接端口是 TCP 5197。","client 机器通常只需要出站访问。","跨网络时,在 manager 防火墙放行 TCP 5197 入站,并在路由器转发到 manager PC。","5179 是 manager UI 端口,仅在需要远程浏览器直接打开 Wall 时考虑暴露。","优先使用 VPN、Tailscale、ZeroTier 或固定 IP allowlist。"],commandsTitle:"当前命令",commandsBody:"产品方向是跨操作系统使用一条终端命令,而不是先找网站、下载、安装。",managerCommandLabel:"启动 manager",clientCommandLabel:"连接 Client",slotCommandLabel:"固定到 slot 3",manualCommandLabel:"高级手动连接"}),"zh-TW":ua({languageName:"繁體中文",languageLabel:"文件語言",eyebrow:"LiveDesk Manual",title:"用一行指令開啟本機 Screen Wall",intro:"LiveDesk 是 npm-first 的遠端工作台,用一台 manager PC 監看多台工作站、AI 主機、Mac mini 與開發機。",managerTitle:"1. 執行 manager",managerBody:"在負責看畫面的電腦上執行 LiveDesk manager。它會同時啟動 UI 與本機伺服器,並開啟 http://127.0.0.1:5179。",managerPoints:["指令是 npx -y livedesk。","Screen Wall/API 使用 5179,client 連線使用 5197。","若舊的 LiveDesk 程序占用連接埠,預設會清理後重新啟動。"],clientTitle:"2. 連接 Client",clientBody:"在被監看的電腦上使用同一個 Google 帳號登入。如果 manager 尚未上線,client 會等待上線通知,並以自適應間隔重新檢查。",clientPoints:["自動放入空位: npx -y livedesk client。","固定到 3 號位置: npx -y livedesk client 3。","大量電腦也能持續等待,不需要反覆登入。"],syncTitle:"3. Sync Server 與自動探索",syncBody:"Sync Server 會把目前 manager 登錄為此帳號的 active Screen Wall。client 登入後讀取 Supabase 記錄並依序嘗試連線候選地址。",syncPoints:["記錄包含 host instance、LAN/private 位址、public 候選、pair token 與短 lease。","優先嘗試 LAN 位址,需要時再嘗試外部位址。","manager 位置、VPN、防火牆或路由設定改變後請按 Sync Server。","livedesk.pages.dev 的 Chrome Local Network Access 不再是預設執行路徑。npm manager app 才是真正伺服器。"],platformTitle:"4. 目前平台狀態",platformBody:"Windows 已確認可傳輸即時畫面。macOS 與 Linux 也沿用相同 npm 指令路徑擴充支援。",platformPoints:["Windows: 已確認可用。","macOS: 畫面擷取與控制需要 Screen Recording/Accessibility 權限。","Linux: 已加入 linux-x64 runtime、grim/gnome-screenshot/import/scrot capture、X11/XTEST input backend。音訊尚未啟用。"],firewallTitle:"防火牆與外部地點",firewallBody:"同一 LAN 通常不需要路由器轉發。不同地點連線時,client 必須能到達 manager 的 client 連線埠。",firewallPoints:["client 連線埠是 TCP 5197。","client 電腦通常只需要對外連線。","跨網路時,請在 manager 防火牆允許 TCP 5197 inbound,並從路由器轉發到 manager PC。","5179 是 manager UI 連接埠,只在需要遠端瀏覽器直接打開 Wall 時才考慮公開。","優先使用 VPN、Tailscale、ZeroTier 或固定 IP allowlist。"],commandsTitle:"目前指令",commandsBody:"產品方向是跨作業系統用一行 terminal 指令,而不是先找網站、下載、安裝。",managerCommandLabel:"啟動 manager",clientCommandLabel:"連接 Client",slotCommandLabel:"固定到 slot 3",manualCommandLabel:"進階手動連線"}),es:ua({languageName:"Español",languageLabel:"Idioma del documento",eyebrow:"LiveDesk Manual",title:"Abre un Screen Wall local con un comando",intro:"LiveDesk es una consola npm-first para vigilar muchas estaciones, cajas de IA, Mac mini y equipos de desarrollo desde un PC manager.",managerTitle:"1. Ejecuta el manager",managerBody:"Ejecuta LiveDesk en el equipo que verá las pantallas. El manager inicia la UI y el servidor local juntos y abre http://127.0.0.1:5179.",managerPoints:["El comando es npx -y livedesk.","El manager usa 5179 para Screen Wall/API y 5197 para conexiones client.","Si un proceso antiguo ocupa los puertos, el arranque lo limpia por defecto."],clientTitle:"2. Conecta una Client",clientBody:"En cada equipo monitoreado, inicia sesión con la misma cuenta Google. Si el manager aún no está activo, el client espera una señal en tiempo real y vuelve a comprobar con intervalos adaptativos.",clientPoints:["Usa npx -y livedesk client para el primer slot libre.","Usa npx -y livedesk client 3 para fijar el equipo al slot 3.","Los clients siguen esperando para no repetir login en muchas máquinas."],syncTitle:"3. Sync Server y descubrimiento",syncBody:"Sync Server publica este manager como Screen Wall activo para la cuenta. Los clients leen el registro en Supabase y prueban los endpoints.",syncPoints:["Incluye host instance, direcciones LAN/private, candidatos public, pair token y lease corto.","Primero se prueban candidatos LAN, luego públicos si hace falta.","Pulsa Sync Server después de cambios de manager, VPN, firewall o router.","Chrome Local Network Access en livedesk.pages.dev ya no es el flujo principal. La app npm manager es el servidor real."],platformTitle:"4. Estado de plataformas",platformBody:"Windows ya fue verificado con transferencia de pantalla. macOS y Linux siguen el mismo camino npm.",platformPoints:["Windows: verificado.","macOS: captura y control requieren permisos Screen Recording/Accessibility.","Linux: runtime linux-x64, entrada X11/XTEST y audio de salida mediante PipeWire o PulseAudio."],firewallTitle:"Firewall y sedes remotas",firewallBody:"En la misma LAN normalmente no hace falta port forwarding. Para otra red, los clients deben llegar al puerto client del manager.",firewallPoints:["Puerto de conexión client: TCP 5197.","Los equipos client normalmente solo necesitan salida.","Para redes remotas, permite TCP 5197 inbound en el manager y reenvía TCP 5197 al PC manager.","El puerto 5179 es para la UI del manager; expónlo solo si un navegador remoto debe abrir el Wall.","Prefiere VPN, Tailscale, ZeroTier o allowlist de IP fijas."],commandsTitle:"Comandos actuales",commandsBody:"La dirección del producto es un comando de terminal común entre sistemas, no descargar e instalar desde una web.",managerCommandLabel:"Iniciar manager",clientCommandLabel:"Conectar Client",slotCommandLabel:"Fijar al slot 3",manualCommandLabel:"Conexión manual avanzada"}),fr:ua({languageName:"Français",languageLabel:"Langue du document",eyebrow:"LiveDesk Manual",title:"Ouvrir un Screen Wall local en une commande",intro:"LiveDesk est un deck npm-first pour surveiller plusieurs postes, machines IA, Mac mini et machines dev depuis un PC manager.",managerTitle:"1. Lancer le manager",managerBody:"Lancez LiveDesk sur l’ordinateur qui observe les écrans. Le manager démarre l’UI et le serveur local, puis ouvre http://127.0.0.1:5179.",managerPoints:["La commande est npx -y livedesk.","Le manager utilise 5179 pour Screen Wall/API et 5197 pour les connexions client.","Si un ancien processus utilise les ports, le démarrage le nettoie par défaut."],clientTitle:"2. Connecter une Client",clientBody:"Sur chaque ordinateur surveillé, connectez-vous avec le même compte Google. Si le manager n’est pas encore actif, le client réessaie toutes les 5 secondes.",clientPoints:["Utilisez npx -y livedesk client pour le premier slot libre.","Utilisez npx -y livedesk client 3 pour fixer la machine au slot 3.","Les clients continuent d’attendre pour éviter les reconnexions répétées."],syncTitle:"3. Sync Server et découverte",syncBody:"Sync Server publie ce manager comme Screen Wall actif du compte. Les clients lisent l’enregistrement Supabase et testent les endpoints.",syncPoints:["Il contient host instance, adresses LAN/private, candidats public, pair token et lease court.","Les candidats LAN sont essayés en premier, puis les candidats publics si besoin.","Cliquez Sync Server après un changement de manager, VPN, firewall ou routeur.","Chrome Local Network Access sur livedesk.pages.dev n’est plus le chemin par défaut. L’app npm manager est le vrai serveur."],platformTitle:"4. État des plateformes",platformBody:"Windows est vérifié avec transfert d’écran live. macOS et Linux suivent le même chemin npm.",platformPoints:["Windows: vérifié.","macOS: capture et contrôle nécessitent Screen Recording/Accessibility.","Linux: runtime linux-x64, entrée X11/XTEST et audio de sortie via PipeWire ou PulseAudio."],firewallTitle:"Firewall et sites distants",firewallBody:"Sur le même LAN, le port forwarding est rarement nécessaire. Depuis un autre réseau, les clients doivent atteindre le port client du manager.",firewallPoints:["Port de connexion client: TCP 5197.","Les machines client ont normalement seulement besoin de sortie réseau.","Pour un réseau distant, autorisez TCP 5197 entrant sur le manager et redirigez TCP 5197 vers le PC manager.","Le port 5179 sert à l’UI manager; exposez-le seulement si un navigateur distant doit ouvrir le Wall.","Préférez VPN, Tailscale, ZeroTier ou allowlist IP fixes."],commandsTitle:"Commandes actuelles",commandsBody:"La direction produit est une commande terminal commune aux OS, plutôt qu’un téléchargement manuel depuis un site.",managerCommandLabel:"Démarrer manager",clientCommandLabel:"Connecter Client",slotCommandLabel:"Fixer au slot 3",manualCommandLabel:"Connexion manuelle avancée"}),de:ua({languageName:"Deutsch",languageLabel:"Dokumentsprache",eyebrow:"LiveDesk Manual",title:"Lokale Screen Wall mit einem Befehl öffnen",intro:"LiveDesk ist ein npm-first Arbeitsdeck, um viele Clients, AI-Boxen, Mac minis und Dev-Rechner von einem Manager-PC zu überwachen.",managerTitle:"1. Manager starten",managerBody:"Starte LiveDesk auf dem Computer, der die Screens sieht. Der Manager startet UI und lokalen Server zusammen und öffnet http://127.0.0.1:5179.",managerPoints:["Der Befehl ist npx -y livedesk.","Der Manager nutzt 5179 für Screen Wall/API und 5197 für Client-Verbindungen.","Wenn alte LiveDesk-Prozesse Ports belegen, werden sie beim Start standardmäßig bereinigt."],clientTitle:"2. Client verbinden",clientBody:"Melde dich auf jedem überwachten Computer mit demselben Google-Konto an. Wenn kein Manager online ist, wartet der Client auf ein Live-Signal und prüft mit adaptiven Intervallen erneut.",clientPoints:["npx -y livedesk client nutzt den ersten freien Slot.","npx -y livedesk client 3 fixiert den Rechner auf Slot 3.","Clients warten weiter, damit große Setups kein wiederholtes Login brauchen."],syncTitle:"3. Sync Server und Discovery",syncBody:"Sync Server veröffentlicht diesen Manager als aktive Screen Wall des Kontos. Clients lesen den Supabase-Eintrag und testen Endpoints der Reihe nach.",syncPoints:["Enthält host instance, LAN/private Adressen, public Kandidaten, pair token und kurze lease.","LAN-Kandidaten werden zuerst versucht, danach public Kandidaten.","Nach Änderungen an Manager, VPN, Firewall oder Router Sync Server drücken.","Chrome Local Network Access auf livedesk.pages.dev ist nicht mehr der Standardpfad. Die npm Manager App ist der echte Server."],platformTitle:"4. Plattformstatus",platformBody:"Windows wurde mit Live-Screen-Transfer geprüft. macOS und Linux folgen demselben npm-Pfad.",platformPoints:["Windows: verifiziert.","macOS: Capture und Control brauchen Screen Recording/Accessibility Rechte.","Linux: linux-x64 Runtime, X11/XTEST Eingabe und Ausgangsaudio über PipeWire oder PulseAudio."],firewallTitle:"Firewall und entfernte Standorte",firewallBody:"Im gleichen LAN ist Router-Forwarding meist unnötig. Aus anderen Netzwerken müssen Clients den Client-Port des Managers erreichen.",firewallPoints:["Client-Verbindungsport: TCP 5197.","Client-Rechner brauchen normalerweise nur ausgehenden Zugriff.","Für entfernte Netzwerke TCP 5197 inbound am Manager erlauben und vom Router zum Manager-PC forwarden.","Port 5179 ist für die Manager-UI; nur öffnen, wenn ein Remote-Browser die Wall direkt öffnen muss.","VPN, Tailscale, ZeroTier oder feste IP-Allowlists bevorzugen."],commandsTitle:"Aktuelle Befehle",commandsBody:"Die Produktlinie ist ein Terminalbefehl über Betriebssysteme hinweg, statt Website suchen, herunterladen und installieren.",managerCommandLabel:"Manager starten",clientCommandLabel:"Client verbinden",slotCommandLabel:"An Slot 3 fixieren",manualCommandLabel:"Erweiterte manuelle Verbindung"}),"pt-BR":ua({languageName:"Português",languageLabel:"Idioma do documento",eyebrow:"LiveDesk Manual",title:"Abra um Screen Wall local com um comando",intro:"LiveDesk é um deck npm-first para monitorar várias estações, caixas de IA, Mac minis e máquinas dev a partir de um PC manager.",managerTitle:"1. Execute o manager",managerBody:"Rode LiveDesk no computador que verá as telas. O manager inicia a UI e o servidor local juntos e abre http://127.0.0.1:5179.",managerPoints:["O comando é npx -y livedesk.","O manager usa 5179 para Screen Wall/API e 5197 para conexões client.","Se um processo antigo ocupa as portas, a inicialização limpa por padrão."],clientTitle:"2. Conecte uma Client",clientBody:"Em cada computador monitorado, entre com a mesma conta Google. Se o manager ainda não estiver online, o client aguarda um sinal em tempo real e verifica novamente em intervalos adaptativos.",clientPoints:["Use npx -y livedesk client para o primeiro slot livre.","Use npx -y livedesk client 3 para fixar no slot 3.","Clients continuam tentando para evitar login repetido em muitos PCs."],syncTitle:"3. Sync Server e descoberta",syncBody:"Sync Server publica este manager como Screen Wall ativo da conta. Clients leem o registro no Supabase e testam endpoints.",syncPoints:["Inclui host instance, endereços LAN/private, candidatos public, pair token e lease curto.","Candidatos LAN vêm primeiro; public depois se necessário.","Clique Sync Server após mudanças de manager, VPN, firewall ou roteador.","Chrome Local Network Access em livedesk.pages.dev não é mais o caminho padrão. O app npm manager é o servidor real."],platformTitle:"4. Estado das plataformas",platformBody:"Windows foi verificado com transferência de tela. macOS e Linux seguem o mesmo caminho npm.",platformPoints:["Windows: verificado.","macOS: captura e controle precisam de permissões Screen Recording/Accessibility.","Linux: runtime linux-x64, captura grim/gnome-screenshot/import/scrot e entrada X11/XTEST adicionados. Áudio ainda desativado."],firewallTitle:"Firewall e locais remotos",firewallBody:"Na mesma LAN normalmente não é preciso port forwarding. Em outra rede, clients precisam alcançar a porta client do manager.",firewallPoints:["Porta client: TCP 5197.","Máquinas client normalmente só precisam de saída.","Para redes remotas, permita TCP 5197 inbound no manager e encaminhe TCP 5197 para o PC manager.","A porta 5179 é para a UI do manager; exponha apenas se um navegador remoto precisar abrir o Wall.","Prefira VPN, Tailscale, ZeroTier ou allowlist de IP fixo."],commandsTitle:"Comandos atuais",commandsBody:"A direção do produto é um comando de terminal comum entre sistemas, não baixar e instalar manualmente.",managerCommandLabel:"Iniciar manager",clientCommandLabel:"Conectar Client",slotCommandLabel:"Fixar no slot 3",manualCommandLabel:"Conexão manual avançada"}),it:ua({languageName:"Italiano",languageLabel:"Lingua documento",eyebrow:"LiveDesk Manual",title:"Apri uno Screen Wall locale con un comando",intro:"LiveDesk è un deck npm-first per monitorare Client, box AI, Mac mini e macchine dev da un solo PC manager.",managerTitle:"1. Avvia il manager",managerBody:"Esegui LiveDesk sul computer che guarderà gli schermi. Il manager avvia UI e server locale e apre http://127.0.0.1:5179.",managerPoints:["Il comando è npx -y livedesk.","Il manager usa 5179 per Screen Wall/API e 5197 per connessioni client.","Se vecchi processi occupano le porte, l’avvio le pulisce di default."],clientTitle:"2. Connetti una Client",clientBody:"Su ogni computer monitorato, accedi con lo stesso account Google. Se il manager non è online, il client riprova ogni 5 secondi.",clientPoints:["npx -y livedesk client usa il primo slot libero.","npx -y livedesk client 3 fissa la macchina allo slot 3.","I client continuano a provare per evitare login ripetuti."],syncTitle:"3. Sync Server e discovery",syncBody:"Sync Server pubblica questo manager come Screen Wall attivo dell’account. I client leggono Supabase e provano gli endpoint.",syncPoints:["Include host instance, indirizzi LAN/private, candidati public, pair token e lease breve.","Prima LAN, poi public se necessario.","Premi Sync Server dopo cambi di manager, VPN, firewall o router.","Chrome Local Network Access su livedesk.pages.dev non è più il percorso principale. L’app npm manager è il server reale."],platformTitle:"4. Stato piattaforme",platformBody:"Windows è verificato con trasferimento schermo live. macOS e Linux seguono lo stesso percorso npm.",platformPoints:["Windows: verificato.","macOS: capture e control richiedono permessi Screen Recording/Accessibility.","Linux: aggiunti runtime linux-x64, capture grim/gnome-screenshot/import/scrot e input X11/XTEST. Audio disattivato."],firewallTitle:"Firewall e sedi remote",firewallBody:"Nella stessa LAN di solito non serve port forwarding. Da altre reti, i client devono raggiungere la porta client del manager.",firewallPoints:["Porta client: TCP 5197.","I client normalmente richiedono solo uscita.","Per reti remote, abilita TCP 5197 inbound sul manager e inoltra TCP 5197 al PC manager.","La porta 5179 è per la UI manager; esponila solo se un browser remoto deve aprire il Wall.","Preferisci VPN, Tailscale, ZeroTier o IP allowlist."],commandsTitle:"Comandi attuali",commandsBody:"La direzione è un comando terminale comune tra sistemi operativi, invece di download e install manuale.",managerCommandLabel:"Avvia manager",clientCommandLabel:"Connetti Client",slotCommandLabel:"Fissa allo slot 3",manualCommandLabel:"Connessione manuale avanzata"}),id:ua({languageName:"Bahasa Indonesia",languageLabel:"Bahasa dokumen",eyebrow:"LiveDesk Manual",title:"Buka Screen Wall lokal dengan satu perintah",intro:"LiveDesk adalah deck npm-first untuk memantau banyak Client, AI box, Mac mini, dan mesin dev dari satu PC manager.",managerTitle:"1. Jalankan manager",managerBody:"Jalankan LiveDesk di komputer yang melihat layar. Manager membuka UI dan server lokal bersama, lalu membuka http://127.0.0.1:5179.",managerPoints:["Perintahnya npx -y livedesk.","Manager memakai 5179 untuk Screen Wall/API dan 5197 untuk koneksi client.","Jika proses lama memakai port itu, startup membersihkannya secara default."],clientTitle:"2. Hubungkan Client",clientBody:"Di komputer yang dipantau, masuk dengan akun Google yang sama. Jika manager belum online, client tetap mencoba tiap 5 detik.",clientPoints:["npx -y livedesk client memakai slot kosong pertama.","npx -y livedesk client 3 mengunci mesin ke slot 3.","Client terus mencoba agar banyak PC tidak perlu login berulang."],syncTitle:"3. Sync Server dan discovery",syncBody:"Sync Server menerbitkan manager ini sebagai Screen Wall aktif untuk akun. Client membaca Supabase dan mencoba endpoint.",syncPoints:["Berisi host instance, alamat LAN/private, kandidat public, pair token, dan lease pendek.","Kandidat LAN dicoba lebih dulu, lalu public jika perlu.","Tekan Sync Server setelah perubahan manager, VPN, firewall, atau router.","Chrome Local Network Access di livedesk.pages.dev bukan jalur utama lagi. App npm manager adalah server sebenarnya."],platformTitle:"4. Status platform",platformBody:"Windows sudah diverifikasi dengan live screen transfer. macOS dan Linux mengikuti jalur npm yang sama.",platformPoints:["Windows: terverifikasi.","macOS: capture dan control perlu izin Screen Recording/Accessibility.","Linux: runtime linux-x64, capture grim/gnome-screenshot/import/scrot, dan input X11/XTEST sudah ditambahkan. Audio belum aktif."],firewallTitle:"Firewall dan lokasi luar",firewallBody:"Di LAN yang sama biasanya tidak perlu port forwarding. Dari jaringan lain, client harus mencapai port client manager.",firewallPoints:["Port client: TCP 5197.","Mesin client biasanya hanya perlu akses keluar.","Untuk jaringan luar, izinkan TCP 5197 inbound di manager dan forward TCP 5197 ke PC manager.","Port 5179 untuk UI manager; buka hanya jika browser luar harus membuka Wall.","Utamakan VPN, Tailscale, ZeroTier, atau IP allowlist."],commandsTitle:"Perintah saat ini",commandsBody:"Arah produk adalah satu perintah terminal lintas OS, bukan download dan instal manual.",managerCommandLabel:"Mulai manager",clientCommandLabel:"Hubungkan Client",slotCommandLabel:"Kunci ke slot 3",manualCommandLabel:"Koneksi manual lanjutan"}),vi:ua({languageName:"Tiếng Việt",languageLabel:"Ngôn ngữ tài liệu",eyebrow:"LiveDesk Manual",title:"Mở Screen Wall cục bộ bằng một lệnh",intro:"LiveDesk là deck npm-first để giám sát nhiều Client, máy AI, Mac mini và máy dev từ một PC manager.",managerTitle:"1. Chạy manager",managerBody:"Chạy LiveDesk trên máy dùng để xem màn hình. Manager khởi động UI và server cục bộ cùng lúc, rồi mở http://127.0.0.1:5179.",managerPoints:["Lệnh là npx -y livedesk.","Manager dùng 5179 cho Screen Wall/API và 5197 cho kết nối client.","Nếu tiến trình cũ giữ port, startup mặc định sẽ dọn và khởi động lại."],clientTitle:"2. Kết nối Client",clientBody:"Trên mỗi máy được giám sát, đăng nhập cùng tài khoản Google. Nếu manager chưa online, client tiếp tục thử mỗi 5 giây.",clientPoints:["npx -y livedesk client dùng slot trống đầu tiên.","npx -y livedesk client 3 ghim máy vào slot 3.","Client tiếp tục chờ để nhiều máy không cần đăng nhập lại."],syncTitle:"3. Sync Server và tự phát hiện",syncBody:"Sync Server đăng manager này là Screen Wall đang hoạt động của tài khoản. Client đọc Supabase và thử các endpoint.",syncPoints:["Bao gồm host instance, địa chỉ LAN/private, public candidate, pair token và lease ngắn.","Ưu tiên LAN trước, sau đó thử public nếu cần.","Bấm Sync Server sau thay đổi manager, VPN, firewall hoặc router.","Chrome Local Network Access trên livedesk.pages.dev không còn là đường chạy chính. App npm manager mới là server thật."],platformTitle:"4. Trạng thái nền tảng",platformBody:"Windows đã được kiểm chứng với truyền màn hình live. macOS và Linux đi theo cùng đường npm.",platformPoints:["Windows: đã kiểm chứng.","macOS: capture và control cần quyền Screen Recording/Accessibility.","Linux: đã thêm runtime linux-x64, capture grim/gnome-screenshot/import/scrot và input X11/XTEST. Audio chưa bật."],firewallTitle:"Firewall và địa điểm ngoài",firewallBody:"Trong cùng LAN thường không cần port forwarding. Từ mạng khác, client phải truy cập được port client của manager.",firewallPoints:["Port client: TCP 5197.","Máy client thường chỉ cần kết nối outbound.","Với mạng ngoài, cho phép TCP 5197 inbound trên manager và forward TCP 5197 tới PC manager.","Port 5179 dành cho UI manager; chỉ mở nếu trình duyệt từ xa cần mở Wall trực tiếp.","Ưu tiên VPN, Tailscale, ZeroTier hoặc allowlist IP cố định."],commandsTitle:"Lệnh hiện tại",commandsBody:"Hướng sản phẩm là một lệnh terminal dùng chung giữa các OS, thay vì tải và cài thủ công.",managerCommandLabel:"Khởi động manager",clientCommandLabel:"Kết nối Client",slotCommandLabel:"Ghim vào slot 3",manualCommandLabel:"Kết nối thủ công nâng cao"}),th:ua({languageName:"ไทย",languageLabel:"ภาษาของเอกสาร",eyebrow:"LiveDesk Manual",title:"เปิด Screen Wall ภายในด้วยคำสั่งเดียว",intro:"LiveDesk คือ deck แบบ npm-first สำหรับดู Client, กล่อง AI, Mac mini และเครื่อง dev หลายเครื่องจาก manager PC เครื่องเดียว",managerTitle:"1. รัน manager",managerBody:"รัน LiveDesk บนเครื่องที่ใช้ดูหน้าจอ Manager จะเปิด UI และ local server พร้อมกัน แล้วเปิด http://127.0.0.1:5179",managerPoints:["คำสั่งคือ npx -y livedesk","Manager ใช้ 5179 สำหรับ Screen Wall/API และ 5197 สำหรับ client connection","ถ้า process เก่ายังใช้ port อยู่ startup จะล้างให้โดย default"],clientTitle:"2. เชื่อม Client",clientBody:"บนเครื่องที่ต้อง monitor ให้ login ด้วย Google account เดียวกัน ถ้า manager ยังไม่ออนไลน์ client จะลองใหม่ทุก 5 วินาที",clientPoints:["npx -y livedesk client จะใช้ slot ว่างแรก","npx -y livedesk client 3 จะตรึงเครื่องไว้ที่ slot 3","Client จะรอต่อเนื่องเพื่อไม่ต้อง login ซ้ำเมื่อมีหลายเครื่อง"],syncTitle:"3. Sync Server และ discovery",syncBody:"Sync Server จะ publish manager นี้เป็น active Screen Wall ของ account แล้ว client อ่านข้อมูลจาก Supabase และลอง endpoint ต่างๆ",syncPoints:["มี host instance, LAN/private address, public candidate, pair token และ short lease","ลอง LAN ก่อน แล้วค่อยลอง public ถ้าจำเป็น","กด Sync Server หลังเปลี่ยน manager, VPN, firewall หรือ router","Chrome Local Network Access บน livedesk.pages.dev ไม่ใช่ runtime หลักแล้ว app npm manager คือ server จริง"],platformTitle:"4. สถานะ platform",platformBody:"Windows ตรวจสอบแล้วว่าส่งภาพ live ได้ macOS และ Linux ใช้เส้นทาง npm เดียวกัน",platformPoints:["Windows: verified","macOS: capture/control ต้องใช้สิทธิ์ Screen Recording/Accessibility","Linux: เพิ่ม linux-x64 runtime, grim/gnome-screenshot/import/scrot capture และ X11/XTEST input แล้ว audio ยังปิดอยู่"],firewallTitle:"Firewall และเครื่องต่างสถานที่",firewallBody:"ใน LAN เดียวกันปกติไม่ต้อง port forwarding ถ้าอยู่คนละ network client ต้องเข้าถึง client port ของ manager ได้",firewallPoints:["Client port คือ TCP 5197","เครื่อง client ปกติต้องการ outbound เท่านั้น","ถ้าอยู่นอก network ให้เปิด TCP 5197 inbound ที่ manager และ forward TCP 5197 ไปยัง manager PC","Port 5179 เป็น UI ของ manager เปิดเฉพาะเมื่อ browser ระยะไกลต้องเปิด Wall โดยตรง","ควรใช้ VPN, Tailscale, ZeroTier หรือ IP allowlist ก่อนเปิดสู่ public internet"],commandsTitle:"คำสั่งปัจจุบัน",commandsBody:"ทิศทาง product คือคำสั่ง terminal เดียวข้าม OS ไม่ใช่ download/install จากเว็บไซต์",managerCommandLabel:"เริ่ม manager",clientCommandLabel:"เชื่อม Client",slotCommandLabel:"ตรึง slot 3",manualCommandLabel:"manual connection ขั้นสูง"}),hi:ua({languageName:"हिन्दी",languageLabel:"दस्तावेज़ भाषा",eyebrow:"LiveDesk Manual",title:"एक कमांड से local Screen Wall खोलें",intro:"LiveDesk एक npm-first remote work deck है जिससे एक manager PC से कई Client, AI box, Mac mini और dev machines देखे जा सकते हैं।",managerTitle:"1. manager चलाएं",managerBody:"जिस computer से screens देखनी हैं उस पर LiveDesk manager चलाएं। यह UI और local server साथ में शुरू करता है और http://127.0.0.1:5179 खोलता है।",managerPoints:["कमांड है npx -y livedesk।","Manager Screen Wall/API के लिए 5179 और client connections के लिए 5197 इस्तेमाल करता है।","अगर पुराने LiveDesk ports बचे हों तो startup default रूप से साफ करता है।"],clientTitle:"2. Client जोड़ें",clientBody:"हर monitored computer पर उसी Google account से sign in करें। Manager online न हो तब भी client बंद नहीं होता और हर 5 सेकंड retry करता है।",clientPoints:["npx -y livedesk client पहला खाली slot लेता है।","npx -y livedesk client 3 machine को slot 3 पर pin करता है।","बहुत सी machines हों तब भी बार-बार login की जरूरत नहीं रहती।"],syncTitle:"3. Sync Server और discovery",syncBody:"Sync Server इस manager को account का active Screen Wall बनाकर Supabase में publish करता है। Clients record पढ़कर endpoints try करते हैं।",syncPoints:["इसमें host instance, LAN/private addresses, public candidates, pair token और short lease होता है।","LAN candidates पहले try होते हैं, जरूरत हो तो public candidates।","Manager, VPN, firewall या router बदलने के बाद Sync Server दबाएं।","livedesk.pages.dev का Chrome Local Network Access अब default runtime नहीं है। npm manager app ही real server है।"],platformTitle:"4. Platform status",platformBody:"Windows live screen transfer के साथ verified है। macOS और Linux भी इसी npm path पर आगे बढ़ रहे हैं।",platformPoints:["Windows: verified.","macOS: capture/control के लिए Screen Recording/Accessibility permissions चाहिए।","Linux: linux-x64 runtime, X11/XTEST input और PipeWire/PulseAudio output audio capture उपलब्ध हैं।"],firewallTitle:"Firewall और remote locations",firewallBody:"Same LAN में आम तौर पर router forwarding जरूरी नहीं। दूसरे network से clients को manager client port तक पहुंचना होगा।",firewallPoints:["Client connection port: TCP 5197.","Client machines को आम तौर पर सिर्फ outbound access चाहिए।","Remote network के लिए manager firewall में TCP 5197 inbound allow करें और router से TCP 5197 manager PC पर forward करें।","5179 manager UI port है; इसे तभी expose करें जब remote browser को Wall खोलना हो।","Public internet से पहले VPN, Tailscale, ZeroTier या fixed IP allowlist बेहतर है।"],commandsTitle:"Current commands",commandsBody:"Product direction है OS के बीच एक terminal command, website से download/install नहीं।",managerCommandLabel:"Start manager",clientCommandLabel:"Connect Client",slotCommandLabel:"Pin to slot 3",manualCommandLabel:"Advanced manual connection"}),ar:ua({languageName:"العربية",languageLabel:"لغة المستند",eyebrow:"LiveDesk Manual",title:"افتح Screen Wall محلي بأمر واحد",intro:"LiveDesk هو deck بنمط npm-first لمراقبة عدة Clients وأجهزة AI وMac mini وأجهزة تطوير من كمبيوتر manager واحد.",managerTitle:"1. شغل manager",managerBody:"شغل LiveDesk على الكمبيوتر الذي سيعرض الشاشات. يبدأ manager الواجهة والخادم المحلي معا ويفتح http://127.0.0.1:5179.",managerPoints:["الأمر هو npx -y livedesk.","يستخدم manager المنفذ 5179 للواجهة/API والمنفذ 5197 لاتصالات client.","إذا بقيت عملية LiveDesk قديمة على المنافذ، يتم تنظيفها افتراضيا عند التشغيل."],clientTitle:"2. وصل Client",clientBody:"على كل كمبيوتر تتم مراقبته، سجل الدخول بنفس حساب Google. إذا لم يكن manager متاحا، يبقى client يعمل ويحاول كل 5 ثوان.",clientPoints:["npx -y livedesk client يختار أول slot فارغ.","npx -y livedesk client 3 يثبت الجهاز في slot 3.","clients تستمر بالمحاولة لتجنب تكرار تسجيل الدخول عند كثرة الأجهزة."],syncTitle:"3. Sync Server والاكتشاف",syncBody:"Sync Server ينشر هذا manager كـ active Screen Wall للحساب. يقرأ clients سجل Supabase ويجربون endpoints بالترتيب.",syncPoints:["السجل يتضمن host instance وعناوين LAN/private وpublic candidates وpair token وlease قصير.","يتم تفضيل LAN أولا، ثم public عند الحاجة.","اضغط Sync Server بعد تغيير manager أو VPN أو firewall أو router.","Chrome Local Network Access على livedesk.pages.dev لم يعد مسار التشغيل الافتراضي. تطبيق npm manager هو الخادم الحقيقي."],platformTitle:"4. حالة المنصات",platformBody:"Windows تم التحقق منه مع نقل شاشة live. macOS وLinux يتبعان نفس مسار npm.",platformPoints:["Windows: تم التحقق.","macOS: capture/control يحتاجان أذونات Screen Recording/Accessibility.","Linux: تمت إضافة linux-x64 runtime وgrim/gnome-screenshot/import/scrot capture وX11/XTEST input. الصوت ما زال معطلا."],firewallTitle:"Firewall والمواقع البعيدة",firewallBody:"داخل نفس LAN غالبا لا تحتاج port forwarding. من شبكة أخرى يجب أن يصل client إلى منفذ manager client.",firewallPoints:["منفذ client: TCP 5197.","أجهزة client تحتاج عادة اتصال outbound فقط.","للشبكات البعيدة، اسمح TCP 5197 inbound على manager ووجه TCP 5197 من router إلى manager PC.","المنفذ 5179 مخصص لواجهة manager؛ اكشفه فقط إذا كان متصفح بعيد يحتاج فتح Wall مباشرة.","يفضل VPN أو Tailscale أو ZeroTier أو fixed IP allowlist بدل التعريض العام."],commandsTitle:"الأوامر الحالية",commandsBody:"اتجاه المنتج هو أمر terminal واحد عبر الأنظمة، وليس تحميل وتثبيت يدوي من موقع.",managerCommandLabel:"Start manager",clientCommandLabel:"Connect Client",slotCommandLabel:"Pin to slot 3",manualCommandLabel:"Advanced manual connection"}),tr:ua({languageName:"Türkçe",languageLabel:"Belge dili",eyebrow:"LiveDesk Manual",title:"Yerel Screen Wall tek komutla açılsın",intro:"LiveDesk, tek bir manager PC üzerinden birçok Client, AI box, Mac mini ve dev machine izlemek için npm-first bir çalışma deckidir.",managerTitle:"1. Manager çalıştır",managerBody:"Ekranları izleyecek bilgisayarda LiveDesk manager çalıştır. Manager UI ve local server birlikte başlar ve http://127.0.0.1:5179 açılır.",managerPoints:["Komut: npx -y livedesk.","Manager Screen Wall/API için 5179, client bağlantıları için 5197 kullanır.","Eski LiveDesk process portları tutuyorsa başlangıç varsayılan olarak temizler."],clientTitle:"2. Client bağla",clientBody:"İzlenecek bilgisayarda aynı Google hesabıyla giriş yap. Manager henüz online değilse client kapanmaz ve her 5 saniyede tekrar dener.",clientPoints:["npx -y livedesk client ilk boş slota yerleşir.","npx -y livedesk client 3 makineyi slot 3’e sabitler.","Çok makinede tekrar tekrar login gerekmesin diye client beklemeye devam eder."],syncTitle:"3. Sync Server ve discovery",syncBody:"Sync Server bu managerı hesabın active Screen Wall kaydı olarak Supabase’e yazar. Clients bu kaydı okuyup endpoint adaylarını dener.",syncPoints:["Host instance, LAN/private adresler, public adaylar, pair token ve kısa lease içerir.","Önce LAN adayları, gerekirse public adaylar denenir.","Manager, VPN, firewall veya router değişince Sync Server’a bas.","livedesk.pages.dev üzerindeki Chrome Local Network Access artık default runtime yolu değil. Gerçek server npm manager app."],platformTitle:"4. Platform durumu",platformBody:"Windows live screen transfer ile doğrulandı. macOS ve Linux aynı npm yolunu takip ediyor.",platformPoints:["Windows: doğrulandı.","macOS: capture/control için Screen Recording/Accessibility izinleri gerekir.","Linux: linux-x64 runtime, grim/gnome-screenshot/import/scrot capture ve X11/XTEST input backend eklendi. Audio kapalı."],firewallTitle:"Firewall ve uzak konumlar",firewallBody:"Aynı LAN içinde genelde router forwarding gerekmez. Başka ağdan clients manager client portuna ulaşabilmelidir.",firewallPoints:["Client port: TCP 5197.","Client makineler normalde sadece outbound erişim ister.","Uzak ağ için manager firewall üzerinde TCP 5197 inbound aç ve routerdan TCP 5197yi manager PCye forward et.","5179 manager UI portudur; sadece uzak browser Wall açacaksa expose et.","Public internet yerine VPN, Tailscale, ZeroTier veya sabit IP allowlist tercih et."],commandsTitle:"Güncel komutlar",commandsBody:"Ürün yönü, webden indirip kurmak yerine işletim sistemleri arasında ortak tek terminal komutu.",managerCommandLabel:"Manager başlat",clientCommandLabel:"Client bağla",slotCommandLabel:"Slot 3’e sabitle",manualCommandLabel:"Gelişmiş manuel bağlantı"})},ZT=Object.entries(Lo).map(([n,a])=>({id:n,label:a.languageName}));function JT(){if(typeof navigator>"u")return"en";const n=navigator.languages?.length?navigator.languages:[navigator.language];for(const a of n){const i=a.toLowerCase();if(Lo[a])return a;if(i.startsWith("zh-tw")||i.startsWith("zh-hk"))return"zh-TW";if(i.startsWith("pt"))return"pt-BR";const l=i.split("-")[0];if(Lo[l])return l}return"en"}function Pb({index:n}){const a=n+1;return r.jsx("article",{className:"device-tile placeholder-tile","aria-label":`Empty slot ${a}`,children:r.jsxs("div",{className:"tile-screen","aria-hidden":"true",children:[r.jsx("span",{className:"slot-grid","aria-hidden":"true"}),r.jsxs("span",{className:"empty-screen",children:[r.jsx(ha,{size:24}),r.jsxs("strong",{children:["Slot ",String(a).padStart(3,"0")]})]})]})})}const eE=["cpu","gpu","memory","vram","network","disk","temp"];function tE({device:n,slotNumber:a,selected:i,updateIndicator:l,onSelect:c,onOpenControl:d}){const m=Dw(n),p=new Map(m.metrics.map(v=>[v.id,v])),g=l?.label||m.label;return r.jsxs("div",{className:`wall-device-list-row state-${m.state} ${i?"selected":""}`,role:"row","aria-selected":i,tabIndex:0,onClick:c,onDoubleClick:v=>{v.preventDefault(),d()},onKeyDown:v=>{(v.key==="Enter"||v.key===" ")&&(v.preventDefault(),c())},children:[r.jsxs("span",{className:"wall-device-name",role:"gridcell",children:[r.jsx("span",{className:"wall-device-icon","aria-hidden":"true",children:r.jsx(ha,{size:17})}),r.jsxs("span",{children:[r.jsx("strong",{children:Ga(n)}),r.jsxs("small",{children:["Slot ",String(a).padStart(3,"0"),n.platform?` · ${n.platform}`:"",n.arch?` · ${n.arch}`:""]})]}),i&&r.jsx(vr,{size:16,strokeWidth:3,"aria-label":"Selected"})]}),r.jsxs("span",{className:`wall-device-list-status state-${m.state} ${l?`update-${l.state}`:""}`,role:"gridcell",title:l?.error||g,children:[r.jsx("i",{"aria-hidden":"true"}),r.jsx("strong",{children:g})]}),eE.map(v=>{const x=p.get(v);return r.jsxs("span",{className:`wall-device-list-metric state-${x?.state||"unknown"}`,role:"gridcell",title:x?`${x.label}: ${x.value} · ${x.detail}`:"Not reported",children:[r.jsx("strong",{children:x?.value||"-"}),r.jsx("small",{children:x?.detail||"not reported"}),x?.ratio!==void 0&&r.jsx("i",{style:{width:`${Math.round(x.ratio*100)}%`},"aria-hidden":"true"})]},v)})]})}function nE(n){if(!n)return!1;const a=String(n.platform||"").trim().toLowerCase();return ei(a)&&n.capabilities?.screenRecordingPermission===!1}function ei(n){const a=String(n||"").trim().toLowerCase();return a==="macos"||a==="darwin"||a==="mac"||a==="macosx"}function aE(n){const a=String(n||"").trim().toLowerCase();return a==="linux"||a.startsWith("linux ")}function Ub(n){if(!n)return!1;const a=String(n.capabilities?.liveCaptureConcurrency||"").trim().toLowerCase();return a==="exclusive"?!0:a==="parallel"?!1:ei(n.platform)}function bh({device:n,frame:a,selected:i,controlEnabled:l,showStatus:c,monitorIndex:d,monitorCount:m,onSelect:p,onFocusDevice:g,onOpenControl:v,onCycleMonitor:x,onCanvasReady:b,sendInput:w,updateIndicator:k,presentationReady:j,showPresentationSpinner:C=!1,remoteDiagnostics:M,diagnosticsMode:A="remote"}){const T=u.useRef(null),R=u.useRef(null),B=u.useRef(0),F=u.useRef(null),V=Dw(n),L=["cpu","gpu","memory","vram"].map(z=>V.metrics.find(Ae=>Ae.id===z)).filter(z=>!!z),K=ea(d,m),W=m>1,$=j??!!a,Z=nE(n)&&!$,H=C&&n.connected&&!$&&!Z,pe=RT(n,a,K),ce=pe>=Od?100:pe/Od*100,J=pe>=Od?Od/pe*100:100,I={"--screen-aspect":pe.toFixed(5),"--screen-content-width":`${Math.max(1,Math.min(100,ce)).toFixed(3)}%`,"--screen-content-height":`${Math.max(1,Math.min(100,J)).toFixed(3)}%`},ne=u.useCallback(z=>{T.current=z,b(n.deviceId,z)},[n.deviceId,b]),Y=z=>{const Ae=z.currentTarget.getBoundingClientRect();return I1(z.clientX,z.clientY,Ae)},U=z=>({shiftKey:z.shiftKey,ctrlKey:z.ctrlKey,altKey:z.altKey,metaKey:z.metaKey}),re=u.useCallback(()=>{F.current!==null&&(window.cancelAnimationFrame(F.current),F.current=null)},[]),oe=u.useCallback(()=>{const z=R.current;if(!z)return;R.current=null;const Ae=Number(z.input.normalizedX),Ce=Number(z.input.normalizedY);if(!Number.isFinite(Ae)||!Number.isFinite(Ce))return;w(n.deviceId,{...z.input,normalizedX:Ae,normalizedY:Ce})&&(B.current=performance.now())},[n.deviceId,w]),X=u.useCallback(()=>{F.current===null&&(F.current=window.requestAnimationFrame(()=>{F.current=null,oe()}))},[oe]);u.useEffect(()=>()=>{re(),R.current=null},[re]);const xe=(z,Ae)=>{if(!l)return;const Ce={type:Ae,monitorIndex:K,...Y(z),button:UT(z.button),...U(z)};if(Ae==="pointermove"){const De=performance.now();R.current={input:Ce},De-B.current>=eT&&(re(),oe()),X();return}R.current&&(re(),oe()),w(n.deviceId,Ce)},fe=u.useCallback(z=>{const Ae=document.activeElement;Ae instanceof HTMLElement&&z.contains(Ae)&&Ae.classList.contains("monitor-cycle-button")&&Ae.blur()},[]);return r.jsx("article",{className:`device-tile ${i?"selected":""} ${n.connected?"online":"offline"}`,"data-live-device-id":n.deviceId,style:I,children:r.jsxs("div",{className:`tile-screen ${$?"has-frame":"is-empty"}`,role:"button",tabIndex:l?-1:0,"aria-pressed":i,onClick:z=>{z.target instanceof Element&&z.target.closest(".monitor-cycle")||l||p()},onKeyDown:z=>{z.target instanceof Element&&z.target.closest(".monitor-cycle")||l||(z.key==="Enter"||z.key===" ")&&(z.preventDefault(),p())},onDoubleClick:z=>{z.target instanceof Element&&z.target.closest(".monitor-cycle")||(z.preventDefault(),v())},onPointerLeave:z=>fe(z.currentTarget),children:[r.jsx("span",{className:`screen-content ${$?"has-frame":"is-empty"}`,children:r.jsx("canvas",{ref:ne,className:`remote-canvas ${$?"has-frame":"is-empty"}`,"data-monitor-index":K,tabIndex:l?0:-1,onPointerDown:z=>{l&&(z.preventDefault(),z.stopPropagation(),g(),z.currentTarget.focus(),z.currentTarget.setPointerCapture(z.pointerId),xe(z,"pointerdown"))},onPointerMove:z=>{l&&z.preventDefault(),xe(z,"pointermove")},onPointerLeave:z=>{l&&(xe(z,"pointermove"),re(),oe())},onPointerUp:z=>{l&&(z.preventDefault(),z.stopPropagation(),xe(z,"pointerup"),z.currentTarget.hasPointerCapture(z.pointerId)&&z.currentTarget.releasePointerCapture(z.pointerId))},onPointerCancel:z=>{z.currentTarget.hasPointerCapture(z.pointerId)&&z.currentTarget.releasePointerCapture(z.pointerId)},onWheel:z=>{l&&(z.preventDefault(),w(n.deviceId,{type:"wheel",monitorIndex:K,...Y(z),deltaX:z.deltaX,deltaY:z.deltaY,...U(z)}))},onKeyDown:z=>{l&&(z.preventDefault(),z.stopPropagation(),w(n.deviceId,{type:"keydown",monitorIndex:K,key:z.key,code:z.code,keyCode:z.keyCode||z.which||0,location:z.location,text:z.key.length===1?z.key:"",repeat:z.repeat,...U(z)}))},onKeyUp:z=>{l&&(z.preventDefault(),z.stopPropagation(),w(n.deviceId,{type:"keyup",monitorIndex:K,key:z.key,code:z.code,keyCode:z.keyCode||z.which||0,location:z.location,text:z.key.length===1?z.key:"",repeat:!1,...U(z)}))},onContextMenu:z=>l&&z.preventDefault()},`${n.deviceId}:monitor:${K}`)}),!$&&r.jsx("span",{className:"empty-screen",children:r.jsx(ha,{size:26})}),r.jsx("span",{className:"wall-device-title",title:Ga(n),children:Ga(n)}),H&&r.jsxs("span",{className:"wall-presentation-loading",role:"status","aria-live":"polite",children:[r.jsx("span",{className:"wall-presentation-spinner","aria-hidden":"true"}),r.jsx("strong",{children:"Preparing video"})]}),Z&&r.jsxs("span",{className:"wall-presentation-loading permission-required",role:"status","aria-live":"polite",children:[r.jsx(Dn,{size:24,"aria-hidden":"true"}),r.jsx("strong",{children:"Screen Recording permission required"}),r.jsx("small",{children:"Allow the terminal app on this Mac, then restart LiveDesk."})]}),k&&r.jsxs("span",{className:`wall-update-status state-${k.state}`,role:"status","aria-live":"polite",title:k.error||k.label,children:[k.state==="updated"?r.jsx(Bn,{size:20,"aria-hidden":"true"}):k.state==="failed"?r.jsx(ma,{size:22,"aria-hidden":"true"}):k.state==="offline"?r.jsx(tf,{size:22,"aria-hidden":"true"}):r.jsx("span",{className:"wall-update-spinner","aria-hidden":"true"}),r.jsx("strong",{children:k.label}),k.error?r.jsx("small",{children:k.error}):null]}),c&&r.jsxs(r.Fragment,{children:[r.jsx("span",{className:`tile-health-pill state-${V.state}`,children:V.label}),r.jsx("span",{className:"tile-metrics","aria-label":`${Ga(n)} status`,children:L.map(z=>r.jsxs("span",{className:`tile-metric state-${z.state}`,children:[r.jsx("b",{children:z.label}),r.jsx("em",{children:z.value}),z.ratio!==void 0&&r.jsx("i",{style:{width:`${Math.round(z.ratio*100)}%`}})]},z.id))})]}),M&&r.jsx(jN,{items:M,mode:A}),W&&r.jsxs("span",{className:"monitor-cycle","aria-label":`${Ga(n)} display ${K+1} of ${m}`,onPointerDown:z=>z.stopPropagation(),onClick:z=>z.stopPropagation(),onDoubleClick:z=>z.stopPropagation(),children:[r.jsx("button",{className:"monitor-cycle-button previous",onClick:z=>{z.preventDefault(),z.stopPropagation(),x(-1),z.currentTarget.blur()},title:"Previous display",type:"button",children:r.jsx(tx,{size:18})}),r.jsxs("span",{className:"monitor-cycle-count",children:["Screen ",K+1,"/",m]}),r.jsx("button",{className:"monitor-cycle-button next",onClick:z=>{z.preventDefault(),z.stopPropagation(),x(1),z.currentTarget.blur()},title:"Next display",type:"button",children:r.jsx(Xh,{size:18})})]}),i&&r.jsx("span",{className:"device-selection-check","aria-hidden":"true",children:r.jsx(vr,{size:23,strokeWidth:3.5})})]})})}function sE({visible:n}){return u.useEffect(()=>{if(!n||!dh)return;if(!document.getElementById(Cb)){const i=document.createElement("script");i.id=Cb,i.async=!0,i.crossOrigin="anonymous",i.src=`https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=${encodeURIComponent(Uh)}`,document.head.appendChild(i)}const a=window.setTimeout(()=>{try{window.adsbygoogle=window.adsbygoogle||[],window.adsbygoogle.push({})}catch{}},120);return()=>window.clearTimeout(a)},[n]),n?r.jsxs("aside",{className:`free-ad-banner ${dh?"adsense-ready":"house-ad"}`,"aria-label":"Advertisement",children:[r.jsx("span",{className:"ad-label",children:"Advertisement"}),dh?r.jsx("ins",{className:"adsbygoogle livedesk-ad-unit",style:{display:"block"},"data-ad-client":Uh,"data-ad-slot":jw,"data-ad-format":"auto","data-full-width-responsive":"true"}):r.jsxs("div",{className:"house-ad-copy",children:[r.jsx("strong",{children:"Sponsored placement"}),r.jsx("span",{children:"Local AI gear, mini PCs, GPUs, NAS, and developer tools."})]})]}):null}function rE(){const{session:n,loading:a}=f1(),{target:i,error:l,refresh:c}=h1(n),d=u.useMemo(()=>{if(!i?.active)return null;const y=i.expires_at?Date.parse(i.expires_at):Number.POSITIVE_INFINITY;return Number.isFinite(y)&&y<=Date.now()?null:i},[i]),m=u.useMemo(()=>hy(d),[d]),p=u.useMemo(()=>m.length>0?[...new Set([...m,hn])]:[hn],[m]),[g,v]=u.useState(MT),x=u.useMemo(()=>p.some(y=>!!Qc(y)),[p]),b=!x||g,{status:w,devices:k,error:j,refresh:C,hubBaseUrl:M}=m1(p,b),{entitlement:A,licensed:T,loading:R,error:B,refresh:F}=p1(n),[V,L]=u.useState([]),[K,W]=u.useState(""),[$,Z]=u.useState(""),[H,pe]=u.useState(""),[ce,J]=u.useState(""),[I,ne]=u.useState(""),[Y,U]=u.useState("idle"),[re,oe]=u.useState(!1),[X,xe]=u.useState(null),[fe,z]=u.useState(""),Ae=u.useRef("");Ae.current=ce;const[Ce,De]=u.useState(!1),[He,he]=u.useState(!1),Ge=u.useRef(""),lt=u.useRef(new Map),se=u.useRef(0),[ve,Te]=u.useState(""),[Be,_e]=u.useState(""),[Ke,Ve]=u.useState(!1),rt=u.useRef(null),vt=u.useRef(null);u.useEffect(()=>{ve==="auth"&&n?.access_token&&Te("")},[ve,n?.access_token]);const[q,ye]=u.useState(""),[ke,$e]=u.useState(null),ot=u.useRef(null),pt=u.useRef(0),St=u.useCallback((y,N,_)=>{const O=String(y||"").trim();if(ye(O),ot.current!==null&&(window.clearTimeout(ot.current),ot.current=null),!O){$e(null);return}const D=++pt.current;if($e({id:D,message:O,tone:N}),!_)return;const ae=window.setTimeout(()=>{$e(de=>de?.id===D?null:de),ot.current===ae&&(ot.current=null)},5e3);ot.current=ae},[]),Pe=u.useCallback((y,N="info")=>{St(y,N,!0)},[St]),Et=u.useCallback(y=>{St(y,"progress",!1)},[St]);u.useEffect(()=>()=>{ot.current!==null&&window.clearTimeout(ot.current)},[]);const[ge,Oe]=u.useState(()=>typeof window<"u"&&window.location.pathname.startsWith("/support")?"support":"wall");u.useEffect(()=>{const y=()=>{Oe(window.location.pathname.startsWith("/support")?"support":N=>N==="support"?"wall":N)};return window.addEventListener("popstate",y),()=>window.removeEventListener("popstate",y)},[]);const[ze,Me]=u.useState("large"),[st,mt]=u.useState(!1),[Ee,Ln]=u.useState(!1),[pn,nt]=u.useState(!1),[It,_n]=u.useState(!1),[Ya,Aa]=u.useState(Date.now),ta=u.useRef(null);u.useEffect(()=>()=>{ta.current!==null&&window.clearTimeout(ta.current)},[]);const[Ka,ri]=u.useState(JT),[pa,fs]=u.useState(!1),[Ts,Ma]=u.useState(!1),[P,Xe]=u.useState(!1),[ht,cn]=u.useState(AT),[Ze]=u.useState(_T),[an]=u.useState(zT),[Na]=u.useState(DT),[ee,Ne]=u.useState(Na.selections),We=u.useRef({...Na.topologyFingerprints}),dt=u.useRef({...Na.selections}),[et,At]=u.useState(null),[tt,Tt]=u.useState([]),[Lt,Jt]=u.useState(Ze.fps),[gn,Ht]=u.useState(""),[vn,Vt]=u.useState(0),[wr,Sr]=u.useState(0),[yn,en]=u.useState(()=>new Set),kn=u.useRef(new Map),ga=u.useRef(new Map),Ta=u.useRef(new Map),ii=u.useRef(new Map),jn=u.useRef(new Map),Vs=u.useRef(new Map),An=u.useRef(new Set),qn=u.useRef(new Map),na=u.useRef(""),Hl=u.useRef(""),li=u.useRef(null),Ws=u.useRef(null),Xa=u.useRef(new Map),Fl=u.useRef(null),aa=u.useRef(0),oi=u.useRef(new Map),[Wi,Bl]=u.useState({}),Es=u.useRef(new Map),[Yi,Ho]=u.useState({}),[Cr,Qa]=u.useState(!1),$l=u.useRef(null),Za=u.useRef(null),sa=u.useRef(0),kr=u.useRef(""),Ys=u.useRef(new Map),ms=u.useRef(new Map),Ea=u.useRef(performance.now()),Ks=u.useRef(new Map),Rs=u.useRef(new Map),Ra=u.useRef(new Map),Ja=u.useRef(new Map),Ki=u.useRef(new Map),jr=u.useRef(0),Xi=u.useRef(0),Xs=u.useRef(0),Da=u.useRef(0),Qi=u.useRef(0),ci=u.useRef(new Map),In=u.useRef(new Map),hs=u.useCallback((y,N)=>{const _=qa(y,N),O=performance.now();Ys.current.delete(_),ms.current.delete(_),Ks.current.delete(_),Ki.current.delete(_),In.current.delete(_),Es.current.delete(_),oi.current.delete(_),Ho(D=>{if(!Object.prototype.hasOwnProperty.call(D,_))return D;const ae={...D};return delete ae[_],ae}),Bl(D=>{if(!Object.prototype.hasOwnProperty.call(D,_))return D;const ae={...D};return delete ae[_],ae}),y==="control"?(Rs.current.delete(_),Ra.current.set(_,O)):Ja.current.set(_,O)},[]),[ps,Ds]=u.useState(null),[Pl,ui]=u.useState(!1),[ff,ra]=u.useState([]),[La,Ul]=u.useState("all"),[gs,Fo]=u.useState([]),[uu,du]=u.useState(!1),[ql,Gl]=u.useState(!0),Bo=u.useRef(null),fu=u.useRef(null),mf=u.useRef(null),hf=u.useRef(null),$o=u.useRef(!1),Po=u.useRef(!1),Uo=u.useRef(!1),Zi=u.useRef(0),Qs=u.useRef(0),Vl=ge==="settings",qo=ge==="pricing",mu=ge==="doc",hu=ge==="support",Ji=ge==="share-files",Go=ge==="agents",Ar=!Vl&&!qo&&!mu&&!hu&&!Ji&&!Go,ct=ge==="control";u.useEffect(()=>{Ar||(mt(!1),Ln(!1),nt(!1))},[Ar]);const Ct=Ze.frameMode==="mode4-h264-atlas",va=I||an.frameMode,di=va!=="mode3-h264-hw"?Math.min(30,an.fps):an.fps,pf=k.find(y=>y.deviceId===$)?.platform,pu=aE(pf)?{maxWidth:1280,maxHeight:720}:{maxWidth:3840,maxHeight:2160},fi={maxWidth:Math.min(va!=="mode3-h264-hw"?1920:3840,pu.maxWidth,an.maxWidth),maxHeight:Math.min(va!=="mode3-h264-hw"?1080:2160,pu.maxHeight,an.maxHeight)};u.useEffect(()=>{if(w?.started){if(!n?.access_token){qe("/api/remote/license",{method:"DELETE"},M).catch(()=>{});return}qe("/api/remote/license/sync",{method:"POST",headers:{Authorization:`Bearer ${n.access_token}`}},M).catch(y=>{console.warn("[LiveDesk] Hub license sync failed",y)})}},[A?.updated_at,M,n?.access_token,w?.started]),u.useEffect(()=>{!n?.access_token||!w?.started||w.runtimeRole!=="hub"||!w.deviceId||(async()=>{try{const{data:y,error:N}=await gt.rpc("register_livedesk_device",{p_device_id:w.deviceId,p_device_name:w.deviceName||w.deviceId,p_role:"hub",p_assigned_hub_id:null,p_platform:navigator.platform||navigator.userAgent,p_os_version:navigator.userAgent,p_app_version:w.managerVersion||w.managerPackage||""}),_=Array.isArray(y)?y[0]:y;(N||_?.ok===!1)&&console.warn("[LiveDesk] Hub device role registration failed",N||_?.reason||"unknown-error")}catch(y){console.warn("[LiveDesk] Hub device role registration failed",y)}})()},[n?.access_token,w?.deviceId,w?.deviceName,w?.managerPackage,w?.managerVersion,w?.runtimeRole,w?.started]),u.useEffect(()=>{!n?.access_token||!n.refresh_token||!w?.started||w.runtimeRole!=="hub"||Uo.current||(Uo.current=!0,qe("/api/auth/session",{method:"POST",body:JSON.stringify({accessToken:n.access_token,refreshToken:n.refresh_token,expiresAt:n.expires_at,user:{id:n.user.id,email:n.user.email}})},M||hn).then(y=>{y.hostTarget?.ok===!1&&console.warn("[LiveDesk] Hub host target publish is waiting for a valid Hub role:",y.hostTarget.error||"unknown-error"),C()}).catch(y=>{console.warn("[LiveDesk] Hub session sync failed:",y)}).finally(()=>{Uo.current=!1}))},[M,C,n?.access_token,n?.expires_at,n?.refresh_token,n?.user?.email,n?.user?.id,w?.runtimeRole,w?.started]);const at=u.useMemo(()=>k.filter(y=>y.connected),[k]),gu=La==="all"?at.length:gs.filter(y=>at.some(N=>N.deviceId===y)).length,gf=u.useMemo(()=>at.reduce((y,N)=>Math.max(y,$b(N.slotNumber)),0),[at]),Vo=Math.max(WN,at.length,gf),Mr=u.useMemo(()=>{const y=Array(Vo).fill(null),N=[];for(const _ of at){const O=$b(_.slotNumber);O>0&&O<=Vo&&!y[O-1]?y[O-1]=_:N.push(_)}for(const _ of N){const O=y.findIndex(D=>!D);O<0?y.push(_):y[O]=_}return y},[at]),vu=u.useMemo(()=>Mr.flatMap((y,N)=>y?[{device:y,slotNumber:N+1}]:[]),[Mr]),el=u.useMemo(()=>at.map(y=>y.deviceId),[at]),es=u.useMemo(()=>new Set(V),[V]),ya=u.useMemo(()=>at.filter(y=>es.has(y.deviceId)),[at,es]),Nr=u.useMemo(()=>k.find(y=>y.deviceId===K)||ya[0]||null,[k,K,ya]),zn=u.useMemo(()=>k.find(y=>y.deviceId===$)||null,[$,k]),vs=zn?ea(ee[zn.deviceId],Jn(zn)):0,Fe=ct?zn:Nr,Wo=u.useMemo(()=>new Set(gs),[gs]),mi=u.useMemo(()=>La==="all"?at.map(y=>y.deviceId):at.filter(y=>Wo.has(y.deviceId)).map(y=>y.deviceId),[at,Wo,La]),vf=Fe?eu(Fe,"control"):!1,Yo=ei(Fe?.platform)&&(Fe?.capabilities?.controlPermission===!1||Fe?.capabilities?.accessibilityPermission===!1),tl=Fe?eu(Fe,"audio")||eu(Fe,"remoteAudio"):!1,Ko=(Array.isArray(Fe?.capabilities?.agentTools)?Fe.capabilities.agentTools.map(String):[]).includes("system.power"),Gt=T?A?.plan==="pro"?"pro":"ltd":"free",hi=Al[Gt],Wl=hi.limit,Ls=u.useMemo(()=>{if(!Number.isFinite(Wl))return new Set(at.map(N=>N.deviceId));const y=Fe?[Fe,...at.filter(N=>N.deviceId!==Fe.deviceId)]:at;return new Set(y.slice(0,Wl).map(N=>N.deviceId))},[Wl,at,Fe]),yu=!Fe||Ls.has(Fe.deviceId),bu=Gt==="free",Wt=yu,Tr=Gt==="ltd"?`Plus includes up to ${Vc} Clients. Upgrade to Pro for commercial use and larger walls.`:`Free includes up to ${Gc} Clients. Upgrade to Plus for ${Vc} devices and no ads.`,xu=Gt==="free",ys=Wt&&!!Fe&&vf,Xo=Wt&&!!Fe&&tl,Yl=u.useMemo(()=>Mr.filter(y=>!!y?.connected).slice(0,HT(ze)).map(y=>y.deviceId),[Mr,ze]),Kl=u.useMemo(()=>ze==="list"?[]:Ct?el:Ze.visibleOnlyLive?tt.length>0?tt:Yl:el,[Yl,Ct,tt,Ze.visibleOnlyLive,el,ze]),ts=u.useMemo(()=>ph(Kl.filter(y=>Ls.has(y))),[Ls,Kl]),Zs=u.useMemo(()=>ts.filter(y=>y!==gn),[ts,gn]),Xl=u.useMemo(()=>new Map(at.map(y=>[y.deviceId,y.platform])),[at]),Kt=z1(Ct,ts.length),bn=u.useMemo(()=>Ct?Zs.filter(y=>Pc({mode4Enabled:Ct,directMode4WallEnabled:Kt,isMac:ei(Xl.get(y))})==="atlas"):[],[Kt,Ct,Xl,Zs]),ia=u.useMemo(()=>Zs.filter(y=>Pc({mode4Enabled:Ct,directMode4WallEnabled:Kt,isMac:ei(Xl.get(y))})==="direct"),[Kt,Ct,Xl,Zs]);u.useEffect(()=>{const y=new Set(ts);en(N=>{let _=!1;const O=new Set;for(const D of N)y.has(D)?O.add(D):_=!0;return _?O:N})},[ts]);const wu=u.useMemo(()=>{const y=D=>at.find(ae=>ae.deviceId===D)||null,N=D=>ea(ee[D],y(D)?Jn(y(D)):0),_=(D,ae=N(D))=>D===H&&(Y==="connecting"||Y==="active"||Y==="warm")?jn.current.get(D)||ga.current.get(D)||null:Ct?Xa.current.get(D)||kn.current.get(D)||null:kn.current.get(D)||null,O=(D,ae=N(D))=>{const de=y(D);return`${de?Ga(de):D} · Monitor ${ae+1}`};return{resolveCurrentTarget(){if(ct&&zn){const je=vs;return{type:"control",deviceIds:[zn.deviceId],monitorIndex:je,label:`${Ga(zn)} · Monitor ${je+1}`,sourceCanvas:_(zn.deviceId,je),signature:`control:${zn.deviceId}:${je}:${an.frameMode}`}}const D=ya.map(je=>je.deviceId).filter(je=>Zs.includes(je)),ae=Nr&&Zs.includes(Nr.deviceId)?Nr.deviceId:"",de=D.length>0?D:ae?[ae]:Zs,Re=D.length===0&&!ae;if(de.length===0)return null;if(de.length===1){const je=de[0],Ie=N(je);return{type:Re?"wall":D.length>1?"selection":"device",deviceIds:de,monitorIndex:Ie,label:Re?"Wall":O(je,Ie),sourceCanvas:Re?null:_(je,Ie),signature:`${Re?"wall":"device"}:${je}:${Ie}:${Ct?"atlas":"direct"}`}}return{type:D.length>1?"selection":"wall",deviceIds:de,label:D.length>1?`${de.length} selected Clients`:"Wall",sourceCanvas:null,signature:`${D.length>1?"selection":"wall"}:${de.join(",")}:${ze}`}},getDeviceCanvas:_,composeWall(D,ae={}){return IN(D.map(de=>{const Re=N(de);return{deviceId:de,label:O(de,Re),canvas:_(de,Re)}}),ae)}}},[at,zn,H,Y,an.frameMode,kn,Ct,ee,vs,ya,ct,Zs,Nr,ze]),Qo=VN({hubUrl:M,sourceRegistry:wu,onToast:Pe}),Er=u.useMemo(()=>Wt&&H&&Ls.has(H)&&(Y==="connecting"||Y==="active"||Y==="warm")?[H]:[],[H,Y,Wt,Ls]),Zo=u.useMemo(()=>ph([...ts,...Er]),[Er,ts]),ns=u.useMemo(()=>FT(Ze,ze),[Ze,ze]),_a=O1({mode4Enabled:Ct,directMode4WallEnabled:Kt,adaptiveFps:Lt,directDeviceCount:ia.length,adaptive:Ze.adaptiveLoadShedding}),nl=H1({deviceCount:bn.length,targetFps:Lt,adaptive:Ze.adaptiveLoadShedding});Ct&&bn.length>0;const un=u.useCallback((y,N,_)=>{if(y.width<=0||y.height<=0)return!1;const O=_?Math.min(1,_.maxWidth/y.width,_.maxHeight/y.height):1,D=Math.max(1,Math.round(y.width*O)),ae=Math.max(1,Math.round(y.height*O));(N.width!==D||N.height!==ae)&&(N.width=D,N.height=ae);const de=N.getContext("2d",{alpha:!1});return de?(de.imageSmoothingEnabled=!0,de.imageSmoothingQuality="high",de.drawImage(y,0,0,y.width,y.height,0,0,N.width,N.height),!0):!1},[]),Ia=u.useCallback(y=>{y&&en(N=>{if(N.has(y))return N;const _=new Set(N);return _.add(y),_})},[]),al=u.useCallback(y=>{y&&en(N=>{if(!N.has(y))return N;const _=new Set(N);return _.delete(y),_})},[]),za=u.useCallback((y,N)=>{if(!N||N.width<=0||N.height<=0)return!1;const _=document.createElement("canvas");return _.width=N.width,_.height=N.height,un(N,_)?(Ta.current.set(y,_),Ia(y),!0):!1},[Ia,un]),ba=u.useCallback(y=>{const N=ii.current.get(y);N!==void 0&&(window.cancelAnimationFrame(N),ii.current.delete(y))},[]),Js=u.useCallback(y=>{ba(y),Ta.current.delete(y)},[ba]),sl=u.useCallback((y,N)=>{ba(y);const _=()=>{const O=Ta.current.get(y),D=kn.current.get(y)===N||Xa.current.get(y)===N;if(!O||!D){ii.current.delete(y);return}un(O,N),ii.current.set(y,window.requestAnimationFrame(_))};_()},[un,ba]);u.useEffect(()=>()=>{for(const y of ii.current.values())window.cancelAnimationFrame(y);ii.current.clear()},[]);const Gn=u.useCallback((y,N=!1)=>{if(!An.current.has(y))return!1;const _=performance.now(),O=qn.current.get(y)||0,D=Math.max(1,Math.min(Va.controlWallReuseMaxFps,di)),ae=di>D?Math.max(0,1e3/D-2):0;if(!N&&_-O<ae)return!1;const de=jn.current.get(y)||ga.current.get(y),Re=kn.current.get(y)||Xa.current.get(y);return!de||!Re||!un(de,Re,ns)?!1:(qn.current.set(y,_),Ia(y),Js(y),!0)},[di,ns,Ia,un,Js]);u.useEffect(()=>{na.current=gn},[gn]);const Jo=u.useCallback((y,N)=>{const _=kn.current.get(y);if(_&&_!==N&&(ba(y),za(y,_),So(_)),N){kn.current.set(y,N);const O=Ta.current.get(y);O&&un(O,N)&&(Ia(y),sl(y,N)),Gn(y,!0);return}kn.current.delete(y),Ta.current.has(y)||al(y)},[al,sl,Ia,un,Gn,za,ba]),Su=u.useCallback((y,N)=>{const _=ga.current.get(y);if(_&&_!==N&&So(_),N){ga.current.set(y,N);const O=H===y&&re?jn.current.get(y):null;O&&un(O,N)&&y===$&&Qa(!0);return}ga.current.delete(y)},[$,H,re,un]),rl=u.useCallback((y,N,_)=>{const O=performance.now(),D=qa(y,_.deviceId);let ae=_;if(y==="control"&&_.visualChanged){const Re=w1(_.deviceId),je=ci.current.get(_.deviceId)||0,Ie=Re?O-Re.sentAt:Number.POSITIVE_INFINITY;Re&&Re.inputSeq>je&&Ie<=jb&&(ae={..._,inputFeedbackSeq:Re.inputSeq,inputFeedbackMs:Math.max(0,Ie),inputFeedbackAt:O},ci.current.set(_.deviceId,Re.inputSeq))}ms.current.set(D,O);const de=oi.current.get(D)||{totalFrames:0,totalChangedFrames:0,sampleFrames:0,sampleChangedFrames:0,sampleStartedAt:O,intervalCount:0,intervalSumMs:0,minIntervalMs:Number.POSITIVE_INFINITY,maxIntervalMs:0,intervalSamplesMs:[],seqGapCount:0,maxSeqGap:0,sampleDecoderInputBase:0,sampleDecoderOutputBase:0};y==="control"&&ae.inputFeedbackMs===void 0&&de.lastInfo?.inputFeedbackMs!==void 0&&de.lastInfo.inputFeedbackAt!==void 0&&O-de.lastInfo.inputFeedbackAt<=jb&&(ae={...ae,inputFeedbackSeq:de.lastInfo.inputFeedbackSeq,inputFeedbackMs:de.lastInfo.inputFeedbackMs,inputFeedbackAt:de.lastInfo.inputFeedbackAt}),de.totalFrames+=1,de.sampleFrames+=1,Rb(de,O,ae.frameSeq),ae.visualChanged&&(de.totalChangedFrames+=1,de.sampleChangedFrames+=1),de.lastInfo=ae,oi.current.set(D,de)},[]),Ql=u.useCallback((y,N)=>{rl("wall",y,N),y.kind==="live"&&(Ia(y.deviceId),Js(y.deviceId));const _=re&&H===y.deviceId;na.current!==y.deviceId&&!_&&(An.current.delete(y.deviceId),qn.current.delete(y.deviceId))},[H,re,Ia,rl,Js]),Cu=u.useCallback((y,N)=>{rl("control",y,N),z(ae=>ae&&"");const _=jn.current.get(y.deviceId),O=ga.current.get(y.deviceId);_&&_.width>0&&_.height>0&&(O&&un(_,O),Vs.current.set(y.deviceId,{...y,payload:new Uint8Array(0),byteLength:0}),An.current.add(y.deviceId),y.deviceId===H&&(oe(!0),U(ae=>ae==="connecting"?"active":ae)),Gn(y.deviceId));const D=$l.current;!D||!uT(D,y)||Qa(!0)},[H,un,Gn,rl]);u.useEffect(()=>{const y=window.setInterval(()=>{const N=performance.now(),_={};for(const[O,D]of oi.current.entries()){const ae=Math.max(1,N-D.sampleStartedAt);_[O]={totalFrames:D.totalFrames,totalChangedFrames:D.totalChangedFrames,framesPerSecond:D.sampleFrames*1e3/ae,changedFramesPerSecond:D.sampleChangedFrames*1e3/ae,averageIntervalMs:D.intervalCount>0?D.intervalSumMs/D.intervalCount:0,minIntervalMs:Number.isFinite(D.minIntervalMs)?D.minIntervalMs:0,maxIntervalMs:D.maxIntervalMs,p95IntervalMs:mh(D.intervalSamplesMs,.95),intervalCount:D.intervalCount,seqGapCount:D.seqGapCount,maxSeqGap:D.maxSeqGap,decoderInputFps:Math.max(0,(Number(D.lastInfo?.decoderInputCount||0)-D.sampleDecoderInputBase)*1e3/ae),decoderOutputFps:Math.max(0,(Number(D.lastInfo?.decoderOutputCount||0)-D.sampleDecoderOutputBase)*1e3/ae),sampleMs:ae,lastInfo:D.lastInfo},D.sampleDecoderInputBase=Number(D.lastInfo?.decoderInputCount||0),D.sampleDecoderOutputBase=Number(D.lastInfo?.decoderOutputCount||0),D.sampleChangedFrames=0,Eb(D,N)}Bl(_)},1e3);return()=>window.clearInterval(y)},[]);const pi=u.useCallback((y,N)=>{const _=performance.now(),O=qa(y,N.deviceId);Ys.current.set(O,_),In.current.delete(O),y==="control"?Rs.current.delete(O):Ja.current.delete(O);const D=Es.current.get(O)||{totalFrames:0,sampleFrames:0,sampleStartedAt:_,intervalCount:0,intervalSumMs:0,minIntervalMs:Number.POSITIVE_INFINITY,maxIntervalMs:0,intervalSamplesMs:[],seqGapCount:0,maxSeqGap:0,sampleBytes:0,sampleH264Frames:0,sampleTinyFrames:0,sampleRepeatedFrames:0,sourceSendIntervalsMs:[]};if(D.totalFrames+=1,D.sampleFrames+=1,D.sampleBytes+=Math.max(0,Number(N.byteLength||0)),String(N.frameMode||N.mode||N.codec||N.mimeType||"").toLowerCase().includes("h264")){D.sampleH264Frames+=1,Number(N.byteLength||0)<=iT&&(D.sampleTinyFrames+=1);const je=String(N.contentHash||"");je&&D.lastContentHash===je&&(D.sampleRepeatedFrames+=1),D.lastContentHash=je}const Re=Number(N.agentSendGapMs||0);Re>0&&Re<1e3&&Aw(D.sourceSendIntervalsMs,Re),D.lastFrame=N,Rb(D,_,N.frameSeq),Es.current.set(O,D)},[]);u.useEffect(()=>{const y=window.setInterval(()=>{const N=performance.now(),_={};for(const[O,D]of Es.current.entries()){const ae=Math.max(1,N-D.sampleStartedAt);_[O]={totalFrames:D.totalFrames,framesPerSecond:D.sampleFrames*1e3/ae,averageIntervalMs:D.intervalCount>0?D.intervalSumMs/D.intervalCount:0,minIntervalMs:Number.isFinite(D.minIntervalMs)?D.minIntervalMs:0,maxIntervalMs:D.maxIntervalMs,p95IntervalMs:mh(D.intervalSamplesMs,.95),intervalCount:D.intervalCount,seqGapCount:D.seqGapCount,maxSeqGap:D.maxSeqGap,averageFrameBytes:D.sampleFrames>0?D.sampleBytes/D.sampleFrames:0,tinyFrameRatio:D.sampleH264Frames>0?D.sampleTinyFrames/D.sampleH264Frames:0,repeatedFrameRatio:D.sampleH264Frames>0?D.sampleRepeatedFrames/D.sampleH264Frames:0,sourceSendAverageMs:D.sourceSendIntervalsMs.length>0?D.sourceSendIntervalsMs.reduce((de,Re)=>de+Re,0)/D.sourceSendIntervalsMs.length:0,sourceSendP95Ms:mh(D.sourceSendIntervalsMs,.95),sourceSendSampleCount:D.sourceSendIntervalsMs.length,sampleMs:ae,lastFrame:D.lastFrame},D.sampleBytes=0,D.sampleH264Frames=0,D.sampleTinyFrames=0,D.sampleRepeatedFrames=0,D.sourceSendIntervalsMs.length=0,Eb(D,N)}Ho(_)},1e3);return()=>window.clearInterval(y)},[]);const ec=u.useCallback(y=>{pi("wall",y);const N=kn.current.get(y.deviceId);if(!N)return;const _=F1(Ct,Ze.h264DecodeAcceleration);Qm(N,y,()=>kn.current.get(y.deviceId)!==N,Ql,_.acceleration,_.latencyMode).catch(()=>{})},[Ql,Ct,pi,Ze.h264DecodeAcceleration]),tc=u.useCallback(y=>{pi("control",y);let N=jn.current.get(y.deviceId);N||(N=document.createElement("canvas"),N.setAttribute("aria-hidden","true"),jn.current.set(y.deviceId,N));const _=N,O=Ae.current;Qm(_,y,()=>jn.current.get(y.deviceId)!==_,Cu,an.h264DecodeAcceleration,"interactive").catch(D=>{Ae.current!==O||jn.current.get(y.deviceId)!==_||H!==y.deviceId||z(D instanceof Error?D.message:String(D||"Control H.264 decode failed"))})},[H,an.h264DecodeAcceleration,Cu,pi]),nc=u.useCallback(y=>{const N=Ws.current;N&&N!==y&&So(N),Ws.current=y,jr.current=0,Xi.current=0,Xs.current=0,Da.current=0,Qi.current=0,aa.current=0},[]),On=u.useCallback(()=>{const y=new Set,N=Ws.current,_=Fl.current;if(!N||!_||N.width<=0||N.height<=0||aa.current!==_.layoutVersion)return y;for(const O of _.tiles){const D=Xa.current.get(O.deviceId);if(!D||O.width<=0||O.height<=0)continue;const ae=Math.max(1,Math.round(O.width)),de=Math.max(1,Math.round(O.height)),Re=lT,je=oT;(D.width!==Re||D.height!==je)&&(D.width=Re,D.height=je);const Ie=D.getContext("2d",{alpha:!1});if(Ie){Ie.imageSmoothingEnabled=!0,Ie.imageSmoothingQuality="high";try{Ie.drawImage(N,Math.round(O.x),Math.round(O.y),ae,de,0,0,Re,je),y.add(O.deviceId)}catch{}}}return y},[]),Zl=u.useCallback((y,N)=>{if(N){Xa.current.set(y,N);const O=Ta.current.get(y);O&&un(O,N)&&(Ia(y),sl(y,N)),window.requestAnimationFrame(()=>{On().has(y)&&Js(y)}),window.requestAnimationFrame(()=>Gn(y,!0));return}const _=Xa.current.get(y);ba(y),za(y,_),Xa.current.delete(y),Ta.current.has(y)||al(y)},[al,sl,Ia,On,un,Gn,za,Js,ba]),xa=u.useCallback((y,N)=>{Xs.current=performance.now(),aa.current=Number(y.layoutVersion||0),On();const _=Fl.current?.tiles||[],O=_.length>0?Math.round(y.byteLength/_.length):y.byteLength;for(const D of _){const ae={...y,deviceId:D.deviceId,width:D.width,height:D.height,sourceWidth:D.width,sourceHeight:D.height,byteLength:O};Ql(ae,{...N,deviceId:D.deviceId,width:D.width,height:D.height,byteLength:O}),na.current===D.deviceId&&Gn(D.deviceId,!0)}},[Ql,On,Gn]),gi=u.useCallback(y=>{const N=performance.now();jr.current<=0&&(jr.current=N),Xi.current=N;const _=Ws.current;if(!_)return;const O=Fl.current?.tiles||[],D=O.length>0?Math.round(y.byteLength/O.length):y.byteLength;for(const ae of O)pi("wall",{...y,deviceId:ae.deviceId,width:ae.width,height:ae.height,sourceWidth:ae.width,sourceHeight:ae.height,byteLength:D});Qm(_,y,()=>Ws.current!==_,xa).catch(()=>{})},[xa,pi]),ac=u.useMemo(()=>({fps:_a,maxWidth:ns.maxWidth,maxHeight:ns.maxHeight,quality:Ze.quality,mode:Ct?"mode3-h264-hw":Ze.frameMode,frameMode:Ct?"mode3-h264-hw":Ze.frameMode,streamPurpose:"wall",monitorSelections:ee,onFrame:ec}),[_a,ec,ns,Ct,ee,Ze]),il=u.useCallback(y=>{if(y.deviceId!==H||y.streamPurpose!=="control"||y.monitorIndex!==vs)return;const N=at.find(_=>_.deviceId===y.deviceId);!N?.sessionId||N.sessionId!==y.sessionId||(xe(y),U(_=>_==="connecting"?"active":_))},[at,H,vs]);u.useEffect(()=>{xe(null)},[H,ce,va,vs]);const Rr=ce?`${ce}-monitor-${vs}`:"",sc=u.useMemo(()=>({fps:di,maxWidth:fi.maxWidth,maxHeight:fi.maxHeight,quality:an.quality,mode:va,frameMode:va,streamPurpose:"control",forceRestart:Rr!=="",forceRestartToken:Rr,restartReason:Rr?"control-session-or-monitor-change":"",monitorSelections:ee,onFrame:tc,onStreamReady:il}),[Rr,an.quality,tc,di,va,fi.maxHeight,fi.maxWidth,il,ee]),{frames:er,state:vi,error:ll,stats:wa,restartLive:Jl}=My(ia,Ze.autoStartLive&&ia.length>0,M,ac),{frame:xn,layout:_s,state:as,stats:ku,refreshInputs:ol}=C1(bn,Ct&&Ze.autoStartLive&&bn.length>0,M,{fps:Va.atlasOutputFps,inputFps:nl,width:1920,height:1080,tileWidth:ns.maxWidth,tileHeight:ns.maxHeight,captureDeviceIds:bn,monitorSelections:ee,onFrame:gi});u.useEffect(()=>{Fl.current=_s,_s&&window.requestAnimationFrame(On)},[_s,On]),u.useEffect(()=>{if(!Ct||as!=="open"){jr.current=0,Xi.current=0,Xs.current=0;return}const y=window.setInterval(()=>{const N=Ws.current,_=performance.now();if(!N||document.visibilityState!=="visible"||_-Ea.current<fh)return;const O=Xi.current,D=jr.current;if(O<=0||D<=0||_-O>500)return;const ae=Xs.current||D,de=_-ae;if(de>=nT&&_-Qi.current>=sT){Bc(N)&&(Qi.current=_,Da.current=_);return}de>=tT&&_-Da.current>=aT&&i1(N)&&(Da.current=_)},250);return()=>window.clearInterval(y)},[as,Ct]);const{frames:bs,state:cl,error:yi,stats:rc,restartLive:ul,reconnectLive:Sa}=My(Er,Er.length>0,M,sc);u.useEffect(()=>{const y=dt.current,N=new Set([...Object.keys(y),...Object.keys(ee)]);for(const _ of N){const O=Math.max(0,Math.floor(Number(y[_])||0)),D=Math.max(0,Math.floor(Number(ee[_])||0));O!==D&&(hs("wall",_),hs("control",_),Bc(kn.current.get(_)||null),Bc(jn.current.get(_)||null))}dt.current={...ee}},[ee,hs]);const xs=u.useMemo(()=>{const y=(_,O)=>{const D=at.find(de=>de.deviceId===_),ae=ea(ee[_],D?Jn(D):1);return!Number.isFinite(Number(O.monitorIndex))||Number(O.monitorIndex)===ae},N=new Map;for(const[_,O]of er.entries())y(_,O)&&N.set(_,O);if(Ct&&xn&&bn.length>0){const _=new Map((_s?.tiles||[]).map(D=>[D.deviceId,D])),O=Math.max(1,_.size);for(const D of bn){const ae=_.get(D);ae&&N.set(D,{...xn,deviceId:D,width:ae.width,height:ae.height,sourceWidth:ae.width,sourceHeight:ae.height,byteLength:Math.round(xn.byteLength/O)})}}for(const[_,O]of bs.entries())y(_,O)&&N.set(_,O);for(const _ of Kl){if(!An.current.has(_)||N.has(_))continue;const O=Vs.current.get(_);O&&y(_,O)&&N.set(_,O)}return N},[xn,_s,at,bs,bn,Ct,ee,er,Kl,wr]),tr=ct&&Er.length>0?cl:bn.length>0?as:vi,Dr=ct&&Er.length>0?rc:bn.length>0?ku:wa;u.useCallback((y,N="browser-stale")=>{const _=Jl(y,N),O=ul(y,N);return _||O},[ul,Jl]),u.useEffect(()=>{Ct&&bn.length>0&&!ct&&ol()},[bn.length,Ct,ol,ct]);const nr=u.useCallback((y,N,_)=>{const O=N==="control"?jn.current.get(y)||null:kn.current.get(y)||null;if(!O||!Bc(O))return!1;const D=performance.now();return Ki.current.set(qa(N,y),D),!0},[]),Vn=u.useCallback(y=>{if(va!=="mode3-h264-hw"||!H||I)return!1;const N=at.find(Re=>Re.deviceId===H)||zn,_=String(N?.platform||"").toLowerCase();if(!["win32","windows","win32nt"].includes(_))return!1;const O="mode5-lzo-delta",D=qa("control",H),ae=performance.now();Ra.current.set(D,ae),Ys.current.delete(D),ms.current.delete(D),Ks.current.delete(D),Rs.current.delete(D),In.current.delete(D),Bc(jn.current.get(H)||null),Sa([H],"control-decode-fallback"),ne(O),J(`control-${O}-fallback-${H}-${Date.now()}`),oe(!1),Qa(!1);const de=y==="no first frame"||y==="control-first-frame-timeout";return z(Re=>Re||(de?`Control frame transport timeout; retrying Control with Mode 5 (${y})`:`H.264 decode failed; retrying Control with Mode 5 (${y})`)),U("connecting"),!0},[at,zn,H,I,va,Sa]);u.useEffect(()=>{const y=ct?zn:null;if(y){$l.current={deviceId:y.deviceId,monitorIndex:vs,frameMode:va,startedAtEpochMs:Date.now()},Qa(H===y.deviceId&&re);return}$l.current=null,Qa(!1)},[zn?.sessionId,$,H,re,va,vs,ct]);const Hn=u.useCallback(async y=>{const N=k.find(ae=>ae.deviceId===y);Pc({mode4Enabled:Ct,directMode4WallEnabled:Kt,isMac:ei(N?.platform)});const _=ea(ee[y],N?Jn(N):1),O={fps:Ct?_a:Lt,maxWidth:ns.maxWidth,maxHeight:ns.maxHeight,quality:Ze.quality,mode:Ct?"mode3-h264-hw":Ze.frameMode,frameMode:Ct?"mode3-h264-hw":Ze.frameMode,streamPurpose:"wall",monitorIndex:_},D=await qe(`/api/remote/devices/${encodeURIComponent(y)}/live/start`,{method:"POST",body:JSON.stringify(O)},M);if(D?.ok===!1)throw new Error(D.error||"wall-stream-start-failed");return D},[Lt,nl,k,Kt,_a,ns.maxHeight,ns.maxWidth,M,Ct,ee,Ze.frameMode,Ze.quality]),ss=u.useCallback(async y=>{const N=k.find(O=>O.deviceId===y);Pc({mode4Enabled:Ct,directMode4WallEnabled:Kt,isMac:ei(N?.platform)});const _=await qe(`/api/remote/devices/${encodeURIComponent(y)}/live/stop`,{method:"POST",body:JSON.stringify({streamPurpose:"wall",reason:"control-active"})},M);if(_?.ok===!1)throw new Error(_.error||"wall-stream-stop-failed");return _},[k,Kt,M,Ct]),Wn=u.useCallback(async(y,N="control-session-stop")=>{const _=await qe(`/api/remote/devices/${encodeURIComponent(y)}/live/stop`,{method:"POST",body:JSON.stringify({streamPurpose:"control",reason:N})},M);if(_?.ok===!1)throw new Error(_.error||"control-stream-stop-failed");return _},[M]),Yn=u.useCallback(y=>{const N=jn.current.get(y);N&&(Ta.current.has(y)||kn.current.has(y)||Xa.current.has(y)||za(y,N),So(N),jn.current.delete(y));const _=ga.current.get(y);_&&So(_),Vs.current.delete(y),An.current.delete(y),qn.current.delete(y)},[za]);u.useEffect(()=>{const y=$&&at.find(D=>D.deviceId===$)||null;if(!ct||!Wt||!y){kr.current&&(++sa.current,kr.current="");return}if(H===y.deviceId&&(Y==="active"||Y==="warm"||Y==="connecting")){U(D=>D==="warm"?"active":D);return}if(kr.current===y.deviceId)return;const N=++sa.current,_=H;kr.current=y.deviceId,(async()=>{if(_){if(U("stopping"),await Wn(_,"control-session-switch").catch(()=>{}),N!==sa.current)return;const de=na.current;if(de&&de!==y.deviceId)try{await Hn(de)}catch(Re){Pe(Re instanceof Error?Re.message:String(Re))}finally{na.current===de&&(na.current="",Ht(""))}if(N!==sa.current)return;Yn(_)}if(N!==sa.current)return;if(Ub(y)){try{await ss(y.deviceId)}catch(de){N===sa.current&&(kr.current="",U("error"),Pe(de instanceof Error?de.message:String(de)));return}if(N!==sa.current){await Hn(y.deviceId).catch(()=>{});return}na.current=y.deviceId,Ht(y.deviceId),An.current.add(y.deviceId)}const D=qa("control",y.deviceId),ae=`control-session-${N}-${y.deviceId}`;Ra.current.set(D,performance.now()),Ys.current.delete(D),ms.current.delete(D),In.current.delete(D),Rs.current.delete(D),ne(""),z(""),J(ae),pe(y.deviceId),oe(!1),U("connecting"),sa.current===N&&kr.current===y.deviceId&&(kr.current="")})()},[at,$,H,Y,Wt,Yn,Pe,ct,Hn,Wn,ss]),u.useEffect(()=>{if(!H||at.some(_=>_.deviceId===H))return;const y=H,N=++sa.current;U("stopping"),Wn(y,"control-device-disconnected").catch(()=>{}).finally(()=>{N===sa.current&&(Yn(y),oe(!1),J(""),ne(""),z(""),pe(_=>_===y?"":_),U("idle"))})},[at,H,Yn,Wn]);const ws=Ub(at.find(y=>y.deviceId===H));u.useEffect(()=>{if(Za.current!==null&&(window.clearTimeout(Za.current),Za.current=null),!H||Y==="idle"||Y==="stopping"||Y==="error"||ct||ge==="wall"){H&&!ct&&ge==="wall"&&U(_=>_==="warm"||_==="active"?"active":_);return}U(_=>_==="idle"||_==="stopping"?_:"warm");const y=H,N=++sa.current;return Za.current=window.setTimeout(()=>{Za.current=null,U("stopping"),Wn(y,"control-session-warm-timeout").catch(()=>{}).finally(()=>{N===sa.current&&(Yn(y),oe(!1),J(""),ne(""),z(""),pe(_=>_===y?"":_),U("idle"))})},rT),()=>{Za.current!==null&&(window.clearTimeout(Za.current),Za.current=null)}},[ge,H,Y,Yn,ct,Wn]),u.useEffect(()=>()=>{Za.current!==null&&window.clearTimeout(Za.current)},[]),u.useEffect(()=>{const N=H&&(ws&&(Y==="connecting"||Y==="active"||Y==="warm"||Y==="stopping")||re&&(Y==="active"||Y==="warm"))?H:"";if(Hl.current=N,li.current)return;const _=async()=>{for(;;){const D=Hl.current,ae=na.current;if(ae===D)return;if(ae){try{await Hn(ae)}catch(de){Pe(de instanceof Error?de.message:String(de))}finally{na.current===ae&&(na.current="",Ht(""))}continue}if(!D)return;try{await ss(D)}catch(de){Hl.current=na.current,Pe(de instanceof Error?de.message:String(de));return}So(kn.current.get(D)||null),na.current=D,Ht(D),An.current.add(D)}};let O;O=_().finally(()=>{li.current===O&&(li.current=null),na.current!==Hl.current&&Vt(D=>D+1)}),li.current=O},[H,re,Y,ws,Ct,Pe,Hn,ss,vn]);const ar=H&&at.find(y=>y.deviceId===H)||null,bi=!!(ct&&Ce&&Y==="active"&&ar?.sessionId&&X?.deviceId===ar.deviceId&&X.sessionId===ar.sessionId&&X.streamPurpose==="control"&&X.commandId&&X.captureGeneration>0&&X.monitorIndex===vs),{state:dl,sendInput:ic,stats:Ft,error:Bt}=k1(ys&&bi,M),yt=u.useCallback((y,N)=>!bi||!ar||!X||y!==ar.deviceId?!1:ic(y,{...N,monitorIndex:X.monitorIndex,controlSessionId:ar.sessionId,controlCommandId:X.commandId,captureGeneration:X.captureGeneration}),[bi,ar,X,ic]),Ca=bi&&dl==="open",{state:Oa,lastFrame:eo,stats:Xt,error:lc}=L1(Fe?.deviceId||"",Xo&&He,M),fl=u.useRef(new Map);u.useEffect(()=>{const y=[["hub",j],["registry",l],["license",B],["wall-stream",ll],["control-stream",yi],["control-decode",fe],["remote-input",Bt],["remote-audio",lc]];for(const[N,_]of y){const O=String(_||"").trim();if(N==="hub"&&et?.requiresHubRestart&&et.stage==="updating"){fl.current.delete(N);continue}if(!O){fl.current.delete(N);continue}fl.current.get(N)!==O&&(fl.current.set(N,O),Pe(O))}},[lc,fe,yi,j,Bt,B,l,Pe,ll,et]),u.useEffect(()=>{if(!ct||!Ce||!ys||!Ca||!Fe)return;const y=(O,D)=>{const ae=O.target,de=ae?.tagName?.toLowerCase();ae?.isContentEditable||de==="input"||de==="textarea"||de==="select"||(O.preventDefault(),O.stopPropagation(),yt(Fe.deviceId,{type:D,monitorIndex:ea(ee[Fe.deviceId],Jn(Fe)),key:O.key,code:O.code,keyCode:O.keyCode||O.which||0,location:O.location,text:O.key.length===1?O.key:"",repeat:D==="keydown"?O.repeat:!1,shiftKey:O.shiftKey,ctrlKey:O.ctrlKey,altKey:O.altKey,metaKey:O.metaKey}))},N=O=>y(O,"keydown"),_=O=>y(O,"keyup");return window.addEventListener("keydown",N),window.addEventListener("keyup",_),()=>{window.removeEventListener("keydown",N),window.removeEventListener("keyup",_)}},[ys,Ca,Ce,ee,Fe,yt,ct]),u.useEffect(()=>{const y=()=>{document.visibilityState==="visible"&&(Ea.current=performance.now())};return document.addEventListener("visibilitychange",y),window.addEventListener("pageshow",y),()=>{document.removeEventListener("visibilitychange",y),window.removeEventListener("pageshow",y)}},[]),u.useEffect(()=>{if(!H||Y!=="connecting"&&Y!=="active"&&Y!=="warm"||cl!=="open")return;const y=H,N=qa("control",y),_=window.setInterval(()=>{const O=performance.now();if(document.visibilityState!=="visible"||O-Ea.current<fh)return;const D=Ys.current.get(N)||0,ae=ms.current.get(N)||0,de=Ks.current.get(N)||0,Re=Ra.current.get(N)||O;Ra.current.has(N)||Ra.current.set(N,Re);const je=D<=0,Ie=je?O-Re>pT:O-D>Ib,wt=D>0&&(ae<=0||O-ae>Ib);if(!Ie&&wt){const $s=Ki.current.get(N)||0;O-$s>=zb&&nr(y,"control","decoder-paint-stale");return}if(!Ie)return;const sn=Rs.current.get(N)||0;if(sn>0){if(O-sn<vT)return;Rs.current.delete(N)}const Tn=In.current.get(N)||0;if(Tn>=yT){if(Vn(je?"no first frame":"stale stream"))return;oe(!1),Qa(!1),U("error");return}if(O-de<Ob(gT,Tn))return;ul([y],je?"control-first-frame-timeout":"control-receive-stale")&&(Ks.current.set(N,O),Rs.current.set(N,O),Ra.current.set(N,O),In.current.set(N,Tn+1))},650);return()=>window.clearInterval(_)},[cl,H,Y,Vn,nr,ul]),u.useEffect(()=>{!fe||va!=="mode3-h264-hw"||Vn(fe)||(oe(!1),Qa(!1),U("error"))},[fe,va,Vn]),u.useEffect(()=>{!ct||!H||!yi||U(y=>y==="error"?y:"error")},[yi,H,ct]),u.useEffect(()=>{if(vi!=="open"||!Ze.autoStartLive||ia.length===0)return;const y=window.setInterval(()=>{const N=performance.now();if(document.visibilityState!=="visible"||N-Ea.current<fh)return;const _=1e3/Math.max(1,_a||Ze.fps||1),O=Math.max(xT,Math.ceil(_*4)),D=[];for(const ae of ia){if(D.length>=CT)break;const de=qa("wall",ae),Re=Ys.current.get(de)||0,je=Re<=0,Ie=Ja.current.get(de)||N;if(Ja.current.has(de)||Ja.current.set(de,Ie),je&&N-Ie<=bT)continue;const wt=Ks.current.get(de)||0,sn=ms.current.get(de)||0,Tn=je||N-Re>O,Cs=!je&&(sn<=0||N-sn>O);if(!Tn&&Cs){const Pr=Ki.current.get(de)||0;N-Pr>=zb&&nr(ae,"wall","decoder-paint-stale");continue}if(!Tn)continue;const $s=In.current.get(de)||0;N-wt>=Ob(wT,$s)&&D.push(ae)}if(D.length>0&&Jl(D,D.some(ae=>(Ys.current.get(qa("wall",ae))||0)<=0)?"wall-first-frame-timeout":"wall-frame-stale"))for(const ae of D){const de=qa("wall",ae);Ks.current.set(de,N),Ja.current.set(de,N),Ys.current.delete(de),ms.current.delete(de),In.current.set(de,(In.current.get(de)||0)+1)}},ST);return()=>window.clearInterval(y)},[_a,ia,nr,Jl,Ze.autoStartLive,Ze.fps,vi]),u.useEffect(()=>{Jt(Ze.fps),Zi.current=Date.now(),Qs.current=0},[Ze.fps,Ze.frameMode,Ze.adaptiveLoadShedding]),u.useEffect(()=>{XC({h264DecodeAcceleration:Ze.h264DecodeAcceleration})},[Ze.h264DecodeAcceleration]),u.useEffect(()=>{if(!Wt||ct||ze==="list"||!Ze.visibleOnlyLive){Tt([]);return}const y=Bo.current;if(!y||typeof IntersectionObserver>"u"){Tt(Yl);return}const N=Array.from(y.querySelectorAll("[data-live-device-id]"));if(N.length===0){Tt([]);return}const _=new Map,O=()=>{const ae=ph(N.map(de=>String(de.dataset.liveDeviceId||"")).filter(de=>(_.get(de)||0)>0));Tt(de=>hh(de,ae)?de:ae)},D=new IntersectionObserver(ae=>{for(const de of ae){const Re=String(de.target.dataset.liveDeviceId||"");Re&&_.set(Re,de.isIntersecting?de.intersectionRatio||.01:0)}O()},{root:null,rootMargin:fa.visibleRootMargin,threshold:[0,.01,.2,.6,1]});return N.forEach(ae=>D.observe(ae)),O(),()=>D.disconnect()},[Yl,Wt,ct,Mr,Ze.visibleOnlyLive,ze]),u.useEffect(()=>{if(!Ze.adaptiveLoadShedding||!Ze.autoStartLive||ia.length===0)return;const y=Ze.fps;if(ia.length<=fa.preserveConfiguredFpsDeviceCount){Lt!==y&&(Jt(y),Zi.current=Date.now(),Qs.current=0);return}if(vi!=="open"||wa.sampleMs<500)return;const N=Date.now(),_=ia.length*Lt,O=wa.framesPerSecond,D=wa.bytesPerSecond,ae=Math.max(0,...ia.map(wt=>Yi[qa("wall",wt)]?.maxIntervalMs||0)),de=Math.max(0,...ia.map(wt=>Wi[qa("wall",wt)]?.maxIntervalMs||0)),Re=_>8&&O>0&&O<_*.72;if(O>fa.maxWallFramesPerSecond||D>fa.maxWallBytesPerSecond||ae>fa.maxFrameGapMs||de>fa.maxFrameGapMs||Re){Qs.current=0;const wt=fa.minFps;if(N-Zi.current>=fa.downshiftCooldownMs&&Lt>wt){const sn=Math.max(wt,BT(Lt,wt));sn<Lt&&(Jt(sn),Zi.current=N)}return}const Ie=O<fa.maxWallFramesPerSecond*fa.lowLoadFrameRatio&&D<fa.maxWallBytesPerSecond*fa.lowLoadByteRatio;if(!Ie||Lt>=y){Ie||(Qs.current=0);return}if(!Qs.current){Qs.current=N;return}if(N-Qs.current>=fa.recoveryCooldownMs){const wt=Math.min(y,$T(Lt,y));wt>Lt&&(Jt(wt),Zi.current=N,Qs.current=0)}},[Lt,ia.length,ia,Wi,Yi,vi,wa.bytesPerSecond,wa.framesPerSecond,wa.lastUpdatedAt,wa.sampleMs,Ze.adaptiveLoadShedding,Ze.autoStartLive,Ze.frameMode,Ze.fps]),u.useEffect(()=>{const y=new URLSearchParams(window.location.search).get("checkout");if(y==="success"){Pe("Checkout returned. Waiting for Dodo confirmation."),F();const N=window.setInterval(()=>{F()},4e3),_=window.setTimeout(()=>{window.clearInterval(N)},45e3);return()=>{window.clearInterval(N),window.clearTimeout(_)}}y==="cancelled"&&Pe("Checkout cancelled.")},[F]),u.useEffect(()=>{!Wt&&Ce&&De(!1),!Wt&&He&&he(!1)},[He,Ce,Wt]),u.useEffect(()=>{const y=Fe?.deviceId||"",N=Ge.current;if(Ge.current=y,!N||N===y)return;const _=++se.current,O=y?`audio-${Date.now()}-${Math.random().toString(36).slice(2,8)}`:"";y&&He&<.current.set(y,O);let D=!0;return(async()=>{try{const de=lt.current.get(N);if(lt.current.delete(N),await qe(`/api/remote/devices/${encodeURIComponent(N)}/audio/stop`,{method:"POST",body:JSON.stringify(de?{streamId:de}:{})},M),!D||_!==se.current||!He||!y||!Wt||!tl)return;const Re=await qe(`/api/remote/devices/${encodeURIComponent(y)}/audio/start`,{method:"POST",body:JSON.stringify({streamId:O,mimeType:"audio/pcm;format=f32le",sampleRate:48e3,channels:2})},M);if(Re.ok===!1)throw new Error(Re.error||"Remote audio start failed.")}catch(de){D&&(lt.current.delete(y),he(!1),Pe(de instanceof Error?de.message:String(de)))}})(),()=>{D=!1}},[He,Wt,M,tl,Fe?.deviceId]),u.useEffect(()=>{const y=at.map(O=>O.deviceId),N=new Set(y),_=new Map(at.map(O=>[O.deviceId,O]));for(const O of at){const D=TT(O);D&&(We.current[O.deviceId]=D)}L(O=>{const D=O.filter(ae=>N.has(ae));return hh(O,D)?O:D}),W(O=>O&&N.has(O)?O:""),Z(O=>O&&N.has(O)?O:""),Ne(O=>{let D=!1;const ae={};for(const[de,Re]of Object.entries(O)){const je=_.get(de);if(!je){ae[de]=Re;continue}const Ie=ea(Re,Jn(je));ae[de]=Ie,Ie!==Re&&(D=!0)}return D?ae:O}),Fo(O=>{const D=O.filter(ae=>N.has(ae));return hh(O,D)?O:D})},[at,Vo]),u.useEffect(()=>{ra([]),Gl(!1)},[Gn]),u.useEffect(()=>{!Ji||La!=="selected"||gs.length>0||ya.length===0||Fo(ya.map(y=>y.deviceId))},[ya,gs.length,La,Ji]),u.useEffect(()=>{try{localStorage.setItem(Sw,ht)}catch{}},[ht]),u.useEffect(()=>{IT(Ze)},[Ze]),u.useEffect(()=>{OT(an)},[an]),u.useEffect(()=>{LT(ee,We.current)},[at,ee]);const sr=u.useCallback(async()=>{const y=new Set(p);y.add(hn);try{const ae=await qe("/api/remote/status",void 0,hn);hy({endpoint:ae.agentEndpoint,endpoint_candidates:[...ae.agentEndpointCandidates||[],...ae.hostTargetEndpointCandidates||[]]}).forEach(de=>y.add(de))}catch{}const N=[...y].sort((ae,de)=>{const Re=je=>{const Ie=Qc(je);return Ie==="local"?0:Ie==="loopback"?1:2};return Re(ae)-Re(de)||ae.localeCompare(de)});let _=!1,O="",D=!1;try{for(const ae of N){const de=Qc(ae);try{if(await qe("/api/health",void 0,ae),de==="local")return v(!0),Mo(!0),ae;de==="loopback"&&(_=!0,O=ae)}catch{de==="local"&&(D=!0)}}return _?(D&&Pe("LiveDesk Hub is reachable on this PC, but not through the LAN address yet. Restart LiveDesk Hub to bind 5179 on the network."),v(!0),Mo(!0),O||hn):(v(!1),Mo(!1),"")}catch{return v(!1),Mo(!1),""}},[p]),rs=async()=>{if(Te("auth"),Ma(!1),await sr(),window.liveDesk?.auth?.signInWithGoogle){const _=await window.liveDesk.auth.signInWithGoogle();_?.ok===!1&&(Pe(_.error||"LiveDesk could not open the system browser for Google sign-in."),Te(""));return}const{data:y,error:N}=await gt.auth.signInWithOAuth({provider:"google",options:{redirectTo:`${window.location.origin}/callback`}});N&&(Pe(N.message),Te(""))},oc=async()=>{Te("auth"),Ma(!1),await gt.auth.signOut(),(await window.liveDesk?.auth?.persistSession?.(null))?.runtimeCleared===!1?Pe("Signed out locally. The LiveDesk runtime could not be reached to clear its session."):window.liveDesk?.auth?.persistSession||await qe("/api/auth/session",{method:"DELETE"},hn).catch(()=>{}),Te("")};u.useEffect(()=>{if(!Ts)return;const y=_=>{_.target instanceof Element&&(_.target.closest(".account-popover, .rail-account")||Ma(!1))},N=_=>{_.key==="Escape"&&Ma(!1)};return window.addEventListener("pointerdown",y),window.addEventListener("keydown",N),()=>{window.removeEventListener("pointerdown",y),window.removeEventListener("keydown",N)}},[Ts]);const xi=u.useCallback(async(y={})=>{const N=y.silent===!0,_=y.takeover===!0,O=y.requestLocalAccess===!0;if(!n?.user){N||Pe("Sign in before publishing this host.","error");return}if(!Po.current){Po.current=!0,N||(Te("host"),Et("Hub · 1/4 · Checking the local runtime…"));try{const ae=(O?await sr():"")||M||hn;if(!(w||await qe("/api/remote/status",void 0,ae)).started)throw new Error("Start LiveDesk on this computer before syncing the server.");N||Et("Hub · 2/4 · Sending the signed-in session to the Hub…");const Re=await qe("/api/auth/session",{method:"POST",body:JSON.stringify({accessToken:n.access_token,refreshToken:n.refresh_token,expiresAt:n.expires_at,user:{id:n.user.id,email:n.user.email}})},ae);if(!Re.ok||!Re.authenticated)throw new Error(Re.error||"hub-session-sync-failed");N||Et("Hub · 3/4 · Claiming the Hub role and publishing its endpoint…");const je=await qe("/api/hub/sync",{method:"POST",body:JSON.stringify({takeover:_})},ae);if(!je.ok)throw new Error(je.error||"host-target-failed");if(!N){Et("Hub · 4/4 · Verifying the endpoint and lease renewal…");const Ie=await qe("/api/hub/status",void 0,ae);if(!Ie.hostTargetActive)throw new Error(Ie.hostTargetLease?.lastError||"host-target-not-active-after-sync");const wt=Ie.hostTargetEndpoint||Ie.agentEndpoint||"endpoint ready",sn=Number(Ie.connectedDeviceCount||0),Tn=sn>0?`${sn} client${sn===1?"":"s"} connected.`:"No clients connected yet; reconnect the laptop Client.";Pe(`Hub ready · ${wt} · lease auto-renewal active. ${Tn}`,"success"),(O||x)&&(v(!0),Mo(!0)),C()}c()}catch(D){if(!N){const ae=D instanceof Error?D.message:String(D);ae.includes("local network access")&&(v(!1),Mo(!1)),Pe(`Hub setup failed · ${ae}`,"error")}}finally{Po.current=!1,N||Te("")}}},[M,x,C,c,sr,n,Pe,Et,w]),Lr=u.useCallback((y=!0)=>{ve!=="host"&&(Ve(!1),y&&window.requestAnimationFrame(()=>vt.current?.focus()))},[ve]),_r=u.useCallback(()=>{ve!=="host"&&(vt.current=document.activeElement instanceof HTMLElement?document.activeElement:null,Ma(!1),Ve(!0))},[ve]),Is=u.useCallback(()=>{Ve(!1),xi({takeover:!0,requestLocalAccess:!0})},[xi]);u.useEffect(()=>{if(!Ke)return;const y=window.requestAnimationFrame(()=>rt.current?.focus()),N=O=>{O.target instanceof Element&&O.target.closest(".client-hub-switch-popover, .hub-sync-trigger")||Lr(!1)},_=O=>{O.key==="Escape"&&Lr()};return window.addEventListener("pointerdown",N),window.addEventListener("keydown",_),()=>{window.cancelAnimationFrame(y),window.removeEventListener("pointerdown",N),window.removeEventListener("keydown",_)}},[Lr,Ke]);const rr=u.useCallback(async()=>{if(!(!w?.update?.canApply||ve==="update")){Te("update");try{const y=await qe("/api/update/apply",{method:"POST",body:JSON.stringify({})},M||hn);if(!y.ok)throw new Error(y.error||"LiveDesk update could not be started.");const N=[...new Set((y.targets||[]).map(D=>String(D.deviceId||"").trim()).filter(Boolean))],_=!!(y.managerUpdateAvailable||y.clientPackageUpdateAvailable),O=y.state==="hub-restart-requested"?Date.now():void 0;(N.length>0||_)&&At({operationId:String(y.operationId||`wall-update-${Date.now()}`),targetIds:N,stage:y.state==="clients-updated"?"updated":"updating",requiresHubRestart:_,initialHostInstanceId:String(w.hostInstanceId||""),targetManagerVersion:String(y.latestVersion||w.update?.latestVersion||""),targetClientVersion:String(y.latestClientVersion||w.update?.latestClientVersion||""),restartRequestedAt:O,restartDeadlineAt:O?O+zd:void 0}),Et(y.state==="hub-restart-requested"?Id:Nb),C()}catch(y){Pe(y instanceof Error?y.message:String(y),"error")}finally{Te("")}}},[ve,M,C,Pe,Et,w]);u.useEffect(()=>{const y=et,N=w?.update;if(!y||y.stage!=="updating"||!N)return;const _=N.restartResult?.operationId===y.operationId?N.restartResult:null;if(_?.outcome==="restored"){const je=_.error||`The requested Hub ${_.targetVersion||y.targetManagerVersion||"version"} failed verification.`;At(Ie=>Ie?.operationId===y.operationId?{...Ie,stage:"restored",error:je}:Ie),Pe(`LiveDesk update did not complete. Hub ${_.fallbackVersion||_.appVersion||"the previous version"} was restored. ${je}`,"error");return}if(_?.outcome==="failed"){const je=_.error||"No verified LiveDesk Hub replacement could be started.";At(Ie=>Ie?.operationId===y.operationId?{...Ie,stage:"failed",error:je}:Ie),Pe(`LiveDesk Hub update failed. ${je}`,"error");return}if(N.operationId===y.operationId&&N.state==="failed"){const je=N.error||"LiveDesk update failed before the Hub replacement completed.";At(Ie=>Ie?.operationId===y.operationId?{...Ie,stage:"failed",error:je}:Ie),Pe(je,"error");return}if(N.operationId===y.operationId){if(N.state==="hub-restart-requested"&&!y.restartDeadlineAt){const je=Date.now();At(Ie=>Ie?.operationId===y.operationId?{...Ie,restartRequestedAt:je,restartDeadlineAt:je+zd}:Ie),Et(Id)}!y.requiresHubRestart&&N.state==="clients-updated"&&(At(je=>je?.operationId===y.operationId?{...je,stage:"updated"}:je),Pe("LiveDesk clients updated and reconnected successfully.","success"));return}if(!y.requiresHubRestart)return;const O=w.started===!0&&(!y.initialHostInstanceId||w.hostInstanceId&&w.hostInstanceId!==y.initialHostInstanceId),D=!!N.checkedAt,ae=Tb(N.currentVersion,y.targetManagerVersion),de=!y.targetClientVersion||Tb(N.currentClientVersion,y.targetClientVersion),Re=D&&N.state==="idle"&&N.updateAvailable===!1&&ae&&de;O&&Re&&(At(je=>je?.operationId===y.operationId?{...je,stage:"updated"}:je),Pe(`LiveDesk Hub ${N.currentVersion||_?.appVersion||y.targetManagerVersion} restarted and passed version verification.`,"success"))},[Pe,Et,w,et]),u.useEffect(()=>{const y=et;if(!y||y.stage!=="updating"||!y.requiresHubRestart||y.restartDeadlineAt)return;const N=()=>{const O=Date.now();At(D=>D?.operationId===y.operationId&&D.stage==="updating"&&!D.restartDeadlineAt?{...D,restartRequestedAt:O,restartDeadlineAt:O+zd}:D),Et(Id)};if(w?.update?.state==="hub-restart-requested"){N();return}if(!j)return;const _=window.setTimeout(N,cT);return()=>window.clearTimeout(_)},[j,Et,w?.update?.state,et]),u.useEffect(()=>{const y=et;if(!y||y.stage!=="updating"||!y.requiresHubRestart||!y.restartDeadlineAt)return;const N=Math.max(0,y.restartDeadlineAt-Date.now()),_=y.operationId,O=window.setTimeout(()=>{const D=`LiveDesk Hub did not return and verify ${y.targetManagerVersion||"the requested version"} within ${Math.round(zd/6e4)} minutes. Check the Hub terminal or restart log, then run livedesk@latest manually if the Hub is still offline.`;At(ae=>ae?.operationId===_&&ae.stage==="updating"?{...ae,stage:"failed",error:D}:ae),Pe(D,"error")},N);return()=>window.clearTimeout(O)},[Pe,et]),u.useEffect(()=>{if(!et||et.stage!=="updated")return;const y=et.operationId,N=window.setTimeout(()=>{At(_=>_?.operationId===y?null:_)},8e3);return()=>window.clearTimeout(N)},[et]),u.useEffect(()=>{if(q!==Nb&&q!==Id)return;const y=w?.update;if(String(y?.state||"")==="failed"){Pe(y?.error||"LiveDesk update failed.","error");return}et?.stage==="updated"&&Pe("")},[q,Pe,w?.update?.error,w?.update?.state,et?.stage]);const zs=u.useCallback(async()=>{if(!ve){Te("pin");try{const N=await qe("/api/remote/pairing-pin",{method:"POST"},M||hn);if(!N.ok)throw new Error(N.error||"pin-rotation-failed");await C(),Pe("New PIN published.")}catch(y){Pe(y instanceof Error?y.message:String(y))}finally{Te("")}}},[ve,M,C]),ml=u.useCallback(y=>{y&&(W(y),L(N=>N.includes(y)?N:[...N,y]))},[]),hl=u.useCallback(y=>{if(!y)return;const N=es.has(y);W(_=>N?_===y?"":_:y),L(_=>N?_.filter(O=>O!==y):_.includes(y)?_:[..._,y])},[es]),Ss=u.useCallback(()=>{L([]),W("")},[un]),pl=u.useCallback(()=>{L(at.map(y=>y.deviceId)),W("")},[at]),Os=u.useCallback((y,N)=>{const _=at.find(D=>D.deviceId===y);if(!_)return;const O=Jn(_);O<=1||(Ne(D=>{const ae=ea(D[y],O),de=(ae+N+O)%O;return de===ae?D:{...D,[y]:de}}),hs("wall",y),hs("control",y),en(D=>{if(!D.has(y))return D;const ae=new Set(D);return ae.delete(y),ae}),Vs.current.delete(y),An.current.delete(y),ct&&(oe(!1),U("connecting"),Qa(!1),Z(y)))},[at,hs,ct]);u.useEffect(()=>{const y=N=>{if(ct&&Ce&&ys)return;const _=N.target,O=_?.tagName?.toLowerCase();_?.isContentEditable||O==="input"||O==="textarea"||O==="select"||((N.ctrlKey||N.metaKey)&&N.key.toLowerCase()==="a"&&(N.preventDefault(),pl()),N.key==="Escape"&&Ss())};return window.addEventListener("keydown",y),()=>window.removeEventListener("keydown",y)},[ys,Ss,Ce,pl,ct]);const Mn=(y,N)=>{const _=at.find(de=>de.deviceId===y),O=_?Jn(_):1,D=ea(ee[y],O),ae=ea(N,O);D!==ae&&(Ne(de=>({...de,[y]:ae})),en(de=>{if(!de.has(y))return de;const Re=new Set(de);return Re.delete(y),Re}),Vs.current.delete(y),An.current.delete(y),hs("wall",y),hs("control",y)),ct?(D!==ae&&(oe(!1),U("connecting")),Qa(!1),Z(y)):ml(y)},ir=async y=>{if(Qa(!1),Z(y),Oe("control"),!Ls.has(y)){De(!1),Pe(Tr);return}const N=at.find(O=>O.deviceId===y);if(ei(N?.platform)&&(N?.capabilities?.controlPermission===!1||N?.capabilities?.accessibilityPermission===!1)){De(!1),Pe("Mac remote control needs Accessibility permission. Allow it for the terminal app that starts LiveDesk, then restart the Mac client.","error");return}De(!0),Pe(`Opening remote control${N?` for ${Ga(N)}`:""}.`,"success")},ju=async()=>{if(!Fe){Pe("Select a Client before playing audio.");return}if(!Wt){Pe(Tr);return}if(!tl){Pe("Selected Client does not report remote audio support yet.");return}if(Be)return;const y=!He,N=y?`audio-${Date.now()}-${Math.random().toString(36).slice(2,8)}`:lt.current.get(Fe.deviceId);y&&N?lt.current.set(Fe.deviceId,N):y||lt.current.delete(Fe.deviceId),_e("audio"),he(y);try{await qe(`/api/remote/devices/${encodeURIComponent(Fe.deviceId)}/audio/${y?"start":"stop"}`,{method:"POST",body:JSON.stringify({...N?{streamId:N}:{},mimeType:"audio/pcm;format=f32le",sampleRate:48e3,channels:2})},M),Pe(`${y?"Audio started":"Audio stopped"} for ${Ga(Fe)}.`,"success")}catch(_){y&<.current.delete(Fe.deviceId),he(!1),Pe(_ instanceof Error?_.message:String(_),"error")}finally{_e("")}},bf=y=>{const N=y instanceof Error?y.message:String(y);return/remote-actions-disabled|allowAgent-blocked-by-settings/i.test(N)?"Remote actions are off. Turn on Settings > Agent > Enable AI Agent first.":/power-actions-disabled/i.test(N)?"Power actions are off. Turn on Settings > Agent > Allow power actions first.":/agent-tool-capability-unavailable|unsupported-agent-operation/i.test(N)?"This Client needs a newer LiveDesk Client for power actions.":/device-not-connected|404 device-not-found/i.test(N)?"The Client disconnected before the action could be sent.":N},cc=async y=>{if(!Fe||Be)return;const N=Fe,O={sleep:"Sleep",restart:"Restart",shutdown:"Shut down"}[y];if(window.confirm(`${O} ${Ga(N)}? Unsaved work on that computer may be lost.`)){_e(y),Pe(`Sending ${O.toLowerCase()} request to ${Ga(N)}...`);try{const D=await qe(`/api/remote/devices/${encodeURIComponent(N.deviceId)}/quick-actions`,{method:"POST",body:JSON.stringify({action:y})},M);if(!D.ok)throw new Error(D.error||"The remote action could not be sent.");Pe(`${O} request sent to ${Ga(N)}.`,"success"),C()}catch(D){Pe(bf(D),"error")}finally{_e("")}}},Au=async(y,N,_="files")=>{const O=y.filter(Re=>Re.file.size>=0);if(O.length===0)return{files:0,bytes:0};if(!Wt)throw new Error(Tr);if(N.length===0)throw new Error("Select at least one connected Client.");const D=crypto.randomUUID(),ae=O.reduce((Re,je)=>Re+je.file.size,0);let de=0;Ds({id:D,status:"reading",progress:1,message:`Preparing ${O.length} ${_}`});try{for(let Re=0;Re<O.length;Re+=1){const je=O[Re],Ie=je.file,wt=Math.max(1,Math.ceil(Ie.size/uh));for(let sn=0;sn<wt;sn+=1){const Tn=sn*uh,Cs=Math.min(Ie.size,Tn+uh),$s=Ie.size===0?new ArrayBuffer(0):await Ie.slice(Tn,Cs).arrayBuffer(),Pr=sn===wt-1;Ds({id:D,status:"sending",progress:Math.max(2,Math.min(98,Math.round(de/Math.max(1,ae)*96))),message:`${Re+1}/${O.length} ${je.relativePath}`});const Ps=await qe("/api/remote/files/chunk",{method:"POST",body:JSON.stringify({transferId:D,deviceIds:N,remoteDirectory:ht.trim(),name:Ie.name,relativePath:je.relativePath,size:Ie.size,type:Ie.type,lastModified:Ie.lastModified,offset:Tn,totalBytes:Ie.size,final:Pr,dataBase64:PT($s)})},M);if(!Ps.ok||Ps.queued!==N.length)throw new Error(Ps.error||`file-chunk-not-queued-${Ps.queued}/${N.length}`);de+=$s.byteLength,await new Promise(xf=>window.setTimeout(xf,0))}}return Ds({id:D,status:"queued",progress:100,message:`Sent ${O.length} file${O.length===1?"":"s"} to ${N.length} Client${N.length===1?"":"s"}`}),window.setTimeout(()=>{Ds(Re=>Re?.id===D&&Re.status==="queued"?null:Re)},4500),{files:O.length,bytes:ae}}catch(Re){throw Ds({id:D,status:"failed",progress:100,message:Re instanceof Error?Re.message:String(Re)}),Re}},tn=async y=>{const N=ya.length>0?ya.map(_=>_.deviceId):Fe?[Fe.deviceId]:[];try{await Au(Fb(y),N)}catch(_){Pe(_ instanceof Error?_.message:String(_))}},to=async y=>{try{await Au(Fb(y),mi)}catch(N){Pe(N instanceof Error?N.message:String(N))}};u.useEffect(()=>{$o.current=!1},[]);const gl=y=>{y.preventDefault(),y.stopPropagation(),ui(!1),tn(y.dataTransfer.files)},Ir=async(y="ltd")=>{if(!n){Pe("Sign in with Google before checkout.");return}if(T&&y===Gt){Pe("LiveDesk license is already active.");return}if(T&&Gt==="pro"){Pe("LiveDesk Pro is already active.");return}Te("checkout");try{const{data:N,error:_}=await gt.functions.invoke("dodo-checkout",{body:{plan:y}});if(_)throw _;if(!N?.checkout_url)throw new Error("Checkout function did not return a checkout URL.");window.location.assign(N.checkout_url)}catch(N){Pe(N instanceof Error?N.message:String(N)),Te("")}},wi=kT(),zr=jT(),lr=Zo.filter(y=>xs.has(y)).length,uc=tr==="open"&&Dr.sampleMs>0?`${Dr.framesPerSecond.toFixed(1)} fps`:tr,Le=Fe?ct?bs.get(Fe.deviceId):xs.get(Fe.deviceId):void 0,no=Fe?ea(ee[Fe.deviceId],Jn(Fe)):0,dc=Fe?NT(Fe):[],Or=typeof Le?.monitorIndex=="number"?ea(Le.monitorIndex,Math.max(1,Le.monitorCount||1)):no,Si=(Le&&Or===no?Rw(Le):null)||(Fe?Ew(Fe,no):null);Si&&`${Si.width}${Si.height}`;const vl=Fe?qa(ct?"control":"wall",Fe.deviceId):"",Qt=vl?Yi[vl]:void 0,Kn=vl?Wi[vl]:void 0,xt=Kn?.lastInfo,ao=Qt&&Qt.sampleMs>0?`${Qt.framesPerSecond.toFixed(1)} fps`:"waiting";Qt&&`${Qt.totalFrames}${ao}`,Kn&&Kn.sampleMs>0&&`${Kn.framesPerSecond.toFixed(1)}`,Kn&&Kn.sampleMs>0&&`${Kn.changedFramesPerSecond.toFixed(1)}`,xt&&(`${xt.frameSeq}${xt.decoderLabel}${xt.queueSize}${xt.renderQueueSize}${xt.droppedForLatency}${xt.decoderFlushCount}${xt.decoderTimestampFallbacks}${xt.decoderInputCount}${xt.decoderOutputCount}`,xt.visualChanged),xt&&`${Math.round(xt.queuedForMs)}${Math.round(xt.decodedForMs)}${Math.round(xt.scheduledForMs)}${xt.drawMs.toFixed(1)}`,xt?.targetPaintIntervalMs&&`${xt.targetPaintIntervalMs.toFixed(1)}${(1e3/xt.targetPaintIntervalMs).toFixed(1)}`,Kn&&`${Kn.decoderInputFps.toFixed(1)}${Kn.decoderOutputFps.toFixed(1)}`,xt?.canvasPresentation,xt&&`${xt.interactiveBufferWaitCount}${Math.round(xt.interactiveBufferWaitMs)}${xt.interactiveQueueDrainCount}`,Qt&&(Le?.frameMode==="mode5-lzo-delta"?`${Wc(Qt.averageFrameBytes)}${Le.deltaVisualTileCount||0}${Le.deltaRefreshTileCount||0}${Le.deltaTotalTiles||0}`:`${Wc(Qt.averageFrameBytes)}${Math.round(Qt.tinyFrameRatio*100)}${Math.round(Qt.repeatedFrameRatio*100)}`),Qt&&Qt.sourceSendSampleCount>0&&`${Qt.sourceSendAverageMs.toFixed(1)}${Qt.sourceSendP95Ms.toFixed(1)}`,Db(Qt),Db(Kn),Qt&&Kn&&`${Lb(Qt)}${Lb(Kn)}`,Le?Le.frameMode||Le.mode||Le.codec||Le.mimeType:ct?an.frameMode:Ze.frameMode,ct?an.frameMode:Ze.frameMode,Le?`${Le.width}${Le.height}${Wc(Le.byteLength)}`:ct?`${fi.maxWidth}${fi.maxHeight}`:`${Ze.maxWidth}${Ze.maxHeight}`,Le&&`${Le.isKeyFrame?"key":String(Le.chunkType||"delta")}`,Le&&Le.monitorCount&&((Le.monitorIndex??0)+1,`${Le.monitorCount}`),Le&&[`src ${Math.round(Le.sourceGapMs||0)}ms`,`gap ${Math.round(Le.agentSendGapMs||0)}ms`,`tx ${Math.round(Le.agentSendDurationMs||0)}ms`,`pace ${Math.round(Le.agentPaceMs||0)}ms`,`queue ${Math.round(Le.agentQueueBeforePaceMs||0)}ms`,`age ${Math.round(Le.agentFrameAgeMs||0)}ms`,Le.hubPacketTransitAgeMs!==void 0?`sock ${Math.max(0,Math.round(Le.hubPacketTransitAgeMs))}ms`:"",Le.droppedByAgent?`drop ${Le.droppedByAgent}`:"",Le.hubIngressDropped?`ingress-drop ${Le.hubIngressDropped}`:"",Le.hubClientDropped?`ws-drop ${Le.hubClientDropped}`:"",Le.captureStageMs?`capture ${Math.round(Le.captureStageMs)}ms`:"",Le.requestedFps?`req ${Math.round(Le.requestedFps)}fps`:"",Le.effectiveFps?`eff ${Math.round(Le.effectiveFps)}fps`:"",Le.captureAverageMs?`avg ${Math.round(Le.captureAverageMs)}ms`:"",Le.captureP95Ms?`p95 ${Math.round(Le.captureP95Ms)}ms`:"",Le.slowFrameCount?`slow ${Math.round(Le.slowFrameCount)}`:"",Le.captureHelperRestarts?`helper ${Math.round(Le.captureHelperRestarts)}`:"",Le.convertMs?`rgb565 ${Math.round(Le.convertMs)}ms`:"",Le.compressMs?`lzo ${Math.round(Le.compressMs)}ms`:"",Le.captureSourceSerial?`cap #${Le.captureSourceSerial}`:"",Le.captureSourceSkipped?`cap skip ${Le.captureSourceSkipped}`:"",Le.frameMode==="mode5-lzo-delta"?`tiles ${Le.deltaVisualTileCount||0}+${Le.deltaRefreshTileCount||0}/${Le.deltaTotalTiles||0}@${Le.deltaTileSize||0}`:"",Le.platformProfile||Le.hardwareEncoder||"-",Le.atlasComposeMs?`atlas ${Le.atlasComposeMs.toFixed(1)}ms`:"",Le.atlasPoolStarved?`pool ${Le.atlasPoolStarved}`:"",Le.atlasTileCount?`tiles ${Le.atlasReadyTiles||0}/${Le.atlasTileCount}`:"",Le.atlasInputRevision?`input #${Le.atlasInputRevision}`:"",Le.atlasSourceMaxAgeMs!==void 0?`input-age ${Math.round(Le.atlasSourceMaxAgeMs)}ms`:"",xt?.inputFeedbackMs!==void 0&&xt.inputFeedbackAt!==void 0&&performance.now()-xt.inputFeedbackAt<=Ab?`feedback ${Math.round(xt.inputFeedbackMs)}ms`:"",Le.contentHash?`enc ${Le.contentHash.slice(0,6)}`:"",xt?.visualHash?`pix ${xt.visualHash.slice(0,6)}`:""].filter(Boolean).join(" / "),dl==="open"&&[`${Ft.sentPerSecond.toFixed(1)} hz`,Ft.lastInputSeq>0?`rtt ${Math.round(Ft.lastRoundTripMs)}ms`:"",Ft.lastInputSeq>0?`apply ${Ft.lastAgentApplyMs.toFixed(1)}ms`:"",Ft.droppedPerSecond>0?`drop ${Ft.droppedPerSecond.toFixed(1)}/s`:"",Ft.bufferedAmount>0?`buf ${Wc(Ft.bufferedAmount)}`:"",Ft.lastInputType||"ready"].filter(Boolean).join(" / "),xt?.inputFeedbackMs!==void 0&&xt.inputFeedbackAt!==void 0&&performance.now()-xt.inputFeedbackAt<=Ab&&`${Math.round(xt.inputFeedbackMs)}${xt.inputFeedbackSeq||0}`,He&&[Oa,`context ${Xt.audioContextState}`,`queue ${Math.round(Xt.queuedMs)}ms`,`last ${Math.round(Xt.lastPacketAgeMs)}ms`,`accepted ${Math.round(Xt.lastAcceptedAgeMs)}ms`,`process ${Math.round(Xt.localProcessingAgeMs)}ms`,`remote ${Math.round(Xt.browserAgeMs)}/${Math.round(Xt.captureAgeMs)}ms`,`heartbeat ${Math.round(Xt.captureStatusAgeMs)}ms`,`capture ${Xt.captureAlive&&Xt.helperAlive?"alive":"down"}`,`drop ${Xt.drops}`,`resync ${Xt.resyncs}`,`restart ${Xt.automaticRestarts}`,Xt.pendingDecodes>0?`decode ${Xt.pendingDecodes}`:"",eo?`#${eo.frameSeq}`:"waiting"].filter(Boolean).join(" / "),Le&&`${Le.frameSeq}${ix(Le.receivedAt||Le.capturedAt)}`;const Hr=Math.max(0,Dr.bytesPerSecond*8/1e6),Fr=u.useCallback(()=>{_n(!0),Aa(Date.now()),C(),ta.current!==null&&window.clearTimeout(ta.current),ta.current=window.setTimeout(()=>{ta.current=null,Aa(Date.now()),_n(!1)},650)},[C]),or=u.useCallback(()=>{if(st){mt(!1);return}Ln(!1),mt(!0),Fr()},[Fr,st]),cr=u.useCallback(()=>{if(Ee){Ln(!1);return}mt(!1),Ln(!0),Fr()},[Ee,Fr]),fc=u.useCallback(()=>{if(pn){nt(!1);return}nt(!0),Fr()},[pn,Fr]),Ci=new Map,Hs=new Map;if(st||Ee||pn)for(const y of at){const N=y.deviceId,_=ct&&H===N,D=qa(_?"control":"wall",N),ae=_?bs.get(N):xs.get(N),de=_?jn.current.get(N)||ga.current.get(N)||null:kn.current.get(N)||(Ct?Ws.current:null),Re={device:y,frame:ae,receiveStat:Yi[D],paintStat:Wi[D],codec:de?Cy(de):{},selectedMonitorIndex:ea(ee[N],Jn(y)),control:_,controlInputReady:_&&Ca,controlBinding:_&&X?.deviceId===N?X:void 0,clientHeartbeatMs:Number(w?.heartbeatMs||5e3),now:Date.now()};(st||pn)&&Ci.set(N,DN(Re)),Ee&&Hs.set(N,LN(Re))}const yl=pn?_N({status:w||{},deviceDiagnostics:[...Ci.values()],codec:Cy(),browser:{hardwareConcurrency:navigator.hardwareConcurrency,deviceMemory:Number(navigator.deviceMemory||0)},frameStats:Dr}):[],bl=xn?.atlasReadyTiles??lr,Br=xn?.atlasTileCount??lr,so=bl===Br?`${Br} tiles`:`${bl}/${Br} tiles`,Nu=bn.length>0;ct?`${uc}${Hr.toFixed(Hr>=10?0:1)}`:Nu?`${so}${uc}${Hr.toFixed(Hr>=10?0:1)}`:`${lr}${uc}${Hr.toFixed(Hr>=10?0:1)}`;const Fs=ya.length,Tu=ct?Fe?Ga(Fe):"No Client":Fs===0?"No Client":Fs>1?`${Fs} selected`:Fe?Ga(Fe):"No Client",ur=ct?[Fe?.deviceId].filter(y=>!!y):ya.map(y=>y.deviceId),Nn=Lo[Ka]||Lo.en,ki=Nn.sync||Lo.en.sync,dr=a||R?"checking":T?Gt==="pro"?"Pro LTD active":`Plus LTD ${at.length}/${Vc}`:bu?`Free ${at.length}/${Gc}`:`Upgrade ${at.length}/${Gc}`,xl=T?Gt==="pro"?"Pro active":"Upgrade to Pro LTD":n?ve==="checkout"?"Opening checkout":Gt==="ltd"?"Buy Pro LTD":"Buy Plus LTD":ve==="auth"?"Opening Google":"Sign in to buy",mc=Gt==="pro"||ve==="checkout"||ve==="auth"||a||R,ro=n?.user?.user_metadata,fr=Bb(ro,"avatar_url","picture"),Bs=Bb(ro,"full_name","name")||n?.user?.email||"Google",hc=Bs.slice(0,1).toUpperCase(),Eu=w?.started?"Local app ready":"Start LiveDesk",ji=i?.active?"Published":n?"Not published":"Sign in required",io=/^\d{6}$/.test(w?.pairingPin||"")?w?.pairingPin:"------",lo=y=>{if(typeof window<"u"&&(y==="support"?window.history.pushState({},"","/support"):ge==="support"&&window.history.pushState({},"","/")),y==="control"){const N=zn||Nr;if(N&&(Qa(!1),Z(N.deviceId)),Oe(y),!Wt){De(!1),Pe(Tr);return}De(!0);return}y==="wall"&&Sr(N=>N+1),Oe(y),De(!1)},mr=ve==="update"||et?.stage==="updating"||["dispatching","waiting-for-clients","hub-restart-requested"].includes(w?.update?.state||""),pc=!!(w?.update?.updateAvailable&&w.update.canApply||mr||et?.error||!w&&et),dn=wC(Ka),la=u.useCallback(y=>{if(!et?.targetIds.includes(y))return;const N=w?.update?.operationId===et.operationId?w.update.targets?.find(_=>_.deviceId===y):void 0;switch(N?.state){case"completed":return{state:"updated",label:"Updated"};case"failed":return{state:"failed",label:"Update failed",error:N.error};case"pending-offline":return{state:"offline",label:"Waiting for reconnect"};case"queued":return et.stage==="updating"?{state:"queued",label:"Queued"}:void 0;case"waiting":return et.stage==="updating"?N.candidateSince?{state:"updating",label:"Verifying restart..."}:{state:"updating",label:"Restarting..."}:void 0;default:return et.stage==="updated"?{state:"updated",label:"Updated"}:et.stage==="updating"?{state:"updating",label:"Verifying..."}:void 0}},[w?.update,et]);return r.jsxs("main",{className:"app-shell",children:[r.jsxs("aside",{className:"tool-rail","aria-label":"LiveDesk tools",children:[r.jsx("div",{className:"rail-brand",title:"LiveDesk",children:r.jsx(lx,{size:25})}),r.jsx("nav",{className:"rail-tools",children:KT.map(y=>{const N=y.icon,_=Vd(y.tooltip,Ka);return r.jsxs("button",{className:`rail-tool ${y.id==="sync-server"?"sync-server hub-sync-trigger":""} ${ge===y.id?"active":""} accent-${y.accent}`,onClick:()=>y.id==="sync-server"?_r():lo(y.id),"data-tooltip":y.id==="sync-server"&&Ke?void 0:Mh(y.label,y.tooltip,Ka),"aria-label":`${y.label}. ${_}`,type:"button",disabled:y.id==="sync-server"&&ve==="host","aria-busy":y.id==="sync-server"&&ve==="host","aria-expanded":y.id==="sync-server"?Ke:void 0,"aria-haspopup":y.id==="sync-server"?"dialog":void 0,"aria-controls":y.id==="sync-server"&&Ke?"hub-sync-popover":void 0,children:[y.id==="sync-server"&&ve==="host"?r.jsx("span",{className:"sync-server-spinner","aria-hidden":"true"}):r.jsx(N,{size:19}),r.jsx("span",{children:y.id==="sync-server"&&ve==="host"?"Syncing…":y.label})]},y.id)})}),r.jsxs("div",{className:"rail-footer",children:[XT.map(y=>{const N=y.icon;return r.jsxs("button",{className:`rail-tool compact ${ge===y.id?"active":""} accent-${y.accent}`,onClick:()=>lo(y.id),title:y.label,type:"button",children:[r.jsx(N,{size:18}),r.jsx("span",{children:y.label})]},y.id)}),r.jsxs("button",{className:`rail-tool compact local-settings ${Vl?"active":""}`,onClick:()=>{Oe("settings"),typeof window<"u"&&window.history.pushState({},"","/"),Ma(!1),De(!1),fs(!1)},title:"Settings",type:"button",children:[r.jsx(Gh,{size:18}),r.jsx("span",{children:"Settings"})]}),r.jsxs("button",{className:`rail-account ${n?"signed-in":""}`,onClick:()=>{Ma(y=>!y)},disabled:a||ve==="auth","aria-expanded":Ts,title:n?`Account: ${n.user?.email||Bs}`:"Sign in with Google",type:"button",children:[n?fr?r.jsx("img",{src:fr,alt:Bs,referrerPolicy:"no-referrer"}):r.jsx("span",{className:"avatar-fallback",children:hc}):r.jsx(_l,{size:18}),r.jsx("span",{children:n?"Account":"Google"})]}),Ts&&r.jsxs("section",{className:"account-popover",role:"dialog","aria-label":"LiveDesk account",children:[r.jsxs("div",{className:"account-popover-head",children:[r.jsx("div",{className:"account-popover-avatar",children:n&&fr?r.jsx("img",{src:fr,alt:Bs,referrerPolicy:"no-referrer"}):n?r.jsx("span",{children:hc}):r.jsx(_l,{size:18})}),r.jsxs("div",{children:[r.jsx("strong",{children:n?Bs:"Account"}),r.jsx("p",{children:n?.user?.email||"Not signed in"})]})]}),r.jsxs("dl",{className:"account-popover-facts",children:[r.jsx("dt",{children:"User"}),r.jsx("dd",{children:n?.user?.email||"Not signed in"}),r.jsx("dt",{children:"Network"}),r.jsx("dd",{children:Eu}),r.jsx("dt",{children:"Access"}),r.jsx("dd",{children:ji}),r.jsx("dt",{children:"Plan"}),r.jsx("dd",{children:dr}),r.jsx("dt",{children:"Ads"}),r.jsx("dd",{children:Gt==="free"?"Standard banner":"Removed"})]}),n?r.jsxs("button",{className:"account-popover-action",onClick:oc,disabled:ve==="auth",type:"button",children:[r.jsx(Vb,{size:16}),"Logout"]}):r.jsxs("button",{className:"account-popover-action primary",onClick:rs,disabled:ve==="auth",type:"button",children:[r.jsx(_l,{size:16}),"Sign in with Google"]})]})]})]}),Ke&&r.jsxs("section",{id:"hub-sync-popover",className:"client-hub-switch-popover hub-sync-popover",role:"dialog","aria-modal":"false","aria-labelledby":"hub-sync-title","aria-describedby":"hub-sync-description hub-sync-notice",children:[r.jsxs("header",{className:"client-hub-switch-head",children:[r.jsx("span",{className:"client-hub-switch-mark","aria-hidden":"true",children:r.jsx(yr,{size:19})}),r.jsx("span",{className:"client-hub-switch-eyebrow",children:dn.eyebrow}),r.jsx("button",{className:"client-hub-switch-close",type:"button",onClick:()=>Lr(),"aria-label":dn.closeLabel,children:r.jsx(ma,{size:17})})]}),r.jsx("h2",{id:"hub-sync-title",children:dn.title}),r.jsx("p",{id:"hub-sync-description",children:dn.description}),r.jsxs("p",{id:"hub-sync-notice",className:"client-hub-switch-notice",children:[r.jsx(Dn,{size:16,"aria-hidden":"true"}),r.jsx("span",{children:dn.restartNotice})]}),r.jsxs("div",{className:"client-hub-switch-actions",children:[r.jsx("button",{type:"button",onClick:()=>Lr(),children:dn.cancel}),r.jsxs("button",{ref:rt,className:"primary",type:"button",onClick:Is,disabled:ve==="host",children:[r.jsx(yr,{size:16}),dn.confirm]})]})]}),r.jsxs("section",{className:`desk-stage ${Ar&&!pa?"dock-open":""} ${ct?"control-stage":""} ${Go?"agents-stage":""}`,children:[r.jsxs("header",{className:"topbar",children:[r.jsxs("div",{className:"brand",children:[r.jsx("span",{className:"brand-mark",children:r.jsx(Il,{size:18})}),r.jsxs("div",{className:"brand-copy",children:[r.jsx("h1",{children:"LiveDesk"}),r.jsx("p",{children:"Control wall for local machines"})]}),pc?r.jsxs("button",{className:"topbar-update-button",onClick:()=>{rr()},title:et?.error?`LiveDesk Hub update failed: ${et.error}`:`Update LiveDesk to ${w?.update?.latestVersion||et?.targetManagerVersion||"latest"}`,"aria-label":et?.error?`LiveDesk Hub update failed: ${et.error}`:mr?"LiveDesk update in progress":`Update LiveDesk to ${w?.update?.latestVersion||et?.targetManagerVersion||"latest"}`,type:"button",disabled:mr||!w?.update?.canApply,"aria-busy":mr,children:[mr?r.jsx("span",{className:"topbar-update-spinner","aria-hidden":"true"}):r.jsx(qh,{size:19,"aria-hidden":"true"}),mr&&w?.update?.targetCount?r.jsxs("span",{className:"topbar-update-count","aria-hidden":"true",children:[w.update.completedCount||0,"/",w.update.targetCount]}):null]}):null]}),r.jsxs("div",{className:"topbar-tray",children:[Ji&&!ct&&r.jsxs("div",{className:"file-share-top-route","aria-label":"File distribution route",children:[r.jsxs("div",{children:[r.jsx(Rl,{size:17}),r.jsxs("span",{children:[r.jsx("small",{children:"Source"}),r.jsx("strong",{children:"LiveDesk Hub"})]})]}),r.jsx(Kh,{size:15}),r.jsx(x0,{size:16}),r.jsxs("div",{children:[r.jsx("span",{className:"file-share-top-route-laptops",children:"▣ ▣ ▣"}),r.jsxs("span",{children:[r.jsx("small",{children:"Destination"}),r.jsxs("strong",{children:[gu," Client",gu===1?"":"s"]}),r.jsx("em",{children:ht.trim()||"Desktop/LiveDeskFiles"})]})]})]}),Ar&&!ct&&r.jsxs("div",{className:"top-actions",children:[r.jsx(xb,{active:pn,refreshing:pn&&It,onClick:fc}),r.jsx(yb,{active:st,refreshing:st&&It,onClick:or}),kb&&r.jsx(bb,{active:Ee,refreshing:Ee&&It,onClick:cr}),r.jsx("span",{className:"top-actions-divider","aria-hidden":"true"}),r.jsx(vb,{controller:Qo,onError:Pe}),r.jsx("span",{className:"top-actions-divider","aria-hidden":"true"}),r.jsx("div",{className:"segmented-control","aria-label":"Wall view",children:YT.map(y=>r.jsx("button",{className:ze===y.id?"active":"",onClick:()=>Me(y.id),type:"button",children:y.label},y.id))})]}),ct&&r.jsxs("div",{className:"top-actions",children:[r.jsx(xb,{active:pn,refreshing:pn&&It,onClick:fc}),r.jsx(yb,{active:st,refreshing:st&&It,onClick:or}),kb&&r.jsx(bb,{active:Ee,refreshing:Ee&&It,onClick:cr}),r.jsx("span",{className:"top-actions-divider","aria-hidden":"true"}),r.jsx(vb,{controller:Qo,onError:Pe})]})]})]}),pn&&Ar&&r.jsx(AN,{items:yl,collectedAt:Ya,onClose:()=>nt(!1)}),mu?r.jsxs("section",{className:"doc-page",lang:Ka,dir:Ka==="ar"?"rtl":"ltr",children:[r.jsxs("section",{className:"doc-hero",children:[r.jsxs("div",{className:"doc-hero-copy",children:[r.jsxs("span",{className:"pricing-eyebrow",children:[r.jsx(Gd,{size:16}),Nn.eyebrow]}),r.jsx("h2",{children:Nn.title}),r.jsx("p",{children:Nn.intro})]}),r.jsxs("label",{className:"doc-language-picker",children:[r.jsxs("span",{children:[r.jsx(w0,{size:17}),Nn.languageLabel]}),r.jsx("select",{value:Ka,onChange:y=>ri(y.target.value),children:ZT.map(y=>r.jsx("option",{value:y.id,children:y.label},y.id))})]})]}),r.jsx("section",{className:"doc-grid",children:Nn.sections.map((y,N)=>{const O=[yr,kh,ty,Dn,tf][N]||Gd;return r.jsxs("article",{className:"doc-card",children:[r.jsx("div",{className:"doc-card-icon",children:r.jsx(O,{size:19})}),r.jsxs("div",{children:[r.jsx("h3",{children:y.title}),r.jsx("p",{children:y.body}),r.jsx("ul",{children:y.points.map(D=>r.jsx("li",{children:D},D))})]})]},y.title)})}),ki&&r.jsxs("section",{className:"doc-sync",children:[r.jsxs("div",{className:"doc-sync-head",children:[r.jsx(ty,{size:20}),r.jsxs("div",{children:[r.jsx("h3",{children:ki.title}),r.jsx("p",{children:ki.body})]})]}),r.jsx("ul",{children:ki.points.map(y=>r.jsx("li",{children:y},y))})]}),r.jsxs("section",{className:"doc-firewall",children:[r.jsxs("div",{className:"doc-firewall-head",children:[r.jsx(Dn,{size:20}),r.jsxs("div",{children:[r.jsx("h3",{children:Nn.firewall.title}),r.jsx("p",{children:Nn.firewall.body})]})]}),r.jsx("ul",{children:Nn.firewall.points.map(y=>r.jsx("li",{children:y},y))})]}),r.jsxs("section",{className:"doc-command-panel",children:[r.jsxs("div",{className:"doc-command-head",children:[r.jsx(kh,{size:20}),r.jsxs("div",{children:[r.jsx("h3",{children:Nn.commands.title}),r.jsx("p",{children:Nn.commands.body})]})]}),r.jsxs("div",{className:"doc-command-list",children:[Nn.commands.manager&&r.jsxs("div",{className:"doc-command-row",children:[r.jsx("span",{children:Nn.commands.manager}),r.jsx("code",{children:zr}),r.jsx("button",{className:"icon-button",onClick:()=>Bd(zr),title:"Copy command",type:"button",children:r.jsx(Fm,{size:16})})]}),r.jsxs("div",{className:"doc-command-row",children:[r.jsx("span",{children:Nn.commands.primary}),r.jsx("code",{children:wi}),r.jsx("button",{className:"icon-button",onClick:()=>Bd(wi),title:"Copy command",type:"button",children:r.jsx(Fm,{size:16})})]}),r.jsxs("div",{className:"doc-command-row",children:[r.jsx("span",{children:Nn.commands.slot}),r.jsx("code",{children:`${wi} 3`}),r.jsx("button",{className:"icon-button",onClick:()=>Bd(`${wi} 3`),title:"Copy command",type:"button",children:r.jsx(Fm,{size:16})})]})]})]})]}):Go?r.jsx(hb,{hubUrl:M,devices:k}):Ji?r.jsx(CM,{hubUrl:M,devices:at,localFeatureAccess:Wt,upgradeNotice:Tr,remoteDirectory:ht,onRemoteDirectoryChange:cn,targetMode:La,selectedTargetIds:gs,onTargetModeChange:Ul,onTargetToggle:y=>Fo(N=>N.includes(y)?N.filter(_=>_!==y):[...N,y]),filePickerRef:mf,folderPickerRef:hf,onBrowserFileImport:y=>{to(y)},onBrowserFolderImport:y=>{to(y)},onNotice:Pe,onTransferChange:y=>{if(!y){Ds(null);return}const N=y.state==="failed"||y.state==="partially-failed"||y.state==="cancelled"?"failed":y.state==="completed"?"queued":y.state==="sending"?"sending":"reading";Ds({id:y.id,status:N,progress:y.progress,message:y.message}),["completed","partially-failed","failed","cancelled"].includes(y.state)&&window.setTimeout(()=>Ds(_=>_?.id===y.id?null:_),4500)}}):hu?r.jsx(ax,{session:n,currentProductKey:"livedesk",appVersion:"",onSignIn:rs}):Vl?r.jsx($1,{hubUrl:M,runtimeRole:w?.runtimeRole||"hub",runtimeDeviceId:w?.deviceId||"",sessionAccessToken:n?.access_token||"",pin:r.jsxs(P1,{children:[r.jsx("div",{className:"settings-section-grid",children:r.jsxs("section",{className:"settings-section-card settings-pairing-card",children:[r.jsxs("div",{className:"settings-section-heading",children:[r.jsxs("div",{children:[r.jsx("span",{children:"Pair a Client"}),r.jsx("h3",{children:"Connect another local PC"})]}),r.jsx(ha,{size:20})]}),r.jsxs("div",{className:"settings-pin-display",children:[r.jsx("span",{children:"Hub PIN"}),r.jsx("strong",{children:io}),r.jsxs("button",{className:"text-button ghost compact",onClick:zs,disabled:!w?.started||ve==="pin",type:"button",children:[r.jsx(ti,{size:15}),ve==="pin"?"Creating":"New PIN"]})]})]})}),r.jsxs("section",{className:"settings-section-card",children:[r.jsxs("div",{className:"settings-section-heading",children:[r.jsxs("div",{children:[r.jsx("span",{children:"File transfer"}),r.jsx("h3",{children:"Client destination folder"})]}),r.jsx(To,{size:20})]}),r.jsxs("label",{className:"settings-field",children:[r.jsx("span",{children:"Remote folder"}),r.jsx("input",{value:ht,onChange:y=>cn(y.target.value),placeholder:"Desktop/LiveDeskFiles"})]}),r.jsx("p",{className:"settings-help",children:"Blank uses Desktop/LiveDeskFiles on each Client."})]})]}),agent:r.jsx(q1,{hubUrl:M}),captureGallery:r.jsx(kN,{hubUrl:M,onToast:Pe})}):qo?r.jsxs("section",{className:"pricing-page",children:[r.jsxs("header",{className:"pricing-header",children:[r.jsxs("div",{children:[r.jsxs("span",{className:"pricing-eyebrow",children:[r.jsx(Sh,{size:15})," LiveDesk plans"]}),r.jsx("h2",{children:"Choose the plan that fits your wall"}),r.jsx("p",{children:"Start free with a local screen wall. Upgrade when you need ad-free monitoring, more Clients, or commercial controls."})]}),r.jsx("button",{className:"pricing-close",onClick:()=>lo("wall"),title:"Close pricing",type:"button",children:r.jsx(ma,{size:18})})]}),r.jsxs("div",{className:"pricing-status","aria-live":"polite",children:[r.jsx("span",{children:"Current plan"}),r.jsx("strong",{children:hi.name}),r.jsxs("small",{children:[at.length,"/",hT(Wl)," connected devices"]})]}),r.jsxs("div",{className:"pricing-cards","aria-label":"LiveDesk plans",children:[r.jsxs("article",{className:`pricing-plan-card ${Gt==="free"?"current":""}`,children:[r.jsxs("div",{className:"pricing-plan-topline",children:[r.jsx("span",{className:"pricing-plan-badge free",children:"Free"}),r.jsx("strong",{children:"$0"})]}),r.jsx("p",{className:"pricing-plan-audience",children:Al.free.audience}),r.jsx("h3",{children:"Monitor your local wall"}),r.jsx("p",{children:Al.free.note}),r.jsxs("ul",{children:[r.jsxs("li",{children:[r.jsx(Bn,{size:16})," Up to ",Gc," devices"]}),r.jsxs("li",{children:[r.jsx(Bn,{size:16})," Live view and local control"]}),r.jsxs("li",{children:[r.jsx(Bn,{size:16})," Standard ad-supported banner"]})]}),r.jsx("span",{className:"pricing-current-label",children:Gt==="free"?"Current plan":"Included for everyone"})]}),r.jsxs("article",{className:`pricing-plan-card featured ${Gt==="ltd"?"current":""}`,children:[r.jsxs("div",{className:"pricing-plan-topline",children:[r.jsxs("span",{className:"pricing-plan-badge plus",children:[r.jsx(S0,{size:14})," Plus"]}),r.jsx("strong",{children:"$5"})]}),r.jsx("p",{className:"pricing-plan-audience",children:Al.ltd.audience}),r.jsx("h3",{children:"Ad-free monitoring"}),r.jsx("p",{children:"More room for personal AI Clients without the standard banner."}),r.jsxs("ul",{children:[r.jsxs("li",{children:[r.jsx(Bn,{size:16})," Up to ",Vc," devices"]}),r.jsxs("li",{children:[r.jsx(Bn,{size:16})," No standard ad banner"]}),r.jsxs("li",{children:[r.jsx(Bn,{size:16})," ",Al.ltd.ltd]})]}),r.jsxs("button",{className:"pricing-plan-button",onClick:n?()=>Ir("ltd"):rs,disabled:Gt!=="free"||ve==="checkout"||ve==="auth"||a||R,type:"button",children:[r.jsx(Kc,{size:16}),Gt==="free"?n?"Choose Plus":"Sign in for Plus":"Plus active"]})]}),r.jsxs("article",{className:`pricing-plan-card pro ${Gt==="pro"?"current":""}`,children:[r.jsxs("div",{className:"pricing-plan-topline",children:[r.jsxs("span",{className:"pricing-plan-badge pro",children:[r.jsx(Dn,{size:14})," Pro"]}),r.jsx("strong",{children:"$15"})]}),r.jsx("p",{className:"pricing-plan-audience",children:Al.pro.audience}),r.jsx("h3",{children:"For serious operations"}),r.jsx("p",{children:"Commercial-ready monitoring for larger AI labs, studios, and teams."}),r.jsxs("ul",{children:[r.jsxs("li",{children:[r.jsx(Bn,{size:16})," 30+ Client walls"]}),r.jsxs("li",{children:[r.jsx(Bn,{size:16})," Commercial use included"]}),r.jsxs("li",{children:[r.jsx(Bn,{size:16})," ",Al.pro.ltd]})]}),r.jsxs("button",{className:"pricing-plan-button",onClick:n?()=>Ir("pro"):rs,disabled:Gt==="pro"||ve==="checkout"||ve==="auth"||a||R,type:"button",children:[r.jsx(Kc,{size:16}),Gt==="pro"?"Pro active":n?"Choose Pro":"Sign in for Pro"]})]})]}),r.jsxs("section",{className:"pricing-includes",children:[r.jsxs("div",{children:[r.jsx(ha,{size:17}),r.jsx("span",{children:"Screen wall"}),r.jsx("strong",{children:"120+ slots"})]}),r.jsxs("div",{children:[r.jsx(Ch,{size:17}),r.jsx("span",{children:"Focused control"}),r.jsx("strong",{children:"Local-first"})]}),r.jsxs("div",{children:[r.jsx(ny,{size:17}),r.jsx("span",{children:"Remote audio"}),r.jsx("strong",{children:"Available"})]}),r.jsxs("div",{children:[r.jsx(To,{size:17}),r.jsx("span",{children:"File transfer"}),r.jsx("strong",{children:"Included"})]})]}),r.jsxs("p",{className:"pricing-note",children:[r.jsx(Bn,{size:15})," Commercial use requires Pro. Plus LTD is for personal, creator, and homelab use."]}),r.jsxs("button",{className:"pricing-primary-cta",onClick:n?()=>Ir(Gt==="ltd"?"pro":"ltd"):rs,disabled:mc,type:"button",children:[r.jsx(Kc,{size:18}),xl]})]}):r.jsx(r.Fragment,{children:r.jsxs("section",{className:`monitor-layout ${pa?"dock-collapsed":""}`,children:[r.jsxs("section",{ref:Bo,className:`screen-wall-panel ${Pl?"drop-active":""}`,onClick:y=>{y.currentTarget===y.target&&Ss()},onDragEnter:y=>{y.preventDefault(),ui(!0)},onDragOver:y=>{y.preventDefault(),y.dataTransfer.dropEffect="copy"},onDragLeave:y=>{y.currentTarget===y.target&&ui(!1)},onDrop:gl,children:[bn.length>0&&r.jsx("canvas",{ref:nc,className:"atlas-source-canvas",width:_s?.width||1920,height:_s?.height||1080,"aria-hidden":"true"}),ct?r.jsx("section",{className:"control-focus",children:r.jsxs("div",{className:"control-wall",children:[Fe&&dc.length>1&&r.jsx("div",{className:"control-monitor-tabs","aria-label":"Select monitor",children:dc.map(y=>r.jsxs("button",{className:y.index===no?"active":"",onClick:()=>Mn(Fe.deviceId,y.index),title:ET(y),type:"button",children:[r.jsx("span",{children:y.name||`Display ${y.index+1}`}),y.width>0&&y.height>0&&r.jsxs("small",{children:[y.width,"x",y.height]})]},y.index))}),Fe?r.jsx(bh,{device:Fe,frame:Le,selected:!1,controlEnabled:ys&&Ce&&Ca,showStatus:an.showScreenStatus,monitorIndex:ea(ee[Fe.deviceId],Jn(Fe)),monitorCount:Jn(Fe),onSelect:()=>{},onFocusDevice:()=>Z(Fe.deviceId),onOpenControl:()=>ir(Fe.deviceId),onCycleMonitor:y=>Os(Fe.deviceId,y),onCanvasReady:Su,sendInput:yt,presentationReady:Cr,remoteDiagnostics:Ee?Hs.get(Fe.deviceId):st?Ci.get(Fe.deviceId):void 0,diagnosticsMode:Ee?"debug":"remote"},Fe.deviceId):r.jsxs("div",{className:"control-empty",children:[r.jsx(ha,{size:30}),r.jsx("strong",{children:"No Client"}),r.jsx("span",{children:"Open a Client from the Wall."})]}),Fe&&Yo&&r.jsxs("div",{className:"control-connection-overlay",role:"alert",children:[r.jsx(Dn,{size:22,"aria-hidden":"true"}),r.jsx("strong",{children:"Accessibility permission required"}),r.jsx("small",{children:"On the Mac, allow Accessibility for the terminal app that starts LiveDesk, then restart the Mac client."})]}),Fe&&!Yo&&!Cr&&r.jsxs("div",{className:"control-connection-overlay",role:"status","aria-live":"polite",children:[Y!=="error"&&r.jsx("span",{className:"control-connection-spinner","aria-hidden":"true"}),r.jsx("strong",{children:Y==="error"?"Connection failed":"Connecting"}),r.jsx("small",{children:Y==="error"?"See the notification for details. Reopen Control to retry.":yi||fe?"Control stream is recovering. See the notification for details.":tr==="open"?"Preparing the first control frame":"Opening the Control stream"})]})]})}):ze==="list"?r.jsxs("div",{className:"wall-device-list",role:"grid","aria-label":"Computer status list",children:[r.jsxs("div",{className:"wall-device-list-head",role:"row",children:[r.jsx("span",{role:"columnheader",children:"Computer"}),r.jsx("span",{role:"columnheader",children:"Status"}),r.jsx("span",{role:"columnheader",children:"CPU"}),r.jsx("span",{role:"columnheader",children:"GPU"}),r.jsx("span",{role:"columnheader",children:"RAM"}),r.jsx("span",{role:"columnheader",children:"VRAM"}),r.jsx("span",{role:"columnheader",children:"Network"}),r.jsx("span",{role:"columnheader",children:"HDD"}),r.jsx("span",{role:"columnheader",children:"Temp"})]}),vu.length>0?vu.map(({device:y,slotNumber:N})=>r.jsx(tE,{device:y,slotNumber:N,selected:es.has(y.deviceId),updateIndicator:la(y.deviceId),onSelect:()=>hl(y.deviceId),onOpenControl:()=>ir(y.deviceId)},y.deviceId)):r.jsxs("div",{className:"wall-device-list-empty",children:[r.jsx(ha,{size:24,"aria-hidden":"true"}),r.jsx("strong",{children:"No connected computers"})]})]}):Ct?r.jsx("div",{className:`screen-wall atlas-slot-wall size-${ze}`,onClick:y=>{y.currentTarget===y.target&&Ss()},children:Mr.map((y,N)=>y?r.jsx(bh,{device:y,frame:xs.get(y.deviceId),selected:es.has(y.deviceId),controlEnabled:!1,showStatus:Ze.showScreenStatus,monitorIndex:ea(ee[y.deviceId],Jn(y)),monitorCount:Jn(y),onSelect:()=>hl(y.deviceId),onFocusDevice:()=>ml(y.deviceId),onOpenControl:()=>ir(y.deviceId),onCycleMonitor:_=>Os(y.deviceId,_),onCanvasReady:Pc({mode4Enabled:Ct,directMode4WallEnabled:Kt,isMac:ei(y.platform)})==="direct"?Jo:Zl,sendInput:yt,updateIndicator:la(y.deviceId),presentationReady:Ze.autoStartLive&&ts.includes(y.deviceId)?yn.has(y.deviceId):void 0,showPresentationSpinner:Ze.autoStartLive&&ts.includes(y.deviceId),remoteDiagnostics:Ee?Hs.get(y.deviceId):st?Ci.get(y.deviceId):void 0,diagnosticsMode:Ee?"debug":"remote"},y.deviceId):r.jsx(Pb,{index:N},`slot-${N}`))}):r.jsx("div",{className:`screen-wall size-${ze}`,onClick:y=>{y.currentTarget===y.target&&Ss()},children:Mr.map((y,N)=>y?r.jsx(bh,{device:y,frame:xs.get(y.deviceId),selected:es.has(y.deviceId),controlEnabled:ys&&Ce&&Ca&&Fe?.deviceId===y.deviceId,showStatus:Ze.showScreenStatus,monitorIndex:ea(ee[y.deviceId],Jn(y)),monitorCount:Jn(y),onSelect:()=>hl(y.deviceId),onFocusDevice:()=>ml(y.deviceId),onOpenControl:()=>ir(y.deviceId),onCycleMonitor:_=>Os(y.deviceId,_),onCanvasReady:Jo,sendInput:yt,updateIndicator:la(y.deviceId),presentationReady:Ze.autoStartLive&&ts.includes(y.deviceId)?yn.has(y.deviceId):void 0,showPresentationSpinner:Ze.autoStartLive&&ts.includes(y.deviceId),remoteDiagnostics:Ee?Hs.get(y.deviceId):st?Ci.get(y.deviceId):void 0,diagnosticsMode:Ee?"debug":"remote"},y.deviceId):r.jsx(Pb,{index:N},`slot-${N}`))}),r.jsx(sE,{visible:xu&&!ct})]}),!Vl&&r.jsxs("aside",{className:`right-dock actions-mode ${pa?"collapsed":""}`,"aria-label":"LiveDesk tools",children:[r.jsx("button",{className:"dock-toggle",onClick:()=>fs(y=>!y),"aria-label":pa?"Show control panel":"Hide control panel",title:pa?"Show control panel":"Hide control panel",type:"button",children:pa?r.jsx(tx,{size:18}):r.jsx(Xh,{size:18})}),!pa&&r.jsxs(r.Fragment,{children:[r.jsxs("section",{className:"action-dock",children:[r.jsx("input",{ref:fu,className:"hidden-file-input",type:"file",multiple:!0,onChange:y=>{y.currentTarget.files&&tn(y.currentTarget.files),y.currentTarget.value=""}}),r.jsxs("div",{className:"manual-action-bar",role:"toolbar","aria-label":"Manual Client controls",children:[r.jsx(Ao,{label:"Control",tooltip:Fe?Yo?"Allow Accessibility for the terminal app that starts LiveDesk, then restart the Mac client.":ys?Ca?"Keyboard and mouse control is ready":"Waiting for the current Control capture session":"Remote control is not available on this Client":"Select a Client to use Control",onClick:()=>{if(Fe){if(ct&&Ce){De(!1),Pe("Remote control paused. Screen viewing stays on.");return}ir(Fe.deviceId)}},disabled:!ys||!!Be,active:Ce,pressed:ct&&Ce,children:r.jsx(Ch,{size:17})}),r.jsx(Ao,{label:"Audio",tooltip:Fe?tl?He?"Stop hearing this Client":"Hear this Client":"Audio is not available on this Client":"Select a Client to use Audio",onClick:()=>{ju()},disabled:!Xo||!!Be,active:He,pressed:He,children:Be==="audio"?r.jsx(ni,{className:"spin",size:17}):r.jsx(ny,{size:17})}),r.jsx(Ao,{label:"Send files",tooltip:ya.length===0&&!ct?"Select one or more Clients to send files":Fe?"Choose files and send them to the selected Client":"Select a Client to send files",onClick:()=>{Pe(`Choose files to send to ${Tu}.`),fu.current?.click()},disabled:!!Be||!Wt||(ct?!Fe:ya.length===0),children:r.jsx(To,{size:17})}),r.jsx(Ao,{label:"Restart",tooltip:Fe?Ko?"Restart Client":"Restart is not available on this Client":"Select a Client to restart",onClick:()=>{cc("restart")},disabled:!Fe||!Wt||!!Be||!Ko,children:Be==="restart"?r.jsx(ni,{className:"spin",size:17}):r.jsx(Wh,{size:17})}),r.jsx(Ao,{label:"Sleep",tooltip:Fe?"Put Client to sleep":"Select a Client to put to sleep",onClick:()=>{cc("sleep")},disabled:!Fe||!Wt||!!Be,children:Be==="sleep"?r.jsx(ni,{className:"spin",size:17}):r.jsx(C0,{size:17})}),r.jsx(Ao,{label:"Shut down",tooltip:Fe?"Shut down Client":"Select a Client to shut down",onClick:()=>{cc("shutdown")},disabled:!Fe||!Wt||!!Be,danger:!0,children:Be==="shutdown"?r.jsx(ni,{className:"spin",size:17}):r.jsx(k0,{size:17})})]})]}),r.jsx(hb,{hubUrl:M,devices:k,variant:"dock",selectedDeviceIds:ur})]})]})]})})]}),ps&&r.jsxs("aside",{className:`transfer-bar state-${ps.status}`,"aria-live":"polite",children:[r.jsx("span",{className:"transfer-bar-icon","aria-hidden":"true",children:ps.status==="queued"?r.jsx(Bn,{size:18}):ps.status==="failed"?r.jsx(ma,{size:18}):r.jsx(Qb,{size:18})}),r.jsxs("div",{className:"transfer-bar-copy",children:[r.jsx("p",{children:ps.message}),r.jsx("div",{className:"transfer-bar-track",children:r.jsx("span",{style:{width:`${Math.max(0,Math.min(100,ps.progress))}%`}})})]}),r.jsxs("strong",{children:[Math.max(0,Math.min(100,ps.progress)),"%"]})]}),ke&&r.jsxs("aside",{className:`livedesk-toast tone-${ke.tone}`,role:ke.tone==="error"?"alert":"status","aria-live":ke.tone==="error"?"assertive":"polite","aria-busy":ke.tone==="progress",children:[ke.tone==="progress"&&r.jsx("span",{className:"livedesk-toast-spinner","aria-hidden":"true"}),r.jsx("span",{children:ke.message}),r.jsx("button",{className:"livedesk-toast-close",onClick:()=>Pe(""),type:"button","aria-label":"Dismiss notification",title:"Dismiss notification",children:r.jsx(ma,{size:15})})]})]})}function iE(){if(typeof window>"u")return[];const n=window.location.origin;return window.location.port==="5179"?[n]:["http://127.0.0.1:5179"]}function lE(){const[a,i]=u.useState(null);return u.useEffect(()=>{let l=!1,c=!1;const d=async()=>{for(const g of iE())try{const v=await fetch(`${g}/api/runtime`,{cache:"no-store"});if(!v.ok)continue;const b=(await v.json()).role==="hub"?"hub":"client";c=!0,l||i(b);return}catch{}};d();const m=window.setInterval(()=>{d()},1e3),p=window.setTimeout(()=>{!l&&!c&&i("client")},2500);return()=>{l=!0,window.clearInterval(m),window.clearTimeout(p)}},[null]),a?a==="client"?r.jsx(RC,{}):r.jsx(rE,{}):r.jsxs("main",{className:"runtime-role-boot","aria-busy":"true","aria-label":"Starting LiveDesk",children:[r.jsx("span",{className:"runtime-role-boot-mark","aria-hidden":"true"}),r.jsx("strong",{children:"Starting LiveDesk"}),r.jsx("p",{children:"Loading this computer's assigned role..."})]})}D0.createRoot(document.getElementById("root")).render(r.jsx(u.StrictMode,{children:r.jsx(lE,{})}));
|