Make the stream idle option a slider for time
This commit is contained in:
		@ -23,7 +23,10 @@ import {
 | 
				
			|||||||
import { isDesktop } from 'lib/isDesktop'
 | 
					import { isDesktop } from 'lib/isDesktop'
 | 
				
			||||||
import { openExternalBrowserIfDesktop } from 'lib/openWindow'
 | 
					import { openExternalBrowserIfDesktop } from 'lib/openWindow'
 | 
				
			||||||
import { EngineConnectionStateType } from 'lang/std/engineConnection'
 | 
					import { EngineConnectionStateType } from 'lang/std/engineConnection'
 | 
				
			||||||
import useEngineStreamContext, { EngineStreamState, EngineStreamTransition } from 'hooks/useEngineStreamContext'
 | 
					import useEngineStreamContext, {
 | 
				
			||||||
 | 
					  EngineStreamState,
 | 
				
			||||||
 | 
					  EngineStreamTransition,
 | 
				
			||||||
 | 
					} from 'hooks/useEngineStreamContext'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export function Toolbar({
 | 
					export function Toolbar({
 | 
				
			||||||
  className = '',
 | 
					  className = '',
 | 
				
			||||||
 | 
				
			|||||||
@ -97,10 +97,12 @@ export class CameraControls {
 | 
				
			|||||||
  wasDragging: boolean
 | 
					  wasDragging: boolean
 | 
				
			||||||
  mouseDownPosition: Vector2
 | 
					  mouseDownPosition: Vector2
 | 
				
			||||||
  mouseNewPosition: Vector2
 | 
					  mouseNewPosition: Vector2
 | 
				
			||||||
  old: {
 | 
					  old:
 | 
				
			||||||
    camera: PerspectiveCamera,
 | 
					    | {
 | 
				
			||||||
    target: Vector3,
 | 
					        camera: PerspectiveCamera
 | 
				
			||||||
  } | undefined
 | 
					        target: Vector3
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    | undefined
 | 
				
			||||||
  rotationSpeed = 0.3
 | 
					  rotationSpeed = 0.3
 | 
				
			||||||
  enableRotate = true
 | 
					  enableRotate = true
 | 
				
			||||||
  enablePan = true
 | 
					  enablePan = true
 | 
				
			||||||
@ -960,33 +962,51 @@ export class CameraControls {
 | 
				
			|||||||
    // camera coordinates after a zoom-to-fit... So this is much easier, and
 | 
					    // camera coordinates after a zoom-to-fit... So this is much easier, and
 | 
				
			||||||
    // maps better to screen coordinates.
 | 
					    // maps better to screen coordinates.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    await this.engineCommandManager.sendSceneCommand({
 | 
					    await this.engineCommandManager
 | 
				
			||||||
      type: 'modeling_cmd_batch_req',
 | 
					      .sendSceneCommand({
 | 
				
			||||||
      batch_id: uuidv4(),
 | 
					        type: 'modeling_cmd_batch_req',
 | 
				
			||||||
      responses: true,
 | 
					        batch_id: uuidv4(),
 | 
				
			||||||
      requests: [
 | 
					        responses: true,
 | 
				
			||||||
        {
 | 
					        requests: [
 | 
				
			||||||
          type: 'modeling_cmd_req',
 | 
					          {
 | 
				
			||||||
          cmd_id: uuidv4(),
 | 
					            type: 'modeling_cmd_req',
 | 
				
			||||||
          cmd: {
 | 
					            cmd_id: uuidv4(),
 | 
				
			||||||
            type: 'zoom_to_fit',
 | 
					            cmd: {
 | 
				
			||||||
            object_ids: zoomObjectId ? [zoomObjectId] : [], // leave empty to zoom to all objects
 | 
					              type: 'zoom_to_fit',
 | 
				
			||||||
            padding: panesWidth > 0 ? (window.innerWidth / panesWidth) : 0.2, // padding around the objects
 | 
					              object_ids: zoomObjectId ? [zoomObjectId] : [], // leave empty to zoom to all objects
 | 
				
			||||||
 | 
					              padding: panesWidth > 0 ? window.innerWidth / panesWidth : 0.2, // padding around the objects
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
          },
 | 
					          },
 | 
				
			||||||
        },
 | 
					          {
 | 
				
			||||||
        {
 | 
					            type: 'modeling_cmd_req',
 | 
				
			||||||
          type: 'modeling_cmd_req',
 | 
					            cmd: {
 | 
				
			||||||
          cmd: {
 | 
					              type: 'camera_drag_start',
 | 
				
			||||||
            type: 'camera_drag_start',
 | 
					              interaction: 'pan',
 | 
				
			||||||
            interaction: 'pan',
 | 
					              window: { x: 0, y: 0 },
 | 
				
			||||||
            window: { x: 0, y: 0 },
 | 
					            },
 | 
				
			||||||
 | 
					            cmd_id: uuidv4(),
 | 
				
			||||||
          },
 | 
					          },
 | 
				
			||||||
          cmd_id: uuidv4(),
 | 
					          {
 | 
				
			||||||
        },
 | 
					            type: 'modeling_cmd_req',
 | 
				
			||||||
        {
 | 
					            cmd: {
 | 
				
			||||||
 | 
					              type: 'camera_drag_move',
 | 
				
			||||||
 | 
					              interaction: 'pan',
 | 
				
			||||||
 | 
					              window: {
 | 
				
			||||||
 | 
					                x: goRightPx,
 | 
				
			||||||
 | 
					                y: 0,
 | 
				
			||||||
 | 
					              },
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            cmd_id: uuidv4(),
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
 | 
					      })
 | 
				
			||||||
 | 
					      // engineCommandManager can't subscribe to batch responses so we'll send
 | 
				
			||||||
 | 
					      // this one off by its lonesome after.
 | 
				
			||||||
 | 
					      .then(() =>
 | 
				
			||||||
 | 
					        this.engineCommandManager.sendSceneCommand({
 | 
				
			||||||
          type: 'modeling_cmd_req',
 | 
					          type: 'modeling_cmd_req',
 | 
				
			||||||
          cmd: {
 | 
					          cmd: {
 | 
				
			||||||
            type: 'camera_drag_move',
 | 
					            type: 'camera_drag_end',
 | 
				
			||||||
            interaction: 'pan',
 | 
					            interaction: 'pan',
 | 
				
			||||||
            window: {
 | 
					            window: {
 | 
				
			||||||
              x: goRightPx,
 | 
					              x: goRightPx,
 | 
				
			||||||
@ -994,23 +1014,8 @@ export class CameraControls {
 | 
				
			|||||||
            },
 | 
					            },
 | 
				
			||||||
          },
 | 
					          },
 | 
				
			||||||
          cmd_id: uuidv4(),
 | 
					          cmd_id: uuidv4(),
 | 
				
			||||||
        },
 | 
					        })
 | 
				
			||||||
      ]
 | 
					      )
 | 
				
			||||||
    })
 | 
					 | 
				
			||||||
    // engineCommandManager can't subscribe to batch responses so we'll send
 | 
					 | 
				
			||||||
    // this one off by its lonesome after.
 | 
					 | 
				
			||||||
    .then(() => this.engineCommandManager.sendSceneCommand({
 | 
					 | 
				
			||||||
      type: 'modeling_cmd_req',
 | 
					 | 
				
			||||||
      cmd: {
 | 
					 | 
				
			||||||
        type: 'camera_drag_end',
 | 
					 | 
				
			||||||
        interaction: 'pan',
 | 
					 | 
				
			||||||
        window: {
 | 
					 | 
				
			||||||
          x: goRightPx,
 | 
					 | 
				
			||||||
          y: 0,
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
      },
 | 
					 | 
				
			||||||
      cmd_id: uuidv4(),
 | 
					 | 
				
			||||||
    }))
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  async tweenCameraToQuaternion(
 | 
					  async tweenCameraToQuaternion(
 | 
				
			||||||
 | 
				
			|||||||
@ -29,7 +29,9 @@ export const CommandBar = () => {
 | 
				
			|||||||
  }, [pathname])
 | 
					  }, [pathname])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  useEffect(() => {
 | 
					  useEffect(() => {
 | 
				
			||||||
    if (immediateState.type !== EngineConnectionStateType.ConnectionEstablished) {
 | 
					    if (
 | 
				
			||||||
 | 
					      immediateState.type !== EngineConnectionStateType.ConnectionEstablished
 | 
				
			||||||
 | 
					    ) {
 | 
				
			||||||
      commandBarSend({ type: 'Close' })
 | 
					      commandBarSend({ type: 'Close' })
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }, [immediateState])
 | 
					  }, [immediateState])
 | 
				
			||||||
 | 
				
			|||||||
@ -11,7 +11,8 @@ export function CommandBarOpenButton() {
 | 
				
			|||||||
  const { immediateState } = useNetworkContext()
 | 
					  const { immediateState } = useNetworkContext()
 | 
				
			||||||
  const platform = usePlatform()
 | 
					  const platform = usePlatform()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const isDisabled = immediateState.type !== EngineConnectionStateType.ConnectionEstablished
 | 
					  const isDisabled =
 | 
				
			||||||
 | 
					    immediateState.type !== EngineConnectionStateType.ConnectionEstablished
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  return (
 | 
					  return (
 | 
				
			||||||
    <button
 | 
					    <button
 | 
				
			||||||
 | 
				
			|||||||
@ -9,13 +9,14 @@ import { btnName } from 'lib/cameraControls'
 | 
				
			|||||||
import { trap } from 'lib/trap'
 | 
					import { trap } from 'lib/trap'
 | 
				
			||||||
import { sendSelectEventToEngine } from 'lib/selections'
 | 
					import { sendSelectEventToEngine } from 'lib/selections'
 | 
				
			||||||
import { kclManager, engineCommandManager } from 'lib/singletons'
 | 
					import { kclManager, engineCommandManager } from 'lib/singletons'
 | 
				
			||||||
import {
 | 
					import { EngineCommandManagerEvents } from 'lang/std/engineConnection'
 | 
				
			||||||
  EngineCommandManagerEvents,
 | 
					 | 
				
			||||||
} from 'lang/std/engineConnection'
 | 
					 | 
				
			||||||
import { useRouteLoaderData } from 'react-router-dom'
 | 
					import { useRouteLoaderData } from 'react-router-dom'
 | 
				
			||||||
import { PATHS } from 'lib/paths'
 | 
					import { PATHS } from 'lib/paths'
 | 
				
			||||||
import { IndexLoaderData } from 'lib/types'
 | 
					import { IndexLoaderData } from 'lib/types'
 | 
				
			||||||
import useEngineStreamContext, { EngineStreamState, EngineStreamTransition } from 'hooks/useEngineStreamContext'
 | 
					import useEngineStreamContext, {
 | 
				
			||||||
 | 
					  EngineStreamState,
 | 
				
			||||||
 | 
					  EngineStreamTransition,
 | 
				
			||||||
 | 
					} from 'hooks/useEngineStreamContext'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const EngineStream = () => {
 | 
					export const EngineStream = () => {
 | 
				
			||||||
  const { setAppState } = useAppState()
 | 
					  const { setAppState } = useAppState()
 | 
				
			||||||
@ -46,7 +47,7 @@ export const EngineStream = () => {
 | 
				
			|||||||
  // so those exception people don't see.
 | 
					  // so those exception people don't see.
 | 
				
			||||||
  const REASONABLE_TIME_TO_REFRESH_STREAM_SIZE = 100
 | 
					  const REASONABLE_TIME_TO_REFRESH_STREAM_SIZE = 100
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const configure =  () => {
 | 
					  const configure = () => {
 | 
				
			||||||
    engineStreamActor.send({
 | 
					    engineStreamActor.send({
 | 
				
			||||||
      type: EngineStreamTransition.StartOrReconfigureEngine,
 | 
					      type: EngineStreamTransition.StartOrReconfigureEngine,
 | 
				
			||||||
      modelingMachineActorSend,
 | 
					      modelingMachineActorSend,
 | 
				
			||||||
@ -57,9 +58,9 @@ export const EngineStream = () => {
 | 
				
			|||||||
      onMediaStream(mediaStream: MediaStream) {
 | 
					      onMediaStream(mediaStream: MediaStream) {
 | 
				
			||||||
        engineStreamActor.send({
 | 
					        engineStreamActor.send({
 | 
				
			||||||
          type: EngineStreamTransition.SetMediaStream,
 | 
					          type: EngineStreamTransition.SetMediaStream,
 | 
				
			||||||
          mediaStream
 | 
					          mediaStream,
 | 
				
			||||||
        })
 | 
					        })
 | 
				
			||||||
      }
 | 
					      },
 | 
				
			||||||
    })
 | 
					    })
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -91,11 +92,13 @@ export const EngineStream = () => {
 | 
				
			|||||||
      const canvas = engineStreamState.context.canvasRef?.current
 | 
					      const canvas = engineStreamState.context.canvasRef?.current
 | 
				
			||||||
      if (!canvas) return
 | 
					      if (!canvas) return
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      if (Math.abs(video.width - window.innerWidth) > 4 || Math.abs(video.height - window.innerHeight) > 4) {
 | 
					      if (
 | 
				
			||||||
 | 
					        Math.abs(video.width - window.innerWidth) > 4 ||
 | 
				
			||||||
 | 
					        Math.abs(video.height - window.innerHeight) > 4
 | 
				
			||||||
 | 
					      ) {
 | 
				
			||||||
        timeoutStart.current = Date.now()
 | 
					        timeoutStart.current = Date.now()
 | 
				
			||||||
        configure()
 | 
					        configure()
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					 | 
				
			||||||
    }, REASONABLE_TIME_TO_REFRESH_STREAM_SIZE)
 | 
					    }, REASONABLE_TIME_TO_REFRESH_STREAM_SIZE)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return () => {
 | 
					    return () => {
 | 
				
			||||||
@ -105,7 +108,10 @@ export const EngineStream = () => {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  // When the video and canvas element references are set, start the engine.
 | 
					  // When the video and canvas element references are set, start the engine.
 | 
				
			||||||
  useEffect(() => {
 | 
					  useEffect(() => {
 | 
				
			||||||
    if (engineStreamState.context.canvasRef.current && engineStreamState.context.videoRef.current) {
 | 
					    if (
 | 
				
			||||||
 | 
					      engineStreamState.context.canvasRef.current &&
 | 
				
			||||||
 | 
					      engineStreamState.context.videoRef.current
 | 
				
			||||||
 | 
					    ) {
 | 
				
			||||||
      engineStreamActor.send({
 | 
					      engineStreamActor.send({
 | 
				
			||||||
        type: EngineStreamTransition.StartOrReconfigureEngine,
 | 
					        type: EngineStreamTransition.StartOrReconfigureEngine,
 | 
				
			||||||
        modelingMachineActorSend,
 | 
					        modelingMachineActorSend,
 | 
				
			||||||
@ -114,24 +120,30 @@ export const EngineStream = () => {
 | 
				
			|||||||
        onMediaStream(mediaStream: MediaStream) {
 | 
					        onMediaStream(mediaStream: MediaStream) {
 | 
				
			||||||
          engineStreamActor.send({
 | 
					          engineStreamActor.send({
 | 
				
			||||||
            type: EngineStreamTransition.SetMediaStream,
 | 
					            type: EngineStreamTransition.SetMediaStream,
 | 
				
			||||||
            mediaStream
 | 
					            mediaStream,
 | 
				
			||||||
          })
 | 
					          })
 | 
				
			||||||
        }
 | 
					        },
 | 
				
			||||||
      })
 | 
					      })
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }, [engineStreamState.context.canvasRef.current, engineStreamState.context.videoRef.current])
 | 
					  }, [
 | 
				
			||||||
 | 
					    engineStreamState.context.canvasRef.current,
 | 
				
			||||||
 | 
					    engineStreamState.context.videoRef.current,
 | 
				
			||||||
 | 
					  ])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // On settings change, reconfigure the engine. When paused this gets really tricky,
 | 
					  // On settings change, reconfigure the engine. When paused this gets really tricky,
 | 
				
			||||||
  // and also requires onMediaStream to be set!
 | 
					  // and also requires onMediaStream to be set!
 | 
				
			||||||
  useEffect(() => {
 | 
					  useEffect(() => {
 | 
				
			||||||
    engineStreamActor.send({
 | 
					    engineStreamActor.send({
 | 
				
			||||||
      type: EngineStreamTransition.StartOrReconfigureEngine, modelingMachineActorSend, settings: settingsEngine, setAppState,
 | 
					      type: EngineStreamTransition.StartOrReconfigureEngine,
 | 
				
			||||||
        onMediaStream(mediaStream: MediaStream) {
 | 
					      modelingMachineActorSend,
 | 
				
			||||||
          engineStreamActor.send({
 | 
					      settings: settingsEngine,
 | 
				
			||||||
            type: EngineStreamTransition.SetMediaStream,
 | 
					      setAppState,
 | 
				
			||||||
            mediaStream
 | 
					      onMediaStream(mediaStream: MediaStream) {
 | 
				
			||||||
          })
 | 
					        engineStreamActor.send({
 | 
				
			||||||
        }
 | 
					          type: EngineStreamTransition.SetMediaStream,
 | 
				
			||||||
 | 
					          mediaStream,
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
    })
 | 
					    })
 | 
				
			||||||
  }, [settings.context])
 | 
					  }, [settings.context])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -147,7 +159,7 @@ export const EngineStream = () => {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
  }, [file?.path, engineCommandManager.engineConnection])
 | 
					  }, [file?.path, engineCommandManager.engineConnection])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const IDLE_TIME_MS = 1000 * 6
 | 
					  const IDLE_TIME_MS = Number(streamIdleMode) * 1000 * 60
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // When streamIdleMode is changed, setup or teardown the timeouts
 | 
					  // When streamIdleMode is changed, setup or teardown the timeouts
 | 
				
			||||||
  const timeoutStart = useRef<number | null>(null)
 | 
					  const timeoutStart = useRef<number | null>(null)
 | 
				
			||||||
@ -181,10 +193,10 @@ export const EngineStream = () => {
 | 
				
			|||||||
  }, [modelingMachineState])
 | 
					  }, [modelingMachineState])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  useEffect(() => {
 | 
					  useEffect(() => {
 | 
				
			||||||
    if (!streamIdleMode) return 
 | 
					    if (!streamIdleMode) return
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const onAnyInput = () => {
 | 
					    const onAnyInput = () => {
 | 
				
			||||||
      // Just in case it happens in the middle of the user turning off 
 | 
					      // Just in case it happens in the middle of the user turning off
 | 
				
			||||||
      // idle mode.
 | 
					      // idle mode.
 | 
				
			||||||
      if (!streamIdleMode) {
 | 
					      if (!streamIdleMode) {
 | 
				
			||||||
        timeoutStart.current = null
 | 
					        timeoutStart.current = null
 | 
				
			||||||
@ -200,9 +212,9 @@ export const EngineStream = () => {
 | 
				
			|||||||
          onMediaStream(mediaStream: MediaStream) {
 | 
					          onMediaStream(mediaStream: MediaStream) {
 | 
				
			||||||
            engineStreamActor.send({
 | 
					            engineStreamActor.send({
 | 
				
			||||||
              type: EngineStreamTransition.SetMediaStream,
 | 
					              type: EngineStreamTransition.SetMediaStream,
 | 
				
			||||||
              mediaStream
 | 
					              mediaStream,
 | 
				
			||||||
            })
 | 
					            })
 | 
				
			||||||
          }
 | 
					          },
 | 
				
			||||||
        })
 | 
					        })
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -236,7 +248,7 @@ export const EngineStream = () => {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
  }, [streamIdleMode, engineStreamState.value])
 | 
					  }, [streamIdleMode, engineStreamState.value])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
   const isNetworkOkay =
 | 
					  const isNetworkOkay =
 | 
				
			||||||
    overallState === NetworkHealthState.Ok ||
 | 
					    overallState === NetworkHealthState.Ok ||
 | 
				
			||||||
    overallState === NetworkHealthState.Weak
 | 
					    overallState === NetworkHealthState.Weak
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -273,12 +285,15 @@ export const EngineStream = () => {
 | 
				
			|||||||
      />
 | 
					      />
 | 
				
			||||||
      <canvas
 | 
					      <canvas
 | 
				
			||||||
        key={engineStreamActor.id + 'canvas'}
 | 
					        key={engineStreamActor.id + 'canvas'}
 | 
				
			||||||
      ref={engineStreamState.context.canvasRef} className="cursor-pointer" id="freeze-frame">No canvas support</canvas>
 | 
					        ref={engineStreamState.context.canvasRef}
 | 
				
			||||||
 | 
					        className="cursor-pointer"
 | 
				
			||||||
 | 
					        id="freeze-frame"
 | 
				
			||||||
 | 
					      >
 | 
				
			||||||
 | 
					        No canvas support
 | 
				
			||||||
 | 
					      </canvas>
 | 
				
			||||||
      <ClientSideScene
 | 
					      <ClientSideScene
 | 
				
			||||||
        cameraControls={settings.context.modeling.mouseControls.current}
 | 
					        cameraControls={settings.context.modeling.mouseControls.current}
 | 
				
			||||||
      />
 | 
					      />
 | 
				
			||||||
    </div>
 | 
					    </div>
 | 
				
			||||||
)
 | 
					  )
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
				
			|||||||
@ -14,7 +14,6 @@ import { NetworkMachineIndicator } from './NetworkMachineIndicator'
 | 
				
			|||||||
import { ModelStateIndicator } from './ModelStateIndicator'
 | 
					import { ModelStateIndicator } from './ModelStateIndicator'
 | 
				
			||||||
import { reportRejection } from 'lib/trap'
 | 
					import { reportRejection } from 'lib/trap'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
export function LowerRightControls({
 | 
					export function LowerRightControls({
 | 
				
			||||||
  children,
 | 
					  children,
 | 
				
			||||||
  coreDumpManager,
 | 
					  coreDumpManager,
 | 
				
			||||||
 | 
				
			|||||||
@ -1,7 +1,9 @@
 | 
				
			|||||||
import { useEffect, useState } from 'react'
 | 
					import { useEffect, useState } from 'react'
 | 
				
			||||||
import { useEngineCommands } from './EngineCommands'
 | 
					import { useEngineCommands } from './EngineCommands'
 | 
				
			||||||
import { CustomIcon } from './CustomIcon'
 | 
					import { CustomIcon } from './CustomIcon'
 | 
				
			||||||
import useEngineStreamContext, { EngineStreamState } from 'hooks/useEngineStreamContext'
 | 
					import useEngineStreamContext, {
 | 
				
			||||||
 | 
					  EngineStreamState,
 | 
				
			||||||
 | 
					} from 'hooks/useEngineStreamContext'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const ModelStateIndicator = () => {
 | 
					export const ModelStateIndicator = () => {
 | 
				
			||||||
  const [commands] = useEngineCommands()
 | 
					  const [commands] = useEngineCommands()
 | 
				
			||||||
@ -26,26 +28,13 @@ export const ModelStateIndicator = () => {
 | 
				
			|||||||
  let dataTestId = 'model-state-indicator'
 | 
					  let dataTestId = 'model-state-indicator'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if (engineStreamState.value === EngineStreamState.Paused) {
 | 
					  if (engineStreamState.value === EngineStreamState.Paused) {
 | 
				
			||||||
    className +=
 | 
					    className += 'text-secondary'
 | 
				
			||||||
      'text-secondary'
 | 
					    icon = <CustomIcon data-testid={dataTestId + '-paused'} name="parallel" />
 | 
				
			||||||
    icon = (
 | 
					 | 
				
			||||||
      <CustomIcon
 | 
					 | 
				
			||||||
        data-testid={dataTestId + '-paused'}
 | 
					 | 
				
			||||||
        name="parallel"
 | 
					 | 
				
			||||||
      />
 | 
					 | 
				
			||||||
    )
 | 
					 | 
				
			||||||
  } else if (engineStreamState.value === EngineStreamState.Resuming) {
 | 
					  } else if (engineStreamState.value === EngineStreamState.Resuming) {
 | 
				
			||||||
    className +=
 | 
					    className += 'text-secondary'
 | 
				
			||||||
      'text-secondary'
 | 
					    icon = <CustomIcon data-testid={dataTestId + '-resuming'} name="parallel" />
 | 
				
			||||||
    icon = (
 | 
					 | 
				
			||||||
      <CustomIcon
 | 
					 | 
				
			||||||
        data-testid={dataTestId + '-resuming'}
 | 
					 | 
				
			||||||
        name="parallel"
 | 
					 | 
				
			||||||
      />
 | 
					 | 
				
			||||||
    )
 | 
					 | 
				
			||||||
  } else if (isDone) {
 | 
					  } else if (isDone) {
 | 
				
			||||||
    className +=
 | 
					    className += 'text-secondary'
 | 
				
			||||||
      'text-secondary'
 | 
					 | 
				
			||||||
    icon = (
 | 
					    icon = (
 | 
				
			||||||
      <CustomIcon
 | 
					      <CustomIcon
 | 
				
			||||||
        data-testid={dataTestId + '-execution-done'}
 | 
					        data-testid={dataTestId + '-execution-done'}
 | 
				
			||||||
 | 
				
			|||||||
@ -9,7 +9,7 @@ import {
 | 
				
			|||||||
  useContext,
 | 
					  useContext,
 | 
				
			||||||
  MutableRefObject,
 | 
					  MutableRefObject,
 | 
				
			||||||
  forwardRef,
 | 
					  forwardRef,
 | 
				
			||||||
} from 'react'm
 | 
					} from 'react'
 | 
				
			||||||
import { useHotkeys } from 'react-hotkeys-hook'
 | 
					import { useHotkeys } from 'react-hotkeys-hook'
 | 
				
			||||||
import { SidebarAction, SidebarType, sidebarPanes } from './ModelingPanes'
 | 
					import { SidebarAction, SidebarType, sidebarPanes } from './ModelingPanes'
 | 
				
			||||||
import Tooltip from 'components/Tooltip'
 | 
					import Tooltip from 'components/Tooltip'
 | 
				
			||||||
@ -25,7 +25,7 @@ import { useKclContext } from 'lang/KclProvider'
 | 
				
			|||||||
import { MachineManagerContext } from 'components/MachineManagerProvider'
 | 
					import { MachineManagerContext } from 'components/MachineManagerProvider'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
interface ModelingSidebarProps {
 | 
					interface ModelingSidebarProps {
 | 
				
			||||||
  paneOpacity: '' | 'opacity-20' | 'opacity-40',
 | 
					  paneOpacity: '' | 'opacity-20' | 'opacity-40'
 | 
				
			||||||
  ref: MutableRefObject<HTMLDivElement>
 | 
					  ref: MutableRefObject<HTMLDivElement>
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -38,7 +38,10 @@ function getPlatformString(): 'web' | 'desktop' {
 | 
				
			|||||||
  return isDesktop() ? 'desktop' : 'web'
 | 
					  return isDesktop() ? 'desktop' : 'web'
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const ModelingSidebar = forwardRef(function ModelingSidebar({ paneOpacity }: ModelingSidebarProps, ref) {
 | 
					export const ModelingSidebar = forwardRef(function ModelingSidebar(
 | 
				
			||||||
 | 
					  { paneOpacity }: ModelingSidebarProps,
 | 
				
			||||||
 | 
					  ref
 | 
				
			||||||
 | 
					) {
 | 
				
			||||||
  const machineManager = useContext(MachineManagerContext)
 | 
					  const machineManager = useContext(MachineManagerContext)
 | 
				
			||||||
  const { commandBarSend } = useCommandsContext()
 | 
					  const { commandBarSend } = useCommandsContext()
 | 
				
			||||||
  const kclContext = useKclContext()
 | 
					  const kclContext = useKclContext()
 | 
				
			||||||
 | 
				
			|||||||
@ -22,11 +22,11 @@ export enum EngineStreamTransition {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface EngineStreamContext {
 | 
					export interface EngineStreamContext {
 | 
				
			||||||
  pool: string | null,
 | 
					  pool: string | null
 | 
				
			||||||
  authToken: string | null,
 | 
					  authToken: string | null
 | 
				
			||||||
  mediaStream: MediaStream | null,
 | 
					  mediaStream: MediaStream | null
 | 
				
			||||||
  videoRef: MutableRefObject<HTMLVideoElement | null>,
 | 
					  videoRef: MutableRefObject<HTMLVideoElement | null>
 | 
				
			||||||
  canvasRef: MutableRefObject<HTMLCanvasElement | null>,
 | 
					  canvasRef: MutableRefObject<HTMLCanvasElement | null>
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export function getDimensions(streamWidth: number, streamHeight: number) {
 | 
					export function getDimensions(streamWidth: number, streamHeight: number) {
 | 
				
			||||||
@ -42,185 +42,196 @@ export function getDimensions(streamWidth: number, streamHeight: number) {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const engineStreamMachine = setup({
 | 
					const engineStreamMachine = setup({
 | 
				
			||||||
    types: {
 | 
					  types: {
 | 
				
			||||||
      context: {} as EngineStreamContext,
 | 
					    context: {} as EngineStreamContext,
 | 
				
			||||||
      input: {} as EngineStreamContext,
 | 
					    input: {} as EngineStreamContext,
 | 
				
			||||||
    },
 | 
					  },
 | 
				
			||||||
    actions: {
 | 
					  actions: {
 | 
				
			||||||
      [EngineStreamTransition.Play]({ context }, params: { zoomToFit: boolean }) {
 | 
					    [EngineStreamTransition.Play]({ context }, params: { zoomToFit: boolean }) {
 | 
				
			||||||
        const canvas = context.canvasRef.current
 | 
					      const canvas = context.canvasRef.current
 | 
				
			||||||
        if (!canvas) return false
 | 
					      if (!canvas) return false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        const video = context.videoRef.current
 | 
					      const video = context.videoRef.current
 | 
				
			||||||
        if (!video) return false
 | 
					      if (!video) return false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        const mediaStream = context.mediaStream
 | 
					      const mediaStream = context.mediaStream
 | 
				
			||||||
        if (!mediaStream) return false
 | 
					      if (!mediaStream) return false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        video.style.display = "block"
 | 
					      video.style.display = 'block'
 | 
				
			||||||
        canvas.style.display = "none"
 | 
					      canvas.style.display = 'none'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        video.srcObject = mediaStream
 | 
					      video.srcObject = mediaStream
 | 
				
			||||||
        void sceneInfra.camControls.restoreCameraPosition()
 | 
					      void sceneInfra.camControls
 | 
				
			||||||
 | 
					        .restoreCameraPosition()
 | 
				
			||||||
        .then(() => video.play())
 | 
					        .then(() => video.play())
 | 
				
			||||||
        .catch((e) => {
 | 
					        .catch((e) => {
 | 
				
			||||||
            console.warn('Video playing was prevented', e, video)
 | 
					          console.warn('Video playing was prevented', e, video)
 | 
				
			||||||
        })
 | 
					        })
 | 
				
			||||||
        .then(() => kclManager.executeCode(params.zoomToFit))
 | 
					        .then(() => kclManager.executeCode(params.zoomToFit))
 | 
				
			||||||
        .catch(trap)
 | 
					        .catch(trap)
 | 
				
			||||||
      },
 | 
					    },
 | 
				
			||||||
      [EngineStreamTransition.Pause]({ context }) {
 | 
					    [EngineStreamTransition.Pause]({ context }) {
 | 
				
			||||||
        const video = context.videoRef.current
 | 
					      const video = context.videoRef.current
 | 
				
			||||||
        if (!video) return
 | 
					      if (!video) return
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        video.pause()
 | 
					      video.pause()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        const canvas = context.canvasRef.current
 | 
					      const canvas = context.canvasRef.current
 | 
				
			||||||
        if (!canvas) return
 | 
					      if (!canvas) return
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        canvas.width = video.videoWidth
 | 
					      canvas.width = video.videoWidth
 | 
				
			||||||
        canvas.height = video.videoHeight
 | 
					      canvas.height = video.videoHeight
 | 
				
			||||||
        canvas.style.width = video.videoWidth + 'px'
 | 
					      canvas.style.width = video.videoWidth + 'px'
 | 
				
			||||||
        canvas.style.height = video.videoHeight + 'px'
 | 
					      canvas.style.height = video.videoHeight + 'px'
 | 
				
			||||||
        canvas.style.display = "block"
 | 
					      canvas.style.display = 'block'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        const ctx = canvas.getContext("2d")
 | 
					      const ctx = canvas.getContext('2d')
 | 
				
			||||||
        if (!ctx) return
 | 
					      if (!ctx) return
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        ctx.drawImage(video, 0, 0, canvas.width, canvas.height)
 | 
					      ctx.drawImage(video, 0, 0, canvas.width, canvas.height)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Make sure we're on the next frame for no flickering between canvas
 | 
					      // Make sure we're on the next frame for no flickering between canvas
 | 
				
			||||||
        // and the video elements.
 | 
					      // and the video elements.
 | 
				
			||||||
        window.requestAnimationFrame(() => {
 | 
					      window.requestAnimationFrame(() => {
 | 
				
			||||||
          video.style.display = "none"
 | 
					        video.style.display = 'none'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
          // Destroy the media stream only. We will re-establish it. We could
 | 
					        // Destroy the media stream only. We will re-establish it. We could
 | 
				
			||||||
          // leave everything at pausing, preventing video decoders from running
 | 
					        // leave everything at pausing, preventing video decoders from running
 | 
				
			||||||
          // but we can do even better by significantly reducing network
 | 
					        // but we can do even better by significantly reducing network
 | 
				
			||||||
          // cards also.
 | 
					        // cards also.
 | 
				
			||||||
          context.mediaStream?.getVideoTracks()[0].stop()
 | 
					        context.mediaStream?.getVideoTracks()[0].stop()
 | 
				
			||||||
          video.srcObject = null
 | 
					        video.srcObject = null
 | 
				
			||||||
 | 
					
 | 
				
			||||||
          sceneInfra.camControls.old = {
 | 
					        sceneInfra.camControls.old = {
 | 
				
			||||||
            camera: sceneInfra.camControls.camera.clone(),
 | 
					          camera: sceneInfra.camControls.camera.clone(),
 | 
				
			||||||
            target: sceneInfra.camControls.target.clone()
 | 
					          target: sceneInfra.camControls.target.clone(),
 | 
				
			||||||
          }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
          engineCommandManager.tearDown({ idleMode: true })
 | 
					 | 
				
			||||||
        })
 | 
					 | 
				
			||||||
      },
 | 
					 | 
				
			||||||
      async [EngineStreamTransition.StartOrReconfigureEngine]({ context, event }) {
 | 
					 | 
				
			||||||
        if (!context.authToken) return
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        const video = context.videoRef.current
 | 
					 | 
				
			||||||
        if (!video) return
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        const { width, height } = getDimensions(
 | 
					 | 
				
			||||||
          window.innerWidth,
 | 
					 | 
				
			||||||
          window.innerHeight,
 | 
					 | 
				
			||||||
        )
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        video.width = width
 | 
					 | 
				
			||||||
        video.height = height
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        const settingsNext = {
 | 
					 | 
				
			||||||
          // override the pool param (?pool=) to request a specific engine instance
 | 
					 | 
				
			||||||
          // from a particular pool.
 | 
					 | 
				
			||||||
          pool: context.pool,
 | 
					 | 
				
			||||||
          ...event.settings,
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        engineCommandManager.settings = settingsNext
 | 
					        engineCommandManager.tearDown({ idleMode: true })
 | 
				
			||||||
 | 
					      })
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    async [EngineStreamTransition.StartOrReconfigureEngine]({
 | 
				
			||||||
 | 
					      context,
 | 
				
			||||||
 | 
					      event,
 | 
				
			||||||
 | 
					    }) {
 | 
				
			||||||
 | 
					      if (!context.authToken) return
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        engineCommandManager.start({
 | 
					      const video = context.videoRef.current
 | 
				
			||||||
          setMediaStream: event.onMediaStream,
 | 
					      if (!video) return
 | 
				
			||||||
          setIsStreamReady: (isStreamReady) => event.setAppState({ isStreamReady }),
 | 
					 | 
				
			||||||
          width,
 | 
					 | 
				
			||||||
          height,
 | 
					 | 
				
			||||||
          token: context.authToken,
 | 
					 | 
				
			||||||
          settings: settingsNext,
 | 
					 | 
				
			||||||
          makeDefaultPlanes: () => {
 | 
					 | 
				
			||||||
            return makeDefaultPlanes(kclManager.engineCommandManager)
 | 
					 | 
				
			||||||
          },
 | 
					 | 
				
			||||||
          modifyGrid: (hidden: boolean) => {
 | 
					 | 
				
			||||||
            return modifyGrid(kclManager.engineCommandManager, hidden)
 | 
					 | 
				
			||||||
          },
 | 
					 | 
				
			||||||
        })
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        event.modelingMachineActorSend({
 | 
					      const { width, height } = getDimensions(
 | 
				
			||||||
          type: 'Set context',
 | 
					        window.innerWidth,
 | 
				
			||||||
          data: {
 | 
					        window.innerHeight
 | 
				
			||||||
            streamDimensions: {
 | 
					      )
 | 
				
			||||||
              streamWidth: width,
 | 
					
 | 
				
			||||||
              streamHeight: height,
 | 
					      video.width = width
 | 
				
			||||||
            },
 | 
					      video.height = height
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      const settingsNext = {
 | 
				
			||||||
 | 
					        // override the pool param (?pool=) to request a specific engine instance
 | 
				
			||||||
 | 
					        // from a particular pool.
 | 
				
			||||||
 | 
					        pool: context.pool,
 | 
				
			||||||
 | 
					        ...event.settings,
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      engineCommandManager.settings = settingsNext
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      engineCommandManager.start({
 | 
				
			||||||
 | 
					        setMediaStream: event.onMediaStream,
 | 
				
			||||||
 | 
					        setIsStreamReady: (isStreamReady) =>
 | 
				
			||||||
 | 
					          event.setAppState({ isStreamReady }),
 | 
				
			||||||
 | 
					        width,
 | 
				
			||||||
 | 
					        height,
 | 
				
			||||||
 | 
					        token: context.authToken,
 | 
				
			||||||
 | 
					        settings: settingsNext,
 | 
				
			||||||
 | 
					        makeDefaultPlanes: () => {
 | 
				
			||||||
 | 
					          return makeDefaultPlanes(kclManager.engineCommandManager)
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        modifyGrid: (hidden: boolean) => {
 | 
				
			||||||
 | 
					          return modifyGrid(kclManager.engineCommandManager, hidden)
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					      })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      event.modelingMachineActorSend({
 | 
				
			||||||
 | 
					        type: 'Set context',
 | 
				
			||||||
 | 
					        data: {
 | 
				
			||||||
 | 
					          streamDimensions: {
 | 
				
			||||||
 | 
					            streamWidth: width,
 | 
				
			||||||
 | 
					            streamHeight: height,
 | 
				
			||||||
          },
 | 
					          },
 | 
				
			||||||
        })
 | 
					        },
 | 
				
			||||||
 | 
					      })
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    async [EngineStreamTransition.Resume]({ context, event }) {
 | 
				
			||||||
 | 
					      // engineCommandManager.engineConnection?.reattachMediaStream()
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					}).createMachine({
 | 
				
			||||||
 | 
					  context: (initial) => initial.input,
 | 
				
			||||||
 | 
					  initial: EngineStreamState.Off,
 | 
				
			||||||
 | 
					  states: {
 | 
				
			||||||
 | 
					    [EngineStreamState.Off]: {
 | 
				
			||||||
 | 
					      on: {
 | 
				
			||||||
 | 
					        [EngineStreamTransition.StartOrReconfigureEngine]: {
 | 
				
			||||||
 | 
					          target: EngineStreamState.On,
 | 
				
			||||||
 | 
					          actions: [{ type: EngineStreamTransition.StartOrReconfigureEngine }],
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
      async [EngineStreamTransition.Resume]({ context, event }) {
 | 
					    },
 | 
				
			||||||
        // engineCommandManager.engineConnection?.reattachMediaStream()
 | 
					    [EngineStreamState.On]: {
 | 
				
			||||||
 | 
					      on: {
 | 
				
			||||||
 | 
					        [EngineStreamTransition.SetMediaStream]: {
 | 
				
			||||||
 | 
					          target: EngineStreamState.On,
 | 
				
			||||||
 | 
					          actions: [
 | 
				
			||||||
 | 
					            assign({ mediaStream: ({ context, event }) => event.mediaStream }),
 | 
				
			||||||
 | 
					          ],
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        [EngineStreamTransition.Play]: {
 | 
				
			||||||
 | 
					          target: EngineStreamState.Playing,
 | 
				
			||||||
 | 
					          actions: [
 | 
				
			||||||
 | 
					            { type: EngineStreamTransition.Play, params: { zoomToFit: true } },
 | 
				
			||||||
 | 
					          ],
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
    }
 | 
					    },
 | 
				
			||||||
  }).createMachine({
 | 
					    [EngineStreamState.Playing]: {
 | 
				
			||||||
    context: (initial) => initial.input,
 | 
					      on: {
 | 
				
			||||||
    initial: EngineStreamState.Off,
 | 
					        [EngineStreamTransition.StartOrReconfigureEngine]: {
 | 
				
			||||||
    states: {
 | 
					          target: EngineStreamState.Playing,
 | 
				
			||||||
      [EngineStreamState.Off]: {
 | 
					          reenter: true,
 | 
				
			||||||
        on: {
 | 
					          actions: [{ type: EngineStreamTransition.StartOrReconfigureEngine }],
 | 
				
			||||||
          [EngineStreamTransition.StartOrReconfigureEngine]: {
 | 
					        },
 | 
				
			||||||
            target: EngineStreamState.On,
 | 
					        [EngineStreamTransition.Pause]: {
 | 
				
			||||||
            actions: [{ type: EngineStreamTransition.StartOrReconfigureEngine } ]
 | 
					          target: EngineStreamState.Paused,
 | 
				
			||||||
          }
 | 
					          actions: [{ type: EngineStreamTransition.Pause }],
 | 
				
			||||||
        }
 | 
					        },
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
      [EngineStreamState.On]: {
 | 
					    },
 | 
				
			||||||
        on: {
 | 
					    [EngineStreamState.Paused]: {
 | 
				
			||||||
          [EngineStreamTransition.SetMediaStream]: {
 | 
					      on: {
 | 
				
			||||||
            target: EngineStreamState.On,
 | 
					        [EngineStreamTransition.StartOrReconfigureEngine]: {
 | 
				
			||||||
            actions: [ assign({ mediaStream: ({ context, event }) => event.mediaStream }) ]
 | 
					          target: EngineStreamState.Resuming,
 | 
				
			||||||
          },
 | 
					          actions: [{ type: EngineStreamTransition.StartOrReconfigureEngine }],
 | 
				
			||||||
          [EngineStreamTransition.Play]: {
 | 
					        },
 | 
				
			||||||
            target: EngineStreamState.Playing,
 | 
					 | 
				
			||||||
            actions: [ { type: EngineStreamTransition.Play, params: { zoomToFit: true }} ]
 | 
					 | 
				
			||||||
          }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
      [EngineStreamState.Playing]: {
 | 
					    },
 | 
				
			||||||
        on: {
 | 
					    [EngineStreamState.Resuming]: {
 | 
				
			||||||
          [EngineStreamTransition.StartOrReconfigureEngine]: {
 | 
					      on: {
 | 
				
			||||||
            target: EngineStreamState.Playing,
 | 
					        [EngineStreamTransition.SetMediaStream]: {
 | 
				
			||||||
            reenter: true,
 | 
					          target: EngineStreamState.Resuming,
 | 
				
			||||||
            actions: [{ type: EngineStreamTransition.StartOrReconfigureEngine } ]
 | 
					          actions: [
 | 
				
			||||||
          },
 | 
					            assign({ mediaStream: ({ context, event }) => event.mediaStream }),
 | 
				
			||||||
          [EngineStreamTransition.Pause]: {
 | 
					          ],
 | 
				
			||||||
            target: EngineStreamState.Paused,
 | 
					        },
 | 
				
			||||||
            actions: [ { type: EngineStreamTransition.Pause } ]
 | 
					        [EngineStreamTransition.Play]: {
 | 
				
			||||||
          }
 | 
					          target: EngineStreamState.Playing,
 | 
				
			||||||
        }
 | 
					          actions: [
 | 
				
			||||||
 | 
					            { type: EngineStreamTransition.Play, params: { zoomToFit: false } },
 | 
				
			||||||
 | 
					          ],
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
      [EngineStreamState.Paused]: {
 | 
					    },
 | 
				
			||||||
        on: {
 | 
					  },
 | 
				
			||||||
          [EngineStreamTransition.StartOrReconfigureEngine]: {
 | 
					})
 | 
				
			||||||
            target: EngineStreamState.Resuming,
 | 
					 | 
				
			||||||
            actions: [{ type: EngineStreamTransition.StartOrReconfigureEngine } ]
 | 
					 | 
				
			||||||
          },
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
      },
 | 
					 | 
				
			||||||
      [EngineStreamState.Resuming]: {
 | 
					 | 
				
			||||||
        on: {
 | 
					 | 
				
			||||||
          [EngineStreamTransition.SetMediaStream]: {
 | 
					 | 
				
			||||||
            target: EngineStreamState.Resuming,
 | 
					 | 
				
			||||||
            actions: [ assign({ mediaStream: ({ context, event }) => event.mediaStream }) ]
 | 
					 | 
				
			||||||
          },
 | 
					 | 
				
			||||||
          [EngineStreamTransition.Play]: {
 | 
					 | 
				
			||||||
            target: EngineStreamState.Playing,
 | 
					 | 
				
			||||||
            actions: [ { type: EngineStreamTransition.Play, params: { zoomToFit: false }} ]
 | 
					 | 
				
			||||||
          }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
      },
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  })
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default createActorContext(engineStreamMachine)
 | 
					export default createActorContext(engineStreamMachine)
 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
				
			|||||||
@ -404,7 +404,8 @@ class EngineConnection extends EventTarget {
 | 
				
			|||||||
        default:
 | 
					        default:
 | 
				
			||||||
          if (this.isConnecting()) break
 | 
					          if (this.isConnecting()) break
 | 
				
			||||||
          // Means we never could do an initial connection. Reconnect everything.
 | 
					          // Means we never could do an initial connection. Reconnect everything.
 | 
				
			||||||
          if (!this.pingPongSpan.ping) this.connect({ reconnect: false }).catch(reportRejection)
 | 
					          if (!this.pingPongSpan.ping)
 | 
				
			||||||
 | 
					            this.connect({ reconnect: false }).catch(reportRejection)
 | 
				
			||||||
          break
 | 
					          break
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }, pingIntervalMs)
 | 
					    }, pingIntervalMs)
 | 
				
			||||||
@ -542,7 +543,6 @@ class EngineConnection extends EventTarget {
 | 
				
			|||||||
        type: DisconnectingType.Quit,
 | 
					        type: DisconnectingType.Quit,
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /**
 | 
					  /**
 | 
				
			||||||
@ -1164,7 +1164,7 @@ class EngineConnection extends EventTarget {
 | 
				
			|||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      if (args.reconnect) {
 | 
					      if (args.reconnect) {
 | 
				
			||||||
          createWebSocketConnection()
 | 
					        createWebSocketConnection()
 | 
				
			||||||
      } else {
 | 
					      } else {
 | 
				
			||||||
        this.onNetworkStatusReady = () => {
 | 
					        this.onNetworkStatusReady = () => {
 | 
				
			||||||
          createWebSocketConnection()
 | 
					          createWebSocketConnection()
 | 
				
			||||||
 | 
				
			|||||||
@ -181,13 +181,46 @@ export function createSettings() {
 | 
				
			|||||||
      /**
 | 
					      /**
 | 
				
			||||||
       * Stream resource saving behavior toggle
 | 
					       * Stream resource saving behavior toggle
 | 
				
			||||||
       */
 | 
					       */
 | 
				
			||||||
      streamIdleMode: new Setting<boolean>({
 | 
					      streamIdleMode: new Setting<number | null>({
 | 
				
			||||||
        defaultValue: false,
 | 
					        defaultValue: null,
 | 
				
			||||||
        description: 'Toggle stream idling, saving bandwidth and battery',
 | 
					        description: 'Toggle stream idling, saving bandwidth and battery',
 | 
				
			||||||
        validate: (v) => typeof v === 'boolean',
 | 
					        validate: (v) =>
 | 
				
			||||||
        commandConfig: {
 | 
					          v === null ||
 | 
				
			||||||
          inputType: 'boolean',
 | 
					          (typeof v === 'number' &&
 | 
				
			||||||
        },
 | 
					            Number(v) >= 0 &&
 | 
				
			||||||
 | 
					            Number(v) <= 60),
 | 
				
			||||||
 | 
					        Component: ({ value, updateValue }) => (
 | 
				
			||||||
 | 
					          <div className="flex item-center gap-4 px-2 m-0 py-0">
 | 
				
			||||||
 | 
					            <div className="flex flex-col">
 | 
				
			||||||
 | 
					              <input
 | 
				
			||||||
 | 
					                type="checkbox"
 | 
				
			||||||
 | 
					                checked={value !== null}
 | 
				
			||||||
 | 
					                onChange={(e) => updateValue(!e.currentTarget.checked ? null : 5)}
 | 
				
			||||||
 | 
					                className="block w-4 h-4"
 | 
				
			||||||
 | 
					              />
 | 
				
			||||||
 | 
					              <div></div>
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					            <div className="flex flex-col grow">
 | 
				
			||||||
 | 
					              <input
 | 
				
			||||||
 | 
					                type="range"
 | 
				
			||||||
 | 
					                onChange={(e) =>
 | 
				
			||||||
 | 
					                  updateValue(parseInt(e.currentTarget.value))
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                disabled={value === null}
 | 
				
			||||||
 | 
					                value={value}
 | 
				
			||||||
 | 
					                min={1}
 | 
				
			||||||
 | 
					                max={60}
 | 
				
			||||||
 | 
					                step={1}
 | 
				
			||||||
 | 
					                className="block flex-1"
 | 
				
			||||||
 | 
					              />
 | 
				
			||||||
 | 
					              { value !== null &&
 | 
				
			||||||
 | 
					              <div>
 | 
				
			||||||
 | 
					                {value === 60 ? '1 hour' : value === 1 ? '1 minute' : value + ' minutes'}
 | 
				
			||||||
 | 
					              </div>
 | 
				
			||||||
 | 
					              }
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					          </div>
 | 
				
			||||||
 | 
					        ),
 | 
				
			||||||
      }),
 | 
					      }),
 | 
				
			||||||
      onboardingStatus: new Setting<string>({
 | 
					      onboardingStatus: new Setting<string>({
 | 
				
			||||||
        defaultValue: '',
 | 
					        defaultValue: '',
 | 
				
			||||||
 | 
				
			|||||||
@ -121,7 +121,7 @@ pub struct AppSettings {
 | 
				
			|||||||
    pub dismiss_web_banner: bool,
 | 
					    pub dismiss_web_banner: bool,
 | 
				
			||||||
    /// When the user is idle, and this is true, the stream will be torn down.
 | 
					    /// When the user is idle, and this is true, the stream will be torn down.
 | 
				
			||||||
    #[serde(default, alias = "streamIdleMode", skip_serializing_if = "is_default")]
 | 
					    #[serde(default, alias = "streamIdleMode", skip_serializing_if = "is_default")]
 | 
				
			||||||
    stream_idle_mode: bool,
 | 
					    stream_idle_mode: Option<FloatOrInt>,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// TODO: When we remove backwards compatibility with the old settings file, we can remove this.
 | 
					// TODO: When we remove backwards compatibility with the old settings file, we can remove this.
 | 
				
			||||||
@ -582,7 +582,7 @@ textWrapping = true
 | 
				
			|||||||
                        theme_color: None,
 | 
					                        theme_color: None,
 | 
				
			||||||
                        dismiss_web_banner: false,
 | 
					                        dismiss_web_banner: false,
 | 
				
			||||||
                        enable_ssao: None,
 | 
					                        enable_ssao: None,
 | 
				
			||||||
                        stream_idle_mode: false,
 | 
					                        stream_idle_mode: None,
 | 
				
			||||||
                    },
 | 
					                    },
 | 
				
			||||||
                    modeling: ModelingSettings {
 | 
					                    modeling: ModelingSettings {
 | 
				
			||||||
                        base_unit: UnitLength::In,
 | 
					                        base_unit: UnitLength::In,
 | 
				
			||||||
@ -643,7 +643,7 @@ includeSettings = false
 | 
				
			|||||||
                        theme_color: None,
 | 
					                        theme_color: None,
 | 
				
			||||||
                        dismiss_web_banner: false,
 | 
					                        dismiss_web_banner: false,
 | 
				
			||||||
                        enable_ssao: None,
 | 
					                        enable_ssao: None,
 | 
				
			||||||
                        stream_idle_mode: false,
 | 
					                        stream_idle_mode: None,
 | 
				
			||||||
                    },
 | 
					                    },
 | 
				
			||||||
                    modeling: ModelingSettings {
 | 
					                    modeling: ModelingSettings {
 | 
				
			||||||
                        base_unit: UnitLength::Yd,
 | 
					                        base_unit: UnitLength::Yd,
 | 
				
			||||||
@ -709,7 +709,7 @@ defaultProjectName = "projects-$nnn"
 | 
				
			|||||||
                        theme_color: None,
 | 
					                        theme_color: None,
 | 
				
			||||||
                        dismiss_web_banner: false,
 | 
					                        dismiss_web_banner: false,
 | 
				
			||||||
                        enable_ssao: None,
 | 
					                        enable_ssao: None,
 | 
				
			||||||
                        stream_idle_mode: false,
 | 
					                        stream_idle_mode: None,
 | 
				
			||||||
                    },
 | 
					                    },
 | 
				
			||||||
                    modeling: ModelingSettings {
 | 
					                    modeling: ModelingSettings {
 | 
				
			||||||
                        base_unit: UnitLength::Yd,
 | 
					                        base_unit: UnitLength::Yd,
 | 
				
			||||||
@ -787,7 +787,7 @@ projectDirectory = "/Users/macinatormax/Documents/kittycad-modeling-projects""#;
 | 
				
			|||||||
                        theme_color: None,
 | 
					                        theme_color: None,
 | 
				
			||||||
                        dismiss_web_banner: false,
 | 
					                        dismiss_web_banner: false,
 | 
				
			||||||
                        enable_ssao: None,
 | 
					                        enable_ssao: None,
 | 
				
			||||||
                        stream_idle_mode: false,
 | 
					                        stream_idle_mode: None,
 | 
				
			||||||
                    },
 | 
					                    },
 | 
				
			||||||
                    modeling: ModelingSettings {
 | 
					                    modeling: ModelingSettings {
 | 
				
			||||||
                        base_unit: UnitLength::Mm,
 | 
					                        base_unit: UnitLength::Mm,
 | 
				
			||||||
 | 
				
			|||||||
@ -123,7 +123,7 @@ includeSettings = false
 | 
				
			|||||||
                        theme_color: None,
 | 
					                        theme_color: None,
 | 
				
			||||||
                        dismiss_web_banner: false,
 | 
					                        dismiss_web_banner: false,
 | 
				
			||||||
                        enable_ssao: None,
 | 
					                        enable_ssao: None,
 | 
				
			||||||
                        stream_idle_mode: false,
 | 
					                        stream_idle_mode: None,
 | 
				
			||||||
                    },
 | 
					                    },
 | 
				
			||||||
                    modeling: ModelingSettings {
 | 
					                    modeling: ModelingSettings {
 | 
				
			||||||
                        base_unit: UnitLength::Yd,
 | 
					                        base_unit: UnitLength::Yd,
 | 
				
			||||||
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user