Don't race exiting a sketch scene (#7128)

* Turn sketch exit execute into actor, no more racey exits

* Turn sketch exit execute into actor, no more racey exits

* Fix types

---------

Co-authored-by: Frank Noirot <frank@zoo.dev>
This commit is contained in:
Zookeeper Lee
2025-05-20 21:09:16 -04:00
committed by GitHub
parent 0f0fc39d07
commit 35f5c62633
2 changed files with 64 additions and 66 deletions

View File

@ -161,40 +161,6 @@ export const ModelingMachineProvider = ({
'enable copilot': () => { 'enable copilot': () => {
editorManager.setCopilotEnabled(true) editorManager.setCopilotEnabled(true)
}, },
'sketch exit execute': ({ context: { store } }) => {
// TODO: Remove this async callback. For some reason eslint wouldn't
// let me disable @typescript-eslint/no-misused-promises for the line.
;(async () => {
// When cancelling the sketch mode we should disable sketch mode within the engine.
await engineCommandManager.sendSceneCommand({
type: 'modeling_cmd_req',
cmd_id: uuidv4(),
cmd: { type: 'sketch_mode_disable' },
})
sceneInfra.camControls.syncDirection = 'clientToEngine'
if (cameraProjection.current === 'perspective') {
await sceneInfra.camControls.snapToPerspectiveBeforeHandingBackControlToEngine()
}
sceneInfra.camControls.syncDirection = 'engineToClient'
// TODO: Re-evaluate if this pause/play logic is needed.
store.videoElement?.pause()
return kclManager
.executeCode()
.then(() => {
if (engineCommandManager.idleMode) return
store.videoElement?.play().catch((e) => {
console.warn('Video playing was prevented', e)
})
})
.catch(reportRejection)
})().catch(reportRejection)
},
'Set mouse state': assign(({ context, event }) => { 'Set mouse state': assign(({ context, event }) => {
if (event.type !== 'Set mouse state') return {} if (event.type !== 'Set mouse state') return {}
const nextSegmentHoverMap = () => { const nextSegmentHoverMap = () => {
@ -1303,6 +1269,7 @@ export const ModelingMachineProvider = ({
store: { store: {
...modelingMachineDefaultContext.store, ...modelingMachineDefaultContext.store,
...persistedContext, ...persistedContext,
cameraProjection,
}, },
machineManager, machineManager,
}, },

View File

@ -9,6 +9,8 @@ import {
orthoScale, orthoScale,
quaternionFromUpNForward, quaternionFromUpNForward,
} from '@src/clientSideScene/helpers' } from '@src/clientSideScene/helpers'
import type { Setting } from '@src/lib/settings/initialSettings'
import type { CameraProjectionType } from '@rust/kcl-lib/bindings/CameraProjectionType'
import { DRAFT_DASHED_LINE } from '@src/clientSideScene/sceneConstants' import { DRAFT_DASHED_LINE } from '@src/clientSideScene/sceneConstants'
import { DRAFT_POINT } from '@src/clientSideScene/sceneUtils' import { DRAFT_POINT } from '@src/clientSideScene/sceneUtils'
import { createProfileStartHandle } from '@src/clientSideScene/segments' import { createProfileStartHandle } from '@src/clientSideScene/segments'
@ -299,6 +301,7 @@ export type SegmentOverlayPayload =
export interface Store { export interface Store {
videoElement?: HTMLVideoElement videoElement?: HTMLVideoElement
openPanes: SidebarType[] openPanes: SidebarType[]
cameraProjection?: Setting<CameraProjectionType>
} }
export type SketchTool = export type SketchTool =
@ -333,7 +336,6 @@ export type ModelingMachineEvent =
} }
| { type: 'Sketch no face' } | { type: 'Sketch no face' }
| { type: 'Cancel'; cleanup?: () => void } | { type: 'Cancel'; cleanup?: () => void }
| { type: 'CancelSketch' }
| { | {
type: 'Add start point' | 'Continue existing profile' type: 'Add start point' | 'Continue existing profile'
data: { data: {
@ -856,10 +858,6 @@ export const modelingMachine = setup({
sketchDetails: event.output, sketchDetails: event.output,
} }
}), }),
'tear down client sketch': () => {
sceneEntitiesManager.tearDownSketch({ removeAxis: false })
},
'remove sketch grid': () => sceneEntitiesManager.removeSketchGrid(),
'set up draft line': assign(({ context: { sketchDetails }, event }) => { 'set up draft line': assign(({ context: { sketchDetails }, event }) => {
if (!sketchDetails) return {} if (!sketchDetails) return {}
if (event.type !== 'Add start point') return {} if (event.type !== 'Add start point') return {}
@ -1200,9 +1198,6 @@ export const modelingMachine = setup({
'clientToEngine cam sync direction': () => { 'clientToEngine cam sync direction': () => {
sceneInfra.camControls.syncDirection = 'clientToEngine' sceneInfra.camControls.syncDirection = 'clientToEngine'
}, },
'engineToClient cam sync direction': () => {
sceneInfra.camControls.syncDirection = 'engineToClient'
},
/** TODO: this action is hiding unawaited asynchronous code */ /** TODO: this action is hiding unawaited asynchronous code */
'set selection filter to faces only': () => { 'set selection filter to faces only': () => {
kclManager.setSelectionFilter(['face', 'object']) kclManager.setSelectionFilter(['face', 'object'])
@ -1223,7 +1218,6 @@ export const modelingMachine = setup({
return codeManager.updateEditorWithAstAndWriteToFile(kclManager.ast) return codeManager.updateEditorWithAstAndWriteToFile(kclManager.ast)
}) })
}, },
'Reset Segment Overlays': () => sceneEntitiesManager.resetOverlays(),
'Set context': assign({ 'Set context': assign({
store: ({ context: { store }, event }) => { store: ({ context: { store }, event }) => {
if (event.type !== 'Set context') return store if (event.type !== 'Set context') return store
@ -1542,7 +1536,6 @@ export const modelingMachine = setup({
'Center camera on selection': () => {}, 'Center camera on selection': () => {},
'Submit to Text-to-CAD API': () => {}, 'Submit to Text-to-CAD API': () => {},
'Set sketchDetails': () => {}, 'Set sketchDetails': () => {},
'sketch exit execute': () => {},
'debug-action': (data) => { 'debug-action': (data) => {
console.log('re-eval debug-action', data) console.log('re-eval debug-action', data)
}, },
@ -1610,6 +1603,45 @@ export const modelingMachine = setup({
}, },
// end actions // end actions
actors: { actors: {
sketchExit: fromPromise(
async (args: { input: { context: { store: Store } } }) => {
const store = args.input.context.store
// When cancelling the sketch mode we should disable sketch mode within the engine.
await engineCommandManager.sendSceneCommand({
type: 'modeling_cmd_req',
cmd_id: uuidv4(),
cmd: { type: 'sketch_mode_disable' },
})
sceneInfra.camControls.syncDirection = 'clientToEngine'
if (store.cameraProjection?.current === 'perspective') {
await sceneInfra.camControls.snapToPerspectiveBeforeHandingBackControlToEngine()
}
sceneInfra.camControls.syncDirection = 'engineToClient'
// TODO: Re-evaluate if this pause/play logic is needed.
store.videoElement?.pause()
await kclManager
.executeCode()
.then(() => {
if (engineCommandManager.idleMode) return
store.videoElement?.play().catch((e: Error) => {
console.warn('Video playing was prevented', e)
})
})
.catch(reportRejection)
sceneEntitiesManager.tearDownSketch({ removeAxis: false })
sceneEntitiesManager.removeSketchGrid()
sceneInfra.camControls.syncDirection = 'engineToClient'
sceneEntitiesManager.resetOverlays()
}
),
/* Below are all the do-constrain sketch actors, /* Below are all the do-constrain sketch actors,
* which aren't using updateModelingState and don't have the 'no kcl errors' guard yet */ * which aren't using updateModelingState and don't have the 'no kcl errors' guard yet */
'do-constrain-remove-constraint': fromPromise( 'do-constrain-remove-constraint': fromPromise(
@ -4194,22 +4226,29 @@ export const modelingMachine = setup({
}, },
'undo startSketchOn': { 'undo startSketchOn': {
invoke: { invoke: [
src: 'AST-undo-startSketchOn', {
id: 'AST-undo-startSketchOn', id: 'sketchExit',
input: ({ context: { sketchDetails } }) => ({ sketchDetails }), src: 'sketchExit',
input: ({ context }) => ({ context }),
onDone: {
target: '#Modeling.idle',
actions: 'enter modeling mode',
reenter: true,
}, },
{
src: 'AST-undo-startSketchOn',
id: 'AST-undo-startSketchOn',
input: ({ context: { sketchDetails } }) => ({ sketchDetails }),
onError: { onDone: {
target: '#Modeling.idle', target: '#Modeling.idle',
reenter: true, actions: 'enter modeling mode',
reenter: true,
},
onError: {
target: '#Modeling.idle',
reenter: true,
},
}, },
}, ],
}, },
'Rectangle tool': { 'Rectangle tool': {
@ -4927,7 +4966,6 @@ export const modelingMachine = setup({
on: { on: {
Cancel: '.undo startSketchOn', Cancel: '.undo startSketchOn',
CancelSketch: '.SketchIdle',
'Delete segment': { 'Delete segment': {
reenter: false, reenter: false,
@ -4940,14 +4978,7 @@ export const modelingMachine = setup({
}, },
}, },
exit: [ exit: ['enable copilot'],
'sketch exit execute',
'tear down client sketch',
'remove sketch grid',
'engineToClient cam sync direction',
'Reset Segment Overlays',
'enable copilot',
],
entry: ['add axis n grid', 'clientToEngine cam sync direction'], entry: ['add axis n grid', 'clientToEngine cam sync direction'],
}, },