livedesk 0.1.652 → 0.1.653

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.
@@ -1,2 +1,2 @@
1
- import{j as e}from"./styles-CDIlHYlj.js";import{a as c,ab as X,k as T,Y as xe,aI as Ke,az as ye,X as Q,o as be,l as ee,aJ as je,Z as V,$ as Xe,aK as et,aL as ve,aM as _e,aN as tt,D as st,b as at,aO as it,ax as nt,aP as rt,aQ as ot}from"./icons-CacEbnZu.js";import{s as u}from"./App-smTkH_7F.js";import"./react-Cfedt3N5.js";import"./supabase-C7qjtLN3.js";import"./frame-lifecycle-esOdP-EY.js";const ct=new Set(["browserLanguage","platform","online","viewport","timezone","screen"]),lt={id:"browser-safe-v1",collect(){if(typeof navigator>"u")return{};const s={browserLanguage:navigator.language||"",platform:navigator.platform||"",online:navigator.onLine,timezone:typeof Intl>"u"?"":Intl.DateTimeFormat().resolvedOptions().timeZone||"",viewport:typeof window>"u"?void 0:{width:window.innerWidth,height:window.innerHeight},screen:typeof window>"u"?void 0:{width:window.screen.width,height:window.screen.height}};return Object.fromEntries(Object.entries(s).filter(([i,n])=>ct.has(i)&&n!==void 0))}};function dt(s,i="Support request failed."){if(s&&typeof s=="object"&&"message"in s){const n=String(s.message||"").trim();if(n)return n.replace(/^.*Support request failed:\s*/i,"")}return s instanceof Error?s.message:i}function pt(s=lt){return s.collect()}async function ut(s,i="livedesk"){const{data:n,error:d}=await u.from("app_admins").select("role").eq("user_id",s).limit(1);if(d)throw d;const o=n?.[0]?.role||"user";if(o!=="owner"){const{error:x}=await u.rpc("register_product_access",{p_product_key:i,p_access_type:"free"});if(x)throw x}const[y,m,P]=await Promise.all([u.from("products").select("id, product_key, name, description, sort_order").order("sort_order"),u.from("product_admins").select("product_id, role").eq("user_id",s),u.from("user_products").select("product_id").eq("user_id",s).eq("is_active",!0)]);if(y.error)throw y.error;if(m.error)throw m.error;if(P.error)throw P.error;const j=y.data||[],z=(m.data||[]).map(x=>String(x.product_id)),L=(P.data||[]).map(x=>String(x.product_id)),S=o==="owner",k=S?j.map(x=>x.id):[...new Set([...z,...L])];return{products:j,access:{userId:s,globalRole:o,accessibleProductIds:k,managedProductIds:S?j.map(x=>x.id):z,canViewAllProducts:S,canViewAllPrivatePosts:S,canManageAllProducts:S,canCreateAnnouncements:S||z.length>0}}}async function ht(s=""){const{data:i,error:n}=await u.rpc("search_support_posts",{p_query:s.trim()||null,p_limit:250});if(n)throw n;return i||[]}async function mt(s){const{data:i,error:n}=await u.from("support_attachments").select("*").eq("post_id",s).order("created_at");if(n)throw n;const d=i||[];return Promise.all(d.map(async o=>{const y=await u.storage.from("support-attachments").createSignedUrl(o.storage_path,3600);return{...o,signed_url:y.data?.signedUrl||void 0}}))}async function ft(){const{data:s,error:i}=await u.from("support_notifications").select("*").order("created_at",{ascending:!1}).limit(30);if(i)throw i;return s||[]}async function xt(s){const{error:i}=await u.rpc("mark_support_notification_read",{p_notification_id:s});if(i)throw i}async function ge(s){const{data:i,error:n}=await u.from("support_comments").select("*").eq("post_id",s).order("created_at");if(n)throw n;return i||[]}async function yt(s){const{data:i,error:n}=await u.from("support_votes").select("post_id").eq("user_id",s);if(n)throw n;return new Set((i||[]).map(d=>String(d.post_id)))}async function bt(s){const{data:i,error:n}=await u.rpc("create_support_post",{p_product_id:s.productId,p_post_type:s.postType,p_title:s.title,p_body:s.body,p_visibility:s.visibility,p_app_version:s.appVersion||null,p_platform:typeof navigator>"u"?null:navigator.platform||null,p_os_version:typeof navigator>"u"?null:navigator.platform||null,p_environment:s.includeEnvironment?pt():{}});if(n)throw n;const d=Array.isArray(i)?i[0]?.post_id:i?.post_id;if(!d)throw new Error("Support post was not created.");return String(d)}const jt=new Set(["image/png","image/jpeg","image/webp","text/plain","text/markdown","application/json","application/pdf"]);async function vt(s,i){if(!jt.has(i.type))throw new Error("This file type is not allowed.");if(i.size<=0||i.size>10*1024*1024)throw new Error("Attachments must be smaller than 10 MB.");const n=i.name.replace(/[^a-zA-Z0-9._-]+/g,"-").replace(/^-+|-+$/g,"")||"attachment",d=typeof crypto<"u"&&"randomUUID"in crypto?crypto.randomUUID():`${Date.now()}-${Math.random().toString(16).slice(2)}`,o=`${s}/${d}-${n}`,y=await u.storage.from("support-attachments").upload(o,i,{contentType:i.type,upsert:!1});if(y.error)throw y.error;const{data:m,error:P}=await u.rpc("add_support_attachment",{p_post_id:s,p_storage_path:o,p_file_name:n,p_mime_type:i.type,p_size_bytes:i.size});if(P)throw await u.storage.from("support-attachments").remove([o]),P;const j=Array.isArray(m)?m[0]?.attachment_id:m?.attachment_id;if(!j)throw new Error("Attachment metadata was not created.");return String(j)}async function _t(s,i,n,d){const{error:o}=await u.rpc("update_support_post",{p_post_id:s,p_title:i,p_body:n,p_visibility:d});if(o)throw o}async function gt(s,i){const{error:n}=await u.rpc("add_support_comment",{p_post_id:s,p_body:i});if(n)throw n}async function wt(s){const{data:i,error:n}=await u.rpc("toggle_support_vote",{p_post_id:s});if(n)throw n;const d=Array.isArray(i)?i[0]:i;return{voted:!!d?.voted,totalVotes:Number(d?.total_votes||0)}}async function St(s,i){const{error:n}=await u.rpc("change_support_post_status",{p_post_id:s,p_target_status:i});if(n)throw n}async function Nt(s){const{error:i}=await u.rpc("set_support_post_hidden",{p_post_id:s,p_hidden:!0});if(i)throw i}async function Pt(s,i){const{error:n}=await u.rpc("set_support_post_locked",{p_post_id:s,p_locked:i});if(n)throw n}async function kt(s,i){const{error:n}=await u.rpc("set_support_post_pinned",{p_post_id:s,p_pinned:i});if(n)throw n}async function Ct(s,i){const{error:n}=await u.rpc("mark_support_duplicate",{p_post_id:s,p_duplicate_of_post_id:i});if(n)throw n}async function At(s,i){const{error:n}=await u.rpc("accept_support_answer",{p_post_id:s,p_comment_id:i});if(n)throw n}async function qt(s,i=!0){const{error:n}=await u.rpc("hide_support_comment",{p_comment_id:s,p_hidden:i});if(n)throw n}const ae=[{id:"question",label:"Questions",shortLabel:"Question"},{id:"issue",label:"Issues",shortLabel:"Issue"},{id:"feature",label:"Feature Requests",shortLabel:"Feature"},{id:"announcement",label:"Announcements",shortLabel:"Announcement"}],zt=[{id:"all",label:"All statuses"},{id:"needs-response",label:"Needs response"},{id:"open",label:"Open"},{id:"needs-info",label:"Needs more information"},{id:"in-progress",label:"In progress"},{id:"planned",label:"Planned"},{id:"released",label:"Released"},{id:"closed",label:"Closed"}],Et={question:["open","answered","resolved","closed"],issue:["open","needs-info","confirmed","in-progress","fixed","closed"],feature:["under-review","planned","in-progress","released","declined","closed"],announcement:["published","archived"]},Tt={type:"all",product:"",status:"all",visibility:"all",sort:"recent",query:""};function te(){if(typeof window>"u")return Tt;const s=new URLSearchParams(window.location.search),i=window.location.pathname.match(/^\/support\/(questions|issues|features|announcements)$/)?.[1],n=s.get("type")||i||"",d=n==="questions"||n==="question"?"question":n==="issues"||n==="issue"?"issue":n==="features"||n==="feature"?"feature":n==="announcements"||n==="announcement"?"announcement":"all",o=s.get("visibility"),y=s.get("sort");return{type:d,product:s.get("product")||"",status:s.get("status")||"all",visibility:o==="public"||o==="private"?o:"all",sort:y==="votes"||y==="newest"?y:"recent",query:s.get("q")||""}}function It(s){return s==="all"?"":s==="question"?"questions":s==="issue"?"issues":s==="feature"?"features":"announcements"}function Dt(s,i){const n={...s,...i},d=new URLSearchParams;n.product&&d.set("product",n.product),n.status!=="all"&&d.set("status",n.status),n.visibility!=="all"&&d.set("visibility",n.visibility),n.sort!=="recent"&&d.set("sort",n.sort),n.query&&d.set("q",n.query);const o=It(n.type),y=d.toString();return`/support${o?`/${o}`:""}${y?`?${y}`:""}`}function G(s){const i=new Date(s);return Number.isFinite(i.getTime())?i.toLocaleString(void 0,{dateStyle:"medium",timeStyle:"short"}):s}function se(s){return s.replaceAll("-"," ").replace(/\b\w/g,i=>i.toUpperCase())}function we(s){return s==="question"?it:s==="issue"?nt:s==="feature"?rt:ot}function Se(s){return ae.find(i=>i.id===s)?.shortLabel||s}function b(s){const i=dt(s);return/relation .* does not exist|function .* does not exist|schema cache/i.test(i)?"Support Board database is not installed yet. Apply the latest Supabase migration first.":/not-authenticated|jwt/i.test(i)?"Sign in again to use Support.":/product-access-denied|product-not-found/i.test(i)?"You do not have access to this product.":i}function Bt({session:s,currentProductKey:i="livedesk",appVersion:n="",onSignIn:d}){const[o,y]=c.useState(()=>te()),[m,P]=c.useState(null),[j,z]=c.useState([]),[L,S]=c.useState([]),[k,x]=c.useState(""),[R,U]=c.useState([]),[$,ie]=c.useState([]),[M,W]=c.useState([]),[ne,re]=c.useState(()=>new Set),[Z,J]=c.useState(!0),[v,f]=c.useState(""),[oe,p]=c.useState(""),[Ne,O]=c.useState(!1),[Pe,B]=c.useState(!1),[F,ce]=c.useState(""),[le,K]=c.useState(()=>te().query),[ke,de]=c.useState(!1),[Ce,Ae]=c.useState(!1),[qe,ze]=c.useState(""),[pe,Ee]=c.useState(""),[Te,Ie]=c.useState("free"),[De,Ve]=c.useState(""),[ue,Le]=c.useState(""),[Re,Ue]=c.useState("admin"),[I,he]=c.useState(""),[me,Vt]=c.useState(""),[Y,fe]=c.useState([]),[h,A]=c.useState({productId:"",postType:"question",title:"",body:"",visibility:"public",includeEnvironment:!0,files:[]}),N=c.useCallback(t=>{const r={...o,...t};y(r),typeof window<"u"&&window.history.pushState({},"",Dt(o,t))},[o]);c.useEffect(()=>{const t=()=>y(te());return window.addEventListener("popstate",t),()=>window.removeEventListener("popstate",t)},[]);const _=c.useCallback(async()=>{const t=await ht(o.query);return S(t),s?.user.id&&re(await yt(s.user.id)),t},[o.query,s?.user.id]),H=c.useCallback(async()=>{if(!s?.user.id){P(null),z([]),S([]),x(""),W([]),J(!1);return}J(!0),p("");try{const t=await ut(s.user.id,i);P(t.access),z(t.products),await _(),W(await ft())}catch(t){p(b(t))}finally{J(!1)}},[i,_,s?.user.id]);c.useEffect(()=>{H()},[H]);const g=c.useMemo(()=>{if(!m)return[];const t=j.find(r=>r.product_key===i);return t?m.canViewAllProducts||m.accessibleProductIds.includes(t.id)?[t]:[]:[]},[m,i,j]),D=g[0]?.id||"",q=c.useMemo(()=>{const t=new Map(j.map(l=>[l.id,l.product_key]));return[...L.filter(l=>o.type!=="all"&&l.post_type!==o.type||!D||l.product_id!==D||o.status==="needs-response"&&!["open","needs-info"].includes(l.status)||o.status!=="all"&&o.status!=="needs-response"&&l.status!==o.status||o.visibility!=="all"&&l.visibility!==o.visibility?!1:t.has(l.product_id))].sort((l,w)=>o.sort==="votes"?w.vote_count-l.vote_count||w.updated_at.localeCompare(l.updated_at):o.sort==="newest"?w.created_at.localeCompare(l.created_at):Number(w.is_pinned)-Number(l.is_pinned)||w.updated_at.localeCompare(l.updated_at))},[L,j,o.sort,o.status,o.type,o.visibility,D]),a=q.find(t=>t.id===k)||q[0]||null,E=!!(a&&m&&(m.canViewAllProducts||m.managedProductIds.includes(a.product_id))),$e=!!(a&&s?.user.id===a.author_id)||E;c.useEffect(()=>{if(!a){x(""),U([]),B(!1);return}a.id!==k&&x(a.id)},[a,k]),c.useEffect(()=>{let t=!0;return k?(ge(k).then(r=>{t&&U(r)}).catch(r=>{t&&p(b(r))}),mt(k).then(r=>{t&&ie(r)}).catch(r=>{t&&p(b(r))}),()=>{t=!1}):(U([]),ie([]),()=>{t=!1})},[k]),c.useEffect(()=>{!h.productId&&g[0]&&A(t=>({...t,productId:D||g[0].id}))},[g,h.productId,D]),c.useEffect(()=>{K(o.query)},[o.query]),c.useEffect(()=>{!pe&&g[0]&&Ee(g[0].id),!ue&&g[0]&&Le(g[0].id)},[g,pe,ue]);const C=t=>{a&&S(r=>r.map(l=>l.id===a.id?{...l,...t}:l))},Me=async t=>{if(t.preventDefault(),!(!s||!h.productId)){f("create"),p("");try{const r=await bt({productId:h.productId,postType:h.postType,title:h.title,body:h.body,visibility:h.postType==="announcement"?"public":h.visibility,appVersion:n,includeEnvironment:h.postType==="issue"&&h.includeEnvironment});for(const w of Y)await vt(r,w);const l=await _();O(!1),fe([]),A(w=>({...w,title:"",body:"",files:[]})),x(r),l.some(w=>w.id===r)||await H()}catch(r){p(b(r))}finally{f("")}}},Oe=async t=>{if(t.preventDefault(),!!a){f("edit"),p("");try{await _t(a.id,a.title,a.body,a.visibility),B(!1),await _()}catch(r){p(b(r))}finally{f("")}}},Be=async t=>{if(t.preventDefault(),!(!a||!F.trim())){f("comment"),p("");try{await gt(a.id,F),ce(""),U(await ge(a.id)),await _()}catch(r){p(b(r))}finally{f("")}}},Fe=async()=>{if(!(!a||a.post_type!=="feature")){f("vote"),p("");try{const t=await wt(a.id);re(r=>{const l=new Set(r);return t.voted?l.add(a.id):l.delete(a.id),l}),C({vote_count:t.totalVotes})}catch(t){p(b(t))}finally{f("")}}},Ye=async t=>{if(a){f("status"),p("");try{await St(a.id,t),C({status:t}),await _()}catch(r){p(b(r))}finally{f("")}}},He=async()=>{if(!(!a||!window.confirm("Hide this Support post from the board?"))){f("hide"),p("");try{await Nt(a.id),x(""),await _()}catch(t){p(b(t))}finally{f("")}}},Qe=async()=>{if(a){f("lock"),p("");try{await Pt(a.id,!a.is_locked),C({is_locked:!a.is_locked}),await _()}catch(t){p(b(t))}finally{f("")}}},Ge=async()=>{if(a){f("pin"),p("");try{await kt(a.id,!a.is_pinned),C({is_pinned:!a.is_pinned}),await _()}catch(t){p(b(t))}finally{f("")}}},We=async t=>{if(a){f(`accept-${t}`),p("");try{await At(a.id,t),C({accepted_comment_id:t,status:a.post_type==="question"?"answered":a.status}),await _()}catch(r){p(b(r))}finally{f("")}}},Ze=async()=>{if(!(!a||!I||I===a.id)){f("duplicate"),p("");try{await Ct(a.id,I),x(""),he(""),await _()}catch(t){p(b(t))}finally{f("")}}},Je=async t=>{if(!t.read_at)try{await xt(t.id),W(r=>r.map(l=>l.id===t.id?{...l,read_at:new Date().toISOString()}:l))}catch(r){p(b(r))}t.post_id&&x(t.post_id),de(!1)};return s?Z&&!m?e.jsxs("section",{className:"support-page support-loading",children:[e.jsx(xe,{className:"support-spin",size:23})," Loading Support Board..."]}):e.jsxs("section",{className:"support-page",children:[e.jsx("header",{className:"support-page-header support-page-actions-only",children:e.jsxs("div",{className:"support-header-actions",children:[e.jsxs("div",{className:"support-notification-wrap",children:[e.jsxs("button",{className:"support-secondary-button",type:"button",onClick:()=>de(t=>!t),title:"Notifications",children:[e.jsx(Ke,{size:16})," ",M.filter(t=>!t.read_at).length>0&&e.jsx("b",{children:M.filter(t=>!t.read_at).length})]}),ke&&e.jsxs("div",{className:"support-notification-popover",children:[e.jsx("strong",{children:"Notifications"}),M.length===0?e.jsx("span",{className:"support-muted",children:"No notifications yet."}):M.map(t=>e.jsxs("button",{type:"button",className:t.read_at?"":"unread",onClick:()=>{Je(t)},children:[e.jsx("b",{children:t.title}),e.jsx("span",{children:t.body}),e.jsx("small",{children:G(t.created_at)})]},t.id))]})]}),m?.canViewAllProducts&&e.jsxs("button",{className:"support-secondary-button",type:"button",onClick:()=>Ae(t=>!t),children:[e.jsx(T,{size:16})," Admin"]}),e.jsxs("button",{className:"support-secondary-button",type:"button",onClick:()=>{H()},disabled:Z,children:[e.jsx(xe,{size:16,className:Z?"support-spin":""})," Refresh"]}),e.jsxs("button",{className:"support-primary-button",type:"button",onClick:()=>O(!0),disabled:g.length===0,children:[e.jsx(ye,{size:17})," New request"]})]})}),oe&&e.jsxs("div",{className:"support-error",role:"alert",children:[e.jsx(Q,{size:17})," ",e.jsx("span",{children:oe}),e.jsx("button",{type:"button",onClick:()=>p(""),"aria-label":"Dismiss error",children:e.jsx(Q,{size:15})})]}),Ne&&e.jsxs("form",{className:"support-composer",onSubmit:t=>{Me(t)},children:[e.jsxs("div",{className:"support-section-heading",children:[e.jsxs("div",{children:[e.jsx("span",{children:"New Support request"}),e.jsx("strong",{children:"Describe one actionable problem or idea."})]}),e.jsx("button",{className:"support-icon-button",type:"button",onClick:()=>O(!1),"aria-label":"Close composer",children:e.jsx(Q,{size:18})})]}),e.jsxs("div",{className:"support-form-grid",children:[e.jsxs("label",{children:[e.jsx("span",{children:"Type"}),e.jsx("select",{value:h.postType,onChange:t=>A(r=>({...r,postType:t.target.value})),children:ae.filter(t=>t.id!=="announcement"||m?.canCreateAnnouncements).map(t=>e.jsx("option",{value:t.id,children:t.label},t.id))})]}),e.jsxs("label",{className:"support-form-wide",children:[e.jsx("span",{children:"Title"}),e.jsx("input",{required:!0,minLength:3,maxLength:180,value:h.title,onChange:t=>A(r=>({...r,title:t.target.value})),placeholder:"A short, searchable summary"})]}),e.jsxs("label",{className:"support-form-wide",children:[e.jsx("span",{children:h.postType==="issue"?"What happened?":h.postType==="feature"?"Describe the desired behavior":h.postType==="announcement"?"Announcement":"Question"}),e.jsx("textarea",{required:!0,value:h.body,onChange:t=>A(r=>({...r,body:t.target.value})),placeholder:"Include the context another person needs to respond.",rows:6})]}),e.jsxs("label",{children:[e.jsx("span",{children:"Visibility"}),e.jsxs("select",{value:h.visibility,disabled:h.postType==="announcement",onChange:t=>A(r=>({...r,visibility:t.target.value})),children:[e.jsx("option",{value:"public",children:"Public"}),e.jsx("option",{value:"private",children:"Private"})]})]}),h.postType==="issue"&&e.jsxs("label",{className:"support-checkbox",children:[e.jsx("input",{type:"checkbox",checked:h.includeEnvironment,onChange:t=>A(r=>({...r,includeEnvironment:t.target.checked}))}),e.jsx("span",{children:"Attach safe browser/runtime details"})]}),e.jsxs("label",{className:"support-form-wide",children:[e.jsx("span",{children:"Attachments"}),e.jsx("input",{type:"file",multiple:!0,accept:"image/png,image/jpeg,image/webp,text/plain,text/markdown,application/json,application/pdf",onChange:t=>fe(Array.from(t.target.files||[]).slice(0,5))}),e.jsx("small",{className:"support-muted",children:"Up to 5 files, 10 MB each. Images, text, JSON, Markdown, and PDF."})]})]}),e.jsxs("div",{className:"support-composer-footer",children:[e.jsx("span",{children:Y.length>0?`${Y.length} attachment${Y.length===1?"":"s"} selected.`:"Private posts are visible only to you and VuvoDesk Support."}),e.jsxs("button",{className:"support-primary-button",type:"submit",disabled:v==="create",children:[e.jsx(be,{size:16})," ",v==="create"?"Submitting...":"Submit request"]})]})]}),e.jsxs("div",{className:"support-tabs",role:"tablist","aria-label":"Support post type",children:[e.jsx("button",{className:o.type==="all"?"active":"",type:"button",onClick:()=>N({type:"all"}),children:"All"}),ae.map(t=>{const r=we(t.id);return e.jsxs("button",{className:o.type===t.id?"active":"",type:"button",onClick:()=>N({type:t.id}),children:[e.jsx(r,{size:15})," ",t.label]},t.id)})]}),Ce&&m?.canViewAllProducts&&e.jsxs("section",{className:"support-admin-panel",children:[e.jsxs("div",{className:"support-section-heading",children:[e.jsxs("div",{children:[e.jsx("span",{children:"VuvoDesk owner controls"}),e.jsx("strong",{children:"Manage VuvoDesk support access and staff roles by email."})]}),e.jsx(T,{size:19})]}),e.jsxs("div",{className:"support-admin-grid",children:[e.jsxs("form",{onSubmit:t=>{},children:[e.jsx("strong",{children:"Grant VuvoDesk access"}),e.jsx("input",{type:"email",required:!0,placeholder:"User email",value:qe,onChange:t=>ze(t.target.value)}),e.jsxs("select",{value:Te,onChange:t=>Ie(t.target.value),children:[e.jsx("option",{value:"free",children:"Free"}),e.jsx("option",{value:"user",children:"User"}),e.jsx("option",{value:"paid",children:"Paid"}),e.jsx("option",{value:"beta",children:"Beta"}),e.jsx("option",{value:"staff",children:"Staff"})]}),e.jsxs("button",{className:"support-secondary-button",type:"submit",disabled:me==="access",children:[e.jsx(ee,{size:15})," Grant access"]})]}),e.jsxs("form",{onSubmit:t=>{},children:[e.jsx("strong",{children:"Assign VuvoDesk staff"}),e.jsx("input",{type:"email",required:!0,placeholder:"Staff email",value:De,onChange:t=>Ve(t.target.value)}),e.jsxs("select",{value:Re,onChange:t=>Ue(t.target.value),children:[e.jsx("option",{value:"admin",children:"Admin"}),e.jsx("option",{value:"support",children:"Support"}),e.jsx("option",{value:"moderator",children:"Moderator"})]}),e.jsxs("button",{className:"support-secondary-button",type:"submit",disabled:me==="product-admin",children:[e.jsx(T,{size:15})," Assign staff"]})]})]})]}),e.jsxs("form",{className:"support-search-bar",onSubmit:t=>{t.preventDefault(),N({query:le.trim()})},children:[e.jsxs("label",{children:[e.jsx("span",{children:"Search Support"}),e.jsx("input",{value:le,onChange:t=>K(t.target.value),placeholder:"Search titles and request details"})]}),e.jsxs("button",{className:"support-secondary-button",type:"submit",children:[e.jsx(je,{size:15})," Search"]}),o.query&&e.jsx("button",{className:"support-icon-button",type:"button",onClick:()=>{K(""),N({query:""})},"aria-label":"Clear search",children:e.jsx(Q,{size:16})})]}),e.jsxs("div",{className:"support-filter-bar",children:[e.jsxs("div",{className:"support-filter-title",children:[e.jsx(je,{size:16}),e.jsx("span",{children:"Filter"})]}),e.jsxs("label",{children:[e.jsx("span",{children:"Status"}),e.jsx("select",{value:o.status,onChange:t=>N({status:t.target.value}),children:zt.map(t=>e.jsx("option",{value:t.id,children:t.label},t.id))})]}),e.jsxs("label",{children:[e.jsx("span",{children:"Visibility"}),e.jsxs("select",{value:o.visibility,onChange:t=>N({visibility:t.target.value}),children:[e.jsx("option",{value:"all",children:"All visibility"}),e.jsx("option",{value:"public",children:"Public"}),e.jsx("option",{value:"private",children:"Private"})]})]}),e.jsxs("label",{children:[e.jsx("span",{children:"Sort"}),e.jsxs("select",{value:o.sort,onChange:t=>N({sort:t.target.value}),children:[e.jsx("option",{value:"recent",children:"Recently updated"}),e.jsx("option",{value:"newest",children:"Newest"}),e.jsx("option",{value:"votes",children:"Most votes"})]})]})]}),m?.canViewAllProducts&&e.jsxs("div",{className:"support-admin-shortcuts",children:[e.jsxs("span",{children:[e.jsx(T,{size:15})," VuvoDesk owner"]}),e.jsx("button",{type:"button",onClick:()=>N({status:"needs-response"}),children:"Needs response"}),e.jsx("button",{type:"button",onClick:()=>N({visibility:"private"}),children:"Private requests"}),e.jsx("button",{type:"button",onClick:()=>N({type:"feature",status:"all",sort:"votes"}),children:"High-vote features"})]}),e.jsxs("div",{className:"support-board-layout",children:[e.jsxs("section",{className:"support-post-list","aria-label":"Support posts",children:[e.jsxs("div",{className:"support-list-heading",children:[e.jsxs("span",{children:[q.length," request",q.length===1?"":"s"]}),e.jsx("small",{children:"VuvoDesk"})]}),q.length===0?e.jsxs("div",{className:"support-list-empty",children:[e.jsx(X,{size:25}),e.jsx("strong",{children:"No posts match these filters."}),e.jsx("span",{children:"Start with a focused question, issue report, or feature request."}),g.length>0&&e.jsxs("button",{className:"support-secondary-button",type:"button",onClick:()=>O(!0),children:[e.jsx(ye,{size:16})," Create the first request"]})]}):q.map(t=>{const r=we(t.post_type);return e.jsxs("button",{type:"button",className:`support-post-row ${t.id===a?.id?"active":""}`,onClick:()=>{x(t.id),B(!1)},children:[e.jsx("span",{className:"support-post-row-icon",children:e.jsx(r,{size:17})}),e.jsxs("span",{className:"support-post-row-copy",children:[e.jsxs("span",{className:"support-post-row-meta",children:[e.jsx("em",{children:Se(t.post_type)}),t.visibility==="private"&&e.jsx(V,{size:13})]}),e.jsx("strong",{children:t.title}),e.jsxs("small",{children:[se(t.status)," · ",G(t.updated_at)," · ",t.comment_count," comments"]})]}),t.post_type==="feature"&&e.jsxs("span",{className:"support-vote-count",children:[e.jsx(Xe,{size:14})," ",t.vote_count]})]},t.id)})]}),e.jsx("section",{className:"support-post-detail","aria-label":"Support post detail",children:a?e.jsxs(e.Fragment,{children:[e.jsxs("div",{className:"support-detail-head",children:[e.jsxs("div",{className:"support-detail-badges",children:[e.jsx("span",{className:"support-type-badge",children:Se(a.post_type)}),e.jsx("span",{className:`support-status-badge status-${a.status}`,children:se(a.status)}),a.visibility==="private"&&e.jsxs("span",{className:"support-private-badge",children:[e.jsx(V,{size:13})," Private"]})]}),e.jsxs("div",{className:"support-detail-actions",children:[$e&&e.jsx("button",{className:"support-icon-button",type:"button",onClick:()=>B(t=>!t),title:"Edit post",children:e.jsx(et,{size:16})}),E&&e.jsxs(e.Fragment,{children:[e.jsx("button",{className:"support-icon-button",type:"button",onClick:()=>{Ge()},disabled:v==="pin",title:a.is_pinned?"Unpin post":"Pin post",children:e.jsx(ve,{size:16})}),e.jsx("button",{className:"support-icon-button",type:"button",onClick:()=>{Qe()},disabled:v==="lock",title:a.is_locked?"Unlock post":"Lock post",children:e.jsx(V,{size:16})}),e.jsx("button",{className:"support-icon-button danger",type:"button",onClick:()=>{He()},disabled:v==="hide",title:"Hide post",children:e.jsx(_e,{size:16})})]})]})]}),Pe?e.jsxs("form",{className:"support-edit-form",onSubmit:t=>{Oe(t)},children:[e.jsx("input",{value:a.title,onChange:t=>C({title:t.target.value}),minLength:3,maxLength:180,required:!0}),e.jsx("textarea",{value:a.body,onChange:t=>C({body:t.target.value}),rows:9,required:!0}),e.jsxs("div",{className:"support-edit-actions",children:[e.jsxs("select",{value:a.visibility,onChange:t=>C({visibility:t.target.value}),children:[e.jsx("option",{value:"public",children:"Public"}),e.jsx("option",{value:"private",children:"Private"})]}),e.jsxs("button",{className:"support-primary-button",type:"submit",disabled:v==="edit",children:[e.jsx(ee,{size:16})," Save changes"]})]})]}):e.jsxs(e.Fragment,{children:[e.jsx("h3",{children:a.title}),e.jsx("div",{className:"support-detail-body",children:a.body.split(`
1
+ import{j as e}from"./styles-CO6aHjLW.js";import{a as c,ab as X,k as T,Y as xe,aI as Ke,az as ye,X as Q,o as be,l as ee,aJ as je,Z as V,$ as Xe,aK as et,aL as ve,aM as _e,aN as tt,D as st,b as at,aO as it,ax as nt,aP as rt,aQ as ot}from"./icons-CacEbnZu.js";import{s as u}from"./App-WZAN0Iwh.js";import"./react-Cfedt3N5.js";import"./supabase-C7qjtLN3.js";import"./frame-lifecycle-esOdP-EY.js";const ct=new Set(["browserLanguage","platform","online","viewport","timezone","screen"]),lt={id:"browser-safe-v1",collect(){if(typeof navigator>"u")return{};const s={browserLanguage:navigator.language||"",platform:navigator.platform||"",online:navigator.onLine,timezone:typeof Intl>"u"?"":Intl.DateTimeFormat().resolvedOptions().timeZone||"",viewport:typeof window>"u"?void 0:{width:window.innerWidth,height:window.innerHeight},screen:typeof window>"u"?void 0:{width:window.screen.width,height:window.screen.height}};return Object.fromEntries(Object.entries(s).filter(([i,n])=>ct.has(i)&&n!==void 0))}};function dt(s,i="Support request failed."){if(s&&typeof s=="object"&&"message"in s){const n=String(s.message||"").trim();if(n)return n.replace(/^.*Support request failed:\s*/i,"")}return s instanceof Error?s.message:i}function pt(s=lt){return s.collect()}async function ut(s,i="livedesk"){const{data:n,error:d}=await u.from("app_admins").select("role").eq("user_id",s).limit(1);if(d)throw d;const o=n?.[0]?.role||"user";if(o!=="owner"){const{error:x}=await u.rpc("register_product_access",{p_product_key:i,p_access_type:"free"});if(x)throw x}const[y,m,P]=await Promise.all([u.from("products").select("id, product_key, name, description, sort_order").order("sort_order"),u.from("product_admins").select("product_id, role").eq("user_id",s),u.from("user_products").select("product_id").eq("user_id",s).eq("is_active",!0)]);if(y.error)throw y.error;if(m.error)throw m.error;if(P.error)throw P.error;const j=y.data||[],z=(m.data||[]).map(x=>String(x.product_id)),L=(P.data||[]).map(x=>String(x.product_id)),S=o==="owner",k=S?j.map(x=>x.id):[...new Set([...z,...L])];return{products:j,access:{userId:s,globalRole:o,accessibleProductIds:k,managedProductIds:S?j.map(x=>x.id):z,canViewAllProducts:S,canViewAllPrivatePosts:S,canManageAllProducts:S,canCreateAnnouncements:S||z.length>0}}}async function ht(s=""){const{data:i,error:n}=await u.rpc("search_support_posts",{p_query:s.trim()||null,p_limit:250});if(n)throw n;return i||[]}async function mt(s){const{data:i,error:n}=await u.from("support_attachments").select("*").eq("post_id",s).order("created_at");if(n)throw n;const d=i||[];return Promise.all(d.map(async o=>{const y=await u.storage.from("support-attachments").createSignedUrl(o.storage_path,3600);return{...o,signed_url:y.data?.signedUrl||void 0}}))}async function ft(){const{data:s,error:i}=await u.from("support_notifications").select("*").order("created_at",{ascending:!1}).limit(30);if(i)throw i;return s||[]}async function xt(s){const{error:i}=await u.rpc("mark_support_notification_read",{p_notification_id:s});if(i)throw i}async function ge(s){const{data:i,error:n}=await u.from("support_comments").select("*").eq("post_id",s).order("created_at");if(n)throw n;return i||[]}async function yt(s){const{data:i,error:n}=await u.from("support_votes").select("post_id").eq("user_id",s);if(n)throw n;return new Set((i||[]).map(d=>String(d.post_id)))}async function bt(s){const{data:i,error:n}=await u.rpc("create_support_post",{p_product_id:s.productId,p_post_type:s.postType,p_title:s.title,p_body:s.body,p_visibility:s.visibility,p_app_version:s.appVersion||null,p_platform:typeof navigator>"u"?null:navigator.platform||null,p_os_version:typeof navigator>"u"?null:navigator.platform||null,p_environment:s.includeEnvironment?pt():{}});if(n)throw n;const d=Array.isArray(i)?i[0]?.post_id:i?.post_id;if(!d)throw new Error("Support post was not created.");return String(d)}const jt=new Set(["image/png","image/jpeg","image/webp","text/plain","text/markdown","application/json","application/pdf"]);async function vt(s,i){if(!jt.has(i.type))throw new Error("This file type is not allowed.");if(i.size<=0||i.size>10*1024*1024)throw new Error("Attachments must be smaller than 10 MB.");const n=i.name.replace(/[^a-zA-Z0-9._-]+/g,"-").replace(/^-+|-+$/g,"")||"attachment",d=typeof crypto<"u"&&"randomUUID"in crypto?crypto.randomUUID():`${Date.now()}-${Math.random().toString(16).slice(2)}`,o=`${s}/${d}-${n}`,y=await u.storage.from("support-attachments").upload(o,i,{contentType:i.type,upsert:!1});if(y.error)throw y.error;const{data:m,error:P}=await u.rpc("add_support_attachment",{p_post_id:s,p_storage_path:o,p_file_name:n,p_mime_type:i.type,p_size_bytes:i.size});if(P)throw await u.storage.from("support-attachments").remove([o]),P;const j=Array.isArray(m)?m[0]?.attachment_id:m?.attachment_id;if(!j)throw new Error("Attachment metadata was not created.");return String(j)}async function _t(s,i,n,d){const{error:o}=await u.rpc("update_support_post",{p_post_id:s,p_title:i,p_body:n,p_visibility:d});if(o)throw o}async function gt(s,i){const{error:n}=await u.rpc("add_support_comment",{p_post_id:s,p_body:i});if(n)throw n}async function wt(s){const{data:i,error:n}=await u.rpc("toggle_support_vote",{p_post_id:s});if(n)throw n;const d=Array.isArray(i)?i[0]:i;return{voted:!!d?.voted,totalVotes:Number(d?.total_votes||0)}}async function St(s,i){const{error:n}=await u.rpc("change_support_post_status",{p_post_id:s,p_target_status:i});if(n)throw n}async function Nt(s){const{error:i}=await u.rpc("set_support_post_hidden",{p_post_id:s,p_hidden:!0});if(i)throw i}async function Pt(s,i){const{error:n}=await u.rpc("set_support_post_locked",{p_post_id:s,p_locked:i});if(n)throw n}async function kt(s,i){const{error:n}=await u.rpc("set_support_post_pinned",{p_post_id:s,p_pinned:i});if(n)throw n}async function Ct(s,i){const{error:n}=await u.rpc("mark_support_duplicate",{p_post_id:s,p_duplicate_of_post_id:i});if(n)throw n}async function At(s,i){const{error:n}=await u.rpc("accept_support_answer",{p_post_id:s,p_comment_id:i});if(n)throw n}async function qt(s,i=!0){const{error:n}=await u.rpc("hide_support_comment",{p_comment_id:s,p_hidden:i});if(n)throw n}const ae=[{id:"question",label:"Questions",shortLabel:"Question"},{id:"issue",label:"Issues",shortLabel:"Issue"},{id:"feature",label:"Feature Requests",shortLabel:"Feature"},{id:"announcement",label:"Announcements",shortLabel:"Announcement"}],zt=[{id:"all",label:"All statuses"},{id:"needs-response",label:"Needs response"},{id:"open",label:"Open"},{id:"needs-info",label:"Needs more information"},{id:"in-progress",label:"In progress"},{id:"planned",label:"Planned"},{id:"released",label:"Released"},{id:"closed",label:"Closed"}],Et={question:["open","answered","resolved","closed"],issue:["open","needs-info","confirmed","in-progress","fixed","closed"],feature:["under-review","planned","in-progress","released","declined","closed"],announcement:["published","archived"]},Tt={type:"all",product:"",status:"all",visibility:"all",sort:"recent",query:""};function te(){if(typeof window>"u")return Tt;const s=new URLSearchParams(window.location.search),i=window.location.pathname.match(/^\/support\/(questions|issues|features|announcements)$/)?.[1],n=s.get("type")||i||"",d=n==="questions"||n==="question"?"question":n==="issues"||n==="issue"?"issue":n==="features"||n==="feature"?"feature":n==="announcements"||n==="announcement"?"announcement":"all",o=s.get("visibility"),y=s.get("sort");return{type:d,product:s.get("product")||"",status:s.get("status")||"all",visibility:o==="public"||o==="private"?o:"all",sort:y==="votes"||y==="newest"?y:"recent",query:s.get("q")||""}}function It(s){return s==="all"?"":s==="question"?"questions":s==="issue"?"issues":s==="feature"?"features":"announcements"}function Dt(s,i){const n={...s,...i},d=new URLSearchParams;n.product&&d.set("product",n.product),n.status!=="all"&&d.set("status",n.status),n.visibility!=="all"&&d.set("visibility",n.visibility),n.sort!=="recent"&&d.set("sort",n.sort),n.query&&d.set("q",n.query);const o=It(n.type),y=d.toString();return`/support${o?`/${o}`:""}${y?`?${y}`:""}`}function G(s){const i=new Date(s);return Number.isFinite(i.getTime())?i.toLocaleString(void 0,{dateStyle:"medium",timeStyle:"short"}):s}function se(s){return s.replaceAll("-"," ").replace(/\b\w/g,i=>i.toUpperCase())}function we(s){return s==="question"?it:s==="issue"?nt:s==="feature"?rt:ot}function Se(s){return ae.find(i=>i.id===s)?.shortLabel||s}function b(s){const i=dt(s);return/relation .* does not exist|function .* does not exist|schema cache/i.test(i)?"Support Board database is not installed yet. Apply the latest Supabase migration first.":/not-authenticated|jwt/i.test(i)?"Sign in again to use Support.":/product-access-denied|product-not-found/i.test(i)?"You do not have access to this product.":i}function Bt({session:s,currentProductKey:i="livedesk",appVersion:n="",onSignIn:d}){const[o,y]=c.useState(()=>te()),[m,P]=c.useState(null),[j,z]=c.useState([]),[L,S]=c.useState([]),[k,x]=c.useState(""),[R,U]=c.useState([]),[$,ie]=c.useState([]),[M,W]=c.useState([]),[ne,re]=c.useState(()=>new Set),[Z,J]=c.useState(!0),[v,f]=c.useState(""),[oe,p]=c.useState(""),[Ne,O]=c.useState(!1),[Pe,B]=c.useState(!1),[F,ce]=c.useState(""),[le,K]=c.useState(()=>te().query),[ke,de]=c.useState(!1),[Ce,Ae]=c.useState(!1),[qe,ze]=c.useState(""),[pe,Ee]=c.useState(""),[Te,Ie]=c.useState("free"),[De,Ve]=c.useState(""),[ue,Le]=c.useState(""),[Re,Ue]=c.useState("admin"),[I,he]=c.useState(""),[me,Vt]=c.useState(""),[Y,fe]=c.useState([]),[h,A]=c.useState({productId:"",postType:"question",title:"",body:"",visibility:"public",includeEnvironment:!0,files:[]}),N=c.useCallback(t=>{const r={...o,...t};y(r),typeof window<"u"&&window.history.pushState({},"",Dt(o,t))},[o]);c.useEffect(()=>{const t=()=>y(te());return window.addEventListener("popstate",t),()=>window.removeEventListener("popstate",t)},[]);const _=c.useCallback(async()=>{const t=await ht(o.query);return S(t),s?.user.id&&re(await yt(s.user.id)),t},[o.query,s?.user.id]),H=c.useCallback(async()=>{if(!s?.user.id){P(null),z([]),S([]),x(""),W([]),J(!1);return}J(!0),p("");try{const t=await ut(s.user.id,i);P(t.access),z(t.products),await _(),W(await ft())}catch(t){p(b(t))}finally{J(!1)}},[i,_,s?.user.id]);c.useEffect(()=>{H()},[H]);const g=c.useMemo(()=>{if(!m)return[];const t=j.find(r=>r.product_key===i);return t?m.canViewAllProducts||m.accessibleProductIds.includes(t.id)?[t]:[]:[]},[m,i,j]),D=g[0]?.id||"",q=c.useMemo(()=>{const t=new Map(j.map(l=>[l.id,l.product_key]));return[...L.filter(l=>o.type!=="all"&&l.post_type!==o.type||!D||l.product_id!==D||o.status==="needs-response"&&!["open","needs-info"].includes(l.status)||o.status!=="all"&&o.status!=="needs-response"&&l.status!==o.status||o.visibility!=="all"&&l.visibility!==o.visibility?!1:t.has(l.product_id))].sort((l,w)=>o.sort==="votes"?w.vote_count-l.vote_count||w.updated_at.localeCompare(l.updated_at):o.sort==="newest"?w.created_at.localeCompare(l.created_at):Number(w.is_pinned)-Number(l.is_pinned)||w.updated_at.localeCompare(l.updated_at))},[L,j,o.sort,o.status,o.type,o.visibility,D]),a=q.find(t=>t.id===k)||q[0]||null,E=!!(a&&m&&(m.canViewAllProducts||m.managedProductIds.includes(a.product_id))),$e=!!(a&&s?.user.id===a.author_id)||E;c.useEffect(()=>{if(!a){x(""),U([]),B(!1);return}a.id!==k&&x(a.id)},[a,k]),c.useEffect(()=>{let t=!0;return k?(ge(k).then(r=>{t&&U(r)}).catch(r=>{t&&p(b(r))}),mt(k).then(r=>{t&&ie(r)}).catch(r=>{t&&p(b(r))}),()=>{t=!1}):(U([]),ie([]),()=>{t=!1})},[k]),c.useEffect(()=>{!h.productId&&g[0]&&A(t=>({...t,productId:D||g[0].id}))},[g,h.productId,D]),c.useEffect(()=>{K(o.query)},[o.query]),c.useEffect(()=>{!pe&&g[0]&&Ee(g[0].id),!ue&&g[0]&&Le(g[0].id)},[g,pe,ue]);const C=t=>{a&&S(r=>r.map(l=>l.id===a.id?{...l,...t}:l))},Me=async t=>{if(t.preventDefault(),!(!s||!h.productId)){f("create"),p("");try{const r=await bt({productId:h.productId,postType:h.postType,title:h.title,body:h.body,visibility:h.postType==="announcement"?"public":h.visibility,appVersion:n,includeEnvironment:h.postType==="issue"&&h.includeEnvironment});for(const w of Y)await vt(r,w);const l=await _();O(!1),fe([]),A(w=>({...w,title:"",body:"",files:[]})),x(r),l.some(w=>w.id===r)||await H()}catch(r){p(b(r))}finally{f("")}}},Oe=async t=>{if(t.preventDefault(),!!a){f("edit"),p("");try{await _t(a.id,a.title,a.body,a.visibility),B(!1),await _()}catch(r){p(b(r))}finally{f("")}}},Be=async t=>{if(t.preventDefault(),!(!a||!F.trim())){f("comment"),p("");try{await gt(a.id,F),ce(""),U(await ge(a.id)),await _()}catch(r){p(b(r))}finally{f("")}}},Fe=async()=>{if(!(!a||a.post_type!=="feature")){f("vote"),p("");try{const t=await wt(a.id);re(r=>{const l=new Set(r);return t.voted?l.add(a.id):l.delete(a.id),l}),C({vote_count:t.totalVotes})}catch(t){p(b(t))}finally{f("")}}},Ye=async t=>{if(a){f("status"),p("");try{await St(a.id,t),C({status:t}),await _()}catch(r){p(b(r))}finally{f("")}}},He=async()=>{if(!(!a||!window.confirm("Hide this Support post from the board?"))){f("hide"),p("");try{await Nt(a.id),x(""),await _()}catch(t){p(b(t))}finally{f("")}}},Qe=async()=>{if(a){f("lock"),p("");try{await Pt(a.id,!a.is_locked),C({is_locked:!a.is_locked}),await _()}catch(t){p(b(t))}finally{f("")}}},Ge=async()=>{if(a){f("pin"),p("");try{await kt(a.id,!a.is_pinned),C({is_pinned:!a.is_pinned}),await _()}catch(t){p(b(t))}finally{f("")}}},We=async t=>{if(a){f(`accept-${t}`),p("");try{await At(a.id,t),C({accepted_comment_id:t,status:a.post_type==="question"?"answered":a.status}),await _()}catch(r){p(b(r))}finally{f("")}}},Ze=async()=>{if(!(!a||!I||I===a.id)){f("duplicate"),p("");try{await Ct(a.id,I),x(""),he(""),await _()}catch(t){p(b(t))}finally{f("")}}},Je=async t=>{if(!t.read_at)try{await xt(t.id),W(r=>r.map(l=>l.id===t.id?{...l,read_at:new Date().toISOString()}:l))}catch(r){p(b(r))}t.post_id&&x(t.post_id),de(!1)};return s?Z&&!m?e.jsxs("section",{className:"support-page support-loading",children:[e.jsx(xe,{className:"support-spin",size:23})," Loading Support Board..."]}):e.jsxs("section",{className:"support-page",children:[e.jsx("header",{className:"support-page-header support-page-actions-only",children:e.jsxs("div",{className:"support-header-actions",children:[e.jsxs("div",{className:"support-notification-wrap",children:[e.jsxs("button",{className:"support-secondary-button",type:"button",onClick:()=>de(t=>!t),title:"Notifications",children:[e.jsx(Ke,{size:16})," ",M.filter(t=>!t.read_at).length>0&&e.jsx("b",{children:M.filter(t=>!t.read_at).length})]}),ke&&e.jsxs("div",{className:"support-notification-popover",children:[e.jsx("strong",{children:"Notifications"}),M.length===0?e.jsx("span",{className:"support-muted",children:"No notifications yet."}):M.map(t=>e.jsxs("button",{type:"button",className:t.read_at?"":"unread",onClick:()=>{Je(t)},children:[e.jsx("b",{children:t.title}),e.jsx("span",{children:t.body}),e.jsx("small",{children:G(t.created_at)})]},t.id))]})]}),m?.canViewAllProducts&&e.jsxs("button",{className:"support-secondary-button",type:"button",onClick:()=>Ae(t=>!t),children:[e.jsx(T,{size:16})," Admin"]}),e.jsxs("button",{className:"support-secondary-button",type:"button",onClick:()=>{H()},disabled:Z,children:[e.jsx(xe,{size:16,className:Z?"support-spin":""})," Refresh"]}),e.jsxs("button",{className:"support-primary-button",type:"button",onClick:()=>O(!0),disabled:g.length===0,children:[e.jsx(ye,{size:17})," New request"]})]})}),oe&&e.jsxs("div",{className:"support-error",role:"alert",children:[e.jsx(Q,{size:17})," ",e.jsx("span",{children:oe}),e.jsx("button",{type:"button",onClick:()=>p(""),"aria-label":"Dismiss error",children:e.jsx(Q,{size:15})})]}),Ne&&e.jsxs("form",{className:"support-composer",onSubmit:t=>{Me(t)},children:[e.jsxs("div",{className:"support-section-heading",children:[e.jsxs("div",{children:[e.jsx("span",{children:"New Support request"}),e.jsx("strong",{children:"Describe one actionable problem or idea."})]}),e.jsx("button",{className:"support-icon-button",type:"button",onClick:()=>O(!1),"aria-label":"Close composer",children:e.jsx(Q,{size:18})})]}),e.jsxs("div",{className:"support-form-grid",children:[e.jsxs("label",{children:[e.jsx("span",{children:"Type"}),e.jsx("select",{value:h.postType,onChange:t=>A(r=>({...r,postType:t.target.value})),children:ae.filter(t=>t.id!=="announcement"||m?.canCreateAnnouncements).map(t=>e.jsx("option",{value:t.id,children:t.label},t.id))})]}),e.jsxs("label",{className:"support-form-wide",children:[e.jsx("span",{children:"Title"}),e.jsx("input",{required:!0,minLength:3,maxLength:180,value:h.title,onChange:t=>A(r=>({...r,title:t.target.value})),placeholder:"A short, searchable summary"})]}),e.jsxs("label",{className:"support-form-wide",children:[e.jsx("span",{children:h.postType==="issue"?"What happened?":h.postType==="feature"?"Describe the desired behavior":h.postType==="announcement"?"Announcement":"Question"}),e.jsx("textarea",{required:!0,value:h.body,onChange:t=>A(r=>({...r,body:t.target.value})),placeholder:"Include the context another person needs to respond.",rows:6})]}),e.jsxs("label",{children:[e.jsx("span",{children:"Visibility"}),e.jsxs("select",{value:h.visibility,disabled:h.postType==="announcement",onChange:t=>A(r=>({...r,visibility:t.target.value})),children:[e.jsx("option",{value:"public",children:"Public"}),e.jsx("option",{value:"private",children:"Private"})]})]}),h.postType==="issue"&&e.jsxs("label",{className:"support-checkbox",children:[e.jsx("input",{type:"checkbox",checked:h.includeEnvironment,onChange:t=>A(r=>({...r,includeEnvironment:t.target.checked}))}),e.jsx("span",{children:"Attach safe browser/runtime details"})]}),e.jsxs("label",{className:"support-form-wide",children:[e.jsx("span",{children:"Attachments"}),e.jsx("input",{type:"file",multiple:!0,accept:"image/png,image/jpeg,image/webp,text/plain,text/markdown,application/json,application/pdf",onChange:t=>fe(Array.from(t.target.files||[]).slice(0,5))}),e.jsx("small",{className:"support-muted",children:"Up to 5 files, 10 MB each. Images, text, JSON, Markdown, and PDF."})]})]}),e.jsxs("div",{className:"support-composer-footer",children:[e.jsx("span",{children:Y.length>0?`${Y.length} attachment${Y.length===1?"":"s"} selected.`:"Private posts are visible only to you and VuvoDesk Support."}),e.jsxs("button",{className:"support-primary-button",type:"submit",disabled:v==="create",children:[e.jsx(be,{size:16})," ",v==="create"?"Submitting...":"Submit request"]})]})]}),e.jsxs("div",{className:"support-tabs",role:"tablist","aria-label":"Support post type",children:[e.jsx("button",{className:o.type==="all"?"active":"",type:"button",onClick:()=>N({type:"all"}),children:"All"}),ae.map(t=>{const r=we(t.id);return e.jsxs("button",{className:o.type===t.id?"active":"",type:"button",onClick:()=>N({type:t.id}),children:[e.jsx(r,{size:15})," ",t.label]},t.id)})]}),Ce&&m?.canViewAllProducts&&e.jsxs("section",{className:"support-admin-panel",children:[e.jsxs("div",{className:"support-section-heading",children:[e.jsxs("div",{children:[e.jsx("span",{children:"VuvoDesk owner controls"}),e.jsx("strong",{children:"Manage VuvoDesk support access and staff roles by email."})]}),e.jsx(T,{size:19})]}),e.jsxs("div",{className:"support-admin-grid",children:[e.jsxs("form",{onSubmit:t=>{},children:[e.jsx("strong",{children:"Grant VuvoDesk access"}),e.jsx("input",{type:"email",required:!0,placeholder:"User email",value:qe,onChange:t=>ze(t.target.value)}),e.jsxs("select",{value:Te,onChange:t=>Ie(t.target.value),children:[e.jsx("option",{value:"free",children:"Free"}),e.jsx("option",{value:"user",children:"User"}),e.jsx("option",{value:"paid",children:"Paid"}),e.jsx("option",{value:"beta",children:"Beta"}),e.jsx("option",{value:"staff",children:"Staff"})]}),e.jsxs("button",{className:"support-secondary-button",type:"submit",disabled:me==="access",children:[e.jsx(ee,{size:15})," Grant access"]})]}),e.jsxs("form",{onSubmit:t=>{},children:[e.jsx("strong",{children:"Assign VuvoDesk staff"}),e.jsx("input",{type:"email",required:!0,placeholder:"Staff email",value:De,onChange:t=>Ve(t.target.value)}),e.jsxs("select",{value:Re,onChange:t=>Ue(t.target.value),children:[e.jsx("option",{value:"admin",children:"Admin"}),e.jsx("option",{value:"support",children:"Support"}),e.jsx("option",{value:"moderator",children:"Moderator"})]}),e.jsxs("button",{className:"support-secondary-button",type:"submit",disabled:me==="product-admin",children:[e.jsx(T,{size:15})," Assign staff"]})]})]})]}),e.jsxs("form",{className:"support-search-bar",onSubmit:t=>{t.preventDefault(),N({query:le.trim()})},children:[e.jsxs("label",{children:[e.jsx("span",{children:"Search Support"}),e.jsx("input",{value:le,onChange:t=>K(t.target.value),placeholder:"Search titles and request details"})]}),e.jsxs("button",{className:"support-secondary-button",type:"submit",children:[e.jsx(je,{size:15})," Search"]}),o.query&&e.jsx("button",{className:"support-icon-button",type:"button",onClick:()=>{K(""),N({query:""})},"aria-label":"Clear search",children:e.jsx(Q,{size:16})})]}),e.jsxs("div",{className:"support-filter-bar",children:[e.jsxs("div",{className:"support-filter-title",children:[e.jsx(je,{size:16}),e.jsx("span",{children:"Filter"})]}),e.jsxs("label",{children:[e.jsx("span",{children:"Status"}),e.jsx("select",{value:o.status,onChange:t=>N({status:t.target.value}),children:zt.map(t=>e.jsx("option",{value:t.id,children:t.label},t.id))})]}),e.jsxs("label",{children:[e.jsx("span",{children:"Visibility"}),e.jsxs("select",{value:o.visibility,onChange:t=>N({visibility:t.target.value}),children:[e.jsx("option",{value:"all",children:"All visibility"}),e.jsx("option",{value:"public",children:"Public"}),e.jsx("option",{value:"private",children:"Private"})]})]}),e.jsxs("label",{children:[e.jsx("span",{children:"Sort"}),e.jsxs("select",{value:o.sort,onChange:t=>N({sort:t.target.value}),children:[e.jsx("option",{value:"recent",children:"Recently updated"}),e.jsx("option",{value:"newest",children:"Newest"}),e.jsx("option",{value:"votes",children:"Most votes"})]})]})]}),m?.canViewAllProducts&&e.jsxs("div",{className:"support-admin-shortcuts",children:[e.jsxs("span",{children:[e.jsx(T,{size:15})," VuvoDesk owner"]}),e.jsx("button",{type:"button",onClick:()=>N({status:"needs-response"}),children:"Needs response"}),e.jsx("button",{type:"button",onClick:()=>N({visibility:"private"}),children:"Private requests"}),e.jsx("button",{type:"button",onClick:()=>N({type:"feature",status:"all",sort:"votes"}),children:"High-vote features"})]}),e.jsxs("div",{className:"support-board-layout",children:[e.jsxs("section",{className:"support-post-list","aria-label":"Support posts",children:[e.jsxs("div",{className:"support-list-heading",children:[e.jsxs("span",{children:[q.length," request",q.length===1?"":"s"]}),e.jsx("small",{children:"VuvoDesk"})]}),q.length===0?e.jsxs("div",{className:"support-list-empty",children:[e.jsx(X,{size:25}),e.jsx("strong",{children:"No posts match these filters."}),e.jsx("span",{children:"Start with a focused question, issue report, or feature request."}),g.length>0&&e.jsxs("button",{className:"support-secondary-button",type:"button",onClick:()=>O(!0),children:[e.jsx(ye,{size:16})," Create the first request"]})]}):q.map(t=>{const r=we(t.post_type);return e.jsxs("button",{type:"button",className:`support-post-row ${t.id===a?.id?"active":""}`,onClick:()=>{x(t.id),B(!1)},children:[e.jsx("span",{className:"support-post-row-icon",children:e.jsx(r,{size:17})}),e.jsxs("span",{className:"support-post-row-copy",children:[e.jsxs("span",{className:"support-post-row-meta",children:[e.jsx("em",{children:Se(t.post_type)}),t.visibility==="private"&&e.jsx(V,{size:13})]}),e.jsx("strong",{children:t.title}),e.jsxs("small",{children:[se(t.status)," · ",G(t.updated_at)," · ",t.comment_count," comments"]})]}),t.post_type==="feature"&&e.jsxs("span",{className:"support-vote-count",children:[e.jsx(Xe,{size:14})," ",t.vote_count]})]},t.id)})]}),e.jsx("section",{className:"support-post-detail","aria-label":"Support post detail",children:a?e.jsxs(e.Fragment,{children:[e.jsxs("div",{className:"support-detail-head",children:[e.jsxs("div",{className:"support-detail-badges",children:[e.jsx("span",{className:"support-type-badge",children:Se(a.post_type)}),e.jsx("span",{className:`support-status-badge status-${a.status}`,children:se(a.status)}),a.visibility==="private"&&e.jsxs("span",{className:"support-private-badge",children:[e.jsx(V,{size:13})," Private"]})]}),e.jsxs("div",{className:"support-detail-actions",children:[$e&&e.jsx("button",{className:"support-icon-button",type:"button",onClick:()=>B(t=>!t),title:"Edit post",children:e.jsx(et,{size:16})}),E&&e.jsxs(e.Fragment,{children:[e.jsx("button",{className:"support-icon-button",type:"button",onClick:()=>{Ge()},disabled:v==="pin",title:a.is_pinned?"Unpin post":"Pin post",children:e.jsx(ve,{size:16})}),e.jsx("button",{className:"support-icon-button",type:"button",onClick:()=>{Qe()},disabled:v==="lock",title:a.is_locked?"Unlock post":"Lock post",children:e.jsx(V,{size:16})}),e.jsx("button",{className:"support-icon-button danger",type:"button",onClick:()=>{He()},disabled:v==="hide",title:"Hide post",children:e.jsx(_e,{size:16})})]})]})]}),Pe?e.jsxs("form",{className:"support-edit-form",onSubmit:t=>{Oe(t)},children:[e.jsx("input",{value:a.title,onChange:t=>C({title:t.target.value}),minLength:3,maxLength:180,required:!0}),e.jsx("textarea",{value:a.body,onChange:t=>C({body:t.target.value}),rows:9,required:!0}),e.jsxs("div",{className:"support-edit-actions",children:[e.jsxs("select",{value:a.visibility,onChange:t=>C({visibility:t.target.value}),children:[e.jsx("option",{value:"public",children:"Public"}),e.jsx("option",{value:"private",children:"Private"})]}),e.jsxs("button",{className:"support-primary-button",type:"submit",disabled:v==="edit",children:[e.jsx(ee,{size:16})," Save changes"]})]})]}):e.jsxs(e.Fragment,{children:[e.jsx("h3",{children:a.title}),e.jsx("div",{className:"support-detail-body",children:a.body.split(`
2
2
  `).map((t,r)=>e.jsx("p",{children:t||" "},`${a.id}-body-${r}`))})]}),e.jsxs("div",{className:"support-detail-meta",children:[e.jsxs("span",{children:["Opened ",G(a.created_at)]}),a.app_version&&e.jsxs("span",{children:["Version ",a.app_version]}),a.platform&&e.jsx("span",{children:a.platform}),a.is_locked&&e.jsxs("span",{children:[e.jsx(V,{size:13})," Locked"]}),a.is_pinned&&e.jsxs("span",{children:[e.jsx(ve,{size:13})," Pinned"]})]}),e.jsxs("div",{className:"support-detail-controls",children:[a.post_type==="feature"&&e.jsxs("button",{className:`support-vote-button ${ne.has(a.id)?"voted":""}`,type:"button",onClick:()=>{Fe()},disabled:v==="vote",children:[e.jsx(tt,{size:16})," ",ne.has(a.id)?"Voted":"Vote"," ",e.jsx("b",{children:a.vote_count})]}),E&&e.jsxs("label",{className:"support-status-control",children:[e.jsx("span",{children:"Change status"}),e.jsx("select",{value:a.status,onChange:t=>{Ye(t.target.value)},disabled:v==="status",children:(Et[a.post_type]||[]).map(t=>e.jsx("option",{value:t,children:se(t)},t))})]}),E&&e.jsxs(e.Fragment,{children:[e.jsxs("select",{className:"support-status-control",value:I,onChange:t=>he(t.target.value),children:[e.jsx("option",{value:"",children:"Duplicate of..."}),q.filter(t=>t.id!==a.id&&t.product_id===a.product_id).map(t=>e.jsx("option",{value:t.id,children:t.title.slice(0,55)},t.id))]}),e.jsx("button",{className:"support-secondary-button",type:"button",onClick:()=>{Ze()},disabled:!I||v==="duplicate",children:"Merge duplicate"})]})]}),$.length>0&&e.jsxs("div",{className:"support-attachments",children:[e.jsx("div",{className:"support-section-heading",children:e.jsxs("div",{children:[e.jsx("span",{children:"Attachments"}),e.jsxs("strong",{children:[$.length," file",$.length===1?"":"s"]})]})}),$.map(t=>e.jsxs("a",{href:t.signed_url,target:"_blank",rel:"noreferrer",children:[e.jsx(st,{size:14})," ",t.file_name,e.jsxs("small",{children:[Math.ceil(t.size_bytes/1024)," KB"]})]},t.id))]}),e.jsxs("div",{className:"support-comments",children:[e.jsxs("div",{className:"support-section-heading",children:[e.jsxs("div",{children:[e.jsx("span",{children:"Discussion"}),e.jsxs("strong",{children:[R.length," comment",R.length===1?"":"s"]})]}),a.is_locked&&e.jsxs("span",{className:"support-locked-label",children:[e.jsx(V,{size:14})," Locked"]})]}),R.length===0?e.jsx("p",{className:"support-muted",children:"No response yet. Keep the conversation focused on this request."}):e.jsx("div",{className:"support-comment-list",children:R.map(t=>e.jsxs("article",{className:`support-comment ${t.is_staff_answer?"staff":""} ${a.accepted_comment_id===t.id?"accepted":""}`,children:[e.jsxs("div",{className:"support-comment-head",children:[e.jsx("strong",{children:t.is_staff_answer?"VuvoDesk Support":t.author_id===s.user.id?"You":"Support user"}),t.is_staff_answer&&e.jsxs("span",{children:[e.jsx(T,{size:13})," Staff answer"]}),a.accepted_comment_id===t.id&&e.jsxs("span",{children:[e.jsx(at,{size:13})," Accepted"]}),e.jsx("time",{children:G(t.created_at)})]}),e.jsx("p",{children:t.body}),e.jsxs("div",{className:"support-comment-actions",children:[(s.user.id===a.author_id||E)&&a.post_type==="question"&&a.accepted_comment_id!==t.id&&e.jsxs("button",{className:"support-secondary-button",type:"button",onClick:()=>{We(t.id)},disabled:v===`accept-${t.id}`,children:[e.jsx(ee,{size:14})," Accept answer"]}),E&&e.jsx("button",{className:"support-icon-button danger",type:"button",onClick:()=>{qt(t.id)},title:"Hide comment",children:e.jsx(_e,{size:14})})]})]},t.id))}),!a.is_locked&&e.jsxs("form",{className:"support-comment-form",onSubmit:t=>{Be(t)},children:[e.jsx("textarea",{value:F,onChange:t=>ce(t.target.value),placeholder:"Add a focused reply...",rows:3}),e.jsxs("button",{className:"support-primary-button",type:"submit",disabled:v==="comment"||!F.trim(),children:[e.jsx(be,{size:15})," Reply"]})]})]})]}):e.jsxs("div",{className:"support-detail-empty",children:[e.jsx(X,{size:28}),e.jsx("strong",{children:"Select a Support request"}),e.jsx("span",{children:"Its discussion and staff actions will appear here."})]})})]})]}):e.jsx("section",{className:"support-page support-auth-page",children:e.jsxs("div",{className:"support-empty-hero",children:[e.jsx("span",{className:"support-hero-icon",children:e.jsx(X,{size:28})}),e.jsx("p",{className:"support-eyebrow",children:"VuvoDesk Support"}),e.jsx("h2",{children:"Get help, report issues, and shape the product."}),e.jsx("p",{children:"Sign in to view VuvoDesk support and submit a private or public request."}),e.jsxs("button",{className:"support-primary-button",type:"button",onClick:d,children:[e.jsx(T,{size:17})," Sign in with Google"]})]})})}export{Bt as SupportPage};
@@ -1 +1 @@
1
- import{j as e,M as qe,w as Ye,i as Qe,D as te,e as Ze,h as et}from"./styles-CDIlHYlj.js";import{a as r,g as ne,c as ce,aa as tt,ab as nt,ac as st,ad as De,ae as Ie,X as Se,k as $e,af as it,b as Fe,w as rt,ag as ke,ah as at,ai as ot,O as Ne,W as ct,aj as lt,ak as ut,a2 as dt,J as ht,al as mt}from"./icons-CacEbnZu.js";import{SupportPage as ft}from"./SupportPage-B90rx4D0.js";import{s as V,g as Ce,n as pt,r as gt,a as Ee,b as bt,f as Te,A as wt,H as vt}from"./App-smTkH_7F.js";import{DesktopUpdatePanel as xt}from"./DesktopUpdatePanel-BDIHhw5J.js";import"./react-Cfedt3N5.js";import"./supabase-C7qjtLN3.js";import"./frame-lifecycle-esOdP-EY.js";function yt(i=[]){return[...new Set(i.filter(t=>typeof t=="string").map(t=>t.trim()).filter(Boolean))]}function jt(i=[],t=new Set){return i.find(c=>!t.has(c))||""}function St(i=[],t=new Set){const c=new Set(i);return new Set([...t].filter(o=>c.has(o)))}const kt="client-runtime-connection-unavailable",Nt="VuvoDesk Client is reconnecting to the service on this computer.",Ct=/failed to fetch|fetch failed|network request failed|networkerror when attempting to fetch resource|load failed|signal timed out|operation was aborted due to timeout|the operation timed out/i;function Oe(i,t){if(!i||typeof i!="object")return"";const c=i[t];return typeof c=="string"?c.trim():""}function le(i){return typeof i=="string"?i.trim():Oe(i,"message")||String(i??"").trim()}function Re(i){return/supabase-user-verification-failed:(401|403)/i.test(i)?"Saved sign-in needs refresh. VuvoDesk will retry it automatically.":i}function Ae(i){const t=le(i)||"Client status could not be refreshed.",c=Oe(i,"name").toLowerCase();return c==="timeouterror"||c==="aborterror"||Ct.test(t)?{key:kt,message:Nt}:{key:`client-status-poll:${t}`,message:t}}function Et({actionError:i="",discoveryWakeError:t=null,statusPollError:c=null,runtimeError:o=""}={}){const u=le(i);if(u)return{key:`client-action:${u}`,message:Re(u)};if(t?.key&&t?.message)return t;if(c?.key&&c?.message)return c;const p=le(o);return p?{key:`client-runtime:${p}`,message:Re(p)}:null}function Tt(i,t){if(!t?.message)return{nextKey:"",message:null};const c=String(t.key||t.message).trim();return String(i||"")===c?{nextKey:c,message:null}:{nextKey:c,message:t.message}}const Rt=[{id:"docs",label:"Doc",icon:tt},{id:"support",label:"Support",icon:nt},{id:"pricing",label:"Pricing",icon:st},{id:"settings",label:"Settings",icon:De}],Pe=[0,1e3,2e3,5e3,1e4,2e4,3e4],At=15e3;function Pt(i=""){const t=i.trim().replace(/\/+$/,"");return t||(typeof window>"u"?"":window.location.port==="5179"?window.location.origin:"http://127.0.0.1:5179")}async function H(i,t=!1){const c=t?"":sessionStorage.getItem("livedesk.client.csrf");if(c)return c;const o=await fetch(`${i}/api/runtime/status`,{cache:"no-store"}),u=await o.json().catch(()=>({}));if(!o.ok)throw new Error(u.error||`Client runtime returned HTTP ${o.status}.`);const p=String(u.csrfToken||"");return p&&sessionStorage.setItem("livedesk.client.csrf",p),p}function ue(i){return`${Math.max(0,Math.min(100,Number(i||0))).toFixed(0)}%`}function oe(i){return Number.isFinite(i)?`${Math.max(0,Math.min(100,Number(i))).toFixed(0)}%`:"—"}function Me(i){return Number.isFinite(i)?ue(Number(i)):"0%"}function U(i){const t=Number(i);return!Number.isFinite(t)||t<0?"—":t>=1024**4?`${(t/1024**4).toFixed(t>=10*1024**4?0:1)} TB`:t>=1024**3?`${(t/1024**3).toFixed(t>=10*1024**3?0:1)} GB`:`${(t/1024**2).toFixed(0)} MB`}function Le(i){const t=Math.max(0,Number(i||0));return t>=1024**3?`${(t/1024**3).toFixed(1)} GB/s`:t>=1024**2?`${(t/1024**2).toFixed(1)} MB/s`:t>=1024?`${(t/1024).toFixed(1)} KB/s`:`${t.toFixed(0)} B/s`}function Lt(i,t){const c=Number(i||0)/1073741824,o=Number(t||0)/1024**3;return o>0?`${c.toFixed(1)} / ${o.toFixed(1)} GB`:"Checking..."}function Dt(i,t){return!Number.isFinite(t)||Number(t)<=0?"Dedicated memory unavailable":Number.isFinite(i)?`${U(i)} / ${U(t)}`:`${U(t)} available`}function It(i){const t=Math.max(0,Math.floor(Number(i||0)/60)),c=Math.floor(t/1440),o=Math.floor(t%1440/60),u=t%60;return c>0?`${c}d ${o}h`:o>0?`${o}h ${u}m`:`${u}m`}function ee({icon:i,label:t,value:c,detail:o,percent:u}){return e.jsxs("article",{className:"client-resource-metric",children:[e.jsxs("div",{className:"client-resource-metric-head",children:[e.jsx(i,{size:17}),e.jsx("span",{children:t}),e.jsx("strong",{children:c})]}),e.jsx("small",{title:o,children:o}),e.jsx("i",{"aria-hidden":"true",children:e.jsx("b",{style:{width:Me(u)}})})]})}function $t(i,t){const c=i?.user.user_metadata||{},o=c.full_name||c.name||c.preferred_username||i?.user.email||i?.user.id||t?.userName||t?.auth?.name||t?.userEmail||t?.userId||"Account";return String(o).trim()||"Account"}function Ft(i,t){const c=i?.user.user_metadata||{};return yt([c.avatar_url,c.picture,t?.userAvatarUrl,t?.auth?.avatarUrl])}function Ot({page:i,status:t}){const o={docs:{label:"DOC",title:"Using this computer",body:"This computer connects to its assigned Hub automatically. Keep VuvoDesk running and manage screens, files, and remote control from the Hub."},pricing:{label:"PRICING",title:"VuvoDesk plans",body:"Plans are based on how many computers the Hub manages. This Client computer does not need a separate subscription or setup."},settings:{label:"SETTINGS",title:"Computer preferences",body:"VuvoDesk keeps the verified sign-in on this computer and reconnects to the assigned Hub automatically."}}[i];return e.jsxs("section",{className:"client-service-page",children:[e.jsx("span",{className:"client-unified-section-label",children:o.label}),e.jsx("h2",{children:o.title}),e.jsx("p",{children:o.body}),e.jsxs("div",{className:"client-service-page-card",children:[i==="docs"&&e.jsxs(e.Fragment,{children:[e.jsx("strong",{children:"Automatic connection"}),e.jsx("span",{children:"Start VuvoDesk. This computer signs in, finds the Hub, and becomes available without another action."})]}),i==="pricing"&&e.jsxs(e.Fragment,{children:[e.jsx("strong",{children:"One plan, managed at the Hub"}),e.jsx("span",{children:"Computer access follows the account and plan used by the Hub."})]}),i==="settings"&&e.jsxs(e.Fragment,{children:[e.jsx("strong",{children:"Saved sign-in"}),e.jsx("span",{children:t?.auth?.persisted?"Enabled on this computer":"Sign in once to enable automatic reconnection"})]})]}),i==="settings"&&e.jsx(xt,{})]})}function Mt({session:i,status:t,pin:c,roleChangeMessage:o,videoAccelerationBusy:u,slotInput:p,slotBusy:v,onSignIn:R,onPinChange:_,onSubmitPin:C,onSlotInputChange:d,onSaveSlot:A,onEnableVideoAcceleration:k}){const j=t?.connectionState==="connected",g=t?.videoAcceleration,$=u||g?.busy||g?.state==="installing",b=g?.ready?`${g.encoder||"H.264"} · ${g.driver||"VAAPI"}`:$?"Setting up…":g?.message||"Setup available",y=t?.system?.memoryTotalBytes?Number(t.system.memoryUsedBytes||0)/t.system.memoryTotalBytes*100:0,P=t?.system?.gpus||[],w=P.find(h=>Number.isFinite(h.usagePercent)||Number.isFinite(h.memoryTotalBytes))||P[0],z=Number.isFinite(w?.usagePercent)?Number(w?.usagePercent):null,G=Number.isFinite(w?.memoryTotalBytes)&&Number(w?.memoryTotalBytes)>0&&Number.isFinite(w?.memoryUsedBytes)?Number(w?.memoryUsedBytes)/Number(w?.memoryTotalBytes)*100:null,E=t?.system?.disks||[],T=t?.system?.network,B=!!T?.sampledAt,N=T?.interfaces||[],S=[...new Set(N.map(h=>h.name).filter(Boolean))],L=N.map(h=>h.address).filter(Boolean);return e.jsxs("section",{className:"client-home-page",children:[e.jsxs("header",{className:"client-home-heading",children:[e.jsxs("div",{children:[e.jsx("p",{className:"client-unified-eyebrow",children:"THIS COMPUTER"}),e.jsx("h2",{children:t?.deviceName||"This Computer"})]}),e.jsxs("span",{className:`client-unified-role-chip ${j?"connected":""}`,children:[e.jsx(ce,{size:15})," ",j?"Online":"Connecting"]})]}),e.jsxs("div",{className:"client-home-layout",children:[e.jsxs("section",{className:"client-main-surface","aria-label":"Client computer",children:[e.jsxs("div",{className:"client-main-surface-head",children:[e.jsxs("div",{children:[e.jsx("span",{className:"client-unified-section-label",children:"SYSTEM STATUS"}),e.jsx("strong",{children:"Hardware at a glance"})]}),e.jsxs("small",{children:[e.jsx(rt,{size:14})," Uptime ",It(t?.system?.uptimeSeconds)]})]}),e.jsxs("div",{className:"client-system-dashboard",children:[e.jsxs("section",{className:"client-performance-panel","aria-label":"Performance",children:[e.jsxs("div",{className:"client-system-subhead",children:[e.jsxs("div",{children:[e.jsx("span",{className:"client-unified-section-label",children:"PERFORMANCE"}),e.jsx("strong",{children:"Live resources"})]}),e.jsx("small",{children:"Live updates"})]}),e.jsxs("div",{className:"client-live-metrics",children:[e.jsx(ee,{icon:ke,label:"CPU",value:ue(t?.system?.cpuUsagePercent),detail:t?.system?.cpuLogicalCores?`${t.system.cpuLogicalCores} logical cores · Current processor usage`:"Current processor usage",percent:t?.system?.cpuUsagePercent}),e.jsx(ee,{icon:at,label:"Memory",value:ue(y),detail:Lt(t?.system?.memoryUsedBytes,t?.system?.memoryTotalBytes),percent:y}),e.jsx(ee,{icon:ot,label:"GPU",value:oe(z),detail:w?.name||(t?.system?.hardwareCollecting?"Detecting graphics hardware…":"Graphics usage unavailable"),percent:z}),e.jsx(ee,{icon:ne,label:"VRAM",value:oe(G),detail:Dt(w?.memoryUsedBytes,w?.memoryTotalBytes),percent:G})]})]}),e.jsxs("section",{className:"client-storage-panel","aria-label":"Local storage",children:[e.jsxs("div",{className:"client-system-subhead",children:[e.jsxs("div",{children:[e.jsx("span",{className:"client-unified-section-label",children:"STORAGE"}),e.jsx("strong",{children:"Local drives"})]}),e.jsx("small",{children:E.length>0?`${E.length} ${E.length===1?"drive":"drives"}`:t?.system?.hardwareCollecting?"Detecting…":"Unavailable"})]}),e.jsxs("div",{className:"client-disk-list",children:[E.map(h=>e.jsxs("article",{className:"client-disk-row",children:[e.jsxs("div",{className:"client-disk-row-head",children:[e.jsxs("span",{children:[e.jsx(Ne,{size:16}),e.jsx("strong",{children:h.mount||"Disk"}),e.jsx("small",{children:h.name||"Local disk"})]}),e.jsx("strong",{children:oe(h.usedPercent)})]}),e.jsxs("small",{children:[U(h.freeBytes)," free · ",U(h.totalBytes)," total"]}),e.jsx("i",{"aria-hidden":"true",children:e.jsx("b",{style:{width:Me(h.usedPercent)}})})]},`${h.mount}-${h.name||""}`)),E.length===0&&e.jsxs("div",{className:"client-disk-empty",children:[e.jsx(Ne,{size:18}),e.jsx("span",{children:t?.system?.hardwareCollecting?"Detecting local drives…":"Storage information is not available."})]})]})]}),e.jsxs("section",{className:"client-network-panel","aria-label":"Network activity",children:[e.jsxs("div",{className:"client-system-subhead",children:[e.jsxs("div",{children:[e.jsx("span",{className:"client-unified-section-label",children:"NETWORK"}),e.jsx("strong",{children:"Live traffic"})]}),e.jsx("small",{children:S.length>0?S.join(", "):"Detecting adapter"})]}),e.jsxs("div",{className:"client-network-live",children:[e.jsxs("div",{className:"client-network-adapter",children:[e.jsx(ct,{size:18}),e.jsxs("span",{children:[e.jsx("strong",{children:S.join(", ")||"Network"}),e.jsx("small",{children:L.join(" / ")||"Checking active connection"})]})]}),e.jsxs("div",{className:"client-network-rate download",children:[e.jsx(lt,{size:17}),e.jsxs("span",{children:[e.jsx("small",{children:"Receive"}),e.jsx("strong",{children:Le(B?T?.receiveBytesPerSecond:void 0)})]})]}),e.jsxs("div",{className:"client-network-rate upload",children:[e.jsx(ut,{size:17}),e.jsxs("span",{children:[e.jsx("small",{children:"Send"}),e.jsx("strong",{children:Le(B?T?.sendBytesPerSecond:void 0)})]})]}),e.jsx("small",{className:"client-network-total",children:B?`Total ${U(T?.receivedBytes)} received / ${U(T?.sentBytes)} sent`:"Traffic has not been measured yet."})]})]})]})]}),e.jsxs("aside",{className:"client-connection-panel",children:[e.jsx("span",{className:"client-unified-section-label",children:"LIVEDESK SERVICES"}),e.jsx("h3",{children:j?"Connected to the Hub":t?.authenticated?"Finding the Hub":"Connect this computer"}),j?e.jsxs(e.Fragment,{children:[e.jsx("p",{className:"client-unified-copy",children:"This computer is available from the VuvoDesk Hub."}),e.jsxs("div",{className:"client-service-status-list",children:[e.jsxs("div",{children:[e.jsx(ce,{size:17}),e.jsxs("span",{children:[e.jsx("strong",{children:"Screen sharing"}),e.jsx("small",{children:t?.permissions?.screenCapture?"Active":"Waiting"})]})]}),e.jsxs("div",{children:[e.jsx($e,{size:17}),e.jsxs("span",{children:[e.jsx("strong",{children:"Remote control"}),e.jsx("small",{children:t?.permissions?.remoteInput?"Available":"Unavailable"})]})]}),e.jsxs("div",{children:[e.jsx(dt,{size:17}),e.jsxs("span",{children:[e.jsx("strong",{children:"File delivery"}),e.jsx("small",{children:t?.permissions?.files?"Ready to receive":"Unavailable"})]})]}),e.jsxs("div",{children:[e.jsx(ht,{size:17}),e.jsxs("span",{children:[e.jsx("strong",{children:"Computer audio"}),e.jsx("small",{children:t?.permissions?.audio?"Available":"Unavailable"})]})]})]})]}):e.jsxs(e.Fragment,{children:[e.jsx("p",{className:"client-unified-copy",children:t?.authenticated?"VuvoDesk is checking the registered Hub endpoints. A temporary Hub PIN can be used as a fallback.":"Sign in once on this computer. VuvoDesk saves the verified session, finds the Hub, and starts screen sharing automatically."}),!i&&!t?.authenticated?e.jsxs("button",{className:"client-unified-primary",type:"button",onClick:R,children:[e.jsx(Ie,{size:16})," Sign in with Google"]}):e.jsxs("p",{className:"client-unified-success",children:[e.jsx(Fe,{size:16})," Signed in as ",i?.user.email||i?.user.id||t?.userEmail||t?.userId||"saved account"]}),e.jsxs("div",{className:"client-unified-pin-row",children:[e.jsx(mt,{size:16}),e.jsx("input",{value:c,onChange:h=>_(h.target.value),placeholder:"6-digit Hub PIN",inputMode:"numeric","aria-label":"6-digit Hub PIN"}),e.jsx("button",{className:"client-unified-secondary",type:"button",onClick:C,disabled:c.length!==6,children:"Connect"})]})]}),g?.supported&&e.jsxs("div",{className:`client-video-acceleration state-${g.ready?"ready":g.state||"setup-required"}`,children:[e.jsx(ke,{size:17}),e.jsxs("span",{children:[e.jsx("strong",{children:"Hardware video"}),e.jsx("small",{children:b})]}),!g.ready&&g.canInstall&&e.jsx("button",{type:"button",onClick:k,disabled:$,title:"Install and verify Linux FFmpeg hardware video support",children:$?"Setting up":"Enable"})]}),o&&e.jsx("p",{className:"client-unified-success",children:o}),e.jsxs("div",{className:"client-slot-setting",children:[e.jsxs("label",{htmlFor:"client-slot-number",children:[e.jsx("span",{children:"Slot"}),e.jsx("input",{id:"client-slot-number",value:p,onChange:h=>d(h.target.value),inputMode:"numeric",pattern:"[0-9]*",placeholder:"1-999","aria-label":"Wall slot number",disabled:!j||v})]}),e.jsxs("button",{type:"button",onClick:A,disabled:!j||v||!p,"aria-label":"Save slot number",title:"Save slot number",children:[e.jsx(De,{size:17}),e.jsx("span",{children:v?"Saving":"Set"})]})]})]})]})]})}function Ht({runtimeBaseUrl:i=""}){const[t,c]=r.useState(null),[o,u]=r.useState(null),[p,v]=r.useState(!1),[R,_]=r.useState(""),[C,d]=r.useState(""),[A,k]=r.useState(null),[j,g]=r.useState(null),[$,b]=r.useState(""),[y,P]=r.useState(!1),[w,z]=r.useState(!1),[G,E]=r.useState(""),[T,B]=r.useState(!1),[N,S]=r.useState(null),[L,h]=r.useState(!1),[D,de]=r.useState("computer"),[he,q]=r.useState(!1),me=r.useRef(null),fe=r.useRef(null),se=r.useRef(!1),Y=r.useRef(null),Q=r.useRef(null),pe=r.useRef(!1),W=r.useRef(!1),ge=r.useRef(0),ie=r.useRef(!1),be=r.useRef(""),m=r.useMemo(()=>Pt(i),[i]),re=r.useMemo(()=>$t(o,t),[o,t]),ae=r.useMemo(()=>Ft(o,t),[o,t]),He=JSON.stringify(ae),[Ue,we]=r.useState(()=>new Set),K=jt(ae,Ue),ve=re.slice(0,1).toUpperCase()||"L";r.useEffect(()=>{we(n=>St(ae,n))},[He]);const _e=r.useCallback(n=>{we(s=>{if(!n||s.has(n))return s;const a=new Set(s);return a.add(n),a})},[]),F=r.useCallback(async()=>{if(d(""),window.liveDesk?.auth?.signInWithGoogle){const n=await window.liveDesk.auth.signInWithGoogle();return n?.ok===!1?(d(n.error||"VuvoDesk could not open the system browser for Google sign-in."),!1):!0}return window.location.assign(`${m}/auth/google`),!0},[m]),xe=r.useCallback(async()=>{W.current||(W.current=!0,d("Your saved Google sign-in is incomplete. VuvoDesk is reopening sign-in once to repair it."),await V.auth.signOut({scope:"local"}).catch(()=>{}),u(null),await F()||(W.current=!1))},[F]),ye=r.useCallback(async n=>{const s=Ce(n);if(s)throw new Error(s);if(se.current)return!1;se.current=!0;try{if(window.liveDesk?.auth?.persistSession){const a=await window.liveDesk.auth.persistSession(n);if(a?.ok===!1)throw new Error(a.error||"VuvoDesk could not validate the desktop session.");return d(""),!0}for(let a=0;a<2;a+=1){const l=await H(m,a>0),f=await fetch(`${m}/api/auth/session`,{method:"POST",headers:{"Content-Type":"application/json","X-LiveDesk-CSRF":l},signal:AbortSignal.timeout(1e4),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=await f.json().catch(()=>({}));if(f.status===403&&a===0){sessionStorage.removeItem("livedesk.client.csrf");continue}if(!f.ok)throw new Error(M.error||`Client runtime returned HTTP ${f.status}.`);return d(""),!0}throw new Error("Client runtime rejected the session transfer.")}finally{se.current=!1}},[m]),x=r.useCallback(()=>{const n=Y.current;if(n)return n;const s=(async()=>{try{const a=await fetch(`${m}/api/client/status`,{cache:"no-store",signal:AbortSignal.timeout(1500)}),l=await a.json().catch(()=>({}));if(!a.ok)throw new Error(l.error||`Client runtime returned HTTP ${a.status}.`);if(l.csrfToken&&sessionStorage.setItem("livedesk.client.csrf",l.csrfToken),String(l.role||"").toLowerCase()==="hub")throw new Error("This computer is assigned to the Hub role.");c(l),k(null),g(null)}catch(a){k(Ae(a))}})();return Y.current=s,s.finally(()=>{Y.current===s&&(Y.current=null)}),s},[m]),je=r.useCallback(()=>{const n=Q.current;if(n)return n;const s=(async()=>{const a=await fetch(`${m}/api/client/diagnostics`,{cache:"no-store",signal:AbortSignal.timeout(8e3)}),l=await a.json().catch(()=>({}));if(!a.ok)throw new Error(l.error||`Client diagnostics returned HTTP ${a.status}.`);if(String(l.role||"").toLowerCase()==="hub")throw new Error("This computer is assigned to the Hub role.");c(l)})();return Q.current=s,s.finally(()=>{Q.current===s&&(Q.current=null)}),s},[m]);r.useEffect(()=>{let n=!1,s=null;const a=async()=>{await x(),n||(s=window.setTimeout(()=>{a()},2e3))};return a(),()=>{n=!0,s!==null&&window.clearTimeout(s)}},[x]),r.useEffect(()=>{if(D!=="computer")return;let n=!1,s=null;const a=()=>{n||(s=window.setTimeout(()=>{l()},At))},l=async()=>{document.visibilityState==="visible"&&await je().catch(()=>{}),a()},f=()=>{document.visibilityState==="visible"&&(s!==null&&(window.clearTimeout(s),s=null),l())};return document.addEventListener("visibilitychange",f),l(),()=>{n=!0,document.removeEventListener("visibilitychange",f),s!==null&&window.clearTimeout(s)}},[D,je]),r.useEffect(()=>{if(ie.current)return;const n=Number(t?.slotNumber||0);E(n>0?String(n):"")},[t?.slotNumber]),r.useEffect(()=>{if(!N)return;const n=window.setTimeout(()=>S(null),5e3);return()=>window.clearTimeout(n)},[N]),r.useEffect(()=>{const n=Et({actionError:C,discoveryWakeError:j,statusPollError:A,runtimeError:t?.lastError}),s=Tt(be.current,n);be.current=s.nextKey,s.message&&S({kind:"error",message:s.message})},[j,C,A,t?.lastError]);const Z=r.useCallback(async()=>{const n=Date.now();if(!(!t?.authenticated||t.connectionState==="connected"||!["waiting","reconnecting"].includes(String(t.agent?.state||""))||n-ge.current<4e3)){ge.current=n;try{const s=await fetch(`${m}/api/client/reconnect`,{method:"POST",headers:{"Content-Type":"application/json","X-LiveDesk-CSRF":await H(m)},body:JSON.stringify({onlyIfWaiting:!0,reason:"app-active"})});if(!s.ok)throw new Error(`Client reconnect returned HTTP ${s.status}.`);g(null),await x()}catch(s){g(Ae(s))}}},[m,x,t]);r.useEffect(()=>{const n=()=>{Z()},s=()=>{Z()},a=()=>{document.visibilityState==="visible"&&Z()};return window.addEventListener("online",n),window.addEventListener("focus",s),document.addEventListener("visibilitychange",a),()=>{window.removeEventListener("online",n),window.removeEventListener("focus",s),document.removeEventListener("visibilitychange",a)}},[Z]),r.useEffect(()=>{if(!p||!t||!o?.access_token||t.authenticated)return;if(Ce(o)==="refresh-token-required"){xe().catch(f=>{W.current=!1,d(f instanceof Error?f.message:String(f))});return}let n=!1,s,a=0;const l=async()=>{if(n)return;try{const M=await ye(o);if(await x(),M)return}catch(M){d(M instanceof Error?M.message:String(M))}if(n)return;const f=Pe[Math.min(a,Pe.length-1)];a+=1,s=window.setTimeout(()=>{l()},f)};return l(),()=>{n=!0,s!==void 0&&window.clearTimeout(s)}},[p,x,xe,o,t,ye]),r.useEffect(()=>{const n=new URLSearchParams(window.location.search),s=n.get("auth_error");if(!s)return;d(s),n.delete("auth_error");const a=n.toString();window.history.replaceState(window.history.state,"",`${window.location.pathname}${a?`?${a}`:""}${window.location.hash}`)},[]),r.useEffect(()=>{let n=!0;if(!window.liveDesk?.auth)return u(null),v(!0),x(),()=>{n=!1};const s=window.liveDesk?.auth?.onComplete?.(l=>{l.ok&&l.session?V.auth.setSession(l.session).catch(f=>d(f instanceof Error?f.message:String(f))):l.ok&&l.signedOut?(u(null),v(!0),V.auth.signOut({scope:"local"})):l.error&&d(l.error)});(async()=>{try{const l=window.liveDesk?.auth?.getSession?await window.liveDesk.auth.getSession():await V.auth.getSession();if(!n)return;const f=pt(l);u(f),x()}catch(l){n&&d(l instanceof Error?l.message:String(l))}finally{n&&v(!0)}})();const{data:a}=V.auth.onAuthStateChange((l,f)=>{if(n){if(!f&&window.liveDesk?.auth){v(!0);return}u(f),v(!0),x()}});return()=>{n=!1,s?.(),a.subscription.unsubscribe()}},[x]),r.useEffect(()=>{if(!p||!t||pe.current||W.current||o||t.authenticated)return;const n=new URLSearchParams(window.location.search);if(!n.has("livedesk_launch"))return;pe.current=!0,n.delete("livedesk_launch");const s=n.toString();window.history.replaceState(window.history.state,"",`${window.location.pathname}${s?`?${s}`:""}${window.location.hash}`),F().catch(a=>d(a instanceof Error?a.message:String(a)))},[p,o,F,t]);const Be=async()=>{q(!1),window.liveDesk?.auth?.signOut?(await V.auth.signOut({scope:"local"}),(await window.liveDesk.auth.signOut())?.runtimeCleared===!1&&d("Signed out locally. The VuvoDesk runtime could not be reached to clear its session.")):await fetch(`${m}/api/auth/session`,{method:"DELETE",headers:{"X-LiveDesk-CSRF":await H(m)}}).catch(()=>{}),u(null),await x()},X=r.useCallback((n=!0)=>{y||(h(!1),n&&window.requestAnimationFrame(()=>fe.current?.focus()))},[y]),Ve=r.useCallback(()=>{y||!t?.authenticated||(fe.current=document.activeElement instanceof HTMLElement?document.activeElement:null,b(""),h(!0))},[y,t?.authenticated]);r.useEffect(()=>{if(!L)return;const n=window.requestAnimationFrame(()=>me.current?.focus()),s=l=>{l.target instanceof Element&&l.target.closest(".client-hub-switch-popover, .client-hub-switch-trigger")||X(!1)},a=l=>{l.key==="Escape"&&X()};return window.addEventListener("pointerdown",s),window.addEventListener("keydown",a),()=>{window.cancelAnimationFrame(n),window.removeEventListener("pointerdown",s),window.removeEventListener("keydown",a)}},[X,L]);const ze=async()=>{d("");try{const n=await fetch(`${m}/api/client/pin`,{method:"POST",headers:{"Content-Type":"application/json","X-LiveDesk-CSRF":await H(m)},body:JSON.stringify({pin:R})}),s=await n.json().catch(()=>({}));if(!n.ok||s?.ok===!1)throw new Error(s?.error||"PIN could not be accepted.");_(""),await x()}catch(n){d(n instanceof Error?n.message:String(n))}},Ge=async()=>{h(!1),P(!0),b(""),d("");try{const n=await fetch(`${m}/api/runtime/switch-role`,{method:"POST",headers:{"Content-Type":"application/json","X-LiveDesk-CSRF":await H(m)},body:JSON.stringify({role:"hub"})}),s=await n.json().catch(()=>({}));if(!n.ok||s?.ok===!1)throw new Error(s?.error||`Role change failed with HTTP ${n.status}.`);b("Hub role accepted. VuvoDesk is restarting this computer with the Hub Wall.");const a=Number(t?.runtimePid||0);await Ye({baseUrl:m,targetRole:"hub",previousRuntimePid:a,allowedStates:["running"]}),window.location.reload()}catch(n){d(n instanceof Error?n.message:String(n)),P(!1)}},We=async()=>{if(T)return;const n=Number(G);if(!Number.isInteger(n)||n<1||n>999){S({kind:"error",message:"Enter a slot number from 1 to 999."});return}B(!0),S(null);try{const s=await fetch(`${m}/api/client/slot`,{method:"POST",headers:{"Content-Type":"application/json","X-LiveDesk-CSRF":await H(m)},body:JSON.stringify({slotNumber:n})}),a=await s.json().catch(()=>({}));if(!s.ok||a.ok===!1){if(a.error==="slot-taken"){const l=a.conflict?.deviceName||a.conflict?.hostname||a.conflict?.deviceId||"another computer";throw new Error(`Slot ${String(n).padStart(3,"0")} is already used by ${l}.`)}throw new Error(a.error||`Slot update failed with HTTP ${s.status}.`)}ie.current=!1,E(String(n)),c(a),S({kind:"success",message:`Slot ${String(n).padStart(3,"0")} saved. VuvoDesk is reconnecting this Client.`})}catch(s){S({kind:"error",message:s instanceof Error?s.message:String(s)})}finally{B(!1),await x()}},Ke=async()=>{if(!w){z(!0),d("");try{const n=await fetch(`${m}/api/client/video-acceleration/install`,{method:"POST",headers:{"Content-Type":"application/json","X-LiveDesk-CSRF":await H(m)},body:JSON.stringify({confirm:!0})}),s=await n.json().catch(()=>({}));if(c(s),!n.ok||s.videoAcceleration?.ready!==!0)throw new Error(s.videoAcceleration?.error||s.error||"Linux hardware video setup did not complete.")}catch(n){d(n instanceof Error?n.message:String(n))}finally{z(!1),await x()}}},I=!!(t?.authenticated||o),J=r.useMemo(()=>gt(),[]),Xe=Ee("switch-to-hub",J),Je=Ee("client",J),O=bt(J);return e.jsxs("main",{className:"app-shell client-unified-shell",children:[e.jsxs("aside",{className:"tool-rail","aria-label":"VuvoDesk Client tools",children:[e.jsx("div",{className:"rail-brand",title:"VuvoDesk",children:e.jsx(qe,{size:25})}),e.jsx("nav",{className:"rail-tools","aria-label":"Client navigation",children:e.jsxs("div",{className:"client-main-tool-slot",children:[e.jsxs("button",{className:"rail-tool client-main-tool client-hub-tool client-hub-switch-trigger accent-blue",type:"button",onClick:Ve,disabled:y||!t?.authenticated,"aria-busy":y,"aria-expanded":L,"aria-haspopup":"dialog","aria-controls":L?"client-hub-switch-popover":void 0,"aria-label":`Hub. ${Xe}`,"data-tooltip":L?void 0:Te("Hub","switch-to-hub",J),children:[e.jsx(ne,{size:19}),e.jsx("span",{children:y?"Switching…":"Hub"})]}),e.jsxs("button",{className:`rail-tool client-main-tool accent-blue ${D==="computer"?"active":""}`,type:"button",onClick:()=>de("computer"),"aria-current":D==="computer"?"page":void 0,"aria-label":`Client. ${Je}`,"data-tooltip":Te("Client","client",J),children:[e.jsx(ce,{size:19}),e.jsx("span",{children:"Client"})]})]})}),e.jsxs("div",{className:"rail-footer",children:[Rt.map(n=>{const s=n.icon;return e.jsxs("button",{className:`rail-tool compact ${D===n.id?"active":""}`,type:"button",onClick:()=>de(n.id),title:n.label,children:[e.jsx(s,{size:18}),e.jsx("span",{children:n.label})]},n.id)}),e.jsxs("button",{className:`rail-account ${I?"signed-in":""}`,type:"button",onClick:()=>q(n=>!n),"aria-expanded":he,"aria-haspopup":"dialog","aria-controls":"vuvodesk-account-dialog",title:I?"Account":"Sign in",children:[I&&K?e.jsx("img",{src:K,alt:re,referrerPolicy:"no-referrer",onError:()=>_e(K)},K):I?e.jsx("span",{className:"avatar-fallback",children:ve}):e.jsx(Ie,{size:18}),e.jsx("span",{children:I?"Account":"Sign in"})]})]})]}),L&&e.jsxs("section",{id:"client-hub-switch-popover",className:"client-hub-switch-popover",role:"dialog","aria-modal":"false","aria-labelledby":"client-hub-switch-title","aria-describedby":"client-hub-switch-description client-hub-switch-restart",children:[e.jsxs("header",{className:"client-hub-switch-head",children:[e.jsx("span",{className:"client-hub-switch-mark","aria-hidden":"true",children:e.jsx(ne,{size:19})}),e.jsx("span",{className:"client-hub-switch-eyebrow",children:O.eyebrow}),e.jsx("button",{className:"client-hub-switch-close",type:"button",onClick:()=>X(),"aria-label":O.closeLabel,children:e.jsx(Se,{size:17})})]}),e.jsx("h2",{id:"client-hub-switch-title",children:O.title}),e.jsx("p",{id:"client-hub-switch-description",children:O.description}),e.jsxs("p",{id:"client-hub-switch-restart",className:"client-hub-switch-notice",children:[e.jsx($e,{size:16,"aria-hidden":"true"}),e.jsx("span",{children:O.restartNotice})]}),e.jsxs("div",{className:"client-hub-switch-actions",children:[e.jsx("button",{type:"button",onClick:()=>X(),children:O.cancel}),e.jsxs("button",{ref:me,className:"primary",type:"button",onClick:()=>{Ge()},disabled:y||!t?.authenticated,children:[e.jsx(ne,{size:16}),O.confirm]})]})]}),e.jsx(wt,{open:he,signedIn:I,accountName:I?re:"Account",accountEmail:o?.user.email||t?.userEmail||t?.userId||"",accountInitial:ve,avatarUrl:K,currentPlan:"free",currentPlanKnown:!1,deviceCount:t?1:0,deviceLimit:Number.POSITIVE_INFINITY,licenseLabel:"Plan is managed at the Hub. This Client does not choose or change billing.",networkLabel:t?.connectionState==="connected"?"Connected to Hub":t?"Finding Hub":"Checking local app",accessLabel:t?.auth?.persisted?"Saved sign-in on this computer":I?"Active for this run":"Not signed in",authBusy:!p,checkoutBusy:!1,ltdCheckoutEnabled:!1,monthlyCheckoutEnabled:!1,teamCheckoutEnabled:!1,planChangesEnabled:!1,planManagementLabel:"Plan is managed at the Hub",showWorkspaceSwitcher:!1,workspaces:[],activeWorkspaceId:"",onClose:()=>q(!1),onSignIn:async()=>{q(!1),await F()},onSignOut:Be,onSelectPlan:()=>{}}),e.jsx("section",{className:"desk-stage client-unified-stage",children:D==="support"?e.jsx("section",{className:"client-unified-page",children:e.jsx(ft,{session:o,currentProductKey:"livedesk",appVersion:t?.appVersion||"",onSignIn:()=>{F()}})}):D!=="computer"?e.jsx("section",{className:"client-unified-page",children:e.jsx(Ot,{page:D,status:t})}):e.jsx(Mt,{session:o,status:t,pin:R,roleChangeMessage:$,videoAccelerationBusy:w,slotInput:G,slotBusy:T,onSignIn:()=>{F()},onPinChange:n=>_(n.replace(/\D/g,"").slice(0,6)),onSubmitPin:()=>{ze()},onSlotInputChange:n=>{ie.current=!0,E(n.replace(/\D/g,"").slice(0,3))},onSaveSlot:()=>{We()},onEnableVideoAcceleration:()=>{Ke()}})}),N&&e.jsxs("div",{className:`client-side-toast ${N.kind}`,role:N.kind==="error"?"alert":"status",children:[N.kind==="error"?e.jsx(it,{size:18}):e.jsx(Fe,{size:18}),e.jsx("span",{children:N.message}),e.jsx("button",{type:"button",onClick:()=>S(null),"aria-label":"Dismiss notification",children:e.jsx(Se,{size:16})})]})]})}const Ut=1e3,_t=5e3;function Bt(){return typeof window>"u"?!1:(!!window.navigator.standalone||window.matchMedia("(display-mode: standalone), (display-mode: fullscreen)").matches)&&window.matchMedia("(pointer: coarse)").matches}function Vt(){return typeof window>"u"?[]:[...new Set([window.location.origin,te].filter(Boolean))]}function Qt(){const t=Qe(),c=Bt(),o=t||c,u=t||typeof window>"u"?te:window.location.origin,p=o?{role:"hub",baseUrl:u}:null,[v,R]=r.useState(p),_=r.useRef(null);return r.useEffect(()=>{if(o){R(b=>b?.role==="hub"&&b.baseUrl===u?b:{role:"hub",baseUrl:u});return}let C=!1,d=!1,A=null;const k=new AbortController,j=async()=>{for(const b of Vt()){if(k.signal.aborted)return;try{const P=(await et("/api/runtime/status",{signal:k.signal,timeoutMs:900},b)).role==="hub"?"hub":"client";d=!0,C||R(w=>w?.role===P&&w.baseUrl===b?w:{role:P,baseUrl:b});return}catch{if(k.signal.aborted)return}}},g=()=>{Ze(_,j).finally(()=>{!C&&!k.signal.aborted&&(A=window.setTimeout(g,d?_t:Ut))}).catch(()=>{})};g();const $=window.setTimeout(()=>{!C&&!d&&R(b=>b?.role==="client"&&b.baseUrl===te?b:{role:"client",baseUrl:te})},2500);return()=>{C=!0,k.abort(new Error("VuvoDesk runtime role probe stopped.")),A!==null&&window.clearTimeout(A),window.clearTimeout($)}},[null,o,u]),v?v.role==="client"?e.jsx(Ht,{runtimeBaseUrl:v.baseUrl}):e.jsx(vt,{runtimeBaseUrl:v.baseUrl}):e.jsxs("main",{className:"runtime-role-boot","aria-busy":"true","aria-label":"Starting VuvoDesk",children:[e.jsx("span",{className:"runtime-role-boot-mark","aria-hidden":"true"}),e.jsx("strong",{children:"Starting VuvoDesk"}),e.jsx("p",{children:"Loading this computer's assigned role..."})]})}export{Qt as VuvoDeskApp};
1
+ import{j as e,M as qe,w as Ye,i as Qe,D as te,e as Ze,h as et}from"./styles-CO6aHjLW.js";import{a as r,g as ne,c as ce,aa as tt,ab as nt,ac as st,ad as De,ae as Ie,X as Se,k as $e,af as it,b as Fe,w as rt,ag as ke,ah as at,ai as ot,O as Ne,W as ct,aj as lt,ak as ut,a2 as dt,J as ht,al as mt}from"./icons-CacEbnZu.js";import{SupportPage as ft}from"./SupportPage-BwS_toBH.js";import{s as V,g as Ce,n as pt,r as gt,a as Ee,b as bt,f as Te,A as wt,H as vt}from"./App-WZAN0Iwh.js";import{DesktopUpdatePanel as xt}from"./DesktopUpdatePanel-BPD9IaXS.js";import"./react-Cfedt3N5.js";import"./supabase-C7qjtLN3.js";import"./frame-lifecycle-esOdP-EY.js";function yt(i=[]){return[...new Set(i.filter(t=>typeof t=="string").map(t=>t.trim()).filter(Boolean))]}function jt(i=[],t=new Set){return i.find(c=>!t.has(c))||""}function St(i=[],t=new Set){const c=new Set(i);return new Set([...t].filter(o=>c.has(o)))}const kt="client-runtime-connection-unavailable",Nt="VuvoDesk Client is reconnecting to the service on this computer.",Ct=/failed to fetch|fetch failed|network request failed|networkerror when attempting to fetch resource|load failed|signal timed out|operation was aborted due to timeout|the operation timed out/i;function Oe(i,t){if(!i||typeof i!="object")return"";const c=i[t];return typeof c=="string"?c.trim():""}function le(i){return typeof i=="string"?i.trim():Oe(i,"message")||String(i??"").trim()}function Re(i){return/supabase-user-verification-failed:(401|403)/i.test(i)?"Saved sign-in needs refresh. VuvoDesk will retry it automatically.":i}function Ae(i){const t=le(i)||"Client status could not be refreshed.",c=Oe(i,"name").toLowerCase();return c==="timeouterror"||c==="aborterror"||Ct.test(t)?{key:kt,message:Nt}:{key:`client-status-poll:${t}`,message:t}}function Et({actionError:i="",discoveryWakeError:t=null,statusPollError:c=null,runtimeError:o=""}={}){const u=le(i);if(u)return{key:`client-action:${u}`,message:Re(u)};if(t?.key&&t?.message)return t;if(c?.key&&c?.message)return c;const p=le(o);return p?{key:`client-runtime:${p}`,message:Re(p)}:null}function Tt(i,t){if(!t?.message)return{nextKey:"",message:null};const c=String(t.key||t.message).trim();return String(i||"")===c?{nextKey:c,message:null}:{nextKey:c,message:t.message}}const Rt=[{id:"docs",label:"Doc",icon:tt},{id:"support",label:"Support",icon:nt},{id:"pricing",label:"Pricing",icon:st},{id:"settings",label:"Settings",icon:De}],Pe=[0,1e3,2e3,5e3,1e4,2e4,3e4],At=15e3;function Pt(i=""){const t=i.trim().replace(/\/+$/,"");return t||(typeof window>"u"?"":window.location.port==="5179"?window.location.origin:"http://127.0.0.1:5179")}async function H(i,t=!1){const c=t?"":sessionStorage.getItem("livedesk.client.csrf");if(c)return c;const o=await fetch(`${i}/api/runtime/status`,{cache:"no-store"}),u=await o.json().catch(()=>({}));if(!o.ok)throw new Error(u.error||`Client runtime returned HTTP ${o.status}.`);const p=String(u.csrfToken||"");return p&&sessionStorage.setItem("livedesk.client.csrf",p),p}function ue(i){return`${Math.max(0,Math.min(100,Number(i||0))).toFixed(0)}%`}function oe(i){return Number.isFinite(i)?`${Math.max(0,Math.min(100,Number(i))).toFixed(0)}%`:"—"}function Me(i){return Number.isFinite(i)?ue(Number(i)):"0%"}function U(i){const t=Number(i);return!Number.isFinite(t)||t<0?"—":t>=1024**4?`${(t/1024**4).toFixed(t>=10*1024**4?0:1)} TB`:t>=1024**3?`${(t/1024**3).toFixed(t>=10*1024**3?0:1)} GB`:`${(t/1024**2).toFixed(0)} MB`}function Le(i){const t=Math.max(0,Number(i||0));return t>=1024**3?`${(t/1024**3).toFixed(1)} GB/s`:t>=1024**2?`${(t/1024**2).toFixed(1)} MB/s`:t>=1024?`${(t/1024).toFixed(1)} KB/s`:`${t.toFixed(0)} B/s`}function Lt(i,t){const c=Number(i||0)/1073741824,o=Number(t||0)/1024**3;return o>0?`${c.toFixed(1)} / ${o.toFixed(1)} GB`:"Checking..."}function Dt(i,t){return!Number.isFinite(t)||Number(t)<=0?"Dedicated memory unavailable":Number.isFinite(i)?`${U(i)} / ${U(t)}`:`${U(t)} available`}function It(i){const t=Math.max(0,Math.floor(Number(i||0)/60)),c=Math.floor(t/1440),o=Math.floor(t%1440/60),u=t%60;return c>0?`${c}d ${o}h`:o>0?`${o}h ${u}m`:`${u}m`}function ee({icon:i,label:t,value:c,detail:o,percent:u}){return e.jsxs("article",{className:"client-resource-metric",children:[e.jsxs("div",{className:"client-resource-metric-head",children:[e.jsx(i,{size:17}),e.jsx("span",{children:t}),e.jsx("strong",{children:c})]}),e.jsx("small",{title:o,children:o}),e.jsx("i",{"aria-hidden":"true",children:e.jsx("b",{style:{width:Me(u)}})})]})}function $t(i,t){const c=i?.user.user_metadata||{},o=c.full_name||c.name||c.preferred_username||i?.user.email||i?.user.id||t?.userName||t?.auth?.name||t?.userEmail||t?.userId||"Account";return String(o).trim()||"Account"}function Ft(i,t){const c=i?.user.user_metadata||{};return yt([c.avatar_url,c.picture,t?.userAvatarUrl,t?.auth?.avatarUrl])}function Ot({page:i,status:t}){const o={docs:{label:"DOC",title:"Using this computer",body:"This computer connects to its assigned Hub automatically. Keep VuvoDesk running and manage screens, files, and remote control from the Hub."},pricing:{label:"PRICING",title:"VuvoDesk plans",body:"Plans are based on how many computers the Hub manages. This Client computer does not need a separate subscription or setup."},settings:{label:"SETTINGS",title:"Computer preferences",body:"VuvoDesk keeps the verified sign-in on this computer and reconnects to the assigned Hub automatically."}}[i];return e.jsxs("section",{className:"client-service-page",children:[e.jsx("span",{className:"client-unified-section-label",children:o.label}),e.jsx("h2",{children:o.title}),e.jsx("p",{children:o.body}),e.jsxs("div",{className:"client-service-page-card",children:[i==="docs"&&e.jsxs(e.Fragment,{children:[e.jsx("strong",{children:"Automatic connection"}),e.jsx("span",{children:"Start VuvoDesk. This computer signs in, finds the Hub, and becomes available without another action."})]}),i==="pricing"&&e.jsxs(e.Fragment,{children:[e.jsx("strong",{children:"One plan, managed at the Hub"}),e.jsx("span",{children:"Computer access follows the account and plan used by the Hub."})]}),i==="settings"&&e.jsxs(e.Fragment,{children:[e.jsx("strong",{children:"Saved sign-in"}),e.jsx("span",{children:t?.auth?.persisted?"Enabled on this computer":"Sign in once to enable automatic reconnection"})]})]}),i==="settings"&&e.jsx(xt,{})]})}function Mt({session:i,status:t,pin:c,roleChangeMessage:o,videoAccelerationBusy:u,slotInput:p,slotBusy:v,onSignIn:R,onPinChange:_,onSubmitPin:C,onSlotInputChange:d,onSaveSlot:A,onEnableVideoAcceleration:k}){const j=t?.connectionState==="connected",g=t?.videoAcceleration,$=u||g?.busy||g?.state==="installing",b=g?.ready?`${g.encoder||"H.264"} · ${g.driver||"VAAPI"}`:$?"Setting up…":g?.message||"Setup available",y=t?.system?.memoryTotalBytes?Number(t.system.memoryUsedBytes||0)/t.system.memoryTotalBytes*100:0,P=t?.system?.gpus||[],w=P.find(h=>Number.isFinite(h.usagePercent)||Number.isFinite(h.memoryTotalBytes))||P[0],z=Number.isFinite(w?.usagePercent)?Number(w?.usagePercent):null,G=Number.isFinite(w?.memoryTotalBytes)&&Number(w?.memoryTotalBytes)>0&&Number.isFinite(w?.memoryUsedBytes)?Number(w?.memoryUsedBytes)/Number(w?.memoryTotalBytes)*100:null,E=t?.system?.disks||[],T=t?.system?.network,B=!!T?.sampledAt,N=T?.interfaces||[],S=[...new Set(N.map(h=>h.name).filter(Boolean))],L=N.map(h=>h.address).filter(Boolean);return e.jsxs("section",{className:"client-home-page",children:[e.jsxs("header",{className:"client-home-heading",children:[e.jsxs("div",{children:[e.jsx("p",{className:"client-unified-eyebrow",children:"THIS COMPUTER"}),e.jsx("h2",{children:t?.deviceName||"This Computer"})]}),e.jsxs("span",{className:`client-unified-role-chip ${j?"connected":""}`,children:[e.jsx(ce,{size:15})," ",j?"Online":"Connecting"]})]}),e.jsxs("div",{className:"client-home-layout",children:[e.jsxs("section",{className:"client-main-surface","aria-label":"Client computer",children:[e.jsxs("div",{className:"client-main-surface-head",children:[e.jsxs("div",{children:[e.jsx("span",{className:"client-unified-section-label",children:"SYSTEM STATUS"}),e.jsx("strong",{children:"Hardware at a glance"})]}),e.jsxs("small",{children:[e.jsx(rt,{size:14})," Uptime ",It(t?.system?.uptimeSeconds)]})]}),e.jsxs("div",{className:"client-system-dashboard",children:[e.jsxs("section",{className:"client-performance-panel","aria-label":"Performance",children:[e.jsxs("div",{className:"client-system-subhead",children:[e.jsxs("div",{children:[e.jsx("span",{className:"client-unified-section-label",children:"PERFORMANCE"}),e.jsx("strong",{children:"Live resources"})]}),e.jsx("small",{children:"Live updates"})]}),e.jsxs("div",{className:"client-live-metrics",children:[e.jsx(ee,{icon:ke,label:"CPU",value:ue(t?.system?.cpuUsagePercent),detail:t?.system?.cpuLogicalCores?`${t.system.cpuLogicalCores} logical cores · Current processor usage`:"Current processor usage",percent:t?.system?.cpuUsagePercent}),e.jsx(ee,{icon:at,label:"Memory",value:ue(y),detail:Lt(t?.system?.memoryUsedBytes,t?.system?.memoryTotalBytes),percent:y}),e.jsx(ee,{icon:ot,label:"GPU",value:oe(z),detail:w?.name||(t?.system?.hardwareCollecting?"Detecting graphics hardware…":"Graphics usage unavailable"),percent:z}),e.jsx(ee,{icon:ne,label:"VRAM",value:oe(G),detail:Dt(w?.memoryUsedBytes,w?.memoryTotalBytes),percent:G})]})]}),e.jsxs("section",{className:"client-storage-panel","aria-label":"Local storage",children:[e.jsxs("div",{className:"client-system-subhead",children:[e.jsxs("div",{children:[e.jsx("span",{className:"client-unified-section-label",children:"STORAGE"}),e.jsx("strong",{children:"Local drives"})]}),e.jsx("small",{children:E.length>0?`${E.length} ${E.length===1?"drive":"drives"}`:t?.system?.hardwareCollecting?"Detecting…":"Unavailable"})]}),e.jsxs("div",{className:"client-disk-list",children:[E.map(h=>e.jsxs("article",{className:"client-disk-row",children:[e.jsxs("div",{className:"client-disk-row-head",children:[e.jsxs("span",{children:[e.jsx(Ne,{size:16}),e.jsx("strong",{children:h.mount||"Disk"}),e.jsx("small",{children:h.name||"Local disk"})]}),e.jsx("strong",{children:oe(h.usedPercent)})]}),e.jsxs("small",{children:[U(h.freeBytes)," free · ",U(h.totalBytes)," total"]}),e.jsx("i",{"aria-hidden":"true",children:e.jsx("b",{style:{width:Me(h.usedPercent)}})})]},`${h.mount}-${h.name||""}`)),E.length===0&&e.jsxs("div",{className:"client-disk-empty",children:[e.jsx(Ne,{size:18}),e.jsx("span",{children:t?.system?.hardwareCollecting?"Detecting local drives…":"Storage information is not available."})]})]})]}),e.jsxs("section",{className:"client-network-panel","aria-label":"Network activity",children:[e.jsxs("div",{className:"client-system-subhead",children:[e.jsxs("div",{children:[e.jsx("span",{className:"client-unified-section-label",children:"NETWORK"}),e.jsx("strong",{children:"Live traffic"})]}),e.jsx("small",{children:S.length>0?S.join(", "):"Detecting adapter"})]}),e.jsxs("div",{className:"client-network-live",children:[e.jsxs("div",{className:"client-network-adapter",children:[e.jsx(ct,{size:18}),e.jsxs("span",{children:[e.jsx("strong",{children:S.join(", ")||"Network"}),e.jsx("small",{children:L.join(" / ")||"Checking active connection"})]})]}),e.jsxs("div",{className:"client-network-rate download",children:[e.jsx(lt,{size:17}),e.jsxs("span",{children:[e.jsx("small",{children:"Receive"}),e.jsx("strong",{children:Le(B?T?.receiveBytesPerSecond:void 0)})]})]}),e.jsxs("div",{className:"client-network-rate upload",children:[e.jsx(ut,{size:17}),e.jsxs("span",{children:[e.jsx("small",{children:"Send"}),e.jsx("strong",{children:Le(B?T?.sendBytesPerSecond:void 0)})]})]}),e.jsx("small",{className:"client-network-total",children:B?`Total ${U(T?.receivedBytes)} received / ${U(T?.sentBytes)} sent`:"Traffic has not been measured yet."})]})]})]})]}),e.jsxs("aside",{className:"client-connection-panel",children:[e.jsx("span",{className:"client-unified-section-label",children:"LIVEDESK SERVICES"}),e.jsx("h3",{children:j?"Connected to the Hub":t?.authenticated?"Finding the Hub":"Connect this computer"}),j?e.jsxs(e.Fragment,{children:[e.jsx("p",{className:"client-unified-copy",children:"This computer is available from the VuvoDesk Hub."}),e.jsxs("div",{className:"client-service-status-list",children:[e.jsxs("div",{children:[e.jsx(ce,{size:17}),e.jsxs("span",{children:[e.jsx("strong",{children:"Screen sharing"}),e.jsx("small",{children:t?.permissions?.screenCapture?"Active":"Waiting"})]})]}),e.jsxs("div",{children:[e.jsx($e,{size:17}),e.jsxs("span",{children:[e.jsx("strong",{children:"Remote control"}),e.jsx("small",{children:t?.permissions?.remoteInput?"Available":"Unavailable"})]})]}),e.jsxs("div",{children:[e.jsx(dt,{size:17}),e.jsxs("span",{children:[e.jsx("strong",{children:"File delivery"}),e.jsx("small",{children:t?.permissions?.files?"Ready to receive":"Unavailable"})]})]}),e.jsxs("div",{children:[e.jsx(ht,{size:17}),e.jsxs("span",{children:[e.jsx("strong",{children:"Computer audio"}),e.jsx("small",{children:t?.permissions?.audio?"Available":"Unavailable"})]})]})]})]}):e.jsxs(e.Fragment,{children:[e.jsx("p",{className:"client-unified-copy",children:t?.authenticated?"VuvoDesk is checking the registered Hub endpoints. A temporary Hub PIN can be used as a fallback.":"Sign in once on this computer. VuvoDesk saves the verified session, finds the Hub, and starts screen sharing automatically."}),!i&&!t?.authenticated?e.jsxs("button",{className:"client-unified-primary",type:"button",onClick:R,children:[e.jsx(Ie,{size:16})," Sign in with Google"]}):e.jsxs("p",{className:"client-unified-success",children:[e.jsx(Fe,{size:16})," Signed in as ",i?.user.email||i?.user.id||t?.userEmail||t?.userId||"saved account"]}),e.jsxs("div",{className:"client-unified-pin-row",children:[e.jsx(mt,{size:16}),e.jsx("input",{value:c,onChange:h=>_(h.target.value),placeholder:"6-digit Hub PIN",inputMode:"numeric","aria-label":"6-digit Hub PIN"}),e.jsx("button",{className:"client-unified-secondary",type:"button",onClick:C,disabled:c.length!==6,children:"Connect"})]})]}),g?.supported&&e.jsxs("div",{className:`client-video-acceleration state-${g.ready?"ready":g.state||"setup-required"}`,children:[e.jsx(ke,{size:17}),e.jsxs("span",{children:[e.jsx("strong",{children:"Hardware video"}),e.jsx("small",{children:b})]}),!g.ready&&g.canInstall&&e.jsx("button",{type:"button",onClick:k,disabled:$,title:"Install and verify Linux FFmpeg hardware video support",children:$?"Setting up":"Enable"})]}),o&&e.jsx("p",{className:"client-unified-success",children:o}),e.jsxs("div",{className:"client-slot-setting",children:[e.jsxs("label",{htmlFor:"client-slot-number",children:[e.jsx("span",{children:"Slot"}),e.jsx("input",{id:"client-slot-number",value:p,onChange:h=>d(h.target.value),inputMode:"numeric",pattern:"[0-9]*",placeholder:"1-999","aria-label":"Wall slot number",disabled:!j||v})]}),e.jsxs("button",{type:"button",onClick:A,disabled:!j||v||!p,"aria-label":"Save slot number",title:"Save slot number",children:[e.jsx(De,{size:17}),e.jsx("span",{children:v?"Saving":"Set"})]})]})]})]})]})}function Ht({runtimeBaseUrl:i=""}){const[t,c]=r.useState(null),[o,u]=r.useState(null),[p,v]=r.useState(!1),[R,_]=r.useState(""),[C,d]=r.useState(""),[A,k]=r.useState(null),[j,g]=r.useState(null),[$,b]=r.useState(""),[y,P]=r.useState(!1),[w,z]=r.useState(!1),[G,E]=r.useState(""),[T,B]=r.useState(!1),[N,S]=r.useState(null),[L,h]=r.useState(!1),[D,de]=r.useState("computer"),[he,q]=r.useState(!1),me=r.useRef(null),fe=r.useRef(null),se=r.useRef(!1),Y=r.useRef(null),Q=r.useRef(null),pe=r.useRef(!1),W=r.useRef(!1),ge=r.useRef(0),ie=r.useRef(!1),be=r.useRef(""),m=r.useMemo(()=>Pt(i),[i]),re=r.useMemo(()=>$t(o,t),[o,t]),ae=r.useMemo(()=>Ft(o,t),[o,t]),He=JSON.stringify(ae),[Ue,we]=r.useState(()=>new Set),K=jt(ae,Ue),ve=re.slice(0,1).toUpperCase()||"L";r.useEffect(()=>{we(n=>St(ae,n))},[He]);const _e=r.useCallback(n=>{we(s=>{if(!n||s.has(n))return s;const a=new Set(s);return a.add(n),a})},[]),F=r.useCallback(async()=>{if(d(""),window.liveDesk?.auth?.signInWithGoogle){const n=await window.liveDesk.auth.signInWithGoogle();return n?.ok===!1?(d(n.error||"VuvoDesk could not open the system browser for Google sign-in."),!1):!0}return window.location.assign(`${m}/auth/google`),!0},[m]),xe=r.useCallback(async()=>{W.current||(W.current=!0,d("Your saved Google sign-in is incomplete. VuvoDesk is reopening sign-in once to repair it."),await V.auth.signOut({scope:"local"}).catch(()=>{}),u(null),await F()||(W.current=!1))},[F]),ye=r.useCallback(async n=>{const s=Ce(n);if(s)throw new Error(s);if(se.current)return!1;se.current=!0;try{if(window.liveDesk?.auth?.persistSession){const a=await window.liveDesk.auth.persistSession(n);if(a?.ok===!1)throw new Error(a.error||"VuvoDesk could not validate the desktop session.");return d(""),!0}for(let a=0;a<2;a+=1){const l=await H(m,a>0),f=await fetch(`${m}/api/auth/session`,{method:"POST",headers:{"Content-Type":"application/json","X-LiveDesk-CSRF":l},signal:AbortSignal.timeout(1e4),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=await f.json().catch(()=>({}));if(f.status===403&&a===0){sessionStorage.removeItem("livedesk.client.csrf");continue}if(!f.ok)throw new Error(M.error||`Client runtime returned HTTP ${f.status}.`);return d(""),!0}throw new Error("Client runtime rejected the session transfer.")}finally{se.current=!1}},[m]),x=r.useCallback(()=>{const n=Y.current;if(n)return n;const s=(async()=>{try{const a=await fetch(`${m}/api/client/status`,{cache:"no-store",signal:AbortSignal.timeout(1500)}),l=await a.json().catch(()=>({}));if(!a.ok)throw new Error(l.error||`Client runtime returned HTTP ${a.status}.`);if(l.csrfToken&&sessionStorage.setItem("livedesk.client.csrf",l.csrfToken),String(l.role||"").toLowerCase()==="hub")throw new Error("This computer is assigned to the Hub role.");c(l),k(null),g(null)}catch(a){k(Ae(a))}})();return Y.current=s,s.finally(()=>{Y.current===s&&(Y.current=null)}),s},[m]),je=r.useCallback(()=>{const n=Q.current;if(n)return n;const s=(async()=>{const a=await fetch(`${m}/api/client/diagnostics`,{cache:"no-store",signal:AbortSignal.timeout(8e3)}),l=await a.json().catch(()=>({}));if(!a.ok)throw new Error(l.error||`Client diagnostics returned HTTP ${a.status}.`);if(String(l.role||"").toLowerCase()==="hub")throw new Error("This computer is assigned to the Hub role.");c(l)})();return Q.current=s,s.finally(()=>{Q.current===s&&(Q.current=null)}),s},[m]);r.useEffect(()=>{let n=!1,s=null;const a=async()=>{await x(),n||(s=window.setTimeout(()=>{a()},2e3))};return a(),()=>{n=!0,s!==null&&window.clearTimeout(s)}},[x]),r.useEffect(()=>{if(D!=="computer")return;let n=!1,s=null;const a=()=>{n||(s=window.setTimeout(()=>{l()},At))},l=async()=>{document.visibilityState==="visible"&&await je().catch(()=>{}),a()},f=()=>{document.visibilityState==="visible"&&(s!==null&&(window.clearTimeout(s),s=null),l())};return document.addEventListener("visibilitychange",f),l(),()=>{n=!0,document.removeEventListener("visibilitychange",f),s!==null&&window.clearTimeout(s)}},[D,je]),r.useEffect(()=>{if(ie.current)return;const n=Number(t?.slotNumber||0);E(n>0?String(n):"")},[t?.slotNumber]),r.useEffect(()=>{if(!N)return;const n=window.setTimeout(()=>S(null),5e3);return()=>window.clearTimeout(n)},[N]),r.useEffect(()=>{const n=Et({actionError:C,discoveryWakeError:j,statusPollError:A,runtimeError:t?.lastError}),s=Tt(be.current,n);be.current=s.nextKey,s.message&&S({kind:"error",message:s.message})},[j,C,A,t?.lastError]);const Z=r.useCallback(async()=>{const n=Date.now();if(!(!t?.authenticated||t.connectionState==="connected"||!["waiting","reconnecting"].includes(String(t.agent?.state||""))||n-ge.current<4e3)){ge.current=n;try{const s=await fetch(`${m}/api/client/reconnect`,{method:"POST",headers:{"Content-Type":"application/json","X-LiveDesk-CSRF":await H(m)},body:JSON.stringify({onlyIfWaiting:!0,reason:"app-active"})});if(!s.ok)throw new Error(`Client reconnect returned HTTP ${s.status}.`);g(null),await x()}catch(s){g(Ae(s))}}},[m,x,t]);r.useEffect(()=>{const n=()=>{Z()},s=()=>{Z()},a=()=>{document.visibilityState==="visible"&&Z()};return window.addEventListener("online",n),window.addEventListener("focus",s),document.addEventListener("visibilitychange",a),()=>{window.removeEventListener("online",n),window.removeEventListener("focus",s),document.removeEventListener("visibilitychange",a)}},[Z]),r.useEffect(()=>{if(!p||!t||!o?.access_token||t.authenticated)return;if(Ce(o)==="refresh-token-required"){xe().catch(f=>{W.current=!1,d(f instanceof Error?f.message:String(f))});return}let n=!1,s,a=0;const l=async()=>{if(n)return;try{const M=await ye(o);if(await x(),M)return}catch(M){d(M instanceof Error?M.message:String(M))}if(n)return;const f=Pe[Math.min(a,Pe.length-1)];a+=1,s=window.setTimeout(()=>{l()},f)};return l(),()=>{n=!0,s!==void 0&&window.clearTimeout(s)}},[p,x,xe,o,t,ye]),r.useEffect(()=>{const n=new URLSearchParams(window.location.search),s=n.get("auth_error");if(!s)return;d(s),n.delete("auth_error");const a=n.toString();window.history.replaceState(window.history.state,"",`${window.location.pathname}${a?`?${a}`:""}${window.location.hash}`)},[]),r.useEffect(()=>{let n=!0;if(!window.liveDesk?.auth)return u(null),v(!0),x(),()=>{n=!1};const s=window.liveDesk?.auth?.onComplete?.(l=>{l.ok&&l.session?V.auth.setSession(l.session).catch(f=>d(f instanceof Error?f.message:String(f))):l.ok&&l.signedOut?(u(null),v(!0),V.auth.signOut({scope:"local"})):l.error&&d(l.error)});(async()=>{try{const l=window.liveDesk?.auth?.getSession?await window.liveDesk.auth.getSession():await V.auth.getSession();if(!n)return;const f=pt(l);u(f),x()}catch(l){n&&d(l instanceof Error?l.message:String(l))}finally{n&&v(!0)}})();const{data:a}=V.auth.onAuthStateChange((l,f)=>{if(n){if(!f&&window.liveDesk?.auth){v(!0);return}u(f),v(!0),x()}});return()=>{n=!1,s?.(),a.subscription.unsubscribe()}},[x]),r.useEffect(()=>{if(!p||!t||pe.current||W.current||o||t.authenticated)return;const n=new URLSearchParams(window.location.search);if(!n.has("livedesk_launch"))return;pe.current=!0,n.delete("livedesk_launch");const s=n.toString();window.history.replaceState(window.history.state,"",`${window.location.pathname}${s?`?${s}`:""}${window.location.hash}`),F().catch(a=>d(a instanceof Error?a.message:String(a)))},[p,o,F,t]);const Be=async()=>{q(!1),window.liveDesk?.auth?.signOut?(await V.auth.signOut({scope:"local"}),(await window.liveDesk.auth.signOut())?.runtimeCleared===!1&&d("Signed out locally. The VuvoDesk runtime could not be reached to clear its session.")):await fetch(`${m}/api/auth/session`,{method:"DELETE",headers:{"X-LiveDesk-CSRF":await H(m)}}).catch(()=>{}),u(null),await x()},X=r.useCallback((n=!0)=>{y||(h(!1),n&&window.requestAnimationFrame(()=>fe.current?.focus()))},[y]),Ve=r.useCallback(()=>{y||!t?.authenticated||(fe.current=document.activeElement instanceof HTMLElement?document.activeElement:null,b(""),h(!0))},[y,t?.authenticated]);r.useEffect(()=>{if(!L)return;const n=window.requestAnimationFrame(()=>me.current?.focus()),s=l=>{l.target instanceof Element&&l.target.closest(".client-hub-switch-popover, .client-hub-switch-trigger")||X(!1)},a=l=>{l.key==="Escape"&&X()};return window.addEventListener("pointerdown",s),window.addEventListener("keydown",a),()=>{window.cancelAnimationFrame(n),window.removeEventListener("pointerdown",s),window.removeEventListener("keydown",a)}},[X,L]);const ze=async()=>{d("");try{const n=await fetch(`${m}/api/client/pin`,{method:"POST",headers:{"Content-Type":"application/json","X-LiveDesk-CSRF":await H(m)},body:JSON.stringify({pin:R})}),s=await n.json().catch(()=>({}));if(!n.ok||s?.ok===!1)throw new Error(s?.error||"PIN could not be accepted.");_(""),await x()}catch(n){d(n instanceof Error?n.message:String(n))}},Ge=async()=>{h(!1),P(!0),b(""),d("");try{const n=await fetch(`${m}/api/runtime/switch-role`,{method:"POST",headers:{"Content-Type":"application/json","X-LiveDesk-CSRF":await H(m)},body:JSON.stringify({role:"hub"})}),s=await n.json().catch(()=>({}));if(!n.ok||s?.ok===!1)throw new Error(s?.error||`Role change failed with HTTP ${n.status}.`);b("Hub role accepted. VuvoDesk is restarting this computer with the Hub Wall.");const a=Number(t?.runtimePid||0);await Ye({baseUrl:m,targetRole:"hub",previousRuntimePid:a,allowedStates:["running"]}),window.location.reload()}catch(n){d(n instanceof Error?n.message:String(n)),P(!1)}},We=async()=>{if(T)return;const n=Number(G);if(!Number.isInteger(n)||n<1||n>999){S({kind:"error",message:"Enter a slot number from 1 to 999."});return}B(!0),S(null);try{const s=await fetch(`${m}/api/client/slot`,{method:"POST",headers:{"Content-Type":"application/json","X-LiveDesk-CSRF":await H(m)},body:JSON.stringify({slotNumber:n})}),a=await s.json().catch(()=>({}));if(!s.ok||a.ok===!1){if(a.error==="slot-taken"){const l=a.conflict?.deviceName||a.conflict?.hostname||a.conflict?.deviceId||"another computer";throw new Error(`Slot ${String(n).padStart(3,"0")} is already used by ${l}.`)}throw new Error(a.error||`Slot update failed with HTTP ${s.status}.`)}ie.current=!1,E(String(n)),c(a),S({kind:"success",message:`Slot ${String(n).padStart(3,"0")} saved. VuvoDesk is reconnecting this Client.`})}catch(s){S({kind:"error",message:s instanceof Error?s.message:String(s)})}finally{B(!1),await x()}},Ke=async()=>{if(!w){z(!0),d("");try{const n=await fetch(`${m}/api/client/video-acceleration/install`,{method:"POST",headers:{"Content-Type":"application/json","X-LiveDesk-CSRF":await H(m)},body:JSON.stringify({confirm:!0})}),s=await n.json().catch(()=>({}));if(c(s),!n.ok||s.videoAcceleration?.ready!==!0)throw new Error(s.videoAcceleration?.error||s.error||"Linux hardware video setup did not complete.")}catch(n){d(n instanceof Error?n.message:String(n))}finally{z(!1),await x()}}},I=!!(t?.authenticated||o),J=r.useMemo(()=>gt(),[]),Xe=Ee("switch-to-hub",J),Je=Ee("client",J),O=bt(J);return e.jsxs("main",{className:"app-shell client-unified-shell",children:[e.jsxs("aside",{className:"tool-rail","aria-label":"VuvoDesk Client tools",children:[e.jsx("div",{className:"rail-brand",title:"VuvoDesk",children:e.jsx(qe,{size:25})}),e.jsx("nav",{className:"rail-tools","aria-label":"Client navigation",children:e.jsxs("div",{className:"client-main-tool-slot",children:[e.jsxs("button",{className:"rail-tool client-main-tool client-hub-tool client-hub-switch-trigger accent-blue",type:"button",onClick:Ve,disabled:y||!t?.authenticated,"aria-busy":y,"aria-expanded":L,"aria-haspopup":"dialog","aria-controls":L?"client-hub-switch-popover":void 0,"aria-label":`Hub. ${Xe}`,"data-tooltip":L?void 0:Te("Hub","switch-to-hub",J),children:[e.jsx(ne,{size:19}),e.jsx("span",{children:y?"Switching…":"Hub"})]}),e.jsxs("button",{className:`rail-tool client-main-tool accent-blue ${D==="computer"?"active":""}`,type:"button",onClick:()=>de("computer"),"aria-current":D==="computer"?"page":void 0,"aria-label":`Client. ${Je}`,"data-tooltip":Te("Client","client",J),children:[e.jsx(ce,{size:19}),e.jsx("span",{children:"Client"})]})]})}),e.jsxs("div",{className:"rail-footer",children:[Rt.map(n=>{const s=n.icon;return e.jsxs("button",{className:`rail-tool compact ${D===n.id?"active":""}`,type:"button",onClick:()=>de(n.id),title:n.label,children:[e.jsx(s,{size:18}),e.jsx("span",{children:n.label})]},n.id)}),e.jsxs("button",{className:`rail-account ${I?"signed-in":""}`,type:"button",onClick:()=>q(n=>!n),"aria-expanded":he,"aria-haspopup":"dialog","aria-controls":"vuvodesk-account-dialog",title:I?"Account":"Sign in",children:[I&&K?e.jsx("img",{src:K,alt:re,referrerPolicy:"no-referrer",onError:()=>_e(K)},K):I?e.jsx("span",{className:"avatar-fallback",children:ve}):e.jsx(Ie,{size:18}),e.jsx("span",{children:I?"Account":"Sign in"})]})]})]}),L&&e.jsxs("section",{id:"client-hub-switch-popover",className:"client-hub-switch-popover",role:"dialog","aria-modal":"false","aria-labelledby":"client-hub-switch-title","aria-describedby":"client-hub-switch-description client-hub-switch-restart",children:[e.jsxs("header",{className:"client-hub-switch-head",children:[e.jsx("span",{className:"client-hub-switch-mark","aria-hidden":"true",children:e.jsx(ne,{size:19})}),e.jsx("span",{className:"client-hub-switch-eyebrow",children:O.eyebrow}),e.jsx("button",{className:"client-hub-switch-close",type:"button",onClick:()=>X(),"aria-label":O.closeLabel,children:e.jsx(Se,{size:17})})]}),e.jsx("h2",{id:"client-hub-switch-title",children:O.title}),e.jsx("p",{id:"client-hub-switch-description",children:O.description}),e.jsxs("p",{id:"client-hub-switch-restart",className:"client-hub-switch-notice",children:[e.jsx($e,{size:16,"aria-hidden":"true"}),e.jsx("span",{children:O.restartNotice})]}),e.jsxs("div",{className:"client-hub-switch-actions",children:[e.jsx("button",{type:"button",onClick:()=>X(),children:O.cancel}),e.jsxs("button",{ref:me,className:"primary",type:"button",onClick:()=>{Ge()},disabled:y||!t?.authenticated,children:[e.jsx(ne,{size:16}),O.confirm]})]})]}),e.jsx(wt,{open:he,signedIn:I,accountName:I?re:"Account",accountEmail:o?.user.email||t?.userEmail||t?.userId||"",accountInitial:ve,avatarUrl:K,currentPlan:"free",currentPlanKnown:!1,deviceCount:t?1:0,deviceLimit:Number.POSITIVE_INFINITY,licenseLabel:"Plan is managed at the Hub. This Client does not choose or change billing.",networkLabel:t?.connectionState==="connected"?"Connected to Hub":t?"Finding Hub":"Checking local app",accessLabel:t?.auth?.persisted?"Saved sign-in on this computer":I?"Active for this run":"Not signed in",authBusy:!p,checkoutBusy:!1,ltdCheckoutEnabled:!1,monthlyCheckoutEnabled:!1,teamCheckoutEnabled:!1,planChangesEnabled:!1,planManagementLabel:"Plan is managed at the Hub",showWorkspaceSwitcher:!1,workspaces:[],activeWorkspaceId:"",onClose:()=>q(!1),onSignIn:async()=>{q(!1),await F()},onSignOut:Be,onSelectPlan:()=>{}}),e.jsx("section",{className:"desk-stage client-unified-stage",children:D==="support"?e.jsx("section",{className:"client-unified-page",children:e.jsx(ft,{session:o,currentProductKey:"livedesk",appVersion:t?.appVersion||"",onSignIn:()=>{F()}})}):D!=="computer"?e.jsx("section",{className:"client-unified-page",children:e.jsx(Ot,{page:D,status:t})}):e.jsx(Mt,{session:o,status:t,pin:R,roleChangeMessage:$,videoAccelerationBusy:w,slotInput:G,slotBusy:T,onSignIn:()=>{F()},onPinChange:n=>_(n.replace(/\D/g,"").slice(0,6)),onSubmitPin:()=>{ze()},onSlotInputChange:n=>{ie.current=!0,E(n.replace(/\D/g,"").slice(0,3))},onSaveSlot:()=>{We()},onEnableVideoAcceleration:()=>{Ke()}})}),N&&e.jsxs("div",{className:`client-side-toast ${N.kind}`,role:N.kind==="error"?"alert":"status",children:[N.kind==="error"?e.jsx(it,{size:18}):e.jsx(Fe,{size:18}),e.jsx("span",{children:N.message}),e.jsx("button",{type:"button",onClick:()=>S(null),"aria-label":"Dismiss notification",children:e.jsx(Se,{size:16})})]})]})}const Ut=1e3,_t=5e3;function Bt(){return typeof window>"u"?!1:(!!window.navigator.standalone||window.matchMedia("(display-mode: standalone), (display-mode: fullscreen)").matches)&&window.matchMedia("(pointer: coarse)").matches}function Vt(){return typeof window>"u"?[]:[...new Set([window.location.origin,te].filter(Boolean))]}function Qt(){const t=Qe(),c=Bt(),o=t||c,u=t||typeof window>"u"?te:window.location.origin,p=o?{role:"hub",baseUrl:u}:null,[v,R]=r.useState(p),_=r.useRef(null);return r.useEffect(()=>{if(o){R(b=>b?.role==="hub"&&b.baseUrl===u?b:{role:"hub",baseUrl:u});return}let C=!1,d=!1,A=null;const k=new AbortController,j=async()=>{for(const b of Vt()){if(k.signal.aborted)return;try{const P=(await et("/api/runtime/status",{signal:k.signal,timeoutMs:900},b)).role==="hub"?"hub":"client";d=!0,C||R(w=>w?.role===P&&w.baseUrl===b?w:{role:P,baseUrl:b});return}catch{if(k.signal.aborted)return}}},g=()=>{Ze(_,j).finally(()=>{!C&&!k.signal.aborted&&(A=window.setTimeout(g,d?_t:Ut))}).catch(()=>{})};g();const $=window.setTimeout(()=>{!C&&!d&&R(b=>b?.role==="client"&&b.baseUrl===te?b:{role:"client",baseUrl:te})},2500);return()=>{C=!0,k.abort(new Error("VuvoDesk runtime role probe stopped.")),A!==null&&window.clearTimeout(A),window.clearTimeout($)}},[null,o,u]),v?v.role==="client"?e.jsx(Ht,{runtimeBaseUrl:v.baseUrl}):e.jsx(vt,{runtimeBaseUrl:v.baseUrl}):e.jsxs("main",{className:"runtime-role-boot","aria-busy":"true","aria-label":"Starting VuvoDesk",children:[e.jsx("span",{className:"runtime-role-boot-mark","aria-hidden":"true"}),e.jsx("strong",{children:"Starting VuvoDesk"}),e.jsx("p",{children:"Loading this computer's assigned role..."})]})}export{Qt as VuvoDeskApp};
@@ -1 +1 @@
1
- import{c as r,j as t,P as a,D as e,t as o,s}from"./styles-CDIlHYlj.js";import{a as i}from"./icons-CacEbnZu.js";import{H as m}from"./App-smTkH_7F.js";import"./react-Cfedt3N5.js";import"./supabase-C7qjtLN3.js";import"./frame-lifecycle-esOdP-EY.js";r.createRoot(document.getElementById("root")).render(t.jsx(i.StrictMode,{children:t.jsx(a,{automaticRetry:!0,children:t.jsx(m,{runtimeBaseUrl:e})})}));o();s();
1
+ import{c as r,j as t,P as a,D as e,t as o,s}from"./styles-CO6aHjLW.js";import{a as i}from"./icons-CacEbnZu.js";import{H as m}from"./App-WZAN0Iwh.js";import"./react-Cfedt3N5.js";import"./supabase-C7qjtLN3.js";import"./frame-lifecycle-esOdP-EY.js";r.createRoot(document.getElementById("root")).render(t.jsx(i.StrictMode,{children:t.jsx(a,{automaticRetry:!0,children:t.jsx(m,{runtimeBaseUrl:e})})}));o();s();
@@ -1,2 +1,2 @@
1
- const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/VuvoDeskApp-BM4kuSAh.js","assets/styles-CDIlHYlj.js","assets/icons-CacEbnZu.js","assets/react-Cfedt3N5.js","assets/styles-BbMJwyw_.css","assets/SupportPage-B90rx4D0.js","assets/App-smTkH_7F.js","assets/supabase-C7qjtLN3.js","assets/frame-lifecycle-esOdP-EY.js","assets/App-DFLBNvIi.css","assets/SupportPage-Cxx61WwR.css","assets/DesktopUpdatePanel-BDIHhw5J.js","assets/VuvoDeskApp-ao5vkK6F.css"])))=>i.map(i=>d[i]);
2
- import{j as e,L as F,M as V,F as K,p as i,a as G,b as Y,T as X,i as N,c as J,P as Q,t as Z,s as ee,_ as se,r as ne,d as ae}from"./styles-CDIlHYlj.js";import{a as x,A as o,M as ie,C as E,S as re,E as O,b as y,c as M,L as S,T as v,d as j,e as I,f as g,W as te,R as oe,g as le,D as h,N as _,h as ce,i as de,j as P,k as A,G as R,l as he}from"./icons-CacEbnZu.js";import"./react-Cfedt3N5.js";const T="npx -y --prefer-online livedesk@latest",pe="".trim().toLowerCase(),U=pe==="stable"?"stable":"dev",f=`https://livedesk-desktop-updates.lovecrdm.workers.dev/download/${U}`,ue=`${f}/windows/x64`,me=`${f}/macos/x64`,xe=`${f}/macos/arm64`,je=`${f}/linux/x64`,fe=`${f}/linux/x64/deb`,k="".trim().toLowerCase()==="true",d="".trim().toLowerCase()==="true",p="".trim().toLowerCase()==="true",w="".trim().toLowerCase()==="true",b=d||p||w;function ve(){if(typeof window>"u")return"home";const s=window.location.pathname.replace(/\/+$/,"")||"/";return s==="/"?"home":s==="/download"?"download":s==="/pricing"?"pricing":s==="/support"?"support":s==="/terms"?"terms":s==="/privacy"?"privacy":s==="/refunds"?"refunds":"not-found"}function W(){return e.jsxs("a",{className:"site-brand",href:"/","aria-label":"VuvoDesk home",children:[e.jsx("span",{className:"site-brand-mark",children:e.jsx(V,{size:25})}),e.jsx("span",{children:"VuvoDesk"})]})}function we({page:s}){const a=[{label:"Product",href:"/#product",active:s==="home"},{label:"Pricing",href:"/pricing",active:s==="pricing"},{label:"Download",href:"/download",active:s==="download"},{label:"Support",href:"/support",active:s==="support"}];return e.jsx("header",{className:"site-header",children:e.jsxs("div",{className:"site-header-inner",children:[e.jsx(W,{}),e.jsx("nav",{className:"site-nav","aria-label":"Main navigation",children:a.map(n=>e.jsx("a",{className:n.active?"active":"",href:n.href,children:n.label},n.label))}),e.jsxs("div",{className:"site-header-actions",children:[e.jsx("a",{className:"site-text-link",href:"/app",children:"Open web console"}),e.jsxs("a",{className:"site-button site-button-small",href:"/download",children:["Get VuvoDesk ",e.jsx(o,{size:15})]})]}),e.jsxs("details",{className:"site-mobile-menu",children:[e.jsx("summary",{"aria-label":"Open navigation",children:e.jsx(ie,{size:22})}),e.jsxs("nav",{"aria-label":"Mobile navigation",children:[a.map(n=>e.jsxs("a",{href:n.href,children:[n.label,e.jsx(E,{size:15})]},n.label)),e.jsxs("a",{href:"/app",children:["Open web console",e.jsx(E,{size:15})]})]})]})]})})}function ge(){return e.jsxs("footer",{className:"site-footer",children:[e.jsxs("div",{className:"site-footer-main",children:[e.jsxs("div",{children:[e.jsx(W,{}),e.jsx("p",{children:"One calm command center for every computer you manage."})]}),e.jsxs("div",{className:"site-footer-links",children:[e.jsxs("div",{children:[e.jsx("strong",{children:"Product"}),e.jsx("a",{href:"/#product",children:"Overview"}),e.jsx("a",{href:"/pricing",children:"Pricing"}),e.jsx("a",{href:"/download",children:"Download"})]}),e.jsxs("div",{children:[e.jsx("strong",{children:"Use VuvoDesk"}),e.jsx("a",{href:"/app",children:"Web console"}),e.jsx("a",{href:"/support",children:"Support"})]}),e.jsxs("div",{children:[e.jsx("strong",{children:"Platforms"}),e.jsx("span",{children:"Windows"}),e.jsx("span",{children:"macOS"}),e.jsx("span",{children:"Linux"}),e.jsx("span",{children:"Mobile PWA"})]}),e.jsxs("div",{children:[e.jsx("strong",{children:"Legal"}),e.jsx("a",{href:"/terms",children:"Terms"}),e.jsx("a",{href:"/privacy",children:"Privacy"}),e.jsx("a",{href:"/refunds",children:"Cancellation & refunds"})]})]})]}),e.jsxs("div",{className:"site-footer-bottom",children:[e.jsx("span",{children:"© 2026 VuvoDesk"}),e.jsx("span",{children:"Built for people who run real work across many screens."})]})]})}function be(){const s=[{name:"Studio-01",status:"Online",className:"screen-blue",cpu:"42%",ram:"61%"},{name:"Render-Mac",status:"Online",className:"screen-purple",cpu:"67%",ram:"74%"},{name:"Linux-Build",status:"Online",className:"screen-green",cpu:"29%",ram:"48%"},{name:"Office-PC",status:"Idle",className:"screen-orange",cpu:"18%",ram:"37%"}];return e.jsxs("div",{className:"product-mockup","aria-label":"VuvoDesk screen wall preview",children:[e.jsxs("div",{className:"mockup-window-bar",children:[e.jsxs("div",{children:[e.jsx("i",{}),e.jsx("i",{}),e.jsx("i",{})]}),e.jsx("span",{children:"VuvoDesk · Wall"}),e.jsxs("span",{className:"mockup-online",children:[e.jsx("i",{})," 4 online"]})]}),e.jsxs("div",{className:"mockup-body",children:[e.jsxs("aside",{className:"mockup-rail","aria-hidden":"true",children:[e.jsx("span",{className:"active",children:e.jsx(I,{size:17})}),e.jsx("span",{children:e.jsx(g,{size:17})}),e.jsx("span",{children:e.jsx(_,{size:17})}),e.jsx("span",{children:e.jsx(R,{size:17})})]}),e.jsx("div",{className:"mockup-wall",children:s.map(a=>e.jsxs("article",{className:"mockup-screen",children:[e.jsxs("div",{className:`mockup-screen-image ${a.className}`,children:[e.jsx("span",{className:"mockup-code-line wide"}),e.jsx("span",{className:"mockup-code-line"}),e.jsx("span",{className:"mockup-code-line short"}),e.jsx("span",{className:"mockup-panel-block"})]}),e.jsxs("div",{className:"mockup-screen-title",children:[e.jsx("strong",{children:a.name}),e.jsxs("span",{children:[e.jsx("i",{})," ",a.status]})]}),e.jsxs("div",{className:"mockup-metrics",children:[e.jsxs("span",{children:["CPU ",e.jsx("b",{children:a.cpu})]}),e.jsxs("span",{children:["RAM ",e.jsx("b",{children:a.ram})]})]})]},a.name))})]}),e.jsxs("div",{className:"mockup-control-card",children:[e.jsx(g,{size:18}),e.jsxs("span",{children:[e.jsx("small",{children:"Selected computer"}),e.jsx("strong",{children:"Render-Mac"})]}),e.jsx("button",{type:"button",children:"Control"})]})]})}function B({compact:s=!1}){const a=[{key:"free",name:i.free.name,price:i.free.price,suffix:"forever",audience:i.free.audience,description:"A practical personal wall, supported by a standard banner.",features:[`Up to ${K} devices`,"1 person","Standard banner"],action:"Start free",href:"/app",available:!0},{key:"plus",name:i.ltd.name,price:i.ltd.price,suffix:"/ month",audience:i.ltd.audience,description:"A clean personal command center for creators and homelabs.",features:[`Up to ${G} devices`,"1 person · no standard banner","Personal use"],ltd:i.ltd.ltd,action:p||d?"Choose Plus":"Paid checkout opening soon",href:p||d?"/app?view=pricing":"/support#billing",available:p||d},{key:"pro",name:i.pro.name,price:i.pro.price,suffix:"/ month",audience:i.pro.audience,description:"Monitoring for schools, academies, studios, offices, and growing commercial fleets.",features:[`Up to ${Y} devices`,"1 person","Ad-free school, academy, and commercial use"],ltd:i.pro.ltd,action:p||d?"Choose Business":"Paid checkout opening soon",href:p||d?"/app?view=pricing":"/support#billing",available:p||d},{key:"team",name:i.team.name,price:i.team.price,suffix:"/ month",audience:i.team.audience,description:"A shared Team Space for operators who manage the same computers together.",features:["Unlimited computers",`${X} people with their own accounts`,"Commercial use · Owner and Operator roles"],action:w?"Choose Team":"Paid checkout opening soon",href:w?"/app?view=pricing":"/support#billing",available:w}];return e.jsx("div",{className:`site-pricing-grid ${s?"compact":""}`,children:a.map(n=>e.jsxs("article",{className:`site-price-card ${n.key==="plus"?"featured":""} ${n.available?"":"pending"}`,children:[e.jsxs("div",{className:"site-price-heading",children:[e.jsx("span",{children:n.name}),n.key==="plus"&&e.jsx("em",{children:"Most popular"})]}),e.jsx("p",{className:"site-price-audience",children:n.audience}),e.jsxs("div",{className:"site-price",children:[e.jsx("strong",{children:n.price}),e.jsx("small",{children:n.suffix})]}),e.jsx("p",{children:n.description}),e.jsx("ul",{children:n.features.map(t=>e.jsxs("li",{children:[e.jsx(he,{size:16})," ",t]},t))}),n.ltd&&e.jsxs("div",{className:"site-ltd",children:[e.jsxs("span",{children:[n.name," LTD"]}),e.jsx("strong",{children:n.ltd.replace(" LTD","")}),e.jsx("small",{children:d?"one-time · available now":"one-time · checkout opening soon"})]}),e.jsxs("a",{className:`site-price-action ${n.key==="plus"&&n.available?"primary":""} ${n.available?"":"pending"}`,href:n.href,children:[n.action,e.jsx(o,{size:15})]})]},n.key))})}function ye(){return e.jsx(e.Fragment,{children:e.jsxs("main",{children:[e.jsxs("section",{className:"site-hero",children:[e.jsxs("div",{className:"site-hero-copy",children:[e.jsxs("span",{className:"site-kicker",children:[e.jsx(re,{size:15})," The screen wall for real work"]}),e.jsxs("h1",{children:["Every computer.",e.jsx("br",{}),e.jsx("span",{children:"One calm command center."})]}),e.jsx("p",{children:"See every screen, focus the computer that needs you, and keep work moving from your desk or phone."}),e.jsxs("div",{className:"site-hero-actions",children:[e.jsxs("a",{className:"site-button",href:"/download",children:["Download VuvoDesk ",e.jsx(o,{size:17})]}),e.jsxs("a",{className:"site-button site-button-secondary",href:"/app",children:["Open web console ",e.jsx(O,{size:17})]})]}),e.jsxs("div",{className:"site-hero-notes",children:[e.jsxs("span",{children:[e.jsx(y,{size:15})," Free for 5 devices"]}),e.jsxs("span",{children:[e.jsx(y,{size:15})," No credit card"]}),e.jsxs("span",{children:[e.jsx(y,{size:15})," Windows · macOS · Linux"]})]})]}),e.jsxs("div",{className:"site-hero-visual",children:[e.jsx("div",{className:"site-orbit site-orbit-one"}),e.jsx("div",{className:"site-orbit site-orbit-two"}),e.jsx(be,{})]})]}),e.jsxs("section",{className:"platform-strip","aria-label":"Supported platforms",children:[e.jsx("span",{children:"Built for the computers you already run"}),e.jsxs("div",{children:[e.jsxs("strong",{children:[e.jsx(M,{size:18})," Windows"]}),e.jsxs("strong",{children:[e.jsx(S,{size:18})," macOS"]}),e.jsxs("strong",{children:[e.jsx(v,{size:18})," Linux"]}),e.jsxs("strong",{children:[e.jsx(j,{size:18})," iPhone & Android"]}),e.jsxs("strong",{children:[e.jsx(O,{size:18})," Any browser"]})]})]}),e.jsxs("section",{className:"site-section site-value-section",id:"product",children:[e.jsxs("div",{className:"site-section-heading",children:[e.jsx("span",{children:"Why VuvoDesk"}),e.jsxs("h2",{children:["Spend less time finding the problem.",e.jsx("br",{}),"Spend more time fixing it."]}),e.jsx("p",{children:"VuvoDesk keeps the fleet visible without turning your desktop into a pile of remote windows."})]}),e.jsxs("div",{className:"site-value-grid",children:[e.jsxs("article",{className:"site-value-card large blue",children:[e.jsx("span",{className:"site-feature-icon",children:e.jsx(I,{size:23})}),e.jsx("h3",{children:"A wall that stays readable"}),e.jsx("p",{children:"Move from a compact list to large live previews. Keep the signal you need and hide the noise you do not."}),e.jsxs("div",{className:"feature-wall-demo",children:[e.jsx("i",{}),e.jsx("i",{}),e.jsx("i",{}),e.jsx("i",{}),e.jsx("i",{}),e.jsx("i",{})]})]}),e.jsxs("article",{className:"site-value-card mint",children:[e.jsx("span",{className:"site-feature-icon",children:e.jsx(g,{size:23})}),e.jsx("h3",{children:"Control without losing context"}),e.jsx("p",{children:"Jump from Wall to focused Control, then return to the same fleet view."}),e.jsxs("div",{className:"feature-cursor-demo",children:[e.jsx(g,{size:31}),e.jsx("span",{children:"Direct control"})]})]}),e.jsxs("article",{className:"site-value-card violet",children:[e.jsx("span",{className:"site-feature-icon",children:e.jsx(te,{size:23})}),e.jsx("h3",{children:"Your fleet in your pocket"}),e.jsx("p",{children:"Install the PWA on iPhone or Android and check the room before you walk back to it."}),e.jsxs("div",{className:"feature-phone-demo",children:[e.jsx("span",{}),e.jsxs("div",{children:[e.jsx("i",{}),e.jsx("i",{}),e.jsx("i",{})]})]})]}),e.jsxs("article",{className:"site-value-card wide dark",children:[e.jsxs("div",{children:[e.jsxs("span",{className:"site-kicker dark",children:[e.jsx(oe,{size:14})," Local-first when possible"]}),e.jsx("h3",{children:"Fast where you work. Reachable when you leave."}),e.jsx("p",{children:"VuvoDesk prefers the shortest useful route and keeps the current screen moving instead of building a stale queue."})]}),e.jsxs("div",{className:"feature-route",children:[e.jsxs("span",{children:[e.jsx(S,{size:19})," Hub"]}),e.jsx("i",{}),e.jsxs("span",{children:[e.jsx(le,{size:19})," Clients"]}),e.jsx("i",{}),e.jsxs("span",{children:[e.jsx(j,{size:19})," PWA"]})]})]})]})]}),e.jsxs("section",{className:"site-section site-workflow",children:[e.jsxs("div",{className:"site-section-heading centered",children:[e.jsx("span",{children:"Start in three steps"}),e.jsx("h2",{children:"From zero to a live wall in minutes."})]}),e.jsxs("div",{className:"workflow-grid",children:[e.jsxs("article",{children:[e.jsx("b",{children:"01"}),e.jsx("span",{children:e.jsx(h,{size:21})}),e.jsx("h3",{children:"Install the Hub"}),e.jsx("p",{children:"Choose the computer that will organize your VuvoDesk fleet."})]}),e.jsxs("article",{children:[e.jsx("b",{children:"02"}),e.jsx("span",{children:e.jsx(_,{size:21})}),e.jsx("h3",{children:"Connect Clients"}),e.jsx("p",{children:"Use one PIN to add the Windows, Mac, and Linux computers you manage."})]}),e.jsxs("article",{children:[e.jsx("b",{children:"03"}),e.jsx("span",{children:e.jsx(j,{size:21})}),e.jsx("h3",{children:"Manage from anywhere"}),e.jsx("p",{children:"Open the web console or install the mobile PWA and keep every screen close."})]})]})]}),e.jsxs("section",{className:"site-section site-pricing-section",children:[e.jsxs("div",{className:"site-section-heading centered",children:[e.jsx("span",{children:"Simple pricing"}),e.jsx("h2",{children:"Start free. Upgrade when the wall earns its place."}),e.jsx("p",{children:b?"Monthly prices stay easy to understand. Lifetime offers are available separately below each paid plan.":"Free is available now. Paid checkout stays closed until live billing and customer support are verified."})]}),e.jsx(B,{compact:!0}),e.jsxs("a",{className:"site-inline-link",href:"/pricing",children:["Compare every plan ",e.jsx(o,{size:15})]})]}),e.jsxs("section",{className:"site-final-cta",children:[e.jsxs("div",{children:[e.jsxs("span",{className:"site-kicker dark",children:[e.jsx(ce,{size:14})," Your fleet, one glance away"]}),e.jsxs("h2",{children:["Give every computer",e.jsx("br",{}),"one clear place to report."]})]}),e.jsxs("div",{children:[e.jsx("p",{children:"Start with five devices for free. Install the Hub, connect a Client, and open your first live wall."}),e.jsxs("div",{children:[e.jsxs("a",{className:"site-button light",href:"/download",children:["Get VuvoDesk ",e.jsx(o,{size:17})]}),e.jsx("a",{className:"site-button ghost",href:"/app",children:"Open console"})]})]})]})]})})}function ke(){const[s,a]=x.useState(!1),n=async()=>{try{await navigator.clipboard.writeText(T),a(!0),window.setTimeout(()=>a(!1),1800)}catch{a(!1)}};return e.jsxs("main",{className:"site-subpage",children:[e.jsxs("section",{className:"site-subpage-hero download-hero",children:[e.jsxs("span",{className:"site-kicker",children:[e.jsx(h,{size:15})," Download VuvoDesk"]}),e.jsxs("h1",{children:["Turn one computer into",e.jsx("br",{}),e.jsx("span",{children:"your command center."})]}),e.jsxs("p",{children:["Use the latest temporary installer for your computer. Available links follow the newest matching file on the ",U," channel; an unavailable platform is never linked to a missing file."]})]}),e.jsxs("section",{className:"download-grid",children:[e.jsxs("article",{id:"windows",children:[e.jsx("span",{className:"download-platform-icon windows",children:e.jsx(M,{size:27})}),e.jsxs("div",{children:[e.jsx("small",{children:"Windows"}),e.jsx("span",{className:"download-version",children:"Latest temporary build · x64 Electron"}),e.jsx("span",{className:"download-preview-note",children:"Temporary installer · unsigned"}),e.jsx("h2",{children:"Install VuvoDesk for Windows"}),e.jsx("p",{children:"Download the normal Electron setup program. Windows SmartScreen may warn about this preview until code signing is ready."})]}),e.jsxs("a",{className:"download-installer-link",href:ue,children:["Download Windows installer ",e.jsx(h,{size:16})]})]}),e.jsxs("article",{id:"macos",children:[e.jsx("span",{className:"download-platform-icon mac",children:e.jsx(S,{size:27})}),e.jsxs("div",{children:[e.jsx("small",{children:"macOS"}),e.jsx("span",{className:"download-version",children:"Intel & Apple silicon"}),e.jsx("span",{className:"download-preview-note",children:k?"Temporary installer · ad-hoc signed · not notarized":"Desktop installer not published yet"}),e.jsx("h2",{children:"Install VuvoDesk for macOS"}),e.jsx("p",{children:k?"Choose the chip in your Mac. On first launch, macOS may require Open Anyway in Privacy & Security because this preview is not Apple-notarized.":"Use the terminal install below while signed and notarized desktop packages are being prepared."})]}),k?e.jsxs("div",{className:"download-actions",children:[e.jsxs("a",{className:"download-installer-link",href:xe,children:["Apple silicon ",e.jsx(h,{size:16})]}),e.jsxs("a",{className:"download-installer-link secondary",href:me,children:["Intel Mac ",e.jsx(h,{size:16})]})]}):e.jsx("div",{className:"download-actions",children:e.jsxs("a",{className:"download-installer-link secondary",href:"#install-command",children:["Use terminal install ",e.jsx(v,{size:16})]})})]}),e.jsxs("article",{id:"linux",children:[e.jsx("span",{className:"download-platform-icon linux",children:e.jsx(v,{size:27})}),e.jsxs("div",{children:[e.jsx("small",{children:"Linux"}),e.jsx("span",{className:"download-version",children:"Latest temporary build · x64 · X11"}),e.jsx("span",{className:"download-preview-note",children:"Temporary installer"}),e.jsx("h2",{children:"Install VuvoDesk for Linux"}),e.jsx("p",{children:"Use the DEB package on Ubuntu, Debian, or Mint. For other distributions, download AppImage, mark it executable, then open it."})]}),e.jsxs("div",{className:"download-actions",children:[e.jsxs("a",{className:"download-installer-link",href:fe,children:["Download .deb ",e.jsx(h,{size:16})]}),e.jsxs("a",{className:"download-installer-link secondary",href:je,children:["Download AppImage ",e.jsx(h,{size:16})]})]})]})]}),e.jsxs("section",{className:"install-command-card",id:"install-command",children:[e.jsxs("div",{children:[e.jsxs("span",{children:[e.jsx(v,{size:18})," One command install"]}),e.jsx("h2",{children:"Terminal fallback and advanced setup"}),e.jsx("p",{children:"Use the npm package when you prefer a command-line install or the temporary desktop package does not suit your system."})]}),e.jsxs("div",{className:"install-command",children:[e.jsx("code",{children:T}),e.jsxs("button",{type:"button",onClick:()=>{n()},children:[e.jsx(de,{size:17})," ",s?"Copied":"Copy"]})]}),e.jsxs("ol",{children:[e.jsxs("li",{children:[e.jsx("b",{children:"1"}),e.jsx("span",{children:"Run the command on the computer that will be your Hub."})]}),e.jsxs("li",{children:[e.jsx("b",{children:"2"}),e.jsx("span",{children:"Open Settings and create a Hub PIN."})]}),e.jsxs("li",{children:[e.jsx("b",{children:"3"}),e.jsx("span",{children:"Run the same package as a Client on the other computers."})]})]})]}),e.jsxs("section",{className:"mobile-install-section",id:"mobile",children:[e.jsxs("div",{className:"mobile-install-heading",children:[e.jsx("span",{children:e.jsx(j,{size:26})}),e.jsxs("div",{children:[e.jsxs("small",{children:["iPhone & Android · PWA ",F]}),e.jsx("h2",{children:"No app store required."}),e.jsx("p",{children:"Open the public web address, sign in, then install VuvoDesk on the Home Screen. The PWA always starts directly in the console."})]})]}),e.jsxs("div",{className:"mobile-web-address",children:[e.jsx("span",{children:"Mobile web address"}),e.jsx("a",{href:"https://vuvodesk.com/app",children:"https://vuvodesk.com/app"}),e.jsxs("a",{className:"site-button",href:"/app",children:["Open mobile console ",e.jsx(o,{size:16})]})]}),e.jsxs("div",{className:"pwa-guide-grid",children:[e.jsxs("article",{children:[e.jsx("strong",{children:"iPhone & iPad"}),e.jsxs("ol",{children:[e.jsx("li",{children:"Open the address in Safari."}),e.jsx("li",{children:"Tap Share, then Add to Home Screen."}),e.jsx("li",{children:"Choose Open as Web App and add VuvoDesk."})]})]}),e.jsxs("article",{children:[e.jsx("strong",{children:"Android"}),e.jsxs("ol",{children:[e.jsx("li",{children:"Open the address in Chrome."}),e.jsx("li",{children:"Open the browser menu and tap Install app."}),e.jsx("li",{children:"Launch VuvoDesk from the Home Screen."})]})]})]})]})]})}function Ne(){return e.jsxs("main",{className:"site-subpage pricing-subpage",children:[e.jsxs("section",{className:"site-subpage-hero",children:[e.jsxs("span",{className:"site-kicker",children:[e.jsx(P,{size:15})," VuvoDesk pricing"]}),e.jsxs("h1",{children:["Clear plans for",e.jsx("br",{}),e.jsx("span",{children:"growing screen walls."})]}),e.jsxs("p",{children:["Start with one account, choose Pro / Business for a school, academy, or commercial workplace, or give each operator a separate login with Team. ",!b&&"Paid checkout is not open yet."]})]}),e.jsx(B,{}),e.jsxs("section",{className:"pricing-explainer",children:[e.jsxs("article",{children:[e.jsx(A,{size:21}),e.jsxs("div",{children:[e.jsx("h3",{children:"Free stays useful"}),e.jsx("p",{children:"The standard banner helps keep a five-device personal wall available without a subscription."})]})]}),e.jsxs("article",{children:[e.jsx(P,{size:21}),e.jsxs("div",{children:[e.jsx("h3",{children:"Monthly and lifetime are separate"}),e.jsx("p",{children:"$5 Plus, $15 Pro / Business, and $49 Team are monthly prices. Existing Plus LTD $79 and Pro LTD $199 remain one-time offers."})]})]}),e.jsxs("article",{children:[e.jsx(R,{size:21}),e.jsxs("div",{children:[e.jsx("h3",{children:"Upgrade inside VuvoDesk"}),e.jsx("p",{children:"Sign in to the console, open Pricing, and choose the plan that matches the way you use the fleet."})]})]})]}),e.jsxs("section",{className:"pricing-bottom-cta",children:[e.jsxs("div",{children:[e.jsx("h2",{children:"Start with the computers you have today."}),e.jsx("p",{children:"No credit card is needed for Free."})]}),e.jsxs("a",{className:"site-button",href:"/app",children:["Start free ",e.jsx(o,{size:16})]})]})]})}function Se(){return e.jsxs("main",{className:"site-subpage policy-subpage",children:[e.jsxs("section",{className:"site-subpage-hero",children:[e.jsxs("span",{className:"site-kicker",children:[e.jsx(A,{size:15})," VuvoDesk support"]}),e.jsxs("h1",{children:["Start with the shortest",e.jsx("br",{}),e.jsx("span",{children:"path back to work."})]}),e.jsx("p",{children:"Installation help is public. Account-specific diagnostics and billing requests stay inside your signed-in Support board so they are tied to the correct account."})]}),e.jsxs("section",{className:"support-option-grid",children:[e.jsxs("article",{children:[e.jsx(h,{size:23}),e.jsx("h2",{children:"Install or update"}),e.jsx("p",{children:"Use the current Windows or Linux preview package, or the terminal command for every supported desktop."}),e.jsxs("a",{href:"/download",children:["Open download help ",e.jsx(o,{size:15})]})]}),e.jsxs("article",{children:[e.jsx(j,{size:23}),e.jsx("h2",{children:"Connection trouble"}),e.jsx("p",{children:"Sign in, open the Support board, and include the affected computer name, operating system, VuvoDesk version, and the exact message you saw."}),e.jsxs("a",{href:"/support/issues",children:["Open signed-in Support ",e.jsx(o,{size:15})]})]}),e.jsxs("article",{id:"billing",children:[e.jsx(P,{size:23}),e.jsx("h2",{children:"Account or billing"}),e.jsx("p",{children:b?"Use the signed-in Support board for cancellation, refund, duplicate-payment, or access requests.":"Paid checkout is currently closed. No paid entitlement should be granted until verified checkout becomes available."}),e.jsxs("a",{href:"/support/questions",children:["Open account Support ",e.jsx(o,{size:15})]})]})]}),e.jsxs("section",{className:"support-checklist",children:[e.jsx("h2",{children:"Before sending a report"}),e.jsxs("ol",{children:[e.jsx("li",{children:"Confirm the Hub and Client show the same VuvoDesk release."}),e.jsx("li",{children:"Write down whether the problem affects Wall, Control, clipboard, or sign-in."}),e.jsx("li",{children:"Never post a Hub PIN, pairing token, access token, or payment credential."})]})]})]})}function Pe(){return e.jsxs(C,{title:"Terms of Service",summary:"The practical rules for using the VuvoDesk preview.",updated:"August 30, 2026",children:[e.jsx("h2",{children:"1. Authorized use"}),e.jsx("p",{children:"Use VuvoDesk only on computers and networks you own or are explicitly allowed to manage. You are responsible for every remote-control action performed through your account, Hub, Team Space, or pairing credential."}),e.jsx("h2",{children:"2. Accounts and Team Spaces"}),e.jsx("p",{children:"Keep sign-in and pairing credentials private. Team members use separate accounts. A Team Owner is responsible for invitations, roles, removals, and the computers shared with the Team Space."}),e.jsx("h2",{children:"3. Plans and payment"}),e.jsxs("p",{children:["Free, Plus, Pro / Business, and Team features are limited by the plan shown in the product. A displayed price is not a promise that checkout is open. Paid access starts only after a verified payment event grants the entitlement. Cancellation and refund handling follows the ",e.jsx("a",{href:"/refunds",children:"Cancellation & Refund Policy"}),"."]}),e.jsx("h2",{children:"4. Codex Agent features"}),e.jsx("p",{children:"Codex Agent features require a separately installed and signed-in Codex environment and a separate OpenAI account. OpenAI terms apply to that account and service. VuvoDesk does not sell Codex access and is not an OpenAI product."}),e.jsx("h2",{children:"5. Preview software"}),e.jsx("p",{children:"Unsigned or non-notarized installers are clearly labeled preview builds. Do not use preview software for safety-critical systems or where a temporary loss of screen, input, or connectivity could cause harm. Keep local access and backups available."}),e.jsx("h2",{children:"6. Acceptable behavior"}),e.jsx("p",{children:"Do not use VuvoDesk to access another person's device without permission, evade security controls, distribute malware, interfere with the service, or violate law or third-party rights."}),e.jsx("h2",{children:"7. Changes and termination"}),e.jsx("p",{children:"We may suspend access that creates security, abuse, or payment risk. Material policy changes will be dated on this page. Mandatory consumer rights in your country are not excluded."}),e.jsx("h2",{children:"8. Support"}),e.jsxs("p",{children:["Use the ",e.jsx("a",{href:"/support",children:"Support page"})," for product and account requests. Do not include passwords, tokens, or payment credentials."]})]})}function De(){return e.jsxs(C,{title:"Privacy Notice",summary:"What VuvoDesk handles, where it goes, and what is kept local.",updated:"August 30, 2026",children:[e.jsx("h2",{children:"1. Account and product data"}),e.jsx("p",{children:"VuvoDesk processes sign-in identity, device and workspace identifiers, roles, plan entitlements, connection status, version information, and Support content needed to operate the service."}),e.jsx("h2",{children:"2. Screen and input traffic"}),e.jsx("p",{children:"VuvoDesk is designed to send live screen, audio, clipboard, and remote-input traffic directly between your Hub, Clients, and browser when the connection succeeds. The Cloudflare signaling service exchanges authenticated connection metadata and does not intentionally store or forward the screen-media payload. Local diagnostic logs may record status and error details, not passwords or payment credentials."}),e.jsx("h2",{children:"3. Service providers"}),e.jsx("p",{children:"Google and Supabase provide sign-in and account storage. Cloudflare provides the public site, update delivery, and authenticated connection signaling. Dodo Payments is used only when paid checkout is enabled. Optional Codex Agent actions use the separately signed-in OpenAI service selected by the user."}),e.jsx("h2",{children:"4. Retention and control"}),e.jsxs("p",{children:["Account, workspace, entitlement, and Support records are retained while needed to provide the service, resolve disputes, prevent abuse, or meet legal duties. Request account access, correction, export, or deletion through the signed-in ",e.jsx("a",{href:"/support/questions",children:"Support board"}),". Some records may be retained where law or fraud prevention requires it."]}),e.jsx("h2",{children:"5. Security and transfers"}),e.jsx("p",{children:"Internet services may process data in more than one country. We use authenticated sessions, restricted service credentials, and encrypted network transport where supported, but no system can promise absolute security."}),e.jsx("h2",{children:"6. Policy changes"}),e.jsx("p",{children:"Material changes will update the date on this page. Review this notice before enabling new remote-control, clipboard, Team, payment, or Agent features."})]})}function Ae(){return e.jsxs(C,{title:"Cancellation & Refund Policy",summary:"A plain-language policy before any paid checkout is opened.",updated:"August 30, 2026",children:[e.jsx("h2",{children:"1. Current checkout status"}),e.jsx("p",{children:b?"Paid checkout is available only for the plans whose buttons are active.":"Paid checkout is currently closed. Preview access does not create a paid subscription or lifetime entitlement."}),e.jsx("h2",{children:"2. Subscriptions"}),e.jsx("p",{children:"You may request cancellation at any time. Cancellation stops the next renewal and normally keeps access through the already-paid billing period. A cancellation request must be submitted before the renewal is charged."}),e.jsx("h2",{children:"3. Initial purchase refunds"}),e.jsx("p",{children:"For an initial paid purchase, request a refund within 14 calendar days of payment. Duplicate or unauthorized charges and a material service failure will be reviewed even after that period. Mandatory local refund rights always apply."}),e.jsx("h2",{children:"4. Lifetime purchases"}),e.jsx("p",{children:"A lifetime purchase covers the product entitlement described at checkout, not third-party services, future paid add-ons, or a promise that every preview feature will remain unchanged. The same 14-day initial-purchase request window applies."}),e.jsx("h2",{children:"5. How to request"}),e.jsxs("p",{children:["Open the signed-in ",e.jsx("a",{href:"/support/questions",children:"Support board"})," and provide the account email, plan, purchase date, and reason. Never include full card details, passwords, API keys, or access tokens. Access may be removed after a full refund or successful payment dispute."]})]})}function C({title:s,summary:a,updated:n,children:t}){return e.jsxs("main",{className:"site-subpage policy-subpage",children:[e.jsxs("section",{className:"site-subpage-hero policy-hero",children:[e.jsxs("span",{className:"site-kicker",children:[e.jsx(A,{size:15})," VuvoDesk policy"]}),e.jsx("h1",{children:s}),e.jsx("p",{children:a}),e.jsxs("small",{children:["Last updated: ",n]})]}),e.jsx("article",{className:"policy-document",children:t})]})}function Ce(){return e.jsxs("main",{className:"site-not-found",children:[e.jsx("span",{className:"site-brand-mark",children:e.jsx(V,{size:25})}),e.jsx("small",{children:"404"}),e.jsx("h1",{children:"This screen is not on the wall."}),e.jsx("p",{children:"The page may have moved, but your VuvoDesk console is still here."}),e.jsxs("div",{children:[e.jsx("a",{className:"site-button",href:"/",children:"VuvoDesk home"}),e.jsx("a",{className:"site-button site-button-secondary",href:"/app",children:"Open console"})]})]})}function Le(){const s=ve();return x.useEffect(()=>{const a={home:"VuvoDesk — Every computer. One calm command center.",download:"Download VuvoDesk — Windows, macOS, and Linux",pricing:"VuvoDesk Pricing — Free, Plus, Pro / Business, and Team",support:"VuvoDesk Support",terms:"VuvoDesk Terms of Service",privacy:"VuvoDesk Privacy Notice",refunds:"VuvoDesk Cancellation & Refund Policy","not-found":"Page not found — VuvoDesk"},n={home:"See and control the computers you manage from one clear VuvoDesk screen wall.",download:"Download the currently available VuvoDesk desktop preview or install it with one npm command.",pricing:"Compare VuvoDesk Free, Plus, Pro / Business, and Team plan boundaries.",support:"Get public VuvoDesk installation help or open the signed-in Support board.",terms:"Read the VuvoDesk terms for authorized remote-control use, plans, preview software, and Codex Agent features.",privacy:"Learn how VuvoDesk handles account, device, support, signaling, and direct screen traffic data.",refunds:"Read the VuvoDesk cancellation and refund policy before paid checkout opens.","not-found":"The requested VuvoDesk page was not found."};document.title=a[s];const m=`https://vuvodesk.com${s==="not-found"?"/":window.location.pathname.replace(/\/+$/,"")||"/"}`;let r=document.querySelector('link[rel="canonical"]');r||(r=document.createElement("link"),r.rel="canonical",document.head.append(r)),r.href=m;const c=document.querySelector('meta[name="description"]');c&&(c.content=n[s]);let l=document.querySelector('meta[property="og:url"]');return l||(l=document.createElement("meta"),l.setAttribute("property","og:url"),document.head.append(l)),l.content=m,document.documentElement.classList.add("livedesk-marketing-active"),()=>document.documentElement.classList.remove("livedesk-marketing-active")},[s]),e.jsxs("div",{className:"marketing-site",children:[e.jsx(we,{page:s}),s==="home"?e.jsx(ye,{}):s==="download"?e.jsx(ke,{}):s==="pricing"?e.jsx(Ne,{}):s==="support"?e.jsx(Se,{}):s==="terms"?e.jsx(Pe,{}):s==="privacy"?e.jsx(De,{}):s==="refunds"?e.jsx(Ae,{}):e.jsx(Ce,{}),e.jsx(ge,{})]})}function ze(s){return!!s.navigatorStandalone||!!s.displayModeStandalone||!!s.displayModeFullscreen}function Ee({publicOrigin:s,electron:a,pathname:n,search:t="",navigatorStandalone:m=!1,displayModeStandalone:r=!1,displayModeFullscreen:c=!1,marketingPreview:l=!1}){if(a||!s&&!l)return{kind:"app",redirectPath:""};const L=new URLSearchParams(t),$=L.has("pwa")||ze({navigatorStandalone:m,displayModeStandalone:r,displayModeFullscreen:c}),z=n==="/app"||n.startsWith("/app/"),q=n.startsWith("/support/"),H=L.has("checkout");return $||H?{kind:"app",redirectPath:z?"":"/app"}:z||q?{kind:"app",redirectPath:""}:{kind:"site",redirectPath:""}}function Oe({automaticRetry:s}){const[a,n]=x.useState(null),[t,m]=x.useState("");return x.useEffect(()=>{let r=!0;return se(()=>import("./VuvoDeskApp-BM4kuSAh.js"),__vite__mapDeps([0,1,2,3,4,5,6,7,8,9,10,11,12])).then(c=>{r&&n(()=>c.VuvoDeskApp)}).catch(c=>{if(!r)return;console.warn("[VuvoDesk] app startup load failed.",c);const l=window.navigator.onLine?"The latest app file could not be loaded.":"Reconnect to the internet, then try again.";m(l),ne("load-error",s)}),()=>{r=!1}},[s]),a?e.jsx(a,{}):t?e.jsx(ae,{detail:t,automaticRetry:s}):e.jsx("main",{className:"runtime-role-boot","aria-label":"Starting VuvoDesk","aria-busy":"true",children:e.jsx("span",{className:"runtime-role-boot-mark","aria-hidden":"true"})})}window.liveDesk&&document.documentElement.classList.add("livedesk-electron-desktop");const u=new URL(window.location.href),Te=!!window.navigator.standalone,D=Ee({publicOrigin:N(),electron:!!window.liveDesk,pathname:u.pathname,search:u.search,navigatorStandalone:Te,displayModeStandalone:window.matchMedia("(display-mode: standalone)").matches,displayModeFullscreen:window.matchMedia("(display-mode: fullscreen)").matches,marketingPreview:u.searchParams.get("site")==="1"});D.redirectPath&&(u.pathname=D.redirectPath,window.history.replaceState({},"",`${u.pathname}${u.search}${u.hash}`));J.createRoot(document.getElementById("root")).render(e.jsx(x.StrictMode,{children:D.kind==="site"?e.jsx(Le,{}):e.jsx(Q,{automaticRetry:N(),children:e.jsx(Oe,{automaticRetry:N()})})}));Z();ee();
1
+ const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/VuvoDeskApp-BIrWZOFb.js","assets/styles-CO6aHjLW.js","assets/icons-CacEbnZu.js","assets/react-Cfedt3N5.js","assets/styles-DDKP4MTD.css","assets/SupportPage-BwS_toBH.js","assets/App-WZAN0Iwh.js","assets/supabase-C7qjtLN3.js","assets/frame-lifecycle-esOdP-EY.js","assets/App-DFLBNvIi.css","assets/SupportPage-Cxx61WwR.css","assets/DesktopUpdatePanel-BPD9IaXS.js","assets/VuvoDeskApp-ao5vkK6F.css"])))=>i.map(i=>d[i]);
2
+ import{j as e,L as F,M as V,F as K,p as i,a as G,b as Y,T as X,i as N,c as J,P as Q,t as Z,s as ee,_ as se,r as ne,d as ae}from"./styles-CO6aHjLW.js";import{a as x,A as o,M as ie,C as E,S as re,E as O,b as y,c as M,L as S,T as v,d as j,e as I,f as g,W as te,R as oe,g as le,D as h,N as _,h as ce,i as de,j as P,k as A,G as R,l as he}from"./icons-CacEbnZu.js";import"./react-Cfedt3N5.js";const T="npx -y --prefer-online livedesk@latest",pe="".trim().toLowerCase(),U=pe==="stable"?"stable":"dev",f=`https://livedesk-desktop-updates.lovecrdm.workers.dev/download/${U}`,ue=`${f}/windows/x64`,me=`${f}/macos/x64`,xe=`${f}/macos/arm64`,je=`${f}/linux/x64`,fe=`${f}/linux/x64/deb`,k="".trim().toLowerCase()==="true",d="".trim().toLowerCase()==="true",p="".trim().toLowerCase()==="true",w="".trim().toLowerCase()==="true",b=d||p||w;function ve(){if(typeof window>"u")return"home";const s=window.location.pathname.replace(/\/+$/,"")||"/";return s==="/"?"home":s==="/download"?"download":s==="/pricing"?"pricing":s==="/support"?"support":s==="/terms"?"terms":s==="/privacy"?"privacy":s==="/refunds"?"refunds":"not-found"}function W(){return e.jsxs("a",{className:"site-brand",href:"/","aria-label":"VuvoDesk home",children:[e.jsx("span",{className:"site-brand-mark",children:e.jsx(V,{size:25})}),e.jsx("span",{children:"VuvoDesk"})]})}function we({page:s}){const a=[{label:"Product",href:"/#product",active:s==="home"},{label:"Pricing",href:"/pricing",active:s==="pricing"},{label:"Download",href:"/download",active:s==="download"},{label:"Support",href:"/support",active:s==="support"}];return e.jsx("header",{className:"site-header",children:e.jsxs("div",{className:"site-header-inner",children:[e.jsx(W,{}),e.jsx("nav",{className:"site-nav","aria-label":"Main navigation",children:a.map(n=>e.jsx("a",{className:n.active?"active":"",href:n.href,children:n.label},n.label))}),e.jsxs("div",{className:"site-header-actions",children:[e.jsx("a",{className:"site-text-link",href:"/app",children:"Open web console"}),e.jsxs("a",{className:"site-button site-button-small",href:"/download",children:["Get VuvoDesk ",e.jsx(o,{size:15})]})]}),e.jsxs("details",{className:"site-mobile-menu",children:[e.jsx("summary",{"aria-label":"Open navigation",children:e.jsx(ie,{size:22})}),e.jsxs("nav",{"aria-label":"Mobile navigation",children:[a.map(n=>e.jsxs("a",{href:n.href,children:[n.label,e.jsx(E,{size:15})]},n.label)),e.jsxs("a",{href:"/app",children:["Open web console",e.jsx(E,{size:15})]})]})]})]})})}function ge(){return e.jsxs("footer",{className:"site-footer",children:[e.jsxs("div",{className:"site-footer-main",children:[e.jsxs("div",{children:[e.jsx(W,{}),e.jsx("p",{children:"One calm command center for every computer you manage."})]}),e.jsxs("div",{className:"site-footer-links",children:[e.jsxs("div",{children:[e.jsx("strong",{children:"Product"}),e.jsx("a",{href:"/#product",children:"Overview"}),e.jsx("a",{href:"/pricing",children:"Pricing"}),e.jsx("a",{href:"/download",children:"Download"})]}),e.jsxs("div",{children:[e.jsx("strong",{children:"Use VuvoDesk"}),e.jsx("a",{href:"/app",children:"Web console"}),e.jsx("a",{href:"/support",children:"Support"})]}),e.jsxs("div",{children:[e.jsx("strong",{children:"Platforms"}),e.jsx("span",{children:"Windows"}),e.jsx("span",{children:"macOS"}),e.jsx("span",{children:"Linux"}),e.jsx("span",{children:"Mobile PWA"})]}),e.jsxs("div",{children:[e.jsx("strong",{children:"Legal"}),e.jsx("a",{href:"/terms",children:"Terms"}),e.jsx("a",{href:"/privacy",children:"Privacy"}),e.jsx("a",{href:"/refunds",children:"Cancellation & refunds"})]})]})]}),e.jsxs("div",{className:"site-footer-bottom",children:[e.jsx("span",{children:"© 2026 VuvoDesk"}),e.jsx("span",{children:"Built for people who run real work across many screens."})]})]})}function be(){const s=[{name:"Studio-01",status:"Online",className:"screen-blue",cpu:"42%",ram:"61%"},{name:"Render-Mac",status:"Online",className:"screen-purple",cpu:"67%",ram:"74%"},{name:"Linux-Build",status:"Online",className:"screen-green",cpu:"29%",ram:"48%"},{name:"Office-PC",status:"Idle",className:"screen-orange",cpu:"18%",ram:"37%"}];return e.jsxs("div",{className:"product-mockup","aria-label":"VuvoDesk screen wall preview",children:[e.jsxs("div",{className:"mockup-window-bar",children:[e.jsxs("div",{children:[e.jsx("i",{}),e.jsx("i",{}),e.jsx("i",{})]}),e.jsx("span",{children:"VuvoDesk · Wall"}),e.jsxs("span",{className:"mockup-online",children:[e.jsx("i",{})," 4 online"]})]}),e.jsxs("div",{className:"mockup-body",children:[e.jsxs("aside",{className:"mockup-rail","aria-hidden":"true",children:[e.jsx("span",{className:"active",children:e.jsx(I,{size:17})}),e.jsx("span",{children:e.jsx(g,{size:17})}),e.jsx("span",{children:e.jsx(_,{size:17})}),e.jsx("span",{children:e.jsx(R,{size:17})})]}),e.jsx("div",{className:"mockup-wall",children:s.map(a=>e.jsxs("article",{className:"mockup-screen",children:[e.jsxs("div",{className:`mockup-screen-image ${a.className}`,children:[e.jsx("span",{className:"mockup-code-line wide"}),e.jsx("span",{className:"mockup-code-line"}),e.jsx("span",{className:"mockup-code-line short"}),e.jsx("span",{className:"mockup-panel-block"})]}),e.jsxs("div",{className:"mockup-screen-title",children:[e.jsx("strong",{children:a.name}),e.jsxs("span",{children:[e.jsx("i",{})," ",a.status]})]}),e.jsxs("div",{className:"mockup-metrics",children:[e.jsxs("span",{children:["CPU ",e.jsx("b",{children:a.cpu})]}),e.jsxs("span",{children:["RAM ",e.jsx("b",{children:a.ram})]})]})]},a.name))})]}),e.jsxs("div",{className:"mockup-control-card",children:[e.jsx(g,{size:18}),e.jsxs("span",{children:[e.jsx("small",{children:"Selected computer"}),e.jsx("strong",{children:"Render-Mac"})]}),e.jsx("button",{type:"button",children:"Control"})]})]})}function B({compact:s=!1}){const a=[{key:"free",name:i.free.name,price:i.free.price,suffix:"forever",audience:i.free.audience,description:"A practical personal wall, supported by a standard banner.",features:[`Up to ${K} devices`,"1 person","Standard banner"],action:"Start free",href:"/app",available:!0},{key:"plus",name:i.ltd.name,price:i.ltd.price,suffix:"/ month",audience:i.ltd.audience,description:"A clean personal command center for creators and homelabs.",features:[`Up to ${G} devices`,"1 person · no standard banner","Personal use"],ltd:i.ltd.ltd,action:p||d?"Choose Plus":"Paid checkout opening soon",href:p||d?"/app?view=pricing":"/support#billing",available:p||d},{key:"pro",name:i.pro.name,price:i.pro.price,suffix:"/ month",audience:i.pro.audience,description:"Monitoring for schools, academies, studios, offices, and growing commercial fleets.",features:[`Up to ${Y} devices`,"1 person","Ad-free school, academy, and commercial use"],ltd:i.pro.ltd,action:p||d?"Choose Business":"Paid checkout opening soon",href:p||d?"/app?view=pricing":"/support#billing",available:p||d},{key:"team",name:i.team.name,price:i.team.price,suffix:"/ month",audience:i.team.audience,description:"A shared Team Space for operators who manage the same computers together.",features:["Unlimited computers",`${X} people with their own accounts`,"Commercial use · Owner and Operator roles"],action:w?"Choose Team":"Paid checkout opening soon",href:w?"/app?view=pricing":"/support#billing",available:w}];return e.jsx("div",{className:`site-pricing-grid ${s?"compact":""}`,children:a.map(n=>e.jsxs("article",{className:`site-price-card ${n.key==="plus"?"featured":""} ${n.available?"":"pending"}`,children:[e.jsxs("div",{className:"site-price-heading",children:[e.jsx("span",{children:n.name}),n.key==="plus"&&e.jsx("em",{children:"Most popular"})]}),e.jsx("p",{className:"site-price-audience",children:n.audience}),e.jsxs("div",{className:"site-price",children:[e.jsx("strong",{children:n.price}),e.jsx("small",{children:n.suffix})]}),e.jsx("p",{children:n.description}),e.jsx("ul",{children:n.features.map(t=>e.jsxs("li",{children:[e.jsx(he,{size:16})," ",t]},t))}),n.ltd&&e.jsxs("div",{className:"site-ltd",children:[e.jsxs("span",{children:[n.name," LTD"]}),e.jsx("strong",{children:n.ltd.replace(" LTD","")}),e.jsx("small",{children:d?"one-time · available now":"one-time · checkout opening soon"})]}),e.jsxs("a",{className:`site-price-action ${n.key==="plus"&&n.available?"primary":""} ${n.available?"":"pending"}`,href:n.href,children:[n.action,e.jsx(o,{size:15})]})]},n.key))})}function ye(){return e.jsx(e.Fragment,{children:e.jsxs("main",{children:[e.jsxs("section",{className:"site-hero",children:[e.jsxs("div",{className:"site-hero-copy",children:[e.jsxs("span",{className:"site-kicker",children:[e.jsx(re,{size:15})," The screen wall for real work"]}),e.jsxs("h1",{children:["Every computer.",e.jsx("br",{}),e.jsx("span",{children:"One calm command center."})]}),e.jsx("p",{children:"See every screen, focus the computer that needs you, and keep work moving from your desk or phone."}),e.jsxs("div",{className:"site-hero-actions",children:[e.jsxs("a",{className:"site-button",href:"/download",children:["Download VuvoDesk ",e.jsx(o,{size:17})]}),e.jsxs("a",{className:"site-button site-button-secondary",href:"/app",children:["Open web console ",e.jsx(O,{size:17})]})]}),e.jsxs("div",{className:"site-hero-notes",children:[e.jsxs("span",{children:[e.jsx(y,{size:15})," Free for 5 devices"]}),e.jsxs("span",{children:[e.jsx(y,{size:15})," No credit card"]}),e.jsxs("span",{children:[e.jsx(y,{size:15})," Windows · macOS · Linux"]})]})]}),e.jsxs("div",{className:"site-hero-visual",children:[e.jsx("div",{className:"site-orbit site-orbit-one"}),e.jsx("div",{className:"site-orbit site-orbit-two"}),e.jsx(be,{})]})]}),e.jsxs("section",{className:"platform-strip","aria-label":"Supported platforms",children:[e.jsx("span",{children:"Built for the computers you already run"}),e.jsxs("div",{children:[e.jsxs("strong",{children:[e.jsx(M,{size:18})," Windows"]}),e.jsxs("strong",{children:[e.jsx(S,{size:18})," macOS"]}),e.jsxs("strong",{children:[e.jsx(v,{size:18})," Linux"]}),e.jsxs("strong",{children:[e.jsx(j,{size:18})," iPhone & Android"]}),e.jsxs("strong",{children:[e.jsx(O,{size:18})," Any browser"]})]})]}),e.jsxs("section",{className:"site-section site-value-section",id:"product",children:[e.jsxs("div",{className:"site-section-heading",children:[e.jsx("span",{children:"Why VuvoDesk"}),e.jsxs("h2",{children:["Spend less time finding the problem.",e.jsx("br",{}),"Spend more time fixing it."]}),e.jsx("p",{children:"VuvoDesk keeps the fleet visible without turning your desktop into a pile of remote windows."})]}),e.jsxs("div",{className:"site-value-grid",children:[e.jsxs("article",{className:"site-value-card large blue",children:[e.jsx("span",{className:"site-feature-icon",children:e.jsx(I,{size:23})}),e.jsx("h3",{children:"A wall that stays readable"}),e.jsx("p",{children:"Move from a compact list to large live previews. Keep the signal you need and hide the noise you do not."}),e.jsxs("div",{className:"feature-wall-demo",children:[e.jsx("i",{}),e.jsx("i",{}),e.jsx("i",{}),e.jsx("i",{}),e.jsx("i",{}),e.jsx("i",{})]})]}),e.jsxs("article",{className:"site-value-card mint",children:[e.jsx("span",{className:"site-feature-icon",children:e.jsx(g,{size:23})}),e.jsx("h3",{children:"Control without losing context"}),e.jsx("p",{children:"Jump from Wall to focused Control, then return to the same fleet view."}),e.jsxs("div",{className:"feature-cursor-demo",children:[e.jsx(g,{size:31}),e.jsx("span",{children:"Direct control"})]})]}),e.jsxs("article",{className:"site-value-card violet",children:[e.jsx("span",{className:"site-feature-icon",children:e.jsx(te,{size:23})}),e.jsx("h3",{children:"Your fleet in your pocket"}),e.jsx("p",{children:"Install the PWA on iPhone or Android and check the room before you walk back to it."}),e.jsxs("div",{className:"feature-phone-demo",children:[e.jsx("span",{}),e.jsxs("div",{children:[e.jsx("i",{}),e.jsx("i",{}),e.jsx("i",{})]})]})]}),e.jsxs("article",{className:"site-value-card wide dark",children:[e.jsxs("div",{children:[e.jsxs("span",{className:"site-kicker dark",children:[e.jsx(oe,{size:14})," Local-first when possible"]}),e.jsx("h3",{children:"Fast where you work. Reachable when you leave."}),e.jsx("p",{children:"VuvoDesk prefers the shortest useful route and keeps the current screen moving instead of building a stale queue."})]}),e.jsxs("div",{className:"feature-route",children:[e.jsxs("span",{children:[e.jsx(S,{size:19})," Hub"]}),e.jsx("i",{}),e.jsxs("span",{children:[e.jsx(le,{size:19})," Clients"]}),e.jsx("i",{}),e.jsxs("span",{children:[e.jsx(j,{size:19})," PWA"]})]})]})]})]}),e.jsxs("section",{className:"site-section site-workflow",children:[e.jsxs("div",{className:"site-section-heading centered",children:[e.jsx("span",{children:"Start in three steps"}),e.jsx("h2",{children:"From zero to a live wall in minutes."})]}),e.jsxs("div",{className:"workflow-grid",children:[e.jsxs("article",{children:[e.jsx("b",{children:"01"}),e.jsx("span",{children:e.jsx(h,{size:21})}),e.jsx("h3",{children:"Install the Hub"}),e.jsx("p",{children:"Choose the computer that will organize your VuvoDesk fleet."})]}),e.jsxs("article",{children:[e.jsx("b",{children:"02"}),e.jsx("span",{children:e.jsx(_,{size:21})}),e.jsx("h3",{children:"Connect Clients"}),e.jsx("p",{children:"Use one PIN to add the Windows, Mac, and Linux computers you manage."})]}),e.jsxs("article",{children:[e.jsx("b",{children:"03"}),e.jsx("span",{children:e.jsx(j,{size:21})}),e.jsx("h3",{children:"Manage from anywhere"}),e.jsx("p",{children:"Open the web console or install the mobile PWA and keep every screen close."})]})]})]}),e.jsxs("section",{className:"site-section site-pricing-section",children:[e.jsxs("div",{className:"site-section-heading centered",children:[e.jsx("span",{children:"Simple pricing"}),e.jsx("h2",{children:"Start free. Upgrade when the wall earns its place."}),e.jsx("p",{children:b?"Monthly prices stay easy to understand. Lifetime offers are available separately below each paid plan.":"Free is available now. Paid checkout stays closed until live billing and customer support are verified."})]}),e.jsx(B,{compact:!0}),e.jsxs("a",{className:"site-inline-link",href:"/pricing",children:["Compare every plan ",e.jsx(o,{size:15})]})]}),e.jsxs("section",{className:"site-final-cta",children:[e.jsxs("div",{children:[e.jsxs("span",{className:"site-kicker dark",children:[e.jsx(ce,{size:14})," Your fleet, one glance away"]}),e.jsxs("h2",{children:["Give every computer",e.jsx("br",{}),"one clear place to report."]})]}),e.jsxs("div",{children:[e.jsx("p",{children:"Start with five devices for free. Install the Hub, connect a Client, and open your first live wall."}),e.jsxs("div",{children:[e.jsxs("a",{className:"site-button light",href:"/download",children:["Get VuvoDesk ",e.jsx(o,{size:17})]}),e.jsx("a",{className:"site-button ghost",href:"/app",children:"Open console"})]})]})]})]})})}function ke(){const[s,a]=x.useState(!1),n=async()=>{try{await navigator.clipboard.writeText(T),a(!0),window.setTimeout(()=>a(!1),1800)}catch{a(!1)}};return e.jsxs("main",{className:"site-subpage",children:[e.jsxs("section",{className:"site-subpage-hero download-hero",children:[e.jsxs("span",{className:"site-kicker",children:[e.jsx(h,{size:15})," Download VuvoDesk"]}),e.jsxs("h1",{children:["Turn one computer into",e.jsx("br",{}),e.jsx("span",{children:"your command center."})]}),e.jsxs("p",{children:["Use the latest temporary installer for your computer. Available links follow the newest matching file on the ",U," channel; an unavailable platform is never linked to a missing file."]})]}),e.jsxs("section",{className:"download-grid",children:[e.jsxs("article",{id:"windows",children:[e.jsx("span",{className:"download-platform-icon windows",children:e.jsx(M,{size:27})}),e.jsxs("div",{children:[e.jsx("small",{children:"Windows"}),e.jsx("span",{className:"download-version",children:"Latest temporary build · x64 Electron"}),e.jsx("span",{className:"download-preview-note",children:"Temporary installer · unsigned"}),e.jsx("h2",{children:"Install VuvoDesk for Windows"}),e.jsx("p",{children:"Download the normal Electron setup program. Windows SmartScreen may warn about this preview until code signing is ready."})]}),e.jsxs("a",{className:"download-installer-link",href:ue,children:["Download Windows installer ",e.jsx(h,{size:16})]})]}),e.jsxs("article",{id:"macos",children:[e.jsx("span",{className:"download-platform-icon mac",children:e.jsx(S,{size:27})}),e.jsxs("div",{children:[e.jsx("small",{children:"macOS"}),e.jsx("span",{className:"download-version",children:"Intel & Apple silicon"}),e.jsx("span",{className:"download-preview-note",children:k?"Temporary installer · ad-hoc signed · not notarized":"Desktop installer not published yet"}),e.jsx("h2",{children:"Install VuvoDesk for macOS"}),e.jsx("p",{children:k?"Choose the chip in your Mac. On first launch, macOS may require Open Anyway in Privacy & Security because this preview is not Apple-notarized.":"Use the terminal install below while signed and notarized desktop packages are being prepared."})]}),k?e.jsxs("div",{className:"download-actions",children:[e.jsxs("a",{className:"download-installer-link",href:xe,children:["Apple silicon ",e.jsx(h,{size:16})]}),e.jsxs("a",{className:"download-installer-link secondary",href:me,children:["Intel Mac ",e.jsx(h,{size:16})]})]}):e.jsx("div",{className:"download-actions",children:e.jsxs("a",{className:"download-installer-link secondary",href:"#install-command",children:["Use terminal install ",e.jsx(v,{size:16})]})})]}),e.jsxs("article",{id:"linux",children:[e.jsx("span",{className:"download-platform-icon linux",children:e.jsx(v,{size:27})}),e.jsxs("div",{children:[e.jsx("small",{children:"Linux"}),e.jsx("span",{className:"download-version",children:"Latest temporary build · x64 · X11"}),e.jsx("span",{className:"download-preview-note",children:"Temporary installer"}),e.jsx("h2",{children:"Install VuvoDesk for Linux"}),e.jsx("p",{children:"Use the DEB package on Ubuntu, Debian, or Mint. For other distributions, download AppImage, mark it executable, then open it."})]}),e.jsxs("div",{className:"download-actions",children:[e.jsxs("a",{className:"download-installer-link",href:fe,children:["Download .deb ",e.jsx(h,{size:16})]}),e.jsxs("a",{className:"download-installer-link secondary",href:je,children:["Download AppImage ",e.jsx(h,{size:16})]})]})]})]}),e.jsxs("section",{className:"install-command-card",id:"install-command",children:[e.jsxs("div",{children:[e.jsxs("span",{children:[e.jsx(v,{size:18})," One command install"]}),e.jsx("h2",{children:"Terminal fallback and advanced setup"}),e.jsx("p",{children:"Use the npm package when you prefer a command-line install or the temporary desktop package does not suit your system."})]}),e.jsxs("div",{className:"install-command",children:[e.jsx("code",{children:T}),e.jsxs("button",{type:"button",onClick:()=>{n()},children:[e.jsx(de,{size:17})," ",s?"Copied":"Copy"]})]}),e.jsxs("ol",{children:[e.jsxs("li",{children:[e.jsx("b",{children:"1"}),e.jsx("span",{children:"Run the command on the computer that will be your Hub."})]}),e.jsxs("li",{children:[e.jsx("b",{children:"2"}),e.jsx("span",{children:"Open Settings and create a Hub PIN."})]}),e.jsxs("li",{children:[e.jsx("b",{children:"3"}),e.jsx("span",{children:"Run the same package as a Client on the other computers."})]})]})]}),e.jsxs("section",{className:"mobile-install-section",id:"mobile",children:[e.jsxs("div",{className:"mobile-install-heading",children:[e.jsx("span",{children:e.jsx(j,{size:26})}),e.jsxs("div",{children:[e.jsxs("small",{children:["iPhone & Android · PWA ",F]}),e.jsx("h2",{children:"No app store required."}),e.jsx("p",{children:"Open the public web address, sign in, then install VuvoDesk on the Home Screen. The PWA always starts directly in the console."})]})]}),e.jsxs("div",{className:"mobile-web-address",children:[e.jsx("span",{children:"Mobile web address"}),e.jsx("a",{href:"https://vuvodesk.com/app",children:"https://vuvodesk.com/app"}),e.jsxs("a",{className:"site-button",href:"/app",children:["Open mobile console ",e.jsx(o,{size:16})]})]}),e.jsxs("div",{className:"pwa-guide-grid",children:[e.jsxs("article",{children:[e.jsx("strong",{children:"iPhone & iPad"}),e.jsxs("ol",{children:[e.jsx("li",{children:"Open the address in Safari."}),e.jsx("li",{children:"Tap Share, then Add to Home Screen."}),e.jsx("li",{children:"Choose Open as Web App and add VuvoDesk."})]})]}),e.jsxs("article",{children:[e.jsx("strong",{children:"Android"}),e.jsxs("ol",{children:[e.jsx("li",{children:"Open the address in Chrome."}),e.jsx("li",{children:"Open the browser menu and tap Install app."}),e.jsx("li",{children:"Launch VuvoDesk from the Home Screen."})]})]})]})]})]})}function Ne(){return e.jsxs("main",{className:"site-subpage pricing-subpage",children:[e.jsxs("section",{className:"site-subpage-hero",children:[e.jsxs("span",{className:"site-kicker",children:[e.jsx(P,{size:15})," VuvoDesk pricing"]}),e.jsxs("h1",{children:["Clear plans for",e.jsx("br",{}),e.jsx("span",{children:"growing screen walls."})]}),e.jsxs("p",{children:["Start with one account, choose Pro / Business for a school, academy, or commercial workplace, or give each operator a separate login with Team. ",!b&&"Paid checkout is not open yet."]})]}),e.jsx(B,{}),e.jsxs("section",{className:"pricing-explainer",children:[e.jsxs("article",{children:[e.jsx(A,{size:21}),e.jsxs("div",{children:[e.jsx("h3",{children:"Free stays useful"}),e.jsx("p",{children:"The standard banner helps keep a five-device personal wall available without a subscription."})]})]}),e.jsxs("article",{children:[e.jsx(P,{size:21}),e.jsxs("div",{children:[e.jsx("h3",{children:"Monthly and lifetime are separate"}),e.jsx("p",{children:"$5 Plus, $15 Pro / Business, and $49 Team are monthly prices. Existing Plus LTD $79 and Pro LTD $199 remain one-time offers."})]})]}),e.jsxs("article",{children:[e.jsx(R,{size:21}),e.jsxs("div",{children:[e.jsx("h3",{children:"Upgrade inside VuvoDesk"}),e.jsx("p",{children:"Sign in to the console, open Pricing, and choose the plan that matches the way you use the fleet."})]})]})]}),e.jsxs("section",{className:"pricing-bottom-cta",children:[e.jsxs("div",{children:[e.jsx("h2",{children:"Start with the computers you have today."}),e.jsx("p",{children:"No credit card is needed for Free."})]}),e.jsxs("a",{className:"site-button",href:"/app",children:["Start free ",e.jsx(o,{size:16})]})]})]})}function Se(){return e.jsxs("main",{className:"site-subpage policy-subpage",children:[e.jsxs("section",{className:"site-subpage-hero",children:[e.jsxs("span",{className:"site-kicker",children:[e.jsx(A,{size:15})," VuvoDesk support"]}),e.jsxs("h1",{children:["Start with the shortest",e.jsx("br",{}),e.jsx("span",{children:"path back to work."})]}),e.jsx("p",{children:"Installation help is public. Account-specific diagnostics and billing requests stay inside your signed-in Support board so they are tied to the correct account."})]}),e.jsxs("section",{className:"support-option-grid",children:[e.jsxs("article",{children:[e.jsx(h,{size:23}),e.jsx("h2",{children:"Install or update"}),e.jsx("p",{children:"Use the current Windows or Linux preview package, or the terminal command for every supported desktop."}),e.jsxs("a",{href:"/download",children:["Open download help ",e.jsx(o,{size:15})]})]}),e.jsxs("article",{children:[e.jsx(j,{size:23}),e.jsx("h2",{children:"Connection trouble"}),e.jsx("p",{children:"Sign in, open the Support board, and include the affected computer name, operating system, VuvoDesk version, and the exact message you saw."}),e.jsxs("a",{href:"/support/issues",children:["Open signed-in Support ",e.jsx(o,{size:15})]})]}),e.jsxs("article",{id:"billing",children:[e.jsx(P,{size:23}),e.jsx("h2",{children:"Account or billing"}),e.jsx("p",{children:b?"Use the signed-in Support board for cancellation, refund, duplicate-payment, or access requests.":"Paid checkout is currently closed. No paid entitlement should be granted until verified checkout becomes available."}),e.jsxs("a",{href:"/support/questions",children:["Open account Support ",e.jsx(o,{size:15})]})]})]}),e.jsxs("section",{className:"support-checklist",children:[e.jsx("h2",{children:"Before sending a report"}),e.jsxs("ol",{children:[e.jsx("li",{children:"Confirm the Hub and Client show the same VuvoDesk release."}),e.jsx("li",{children:"Write down whether the problem affects Wall, Control, clipboard, or sign-in."}),e.jsx("li",{children:"Never post a Hub PIN, pairing token, access token, or payment credential."})]})]})]})}function Pe(){return e.jsxs(C,{title:"Terms of Service",summary:"The practical rules for using the VuvoDesk preview.",updated:"August 30, 2026",children:[e.jsx("h2",{children:"1. Authorized use"}),e.jsx("p",{children:"Use VuvoDesk only on computers and networks you own or are explicitly allowed to manage. You are responsible for every remote-control action performed through your account, Hub, Team Space, or pairing credential."}),e.jsx("h2",{children:"2. Accounts and Team Spaces"}),e.jsx("p",{children:"Keep sign-in and pairing credentials private. Team members use separate accounts. A Team Owner is responsible for invitations, roles, removals, and the computers shared with the Team Space."}),e.jsx("h2",{children:"3. Plans and payment"}),e.jsxs("p",{children:["Free, Plus, Pro / Business, and Team features are limited by the plan shown in the product. A displayed price is not a promise that checkout is open. Paid access starts only after a verified payment event grants the entitlement. Cancellation and refund handling follows the ",e.jsx("a",{href:"/refunds",children:"Cancellation & Refund Policy"}),"."]}),e.jsx("h2",{children:"4. Codex Agent features"}),e.jsx("p",{children:"Codex Agent features require a separately installed and signed-in Codex environment and a separate OpenAI account. OpenAI terms apply to that account and service. VuvoDesk does not sell Codex access and is not an OpenAI product."}),e.jsx("h2",{children:"5. Preview software"}),e.jsx("p",{children:"Unsigned or non-notarized installers are clearly labeled preview builds. Do not use preview software for safety-critical systems or where a temporary loss of screen, input, or connectivity could cause harm. Keep local access and backups available."}),e.jsx("h2",{children:"6. Acceptable behavior"}),e.jsx("p",{children:"Do not use VuvoDesk to access another person's device without permission, evade security controls, distribute malware, interfere with the service, or violate law or third-party rights."}),e.jsx("h2",{children:"7. Changes and termination"}),e.jsx("p",{children:"We may suspend access that creates security, abuse, or payment risk. Material policy changes will be dated on this page. Mandatory consumer rights in your country are not excluded."}),e.jsx("h2",{children:"8. Support"}),e.jsxs("p",{children:["Use the ",e.jsx("a",{href:"/support",children:"Support page"})," for product and account requests. Do not include passwords, tokens, or payment credentials."]})]})}function De(){return e.jsxs(C,{title:"Privacy Notice",summary:"What VuvoDesk handles, where it goes, and what is kept local.",updated:"August 30, 2026",children:[e.jsx("h2",{children:"1. Account and product data"}),e.jsx("p",{children:"VuvoDesk processes sign-in identity, device and workspace identifiers, roles, plan entitlements, connection status, version information, and Support content needed to operate the service."}),e.jsx("h2",{children:"2. Screen and input traffic"}),e.jsx("p",{children:"VuvoDesk is designed to send live screen, audio, clipboard, and remote-input traffic directly between your Hub, Clients, and browser when the connection succeeds. The Cloudflare signaling service exchanges authenticated connection metadata and does not intentionally store or forward the screen-media payload. Local diagnostic logs may record status and error details, not passwords or payment credentials."}),e.jsx("h2",{children:"3. Service providers"}),e.jsx("p",{children:"Google and Supabase provide sign-in and account storage. Cloudflare provides the public site, update delivery, and authenticated connection signaling. Dodo Payments is used only when paid checkout is enabled. Optional Codex Agent actions use the separately signed-in OpenAI service selected by the user."}),e.jsx("h2",{children:"4. Retention and control"}),e.jsxs("p",{children:["Account, workspace, entitlement, and Support records are retained while needed to provide the service, resolve disputes, prevent abuse, or meet legal duties. Request account access, correction, export, or deletion through the signed-in ",e.jsx("a",{href:"/support/questions",children:"Support board"}),". Some records may be retained where law or fraud prevention requires it."]}),e.jsx("h2",{children:"5. Security and transfers"}),e.jsx("p",{children:"Internet services may process data in more than one country. We use authenticated sessions, restricted service credentials, and encrypted network transport where supported, but no system can promise absolute security."}),e.jsx("h2",{children:"6. Policy changes"}),e.jsx("p",{children:"Material changes will update the date on this page. Review this notice before enabling new remote-control, clipboard, Team, payment, or Agent features."})]})}function Ae(){return e.jsxs(C,{title:"Cancellation & Refund Policy",summary:"A plain-language policy before any paid checkout is opened.",updated:"August 30, 2026",children:[e.jsx("h2",{children:"1. Current checkout status"}),e.jsx("p",{children:b?"Paid checkout is available only for the plans whose buttons are active.":"Paid checkout is currently closed. Preview access does not create a paid subscription or lifetime entitlement."}),e.jsx("h2",{children:"2. Subscriptions"}),e.jsx("p",{children:"You may request cancellation at any time. Cancellation stops the next renewal and normally keeps access through the already-paid billing period. A cancellation request must be submitted before the renewal is charged."}),e.jsx("h2",{children:"3. Initial purchase refunds"}),e.jsx("p",{children:"For an initial paid purchase, request a refund within 14 calendar days of payment. Duplicate or unauthorized charges and a material service failure will be reviewed even after that period. Mandatory local refund rights always apply."}),e.jsx("h2",{children:"4. Lifetime purchases"}),e.jsx("p",{children:"A lifetime purchase covers the product entitlement described at checkout, not third-party services, future paid add-ons, or a promise that every preview feature will remain unchanged. The same 14-day initial-purchase request window applies."}),e.jsx("h2",{children:"5. How to request"}),e.jsxs("p",{children:["Open the signed-in ",e.jsx("a",{href:"/support/questions",children:"Support board"})," and provide the account email, plan, purchase date, and reason. Never include full card details, passwords, API keys, or access tokens. Access may be removed after a full refund or successful payment dispute."]})]})}function C({title:s,summary:a,updated:n,children:t}){return e.jsxs("main",{className:"site-subpage policy-subpage",children:[e.jsxs("section",{className:"site-subpage-hero policy-hero",children:[e.jsxs("span",{className:"site-kicker",children:[e.jsx(A,{size:15})," VuvoDesk policy"]}),e.jsx("h1",{children:s}),e.jsx("p",{children:a}),e.jsxs("small",{children:["Last updated: ",n]})]}),e.jsx("article",{className:"policy-document",children:t})]})}function Ce(){return e.jsxs("main",{className:"site-not-found",children:[e.jsx("span",{className:"site-brand-mark",children:e.jsx(V,{size:25})}),e.jsx("small",{children:"404"}),e.jsx("h1",{children:"This screen is not on the wall."}),e.jsx("p",{children:"The page may have moved, but your VuvoDesk console is still here."}),e.jsxs("div",{children:[e.jsx("a",{className:"site-button",href:"/",children:"VuvoDesk home"}),e.jsx("a",{className:"site-button site-button-secondary",href:"/app",children:"Open console"})]})]})}function Le(){const s=ve();return x.useEffect(()=>{const a={home:"VuvoDesk — Every computer. One calm command center.",download:"Download VuvoDesk — Windows, macOS, and Linux",pricing:"VuvoDesk Pricing — Free, Plus, Pro / Business, and Team",support:"VuvoDesk Support",terms:"VuvoDesk Terms of Service",privacy:"VuvoDesk Privacy Notice",refunds:"VuvoDesk Cancellation & Refund Policy","not-found":"Page not found — VuvoDesk"},n={home:"See and control the computers you manage from one clear VuvoDesk screen wall.",download:"Download the currently available VuvoDesk desktop preview or install it with one npm command.",pricing:"Compare VuvoDesk Free, Plus, Pro / Business, and Team plan boundaries.",support:"Get public VuvoDesk installation help or open the signed-in Support board.",terms:"Read the VuvoDesk terms for authorized remote-control use, plans, preview software, and Codex Agent features.",privacy:"Learn how VuvoDesk handles account, device, support, signaling, and direct screen traffic data.",refunds:"Read the VuvoDesk cancellation and refund policy before paid checkout opens.","not-found":"The requested VuvoDesk page was not found."};document.title=a[s];const m=`https://vuvodesk.com${s==="not-found"?"/":window.location.pathname.replace(/\/+$/,"")||"/"}`;let r=document.querySelector('link[rel="canonical"]');r||(r=document.createElement("link"),r.rel="canonical",document.head.append(r)),r.href=m;const c=document.querySelector('meta[name="description"]');c&&(c.content=n[s]);let l=document.querySelector('meta[property="og:url"]');return l||(l=document.createElement("meta"),l.setAttribute("property","og:url"),document.head.append(l)),l.content=m,document.documentElement.classList.add("livedesk-marketing-active"),()=>document.documentElement.classList.remove("livedesk-marketing-active")},[s]),e.jsxs("div",{className:"marketing-site",children:[e.jsx(we,{page:s}),s==="home"?e.jsx(ye,{}):s==="download"?e.jsx(ke,{}):s==="pricing"?e.jsx(Ne,{}):s==="support"?e.jsx(Se,{}):s==="terms"?e.jsx(Pe,{}):s==="privacy"?e.jsx(De,{}):s==="refunds"?e.jsx(Ae,{}):e.jsx(Ce,{}),e.jsx(ge,{})]})}function ze(s){return!!s.navigatorStandalone||!!s.displayModeStandalone||!!s.displayModeFullscreen}function Ee({publicOrigin:s,electron:a,pathname:n,search:t="",navigatorStandalone:m=!1,displayModeStandalone:r=!1,displayModeFullscreen:c=!1,marketingPreview:l=!1}){if(a||!s&&!l)return{kind:"app",redirectPath:""};const L=new URLSearchParams(t),$=L.has("pwa")||ze({navigatorStandalone:m,displayModeStandalone:r,displayModeFullscreen:c}),z=n==="/app"||n.startsWith("/app/"),q=n.startsWith("/support/"),H=L.has("checkout");return $||H?{kind:"app",redirectPath:z?"":"/app"}:z||q?{kind:"app",redirectPath:""}:{kind:"site",redirectPath:""}}function Oe({automaticRetry:s}){const[a,n]=x.useState(null),[t,m]=x.useState("");return x.useEffect(()=>{let r=!0;return se(()=>import("./VuvoDeskApp-BIrWZOFb.js"),__vite__mapDeps([0,1,2,3,4,5,6,7,8,9,10,11,12])).then(c=>{r&&n(()=>c.VuvoDeskApp)}).catch(c=>{if(!r)return;console.warn("[VuvoDesk] app startup load failed.",c);const l=window.navigator.onLine?"The latest app file could not be loaded.":"Reconnect to the internet, then try again.";m(l),ne("load-error",s)}),()=>{r=!1}},[s]),a?e.jsx(a,{}):t?e.jsx(ae,{detail:t,automaticRetry:s}):e.jsx("main",{className:"runtime-role-boot","aria-label":"Starting VuvoDesk","aria-busy":"true",children:e.jsx("span",{className:"runtime-role-boot-mark","aria-hidden":"true"})})}window.liveDesk&&document.documentElement.classList.add("livedesk-electron-desktop");const u=new URL(window.location.href),Te=!!window.navigator.standalone,D=Ee({publicOrigin:N(),electron:!!window.liveDesk,pathname:u.pathname,search:u.search,navigatorStandalone:Te,displayModeStandalone:window.matchMedia("(display-mode: standalone)").matches,displayModeFullscreen:window.matchMedia("(display-mode: fullscreen)").matches,marketingPreview:u.searchParams.get("site")==="1"});D.redirectPath&&(u.pathname=D.redirectPath,window.history.replaceState({},"",`${u.pathname}${u.search}${u.hash}`));J.createRoot(document.getElementById("root")).render(e.jsx(x.StrictMode,{children:D.kind==="site"?e.jsx(Le,{}):e.jsx(Q,{automaticRetry:N(),children:e.jsx(Oe,{automaticRetry:N()})})}));Z();ee();