Make the stream idle option a slider for time

This commit is contained in:
49lf
2024-09-19 13:22:07 -04:00
parent 788270d4fc
commit 05a2eada9a
13 changed files with 334 additions and 273 deletions

View File

@ -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 = '',

View File

@ -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(

View File

@ -29,7 +29,9 @@ export const CommandBar = () => {
}, [pathname])
useEffect(() => {
if (immediateState.type !== EngineConnectionStateType.ConnectionEstablished) {
if (
immediateState.type !== EngineConnectionStateType.ConnectionEstablished
) {
commandBarSend({ type: 'Close' })
}
}, [immediateState])

View File

@ -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

View File

@ -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>
)
)
}

View File

@ -14,7 +14,6 @@ import { NetworkMachineIndicator } from './NetworkMachineIndicator'
import { ModelStateIndicator } from './ModelStateIndicator'
import { reportRejection } from 'lib/trap'
export function LowerRightControls({
children,
coreDumpManager,

View File

@ -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'}

View File

@ -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()

View File

@ -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)

View File

@ -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()

View File

@ -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: '',

View File

@ -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,

View File

@ -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,