2727 * - Configurable position and rotation in world space for flexible UI placement
2828 * - Draggable handle bar for repositioning the UI in 3D space
2929 * - Face-camera rotation for optimal viewing angle (Y-axis only)
30+ * - Panel depth: full control panel, compact (when "minimize on play" and teleop active), or hidden
31+ * (semi-transparent Show + slim drag handle).
3032 *
3133 * The UI is positioned in 3D space and designed for VR/AR interaction with
3234 * visual feedback and clear button labeling. All interactions are passed
@@ -66,6 +68,10 @@ interface CloudXRUIProps {
6668 streamingFpsText ?: ReadonlySignal < string > ;
6769 /** Computed signal for pose-to-render latency text - updates without React re-render */
6870 poseToRenderText ?: ReadonlySignal < string > ;
71+ /** From settings: hide control panel when immersive XR begins. */
72+ panelHiddenAtStart ?: boolean ;
73+ /** Immersive XR active; used to apply panelHiddenAtStart on session enter. */
74+ isXRMode ?: boolean ;
6975}
7076
7177// Reusable objects for face-camera rotation (avoid allocations in render loop)
@@ -96,6 +102,8 @@ export default function CloudXR3DUI({
96102 renderFpsText,
97103 streamingFpsText,
98104 poseToRenderText,
105+ panelHiddenAtStart = false ,
106+ isXRMode = false ,
99107} : CloudXRUIProps ) {
100108 const MINIMIZE_ON_PLAY_KEY = 'cxr.isaac.minimizeOnPlay' ;
101109
@@ -112,6 +120,17 @@ export default function CloudXR3DUI({
112120 }
113121 } ) ;
114122
123+ /** Control panel hidden: small Show control (see settings to hide control panel on XR enter). */
124+ const [ panelHidden , setPanelHidden ] = useState ( false ) ;
125+ const prevXRMode = useRef ( false ) ;
126+
127+ useEffect ( ( ) => {
128+ if ( isXRMode && ! prevXRMode . current ) {
129+ setPanelHidden ( panelHiddenAtStart ) ;
130+ }
131+ prevXRMode . current = isXRMode ;
132+ } , [ isXRMode , panelHiddenAtStart ] ) ;
133+
115134 // Keep localStorage in sync when the user toggles the option.
116135 useEffect ( ( ) => {
117136 try {
@@ -125,7 +144,10 @@ export default function CloudXR3DUI({
125144 }
126145 } , [ position [ 0 ] , position [ 1 ] , position [ 2 ] ] ) ;
127146
128- const isMinimized = minimizeOnPlay && playInProgress ;
147+ const isCompact = minimizeOnPlay && playInProgress ;
148+ const isMinimizedLayout = isCompact || panelHidden ;
149+ const handleWidth = panelHidden ? 0.12 : isCompact ? 0.28 : 1.0 ;
150+ const handleY = panelHidden ? - 0.065 : isCompact ? - 0.15 : - 0.42 ;
129151
130152 // Face-camera rotation: smoothly rotate UI to face the user (Y-axis only)
131153 useFrame ( ( state , dt ) => {
@@ -165,40 +187,63 @@ export default function CloudXR3DUI({
165187 >
166188 < mesh
167189 ref = { handleRef }
168- position = { [ 0 , isMinimized ? - 0.15 : - 0.42 , 0.01 ] }
190+ position = { [ 0 , handleY , 0.01 ] }
169191 onPointerEnter = { ( ) => {
170192 const mat = handleRef . current ?. material as MeshStandardMaterial | undefined ;
171193 if ( mat ) {
172194 mat . color . copy ( HANDLE_COLOR_HOVER ) ;
173- mat . opacity = 0.9 ;
195+ mat . opacity = panelHidden ? 0.55 : 0.9 ;
174196 }
175197 } }
176198 onPointerLeave = { ( ) => {
177199 const mat = handleRef . current ?. material as MeshStandardMaterial | undefined ;
178200 if ( mat ) {
179201 mat . color . copy ( HANDLE_COLOR_DEFAULT ) ;
180- mat . opacity = 0.6 ;
202+ mat . opacity = panelHidden ? 0.35 : 0.6 ;
181203 }
182204 } }
183205 >
184- < boxGeometry args = { [ isMinimized ? 0.28 : 1.0 , 0.05 , 0.02 ] } />
185- < meshStandardMaterial color = "#666666" transparent opacity = { 0.6 } roughness = { 0.5 } />
206+ < boxGeometry args = { [ handleWidth , panelHidden ? 0.035 : 0.05 , 0.02 ] } />
207+ < meshStandardMaterial
208+ color = "#666666"
209+ transparent
210+ opacity = { panelHidden ? 0.35 : 0.6 }
211+ roughness = { 0.5 }
212+ />
186213 </ mesh >
187214 </ Handle >
188215
189216 < Container
190217 pixelSize = { 0.001 }
191- width = { isMinimized ? 520 : 2000 }
192- height = { isMinimized ? 320 : 1400 }
218+ width = { panelHidden ? 128 : isCompact ? 520 : 2000 }
219+ height = { panelHidden ? 128 : isCompact ? 320 : 1400 }
193220 alignItems = "center"
194221 justifyContent = "center"
195222 pointerEvents = "auto"
196- padding = { isMinimized ? 24 : 40 }
197- sizeX = { isMinimized ? 0.87 : 3.33 }
198- sizeY = { isMinimized ? 0.53 : 2.33 }
223+ padding = { panelHidden ? 0 : isCompact ? 24 : 40 }
224+ sizeX = { panelHidden ? 0.2 : isCompact ? 0.87 : 3.33 }
225+ sizeY = { panelHidden ? 0.2 : isCompact ? 0.53 : 2.33 }
199226 flexDirection = "column"
200227 >
201- { isMinimized ? (
228+ { panelHidden ? (
229+ < Button
230+ { ...xrButton ( 'show-panel' , ( ) => setPanelHidden ( false ) ) }
231+ variant = "default"
232+ width = { 112 }
233+ height = { 112 }
234+ borderRadius = { 56 }
235+ backgroundColor = "rgba(90, 130, 210, 0.42)"
236+ hover = { {
237+ backgroundColor : 'rgba(90, 130, 210, 0.72)' ,
238+ borderColor : 'rgba(255, 255, 255, 0.6)' ,
239+ borderWidth : 2 ,
240+ } }
241+ >
242+ < Text fontSize = { 26 } color = "rgba(255, 255, 255, 0.95)" fontWeight = "bold" >
243+ Show
244+ </ Text >
245+ </ Button >
246+ ) : isCompact ? (
202247 < Container
203248 width = "100%"
204249 flexDirection = "column"
@@ -230,26 +275,51 @@ export default function CloudXR3DUI({
230275 </ Text >
231276 </ Container >
232277 </ Button >
233- < Button
234- { ...xrButton ( 'reset-min' , onResetTeleop ) }
235- variant = "default"
236- width = { 400 }
237- height = { 80 }
238- borderRadius = { 24 }
239- backgroundColor = "rgba(220, 220, 220, 0.9)"
240- hover = { {
241- backgroundColor : 'rgba(100, 150, 255, 1)' ,
242- borderColor : 'white' ,
243- borderWidth : 2 ,
244- } }
278+ < Container
279+ flexDirection = "row"
280+ gap = { 14 }
281+ alignItems = "center"
282+ justifyContent = "center"
283+ width = "100%"
245284 >
246- < Container flexDirection = "row" alignItems = "center" gap = { 8 } >
247- < Image src = "./arrow-uturn-left.svg" width = { 40 } height = { 40 } />
248- < Text fontSize = { 36 } color = "black" fontWeight = "medium" >
249- Reset
285+ < Button
286+ { ...xrButton ( 'reset-min' , onResetTeleop ) }
287+ variant = "default"
288+ width = { 292 }
289+ height = { 80 }
290+ borderRadius = { 24 }
291+ backgroundColor = "rgba(220, 220, 220, 0.9)"
292+ hover = { {
293+ backgroundColor : 'rgba(100, 150, 255, 1)' ,
294+ borderColor : 'white' ,
295+ borderWidth : 2 ,
296+ } }
297+ >
298+ < Container flexDirection = "row" alignItems = "center" gap = { 8 } >
299+ < Image src = "./arrow-uturn-left.svg" width = { 40 } height = { 40 } />
300+ < Text fontSize = { 36 } color = "black" fontWeight = "medium" >
301+ Reset
302+ </ Text >
303+ </ Container >
304+ </ Button >
305+ < Button
306+ { ...xrButton ( 'hide-panel-compact' , ( ) => setPanelHidden ( true ) ) }
307+ variant = "default"
308+ width = { 94 }
309+ height = { 80 }
310+ borderRadius = { 20 }
311+ backgroundColor = "rgba(70, 75, 90, 0.55)"
312+ hover = { {
313+ backgroundColor : 'rgba(90, 95, 115, 0.85)' ,
314+ borderColor : 'rgba(255, 255, 255, 0.5)' ,
315+ borderWidth : 2 ,
316+ } }
317+ >
318+ < Text fontSize = { 26 } color = "rgba(255, 255, 255, 0.92)" fontWeight = "medium" >
319+ Hide
250320 </ Text >
251- </ Container >
252- </ Button >
321+ </ Button >
322+ </ Container >
253323 </ Container >
254324 ) : (
255325 < Container
@@ -422,9 +492,10 @@ export default function CloudXR3DUI({
422492 ) }
423493 </ Container >
424494 < Text fontSize = { 30 } color = "rgba(220, 220, 220, 1)" >
425- Minimize on play
495+ Minimize on play (compact controls)
426496 </ Text >
427497 </ Container >
498+
428499 </ Container >
429500
430501 { /* Right Column - Controls */ }
@@ -565,7 +636,12 @@ export default function CloudXR3DUI({
565636 </ Container >
566637
567638 { /* Bottom Row */ }
568- < Container flexDirection = "row" justifyContent = "center" >
639+ < Container
640+ flexDirection = "row"
641+ justifyContent = "center"
642+ alignItems = "center"
643+ gap = { 18 }
644+ >
569645 < Button
570646 { ...xrButton ( 'disconnect' , onDisconnect ) }
571647 variant = "destructive"
@@ -586,6 +662,23 @@ export default function CloudXR3DUI({
586662 </ Text >
587663 </ Container >
588664 </ Button >
665+ < Button
666+ { ...xrButton ( 'hide-panel-full' , ( ) => setPanelHidden ( true ) ) }
667+ variant = "default"
668+ width = { 100 }
669+ height = { 90 }
670+ borderRadius = { 22 }
671+ backgroundColor = "rgba(70, 75, 90, 0.55)"
672+ hover = { {
673+ backgroundColor : 'rgba(90, 95, 115, 0.88)' ,
674+ borderColor : 'rgba(255, 255, 255, 0.5)' ,
675+ borderWidth : 2 ,
676+ } }
677+ >
678+ < Text fontSize = { 28 } color = "rgba(255, 255, 255, 0.92)" fontWeight = "medium" >
679+ Hide
680+ </ Text >
681+ </ Button >
589682 </ Container >
590683 </ Container >
591684 </ Container >
0 commit comments