11import { useEffect , useRef , useState } from "react"
22import { Monitor } from "lucide-react"
33import { Section , Spinner } from "@cozystack/ui"
4+ import { useK8sGet , type K8sResource } from "@cozystack/k8s-client"
45import type { ApplicationDefinition , ApplicationInstance } from "@cozystack/types"
56
67interface VncTabProps {
@@ -15,14 +16,35 @@ export function VncTab({ ad, instance }: VncTabProps) {
1516 // VirtualMachineInstance named "<release.prefix><name>"
1617 // (e.g. "vm-instance-demo-vm"), which is what the subresource path needs.
1718 const vmName = `${ ad . spec ?. release ?. prefix ?? "" } ${ instance . metadata . name } `
19+ // Don't open a VNC websocket unless the VM is actually running — there is no
20+ // VirtualMachineInstance to attach to otherwise, and the socket would just
21+ // error out. Poll the VirtualMachine power state.
22+ const { data : vm , isLoading : vmLoading } = useK8sGet <
23+ K8sResource < unknown , { printableStatus ?: string } >
24+ > (
25+ {
26+ apiGroup : "kubevirt.io" ,
27+ apiVersion : "v1" ,
28+ plural : "virtualmachines" ,
29+ name : vmName ,
30+ namespace : ns ?? "" ,
31+ } ,
32+ {
33+ enabled : appKind === "VMInstance" && ! ! vmName && ! ! ns ,
34+ refetchInterval : 5000 ,
35+ } ,
36+ )
37+ const powerStatus = vm ?. status ?. printableStatus
38+ const isRunning = powerStatus === "Running"
1839 const [ error , setError ] = useState < string | null > ( null )
1940 const [ connecting , setConnecting ] = useState ( true )
2041 const [ connected , setConnected ] = useState ( false )
2142 const vncContainerRef = useRef < HTMLDivElement > ( null )
2243 const rfbRef = useRef < any > ( null )
2344
2445 useEffect ( ( ) => {
25- if ( appKind !== "VMInstance" || ! vncContainerRef . current ) return
46+ if ( appKind !== "VMInstance" || ! isRunning || ! vncContainerRef . current )
47+ return
2648
2749 let mounted = true
2850
@@ -96,7 +118,7 @@ export function VncTab({ ad, instance }: VncTabProps) {
96118 rfbRef . current = null
97119 }
98120 }
99- } , [ appKind , ns , vmName ] )
121+ } , [ appKind , ns , vmName , isRunning ] )
100122
101123 if ( appKind !== "VMInstance" ) {
102124 return (
@@ -108,6 +130,34 @@ export function VncTab({ ad, instance }: VncTabProps) {
108130 )
109131 }
110132
133+ if ( vmLoading ) {
134+ return (
135+ < div className = "flex items-center gap-2 p-6 text-sm text-slate-500" >
136+ < Spinner /> Loading…
137+ </ div >
138+ )
139+ }
140+
141+ if ( ! isRunning ) {
142+ return (
143+ < div className = "p-6" >
144+ < Section
145+ title = {
146+ < span className = "inline-flex items-center gap-2" >
147+ < Monitor className = "size-4 text-slate-500" /> VNC Console
148+ </ span >
149+ }
150+ >
151+ < p className = "text-sm text-slate-500" >
152+ The virtual machine is not running
153+ { powerStatus ? ` (status: ${ powerStatus } )` : "" } . Start it to use
154+ the VNC console.
155+ </ p >
156+ </ Section >
157+ </ div >
158+ )
159+ }
160+
111161 const handleCtrlAltDel = ( ) => {
112162 if ( rfbRef . current ) {
113163 rfbRef . current . sendCtrlAltDel ( )
0 commit comments