Persist theme - Reload everything on a disconnect (#3250)
* Reload everything on a disconnect * fix unit-integration tests * Further improvements to connection manager; persist theme across reconnects * Fix up artifactGraph.test * Actually pass the callback * Kurt hmmm (#3308) * kurts attempts * we're almost sane * get tests working, praise be --------- Co-authored-by: 49lf <ircsurfer33@gmail.com> * typo --------- Co-authored-by: Kurt Hutten Irev-Dev <k.hutten@protonmail.ch>
This commit is contained in:
@ -1,4 +1,5 @@
|
|||||||
import { useState, useEffect } from 'react'
|
import { useState, useEffect } from 'react'
|
||||||
|
import { EngineCommandManagerEvents } from 'lang/std/engineConnection'
|
||||||
import { engineCommandManager, sceneInfra } from 'lib/singletons'
|
import { engineCommandManager, sceneInfra } from 'lib/singletons'
|
||||||
import { throttle, isReducedMotion } from 'lib/utils'
|
import { throttle, isReducedMotion } from 'lib/utils'
|
||||||
|
|
||||||
@ -13,9 +14,12 @@ export const CamToggle = () => {
|
|||||||
const [enableRotate, setEnableRotate] = useState(true)
|
const [enableRotate, setEnableRotate] = useState(true)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
engineCommandManager.waitForReady.then(async () => {
|
engineCommandManager.addEventListener(
|
||||||
sceneInfra.camControls.dollyZoom(fov)
|
EngineCommandManagerEvents.SceneReady,
|
||||||
})
|
async () => {
|
||||||
|
sceneInfra.camControls.dollyZoom(fov)
|
||||||
|
}
|
||||||
|
)
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
const toggleCamera = () => {
|
const toggleCamera = () => {
|
||||||
|
@ -176,6 +176,7 @@ const FileTreeItem = ({
|
|||||||
)
|
)
|
||||||
codeManager.writeToFile()
|
codeManager.writeToFile()
|
||||||
|
|
||||||
|
// Prevent seeing the model built one piece at a time when changing files
|
||||||
kclManager.isFirstRender = true
|
kclManager.isFirstRender = true
|
||||||
kclManager.executeCode(true).then(() => {
|
kclManager.executeCode(true).then(() => {
|
||||||
kclManager.isFirstRender = false
|
kclManager.isFirstRender = false
|
||||||
|
@ -47,7 +47,7 @@ const Loading = ({ children }: React.PropsWithChildren) => {
|
|||||||
onConnectionStateChange as EventListener
|
onConnectionStateChange as EventListener
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}, [])
|
}, [engineCommandManager, engineCommandManager.engineConnection])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// Don't set long loading time if there's a more severe error
|
// Don't set long loading time if there's a more severe error
|
||||||
|
@ -78,7 +78,12 @@ import { err, trap } from 'lib/trap'
|
|||||||
import { useCommandsContext } from 'hooks/useCommandsContext'
|
import { useCommandsContext } from 'hooks/useCommandsContext'
|
||||||
import { modelingMachineEvent } from 'editor/manager'
|
import { modelingMachineEvent } from 'editor/manager'
|
||||||
import { hasValidFilletSelection } from 'lang/modifyAst/addFillet'
|
import { hasValidFilletSelection } from 'lang/modifyAst/addFillet'
|
||||||
import { ExportIntent } from 'lang/std/engineConnection'
|
import {
|
||||||
|
ExportIntent,
|
||||||
|
EngineConnectionState,
|
||||||
|
EngineConnectionStateType,
|
||||||
|
EngineConnectionEvents,
|
||||||
|
} from 'lang/std/engineConnection'
|
||||||
|
|
||||||
type MachineContext<T extends AnyStateMachine> = {
|
type MachineContext<T extends AnyStateMachine> = {
|
||||||
state: StateFrom<T>
|
state: StateFrom<T>
|
||||||
@ -154,7 +159,10 @@ export const ModelingMachineProvider = ({
|
|||||||
sceneInfra.camControls.syncDirection = 'engineToClient'
|
sceneInfra.camControls.syncDirection = 'engineToClient'
|
||||||
|
|
||||||
store.videoElement?.pause()
|
store.videoElement?.pause()
|
||||||
|
|
||||||
|
kclManager.isFirstRender = true
|
||||||
kclManager.executeCode().then(() => {
|
kclManager.executeCode().then(() => {
|
||||||
|
kclManager.isFirstRender = false
|
||||||
if (engineCommandManager.engineConnection?.idleMode) return
|
if (engineCommandManager.engineConnection?.idleMode) return
|
||||||
|
|
||||||
store.videoElement?.play().catch((e) => {
|
store.videoElement?.play().catch((e) => {
|
||||||
@ -909,15 +917,19 @@ export const ModelingMachineProvider = ({
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
useSetupEngineManager(streamRef, token, {
|
useSetupEngineManager(
|
||||||
pool: pool,
|
streamRef,
|
||||||
theme: theme.current,
|
|
||||||
highlightEdges: highlightEdges.current,
|
|
||||||
enableSSAO: enableSSAO.current,
|
|
||||||
modelingSend,
|
modelingSend,
|
||||||
modelingContext: modelingState.context,
|
modelingState.context,
|
||||||
showScaleGrid: showScaleGrid.current,
|
{
|
||||||
})
|
pool: pool,
|
||||||
|
theme: theme.current,
|
||||||
|
highlightEdges: highlightEdges.current,
|
||||||
|
enableSSAO: enableSSAO.current,
|
||||||
|
showScaleGrid: showScaleGrid.current,
|
||||||
|
},
|
||||||
|
token
|
||||||
|
)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
kclManager.registerExecuteCallback(() => {
|
kclManager.registerExecuteCallback(() => {
|
||||||
@ -945,17 +957,25 @@ export const ModelingMachineProvider = ({
|
|||||||
}, [modelingState.context.selectionRanges])
|
}, [modelingState.context.selectionRanges])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const offlineCallback = () => {
|
const onConnectionStateChanged = ({ detail }: CustomEvent) => {
|
||||||
// If we are in sketch mode we need to exit it.
|
// If we are in sketch mode we need to exit it.
|
||||||
// TODO: how do i check if we are in a sketch mode, I only want to call
|
// TODO: how do i check if we are in a sketch mode, I only want to call
|
||||||
// this then.
|
// this then.
|
||||||
modelingSend({ type: 'Cancel' })
|
if (detail.type === EngineConnectionStateType.Disconnecting) {
|
||||||
|
modelingSend({ type: 'Cancel' })
|
||||||
|
}
|
||||||
}
|
}
|
||||||
window.addEventListener('offline', offlineCallback)
|
engineCommandManager.engineConnection?.addEventListener(
|
||||||
|
EngineConnectionEvents.ConnectionStateChanged,
|
||||||
|
onConnectionStateChanged as EventListener
|
||||||
|
)
|
||||||
return () => {
|
return () => {
|
||||||
window.removeEventListener('offline', offlineCallback)
|
engineCommandManager.engineConnection?.removeEventListener(
|
||||||
|
EngineConnectionEvents.ConnectionStateChanged,
|
||||||
|
onConnectionStateChanged as EventListener
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}, [modelingSend])
|
}, [engineCommandManager.engineConnection, modelingSend])
|
||||||
|
|
||||||
// Allow using the delete key to delete solids
|
// Allow using the delete key to delete solids
|
||||||
useHotkeys(['backspace', 'delete', 'del'], () => {
|
useHotkeys(['backspace', 'delete', 'del'], () => {
|
||||||
|
@ -64,13 +64,6 @@ export const KclEditorPane = () => {
|
|||||||
: context.app.theme.current
|
: context.app.theme.current
|
||||||
const { copilotLSP, kclLSP } = useLspContext()
|
const { copilotLSP, kclLSP } = useLspContext()
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (typeof window === 'undefined') return
|
|
||||||
const onlineCallback = () => kclManager.executeCode(true)
|
|
||||||
window.addEventListener('online', onlineCallback)
|
|
||||||
return () => window.removeEventListener('online', onlineCallback)
|
|
||||||
}, [])
|
|
||||||
|
|
||||||
// Since these already exist in the editor, we don't need to define them
|
// Since these already exist in the editor, we don't need to define them
|
||||||
// with the wrapper.
|
// with the wrapper.
|
||||||
useHotkeys('mod+z', (e) => {
|
useHotkeys('mod+z', (e) => {
|
||||||
|
@ -191,6 +191,7 @@ export const SettingsAuthProviderBase = ({
|
|||||||
allSettingsIncludesUnitChange ||
|
allSettingsIncludesUnitChange ||
|
||||||
resetSettingsIncludesUnitChange
|
resetSettingsIncludesUnitChange
|
||||||
) {
|
) {
|
||||||
|
// Unit changes requires a re-exec of code
|
||||||
kclManager.isFirstRender = true
|
kclManager.isFirstRender = true
|
||||||
kclManager.executeCode(true).then(() => {
|
kclManager.executeCode(true).then(() => {
|
||||||
kclManager.isFirstRender = false
|
kclManager.isFirstRender = false
|
||||||
|
@ -11,21 +11,27 @@ import { sendSelectEventToEngine } from 'lib/selections'
|
|||||||
import { kclManager, engineCommandManager, sceneInfra } from 'lib/singletons'
|
import { kclManager, engineCommandManager, sceneInfra } from 'lib/singletons'
|
||||||
import { useAppStream } from 'AppState'
|
import { useAppStream } from 'AppState'
|
||||||
import {
|
import {
|
||||||
|
EngineCommandManagerEvents,
|
||||||
EngineConnectionStateType,
|
EngineConnectionStateType,
|
||||||
DisconnectingType,
|
DisconnectingType,
|
||||||
} from 'lang/std/engineConnection'
|
} from 'lang/std/engineConnection'
|
||||||
|
|
||||||
|
enum StreamState {
|
||||||
|
Playing = 'playing',
|
||||||
|
Paused = 'paused',
|
||||||
|
Resuming = 'resuming',
|
||||||
|
Unset = 'unset',
|
||||||
|
}
|
||||||
|
|
||||||
export const Stream = () => {
|
export const Stream = () => {
|
||||||
const [isLoading, setIsLoading] = useState(true)
|
const [isLoading, setIsLoading] = useState(true)
|
||||||
const [isFirstRender, setIsFirstRender] = useState(kclManager.isFirstRender)
|
|
||||||
const [clickCoords, setClickCoords] = useState<{ x: number; y: number }>()
|
const [clickCoords, setClickCoords] = useState<{ x: number; y: number }>()
|
||||||
const videoRef = useRef<HTMLVideoElement>(null)
|
const videoRef = useRef<HTMLVideoElement>(null)
|
||||||
const { settings } = useSettingsAuthContext()
|
const { settings } = useSettingsAuthContext()
|
||||||
const { state, send, context } = useModelingContext()
|
const { state, send, context } = useModelingContext()
|
||||||
const { mediaStream } = useAppStream()
|
const { mediaStream } = useAppStream()
|
||||||
const { overallState, immediateState } = useNetworkContext()
|
const { overallState, immediateState } = useNetworkContext()
|
||||||
const [isFreezeFrame, setIsFreezeFrame] = useState(false)
|
const [streamState, setStreamState] = useState(StreamState.Unset)
|
||||||
const [isPaused, setIsPaused] = useState(false)
|
|
||||||
|
|
||||||
const IDLE = settings.context.app.streamIdleMode.current
|
const IDLE = settings.context.app.streamIdleMode.current
|
||||||
|
|
||||||
@ -38,10 +44,7 @@ export const Stream = () => {
|
|||||||
immediateState.type === EngineConnectionStateType.Disconnecting &&
|
immediateState.type === EngineConnectionStateType.Disconnecting &&
|
||||||
immediateState.value.type === DisconnectingType.Pause
|
immediateState.value.type === DisconnectingType.Pause
|
||||||
) {
|
) {
|
||||||
setIsPaused(true)
|
setStreamState(StreamState.Paused)
|
||||||
}
|
|
||||||
if (immediateState.type === EngineConnectionStateType.Connecting) {
|
|
||||||
setIsPaused(false)
|
|
||||||
}
|
}
|
||||||
}, [immediateState])
|
}, [immediateState])
|
||||||
|
|
||||||
@ -76,8 +79,11 @@ export const Stream = () => {
|
|||||||
let timeoutIdIdleA: ReturnType<typeof setTimeout> | undefined = undefined
|
let timeoutIdIdleA: ReturnType<typeof setTimeout> | undefined = undefined
|
||||||
|
|
||||||
const teardown = () => {
|
const teardown = () => {
|
||||||
|
// Already paused
|
||||||
|
if (streamState === StreamState.Paused) return
|
||||||
|
|
||||||
videoRef.current?.pause()
|
videoRef.current?.pause()
|
||||||
setIsFreezeFrame(true)
|
setStreamState(StreamState.Paused)
|
||||||
sceneInfra.modelingSend({ type: 'Cancel' })
|
sceneInfra.modelingSend({ type: 'Cancel' })
|
||||||
// Give video time to pause
|
// Give video time to pause
|
||||||
window.requestAnimationFrame(() => {
|
window.requestAnimationFrame(() => {
|
||||||
@ -91,7 +97,7 @@ export const Stream = () => {
|
|||||||
timeoutIdIdleA = setTimeout(teardown, IDLE_TIME_MS)
|
timeoutIdIdleA = setTimeout(teardown, IDLE_TIME_MS)
|
||||||
} else if (!engineCommandManager.engineConnection?.isReady()) {
|
} else if (!engineCommandManager.engineConnection?.isReady()) {
|
||||||
clearTimeout(timeoutIdIdleA)
|
clearTimeout(timeoutIdIdleA)
|
||||||
engineCommandManager.engineConnection?.connect(true)
|
setStreamState(StreamState.Resuming)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -106,10 +112,15 @@ export const Stream = () => {
|
|||||||
let timeoutIdIdleB: ReturnType<typeof setTimeout> | undefined = undefined
|
let timeoutIdIdleB: ReturnType<typeof setTimeout> | undefined = undefined
|
||||||
|
|
||||||
const onAnyInput = () => {
|
const onAnyInput = () => {
|
||||||
// Clear both timers
|
if (streamState === StreamState.Playing) {
|
||||||
clearTimeout(timeoutIdIdleA)
|
// Clear both timers
|
||||||
clearTimeout(timeoutIdIdleB)
|
clearTimeout(timeoutIdIdleA)
|
||||||
timeoutIdIdleB = setTimeout(teardown, IDLE_TIME_MS)
|
clearTimeout(timeoutIdIdleB)
|
||||||
|
timeoutIdIdleB = setTimeout(teardown, IDLE_TIME_MS)
|
||||||
|
}
|
||||||
|
if (streamState === StreamState.Paused) {
|
||||||
|
setStreamState(StreamState.Resuming)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (IDLE) {
|
if (IDLE) {
|
||||||
@ -124,7 +135,27 @@ export const Stream = () => {
|
|||||||
timeoutIdIdleB = setTimeout(teardown, IDLE_TIME_MS)
|
timeoutIdIdleB = setTimeout(teardown, IDLE_TIME_MS)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const onSceneReady = () => {
|
||||||
|
kclManager.isFirstRender = true
|
||||||
|
setStreamState(StreamState.Playing)
|
||||||
|
kclManager.executeCode(true).then(() => {
|
||||||
|
videoRef.current?.play().catch((e) => {
|
||||||
|
console.warn('Video playing was prevented', e, videoRef.current)
|
||||||
|
})
|
||||||
|
kclManager.isFirstRender = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
engineCommandManager.addEventListener(
|
||||||
|
EngineCommandManagerEvents.SceneReady,
|
||||||
|
onSceneReady
|
||||||
|
)
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
|
engineCommandManager.removeEventListener(
|
||||||
|
EngineCommandManagerEvents.SceneReady,
|
||||||
|
onSceneReady
|
||||||
|
)
|
||||||
globalThis?.window?.document?.removeEventListener('paste', handlePaste, {
|
globalThis?.window?.document?.removeEventListener('paste', handlePaste, {
|
||||||
capture: true,
|
capture: true,
|
||||||
})
|
})
|
||||||
@ -152,19 +183,7 @@ export const Stream = () => {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, [IDLE])
|
}, [IDLE, streamState])
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
setIsFirstRender(kclManager.isFirstRender)
|
|
||||||
if (!kclManager.isFirstRender)
|
|
||||||
setTimeout(() =>
|
|
||||||
// execute in the next event loop
|
|
||||||
videoRef.current?.play().catch((e) => {
|
|
||||||
console.warn('Video playing was prevented', e, videoRef.current)
|
|
||||||
})
|
|
||||||
)
|
|
||||||
setIsFreezeFrame(!kclManager.isFirstRender)
|
|
||||||
}, [kclManager.isFirstRender])
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (
|
if (
|
||||||
@ -288,7 +307,8 @@ export const Stream = () => {
|
|||||||
<ClientSideScene
|
<ClientSideScene
|
||||||
cameraControls={settings.context.modeling.mouseControls.current}
|
cameraControls={settings.context.modeling.mouseControls.current}
|
||||||
/>
|
/>
|
||||||
{isPaused && (
|
{(streamState === StreamState.Paused ||
|
||||||
|
streamState === StreamState.Resuming) && (
|
||||||
<div className="text-center absolute inset-0">
|
<div className="text-center absolute inset-0">
|
||||||
<div
|
<div
|
||||||
className="flex flex-col items-center justify-center h-screen"
|
className="flex flex-col items-center justify-center h-screen"
|
||||||
@ -310,16 +330,19 @@ export const Stream = () => {
|
|||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
<p className="text-base mt-2 text-primary bold">Paused</p>
|
<p className="text-base mt-2 text-primary bold">
|
||||||
|
{streamState === StreamState.Paused && 'Paused'}
|
||||||
|
{streamState === StreamState.Resuming && 'Resuming'}
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{(!isNetworkOkay || isLoading || isFirstRender) && !isFreezeFrame && (
|
{(!isNetworkOkay || isLoading || kclManager.isFirstRender) && (
|
||||||
<div className="text-center absolute inset-0">
|
<div className="text-center absolute inset-0">
|
||||||
<Loading>
|
<Loading>
|
||||||
{!isNetworkOkay && !isLoading ? (
|
{!isNetworkOkay && !isLoading && !kclManager.isFirstRender ? (
|
||||||
<span data-testid="loading-stream">Stream disconnected...</span>
|
<span data-testid="loading-stream">Stream disconnected...</span>
|
||||||
) : !isLoading && isFirstRender ? (
|
) : !isLoading && kclManager.isFirstRender ? (
|
||||||
<span data-testid="loading-stream">Building scene...</span>
|
<span data-testid="loading-stream">Building scene...</span>
|
||||||
) : (
|
) : (
|
||||||
<span data-testid="loading-stream">Loading stream...</span>
|
<span data-testid="loading-stream">Loading stream...</span>
|
||||||
|
@ -4,29 +4,30 @@ import { deferExecution } from 'lib/utils'
|
|||||||
import { Themes } from 'lib/theme'
|
import { Themes } from 'lib/theme'
|
||||||
import { makeDefaultPlanes, modifyGrid } from 'lang/wasm'
|
import { makeDefaultPlanes, modifyGrid } from 'lang/wasm'
|
||||||
import { useModelingContext } from './useModelingContext'
|
import { useModelingContext } from './useModelingContext'
|
||||||
|
import { useNetworkContext } from 'hooks/useNetworkContext'
|
||||||
import { useAppState, useAppStream } from 'AppState'
|
import { useAppState, useAppStream } from 'AppState'
|
||||||
|
import { SettingsViaQueryString } from 'lib/settings/settingsTypes'
|
||||||
|
import {
|
||||||
|
EngineConnectionStateType,
|
||||||
|
EngineConnectionEvents,
|
||||||
|
DisconnectingType,
|
||||||
|
} from 'lang/std/engineConnection'
|
||||||
|
|
||||||
export function useSetupEngineManager(
|
export function useSetupEngineManager(
|
||||||
streamRef: React.RefObject<HTMLDivElement>,
|
streamRef: React.RefObject<HTMLDivElement>,
|
||||||
token?: string,
|
modelingSend: ReturnType<typeof useModelingContext>['send'],
|
||||||
|
modelingContext: ReturnType<typeof useModelingContext>['context'],
|
||||||
settings = {
|
settings = {
|
||||||
pool: null,
|
pool: null,
|
||||||
theme: Themes.System,
|
theme: Themes.System,
|
||||||
highlightEdges: true,
|
highlightEdges: true,
|
||||||
enableSSAO: true,
|
enableSSAO: true,
|
||||||
modelingSend: (() => {}) as any,
|
|
||||||
modelingContext: {} as any,
|
|
||||||
showScaleGrid: false,
|
showScaleGrid: false,
|
||||||
} as {
|
} as SettingsViaQueryString,
|
||||||
pool: string | null
|
token?: string
|
||||||
theme: Themes
|
|
||||||
highlightEdges: boolean
|
|
||||||
enableSSAO: boolean
|
|
||||||
modelingSend: ReturnType<typeof useModelingContext>['send']
|
|
||||||
modelingContext: ReturnType<typeof useModelingContext>['context']
|
|
||||||
showScaleGrid: boolean
|
|
||||||
}
|
|
||||||
) {
|
) {
|
||||||
|
const networkContext = useNetworkContext()
|
||||||
|
const { pingPongHealth, immediateState } = networkContext
|
||||||
const { setAppState } = useAppState()
|
const { setAppState } = useAppState()
|
||||||
const { setMediaStream } = useAppStream()
|
const { setMediaStream } = useAppStream()
|
||||||
|
|
||||||
@ -35,10 +36,10 @@ export function useSetupEngineManager(
|
|||||||
if (settings.pool) {
|
if (settings.pool) {
|
||||||
// override the pool param (?pool=) to request a specific engine instance
|
// override the pool param (?pool=) to request a specific engine instance
|
||||||
// from a particular pool.
|
// from a particular pool.
|
||||||
engineCommandManager.pool = settings.pool
|
engineCommandManager.settings.pool = settings.pool
|
||||||
}
|
}
|
||||||
|
|
||||||
const startEngineInstance = (restart: boolean = false) => {
|
const startEngineInstance = () => {
|
||||||
// Load the engine command manager once with the initial width and height,
|
// Load the engine command manager once with the initial width and height,
|
||||||
// then we do not want to reload it.
|
// then we do not want to reload it.
|
||||||
const { width: quadWidth, height: quadHeight } = getDimensions(
|
const { width: quadWidth, height: quadHeight } = getDimensions(
|
||||||
@ -50,14 +51,6 @@ export function useSetupEngineManager(
|
|||||||
setIsStreamReady: (isStreamReady) => setAppState({ isStreamReady }),
|
setIsStreamReady: (isStreamReady) => setAppState({ isStreamReady }),
|
||||||
width: quadWidth,
|
width: quadWidth,
|
||||||
height: quadHeight,
|
height: quadHeight,
|
||||||
executeCode: () => {
|
|
||||||
// We only want to execute the code here that we already have set.
|
|
||||||
// Nothing else.
|
|
||||||
kclManager.isFirstRender = true
|
|
||||||
return kclManager.executeCode(true).then(() => {
|
|
||||||
kclManager.isFirstRender = false
|
|
||||||
})
|
|
||||||
},
|
|
||||||
token,
|
token,
|
||||||
settings,
|
settings,
|
||||||
makeDefaultPlanes: () => {
|
makeDefaultPlanes: () => {
|
||||||
@ -67,7 +60,7 @@ export function useSetupEngineManager(
|
|||||||
return modifyGrid(kclManager.engineCommandManager, hidden)
|
return modifyGrid(kclManager.engineCommandManager, hidden)
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
settings.modelingSend({
|
modelingSend({
|
||||||
type: 'Set context',
|
type: 'Set context',
|
||||||
data: {
|
data: {
|
||||||
streamDimensions: {
|
streamDimensions: {
|
||||||
@ -90,9 +83,27 @@ export function useSetupEngineManager(
|
|||||||
}, [
|
}, [
|
||||||
streamRef?.current?.offsetWidth,
|
streamRef?.current?.offsetWidth,
|
||||||
streamRef?.current?.offsetHeight,
|
streamRef?.current?.offsetHeight,
|
||||||
settings.modelingSend,
|
modelingSend,
|
||||||
])
|
])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (pingPongHealth === 'TIMEOUT') {
|
||||||
|
engineCommandManager.tearDown()
|
||||||
|
}
|
||||||
|
}, [pingPongHealth])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const intervalId = setInterval(() => {
|
||||||
|
if (immediateState.type === EngineConnectionStateType.Disconnected) {
|
||||||
|
engineCommandManager.engineConnection = undefined
|
||||||
|
startEngineInstance()
|
||||||
|
}
|
||||||
|
}, 3000)
|
||||||
|
return () => {
|
||||||
|
clearInterval(intervalId)
|
||||||
|
}
|
||||||
|
}, [immediateState])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const handleResize = deferExecution(() => {
|
const handleResize = deferExecution(() => {
|
||||||
const { width, height } = getDimensions(
|
const { width, height } = getDimensions(
|
||||||
@ -100,14 +111,14 @@ export function useSetupEngineManager(
|
|||||||
streamRef?.current?.offsetHeight ?? 0
|
streamRef?.current?.offsetHeight ?? 0
|
||||||
)
|
)
|
||||||
if (
|
if (
|
||||||
settings.modelingContext.store.streamDimensions.streamWidth !== width ||
|
modelingContext.store.streamDimensions.streamWidth !== width ||
|
||||||
settings.modelingContext.store.streamDimensions.streamHeight !== height
|
modelingContext.store.streamDimensions.streamHeight !== height
|
||||||
) {
|
) {
|
||||||
engineCommandManager.handleResize({
|
engineCommandManager.handleResize({
|
||||||
streamWidth: width,
|
streamWidth: width,
|
||||||
streamHeight: height,
|
streamHeight: height,
|
||||||
})
|
})
|
||||||
settings.modelingSend({
|
modelingSend({
|
||||||
type: 'Set context',
|
type: 'Set context',
|
||||||
data: {
|
data: {
|
||||||
streamDimensions: {
|
streamDimensions: {
|
||||||
@ -120,7 +131,7 @@ export function useSetupEngineManager(
|
|||||||
}, 500)
|
}, 500)
|
||||||
|
|
||||||
const onOnline = () => {
|
const onOnline = () => {
|
||||||
startEngineInstance(true)
|
startEngineInstance()
|
||||||
}
|
}
|
||||||
|
|
||||||
const onVisibilityChange = () => {
|
const onVisibilityChange = () => {
|
||||||
@ -136,10 +147,18 @@ export function useSetupEngineManager(
|
|||||||
window.document.addEventListener('visibilitychange', onVisibilityChange)
|
window.document.addEventListener('visibilitychange', onVisibilityChange)
|
||||||
|
|
||||||
const onAnyInput = () => {
|
const onAnyInput = () => {
|
||||||
if (
|
const isEngineNotReadyOrConnecting =
|
||||||
!engineCommandManager.engineConnection?.isReady() &&
|
!engineCommandManager.engineConnection?.isReady() &&
|
||||||
!engineCommandManager.engineConnection?.isConnecting()
|
!engineCommandManager.engineConnection?.isConnecting()
|
||||||
) {
|
|
||||||
|
const conn = engineCommandManager.engineConnection
|
||||||
|
|
||||||
|
const isStreamPaused =
|
||||||
|
conn?.state.type === EngineConnectionStateType.Disconnecting &&
|
||||||
|
conn?.state.value.type === DisconnectingType.Pause
|
||||||
|
|
||||||
|
if (isEngineNotReadyOrConnecting || isStreamPaused) {
|
||||||
|
engineCommandManager.engineConnection = undefined
|
||||||
startEngineInstance()
|
startEngineInstance()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -150,7 +169,6 @@ export function useSetupEngineManager(
|
|||||||
window.document.addEventListener('touchstart', onAnyInput)
|
window.document.addEventListener('touchstart', onAnyInput)
|
||||||
|
|
||||||
const onOffline = () => {
|
const onOffline = () => {
|
||||||
kclManager.isFirstRender = true
|
|
||||||
engineCommandManager.tearDown()
|
engineCommandManager.tearDown()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -211,7 +211,6 @@ export class KclManager {
|
|||||||
type: string
|
type: string
|
||||||
}
|
}
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
await this?.engineCommandManager?.waitForReady
|
|
||||||
const currentExecutionId = executionId || Date.now()
|
const currentExecutionId = executionId || Date.now()
|
||||||
this._cancelTokens.set(currentExecutionId, false)
|
this._cancelTokens.set(currentExecutionId, false)
|
||||||
|
|
||||||
@ -301,7 +300,6 @@ export class KclManager {
|
|||||||
codeManager.updateCodeEditor(newCode)
|
codeManager.updateCodeEditor(newCode)
|
||||||
// Write the file to disk.
|
// Write the file to disk.
|
||||||
await codeManager.writeToFile()
|
await codeManager.writeToFile()
|
||||||
await this?.engineCommandManager?.waitForReady
|
|
||||||
this._ast = { ...newAst }
|
this._ast = { ...newAst }
|
||||||
|
|
||||||
const { logs, errors, programMemory } = await executeAst({
|
const { logs, errors, programMemory } = await executeAst({
|
||||||
|
@ -14,6 +14,10 @@ import {
|
|||||||
} from './artifactGraph'
|
} from './artifactGraph'
|
||||||
import { err } from 'lib/trap'
|
import { err } from 'lib/trap'
|
||||||
import { engineCommandManager, kclManager } from 'lib/singletons'
|
import { engineCommandManager, kclManager } from 'lib/singletons'
|
||||||
|
import {
|
||||||
|
EngineCommandManagerEvents,
|
||||||
|
EngineConnectionEvents,
|
||||||
|
} from 'lang/std/engineConnection'
|
||||||
import { CI, VITE_KC_DEV_TOKEN } from 'env'
|
import { CI, VITE_KC_DEV_TOKEN } from 'env'
|
||||||
import fsp from 'fs/promises'
|
import fsp from 'fs/promises'
|
||||||
import fs from 'fs'
|
import fs from 'fs'
|
||||||
@ -113,42 +117,44 @@ beforeAll(async () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// THESE TEST WILL FAIL without VITE_KC_DEV_TOKEN set in .env.development.local
|
// THESE TEST WILL FAIL without VITE_KC_DEV_TOKEN set in .env.development.local
|
||||||
engineCommandManager.start({
|
await new Promise((resolve) => {
|
||||||
disableWebRTC: true,
|
engineCommandManager.start({
|
||||||
token: VITE_KC_DEV_TOKEN,
|
// disableWebRTC: true,
|
||||||
// there does seem to be a minimum resolution, not sure what it is but 256 works ok.
|
token: VITE_KC_DEV_TOKEN,
|
||||||
width: 256,
|
// there does seem to be a minimum resolution, not sure what it is but 256 works ok.
|
||||||
height: 256,
|
width: 256,
|
||||||
executeCode: () => {},
|
height: 256,
|
||||||
makeDefaultPlanes: () => makeDefaultPlanes(engineCommandManager),
|
makeDefaultPlanes: () => makeDefaultPlanes(engineCommandManager),
|
||||||
setMediaStream: () => {},
|
setMediaStream: () => {},
|
||||||
setIsStreamReady: () => {},
|
setIsStreamReady: () => {},
|
||||||
modifyGrid: async () => {},
|
modifyGrid: async () => {},
|
||||||
|
callbackOnEngineLiteConnect: async () => {
|
||||||
|
const cacheEntries = Object.entries(codeToWriteCacheFor) as [
|
||||||
|
CodeKey,
|
||||||
|
string
|
||||||
|
][]
|
||||||
|
const cacheToWriteToFileTemp: Partial<CacheShape> = {}
|
||||||
|
for (const [codeKey, code] of cacheEntries) {
|
||||||
|
const ast = parse(code)
|
||||||
|
if (err(ast)) {
|
||||||
|
console.error(ast)
|
||||||
|
return Promise.reject(ast)
|
||||||
|
}
|
||||||
|
const result = await kclManager.executeAst(ast)
|
||||||
|
|
||||||
|
cacheToWriteToFileTemp[codeKey] = {
|
||||||
|
orderedCommands: engineCommandManager.orderedCommands,
|
||||||
|
responseMap: engineCommandManager.responseMap,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const cache = JSON.stringify(cacheToWriteToFileTemp)
|
||||||
|
|
||||||
|
await fsp.mkdir(pathStart, { recursive: true })
|
||||||
|
await fsp.writeFile(fullPath, cache)
|
||||||
|
resolve(true)
|
||||||
|
},
|
||||||
|
})
|
||||||
})
|
})
|
||||||
await engineCommandManager.waitForReady
|
|
||||||
|
|
||||||
const cacheEntries = Object.entries(codeToWriteCacheFor) as [
|
|
||||||
CodeKey,
|
|
||||||
string
|
|
||||||
][]
|
|
||||||
const cacheToWriteToFileTemp: Partial<CacheShape> = {}
|
|
||||||
for (const [codeKey, code] of cacheEntries) {
|
|
||||||
const ast = parse(code)
|
|
||||||
if (err(ast)) {
|
|
||||||
console.error(ast)
|
|
||||||
throw ast
|
|
||||||
}
|
|
||||||
await kclManager.executeAst(ast)
|
|
||||||
|
|
||||||
cacheToWriteToFileTemp[codeKey] = {
|
|
||||||
orderedCommands: engineCommandManager.orderedCommands,
|
|
||||||
responseMap: engineCommandManager.responseMap,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const cache = JSON.stringify(cacheToWriteToFileTemp)
|
|
||||||
|
|
||||||
await fsp.mkdir(pathStart, { recursive: true })
|
|
||||||
await fsp.writeFile(fullPath, cache)
|
|
||||||
}, 20_000)
|
}, 20_000)
|
||||||
|
|
||||||
afterAll(() => {
|
afterAll(() => {
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -63,7 +63,7 @@ export class CoreDumpManager {
|
|||||||
|
|
||||||
// Get the backend pool we've requested.
|
// Get the backend pool we've requested.
|
||||||
pool(): string {
|
pool(): string {
|
||||||
return this.engineCommandManager.pool || ''
|
return this.engineCommandManager.settings.pool || ''
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the os information.
|
// Get the os information.
|
||||||
|
@ -104,8 +104,12 @@ export const fileLoader: LoaderFunction = async ({
|
|||||||
// the file system and not the editor.
|
// the file system and not the editor.
|
||||||
codeManager.updateCurrentFilePath(current_file_path)
|
codeManager.updateCurrentFilePath(current_file_path)
|
||||||
codeManager.updateCodeStateEditor(code)
|
codeManager.updateCodeStateEditor(code)
|
||||||
|
|
||||||
// We don't want to call await on execute code since we don't want to block the UI
|
// We don't want to call await on execute code since we don't want to block the UI
|
||||||
kclManager.executeCode(true)
|
kclManager.isFirstRender = true
|
||||||
|
kclManager.executeCode(true).then(() => {
|
||||||
|
kclManager.isFirstRender = false
|
||||||
|
})
|
||||||
|
|
||||||
// Set the file system manager to the project path
|
// Set the file system manager to the project path
|
||||||
// So that WASM gets an updated path for operations
|
// So that WASM gets an updated path for operations
|
||||||
|
@ -2,6 +2,15 @@ import { type Models } from '@kittycad/lib'
|
|||||||
import { Setting, settings } from './initialSettings'
|
import { Setting, settings } from './initialSettings'
|
||||||
import { AtLeast, PathValue, Paths } from 'lib/types'
|
import { AtLeast, PathValue, Paths } from 'lib/types'
|
||||||
import { CommandArgumentConfig } from 'lib/commandTypes'
|
import { CommandArgumentConfig } from 'lib/commandTypes'
|
||||||
|
import { Themes } from 'lib/theme'
|
||||||
|
|
||||||
|
export interface SettingsViaQueryString {
|
||||||
|
pool: string | null
|
||||||
|
theme: Themes
|
||||||
|
highlightEdges: boolean
|
||||||
|
enableSSAO: boolean
|
||||||
|
showScaleGrid: boolean
|
||||||
|
}
|
||||||
|
|
||||||
export enum UnitSystem {
|
export enum UnitSystem {
|
||||||
Imperial = 'imperial',
|
Imperial = 'imperial',
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
import { Program, ProgramMemory, _executor, SourceRange } from '../lang/wasm'
|
import { Program, ProgramMemory, _executor, SourceRange } from '../lang/wasm'
|
||||||
import { EngineCommandManager } from 'lang/std/engineConnection'
|
import {
|
||||||
|
EngineCommandManager,
|
||||||
|
EngineCommandManagerEvents,
|
||||||
|
} from 'lang/std/engineConnection'
|
||||||
import { EngineCommand } from 'lang/std/artifactGraph'
|
import { EngineCommand } from 'lang/std/artifactGraph'
|
||||||
import { Models } from '@kittycad/lib'
|
import { Models } from '@kittycad/lib'
|
||||||
import { v4 as uuidv4 } from 'uuid'
|
import { v4 as uuidv4 } from 'uuid'
|
||||||
@ -82,7 +85,6 @@ export async function enginelessExecutor(
|
|||||||
setIsStreamReady: () => {},
|
setIsStreamReady: () => {},
|
||||||
setMediaStream: () => {},
|
setMediaStream: () => {},
|
||||||
}) as any as EngineCommandManager
|
}) as any as EngineCommandManager
|
||||||
await mockEngineCommandManager.waitForReady
|
|
||||||
mockEngineCommandManager.startNewSession()
|
mockEngineCommandManager.startNewSession()
|
||||||
const programMemory = await _executor(ast, pm, mockEngineCommandManager, true)
|
const programMemory = await _executor(ast, pm, mockEngineCommandManager, true)
|
||||||
await mockEngineCommandManager.waitForAllCommands()
|
await mockEngineCommandManager.waitForAllCommands()
|
||||||
@ -99,7 +101,6 @@ export async function executor(
|
|||||||
setMediaStream: () => {},
|
setMediaStream: () => {},
|
||||||
width: 0,
|
width: 0,
|
||||||
height: 0,
|
height: 0,
|
||||||
executeCode: () => {},
|
|
||||||
makeDefaultPlanes: () => {
|
makeDefaultPlanes: () => {
|
||||||
return new Promise((resolve) => resolve(defaultPlanes))
|
return new Promise((resolve) => resolve(defaultPlanes))
|
||||||
},
|
},
|
||||||
@ -107,9 +108,21 @@ export async function executor(
|
|||||||
return new Promise((resolve) => resolve())
|
return new Promise((resolve) => resolve())
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
await engineCommandManager.waitForReady
|
|
||||||
engineCommandManager.startNewSession()
|
return new Promise((resolve) => {
|
||||||
const programMemory = await _executor(ast, pm, engineCommandManager, false)
|
engineCommandManager.addEventListener(
|
||||||
await engineCommandManager.waitForAllCommands()
|
EngineCommandManagerEvents.SceneReady,
|
||||||
return programMemory
|
async () => {
|
||||||
|
engineCommandManager.startNewSession()
|
||||||
|
const programMemory = await _executor(
|
||||||
|
ast,
|
||||||
|
pm,
|
||||||
|
engineCommandManager,
|
||||||
|
false
|
||||||
|
)
|
||||||
|
await engineCommandManager.waitForAllCommands()
|
||||||
|
Promise.resolve(programMemory)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user