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