Files
modeling-app/src/machines/featureTreeMachine.ts
Andrew Varga 479179dd9b #6734 Clean up unused code (#6736)
* remove unused code in modelingMachine

* remove unused actions in featureTreeMachine

* video.pause is not async

* remove unused param in Toolbar

* remove unused rectangleOrigin from getRectangleCallExpressions

* fmt

* parseProjectRoute is not async anymore

* prefix unused params with underscore

* insertNewStartProfileAt/sketchEntryNodePath param is not used

* remove unused constraintType parameter from getRemoveConstraintsTransform

* underscore unused params

* remove unused scale param in segment.ts

* remove unused for in sceneInfra

* remove unused sketchEntryNodePath from sceneEntitiesManager methods

* remove unused shouldTearDown param

* remove unused planeNodePath param from setup draft methods

* remove unused ast param
2025-05-08 06:58:30 -04:00

703 lines
20 KiB
TypeScript

import toast from 'react-hot-toast'
import { assign, fromPromise, setup } from 'xstate'
import type { Operation } from '@rust/kcl-lib/bindings/Operation'
import {
deleteSelectionPromise,
deletionErrorMessage,
} from '@src/lang/modifyAst/deleteSelection'
import { getNodePathFromSourceRange } from '@src/lang/queryAstNodePathUtils'
import type { Artifact } from '@src/lang/std/artifactGraph'
import { getArtifactFromRange } from '@src/lang/std/artifactGraph'
import type { SourceRange } from '@src/lang/wasm'
import type { EnterEditFlowProps } from '@src/lib/operations'
import {
enterAppearanceFlow,
enterCloneFlow,
enterEditFlow,
enterTranslateFlow,
enterRotateFlow,
} from '@src/lib/operations'
import { kclManager } from '@src/lib/singletons'
import { err } from '@src/lib/trap'
import { commandBarActor } from '@src/lib/singletons'
type FeatureTreeEvent =
| {
type: 'goToKclSource'
data: { targetSourceRange: SourceRange }
}
| {
type: 'selectOperation'
data: { targetSourceRange: SourceRange }
}
| {
type: 'deleteOperation'
data: { targetSourceRange: SourceRange }
}
| {
type: 'enterEditFlow'
data: { targetSourceRange: SourceRange; currentOperation: Operation }
}
| {
type: 'enterAppearanceFlow'
data: { targetSourceRange: SourceRange; currentOperation: Operation }
}
| {
type: 'enterTranslateFlow'
data: { targetSourceRange: SourceRange; currentOperation: Operation }
}
| {
type: 'enterRotateFlow'
data: { targetSourceRange: SourceRange; currentOperation: Operation }
}
| {
type: 'enterCloneFlow'
data: { targetSourceRange: SourceRange; currentOperation: Operation }
}
| { type: 'goToError' }
| { type: 'codePaneOpened' }
| { type: 'selected' }
| { type: 'done' }
| { type: 'xstate.error.actor.prepareEditCommand'; error: Error }
| { type: 'xstate.error.actor.prepareDeleteCommand'; error: Error }
type FeatureTreeContext = {
targetSourceRange?: SourceRange
currentOperation?: Operation
}
export const featureTreeMachineDefaultContext: FeatureTreeContext = {}
export const featureTreeMachine = setup({
types: {
input: {} as FeatureTreeContext,
context: {} as FeatureTreeContext,
events: {} as FeatureTreeEvent,
},
guards: {
codePaneIsOpen: () => false,
},
actors: {
prepareEditCommand: fromPromise(
({
input,
}: {
input: EnterEditFlowProps & {
commandBarSend: (typeof commandBarActor)['send']
}
}) => {
return new Promise((resolve, reject) => {
const { commandBarSend, ...editFlowProps } = input
enterEditFlow(editFlowProps)
.then((result) => {
if (err(result)) {
reject(result)
return
}
input.commandBarSend(result)
resolve(result)
})
.catch(reject)
})
}
),
prepareAppearanceCommand: fromPromise(
({
input,
}: {
input: EnterEditFlowProps & {
commandBarSend: (typeof commandBarActor)['send']
}
}) => {
return new Promise((resolve, reject) => {
const { commandBarSend, ...editFlowProps } = input
enterAppearanceFlow(editFlowProps)
.then((result) => {
if (err(result)) {
reject(result)
return
}
input.commandBarSend(result)
resolve(result)
})
.catch(reject)
})
}
),
prepareTranslateCommand: fromPromise(
({
input,
}: {
input: EnterEditFlowProps & {
commandBarSend: (typeof commandBarActor)['send']
}
}) => {
return new Promise((resolve, reject) => {
const { commandBarSend, ...editFlowProps } = input
enterTranslateFlow(editFlowProps)
.then((result) => {
if (err(result)) {
reject(result)
return
}
input.commandBarSend(result)
resolve(result)
})
.catch(reject)
})
}
),
prepareRotateCommand: fromPromise(
({
input,
}: {
input: EnterEditFlowProps & {
commandBarSend: (typeof commandBarActor)['send']
}
}) => {
return new Promise((resolve, reject) => {
const { commandBarSend, ...editFlowProps } = input
enterRotateFlow(editFlowProps)
.then((result) => {
if (err(result)) {
reject(result)
return
}
input.commandBarSend(result)
resolve(result)
})
.catch(reject)
})
}
),
prepareCloneCommand: fromPromise(
({
input,
}: {
input: EnterEditFlowProps & {
commandBarSend: (typeof commandBarActor)['send']
}
}) => {
return new Promise((resolve, reject) => {
const { commandBarSend, ...editFlowProps } = input
enterCloneFlow(editFlowProps)
.then((result) => {
if (err(result)) {
reject(result)
return
}
input.commandBarSend(result)
resolve(result)
})
.catch(reject)
})
}
),
sendDeleteCommand: fromPromise(
({
input,
}: {
input: {
artifact: Artifact | undefined
targetSourceRange: SourceRange | undefined
}
}) => {
return new Promise((resolve, reject) => {
const { targetSourceRange, artifact } = input
if (!targetSourceRange) {
reject(new Error(deletionErrorMessage))
return
}
const pathToNode = getNodePathFromSourceRange(
kclManager.ast,
targetSourceRange
)
const selection = {
codeRef: {
range: targetSourceRange,
pathToNode,
},
artifact,
}
deleteSelectionPromise(selection)
.then((result) => {
if (err(result)) {
reject(result)
return
}
resolve(result)
})
.catch(reject)
})
}
),
},
actions: {
saveTargetSourceRange: assign({
targetSourceRange: ({ event }) =>
'data' in event && !err(event.data)
? event.data.targetSourceRange
: undefined,
}),
saveCurrentOperation: assign({
currentOperation: ({ event }) =>
'data' in event && 'currentOperation' in event.data
? event.data.currentOperation
: undefined,
}),
clearContext: assign({
targetSourceRange: undefined,
}),
sendSelectionEvent: () => {},
openCodePane: () => {},
scrollToError: () => {},
},
}).createMachine({
/** @xstate-layout N4IgpgJg5mDOIC5QDMwEMAuBXATmAKnmAHQCWEANmAMRQD2+dA0gMYUDKduLYA2gAwBdRKAAOdWKQyk6AOxEgAHogCMAFhXF+27QDYArAGZ9AJhWGAHIYA0IAJ6q1ATkPELFkxf1qL-NQHYLNX0AXxDbVExcAiIyShpYMCoWDAB5UTAcTBlZAWEkEHFJaTkFZQQVJ00dPSNTcytbBwRnXWJ-fR0Op3cXJzUwiPRsPEIwEnIqajBZDEyAUQgpADEKOgB3PIUiqRyy1V1qmoNjM0sbe1V1TSd-XV1+XUM-EyeB8JBIkZjxuKmIJJgObpTLZORbAo7EryArlFQWI46E71c5NRD+fxqdqdfivDQmfx+QyDT7DaJjCbxWgMOjzHA4Og4CFiCS7UqwxBmRG1U4NC7NFSBYgmHH8O5VfgeR4kr7k2L0UiyKCMVgcLg4HjERLJaRK6jasApSDMwqs6H7Cr8IzEfQqby6NS4-ROK1ONEVfT6Npefr8FTmZyGXQysmjeV0RXK5hsTjcEgQOQ0E1QvYciqGbmPOpnCy6d0qe5OYjGFyBEVVExOENRMO-BVKlUx9WaugZWSRgDCdABAAU0LIaCxu2A+wOQQOIMmzanQOVDiZiA7XfwXK8MxZ857vc7Hf7DIHgx9ZbWSAaUpGtYDz3qz3NJ0JttP2bPELoLEWESK1CYTK6TBn3U9It-FeT1DCcSs-FMatvgpS8dQvBMB2oKdihnJRVH8TNkRzPNLgQJwnH0YhBScAxBQsEC7mJI9Qx+EgZjmHBI0WFY1nWeDDV1KB9SvO9ULZGEXwQIwF0MTwVDxPEN3wyTnkXL1nl0SsnmMUJaJrejiEYzIWKWDBVg2YgkKTB9ISfISMI9bDswaPCBUMDpF3En13CscSqw02DYh05ilVYgz2OIUQ8FENA8ACrsAFsov7CBqBMshZAANzoABrEhjy03y9LYoyQrAMKIv06LYtkCAEEVVKWDBXIhAE800wg1wvRFZ1XjI55-HzTwLBIlzCStbxKxooZNLgnL-P0wyOIKoqwEiugYri6Z6UZYKKEwZBGSi4gsom2ZdKmvLZtC8KFpKpayoqqq6Bq6E8ga9Dymam1lKAjqnjFfNKmI0j-EMSwfwxMUYLlX4ASobiQSyaFOOvHjb2NMyWTQ58rM9MSJO0NQHXcNR+UQLx+GFfgMw0XHcatFQwZPYzAWhjJYZyYzExQlHTTRyzyl-GzeVRfDHL65xCNXf0TGCfdaa0yGgUjGHavpqHI3YPicgSxMktSjK9rouDZcZ0E4YNlW1bkSqUru2rHo5lN0Ze79SeeXxnluKp9HdF1-EXfwyMOIiCZAw8xu8iGGflpnFZNpVVYQuRVoZHANq2nbdfG2Jo6gBXjfDmOzdkC3qut+rbYsi1+gXX9nbJldfbtd0ESxIP9AxB1nCtd4Q-Bkh6yjOlE+IVsZk7YdR0HUf+zAcdkfyVHBItZ02lFN9yy6kx3TMPrRXMAlAc6dSu7p3vGH79aTPZ2fOfntMuS0Y5bIF5pAczQHAn3Aswg+WRh3gAp9qIR8XMLQAFp7KIGARLaWcFJhgEAdfYS358xkXaFRDMmJ-TKXuFA8MkZGxqjjHAxqwl9yuAeC4ZS-QAZmAdPmNQuNiw7kopUbQv1sF1gjA2aM+CNSnjVkqQhz0iZWmLJJCmLdTCNFkkYVwPoCaVB6PoBEwdSTp3YbgrhsYeGswHAI+2VxfZaGUnvDEPRvpSMBm4HcGZCJeCUWwnuHCoyqk0S2NsI9eyT10dzRA4FNBkKDJWAI4kCxqE3BY2R1iFF2K8t3eG3EvEWnAqQlclh3ySiqADbq+E6FFh-AYZSK4XAaH8PYuJF5byRgSWmOhfiUm9HSYKRygEgztFAooqwKgVzQRiXTCpSptGwPMkAtMCJiIlg0OBZ0HhEH4SMG0Zu7SMxdJMKUyaUAAozSqcQp4i46lpPfI0rJAoyZYl0L7B0BZeYWFWYdPy6zppBT6VALZVkNC1JcPUg5mSeoZhIopR4Kk94HxUaHBitzcqBSMiZF5cICze03i7PwZFtDr1kj+LEdoKEQTuD4EpPTsrguOpC06hVzqLWWuVGFr4W5uExDY+EZMEQqB6mKBSykvD-jOZ5Q+Mtc5Z0jkQq+gryjnF2eQwJVCQnugMFiPJnpCTqCsN+Upmds4syeVSlovz-EUKCdQ0J+FibtDJv6T0NcVydxBbE1VAqWbQqGfAqy-ogjEBFkwlcdpBQGuaEEVwhIMyCnMISV4NN8X6z5WquQSs5Z5zjpZO23iEBnO9pRZwIFN6BG8A3AkTtJJ+CtK6S1-81GcNPjgTV4lva40ou4YwkohretUJKO+OggjsucKGnlcFj60jWknIe7YlRdg8Toh1wqfFOWrYEKwnRfCekbRUIiNoai2n6A8EU1yw04NLX2gZmrAhFkDQcl0v0ghIOIqKama6nSbrCEAA */
id: 'featureTree',
description: 'Workflows for interacting with the feature tree pane',
context: ({ input }) => input,
states: {
idle: {
on: {
goToKclSource: {
target: 'goingToKclSource',
actions: 'saveTargetSourceRange',
},
selectOperation: {
target: 'selecting',
actions: 'saveTargetSourceRange',
},
enterEditFlow: {
target: 'enteringEditFlow',
actions: ['saveTargetSourceRange', 'saveCurrentOperation'],
},
enterAppearanceFlow: {
target: 'enteringAppearanceFlow',
actions: ['saveTargetSourceRange', 'saveCurrentOperation'],
},
enterTranslateFlow: {
target: 'enteringTranslateFlow',
actions: ['saveTargetSourceRange', 'saveCurrentOperation'],
},
enterRotateFlow: {
target: 'enteringRotateFlow',
actions: ['saveTargetSourceRange', 'saveCurrentOperation'],
},
enterCloneFlow: {
target: 'enteringCloneFlow',
actions: ['saveTargetSourceRange', 'saveCurrentOperation'],
},
deleteOperation: {
target: 'deletingOperation',
actions: ['saveTargetSourceRange'],
},
goToError: 'goingToError',
},
},
goingToKclSource: {
states: {
selecting: {
on: {
selected: {
target: 'done',
},
},
entry: ['sendSelectionEvent'],
},
done: {
entry: ['clearContext'],
always: '#featureTree.idle',
},
openingCodePane: {
on: {
codePaneOpened: 'selecting',
},
entry: 'openCodePane',
},
},
initial: 'openingCodePane',
},
selecting: {
states: {
selecting: {
on: {
selected: 'done',
},
entry: 'sendSelectionEvent',
},
done: {
always: '#featureTree.idle',
entry: 'clearContext',
},
},
initial: 'selecting',
},
enteringEditFlow: {
states: {
selecting: {
on: {
selected: {
target: 'prepareEditCommand',
reenter: true,
},
},
},
done: {
always: '#featureTree.idle',
},
prepareEditCommand: {
invoke: {
src: 'prepareEditCommand',
input: ({ context }) => {
const artifact = context.targetSourceRange
? (getArtifactFromRange(
context.targetSourceRange,
kclManager.artifactGraph
) ?? undefined)
: undefined
return {
// currentOperation is guaranteed to be defined here
operation: context.currentOperation!,
artifact,
commandBarSend: commandBarActor.send,
}
},
onDone: {
target: 'done',
reenter: true,
},
onError: {
target: 'done',
reenter: true,
actions: ({ event }) => {
if ('error' in event && err(event.error)) {
toast.error(event.error.message)
}
},
},
},
},
},
initial: 'selecting',
entry: 'sendSelectionEvent',
exit: ['clearContext'],
},
enteringAppearanceFlow: {
states: {
selecting: {
on: {
selected: {
target: 'prepareAppearanceCommand',
reenter: true,
},
},
},
done: {
always: '#featureTree.idle',
},
prepareAppearanceCommand: {
invoke: {
src: 'prepareAppearanceCommand',
input: ({ context }) => {
const artifact = context.targetSourceRange
? (getArtifactFromRange(
context.targetSourceRange,
kclManager.artifactGraph
) ?? undefined)
: undefined
return {
// currentOperation is guaranteed to be defined here
operation: context.currentOperation!,
artifact,
commandBarSend: commandBarActor.send,
}
},
onDone: {
target: 'done',
reenter: true,
},
onError: {
target: 'done',
reenter: true,
actions: ({ event }) => {
if ('error' in event && err(event.error)) {
toast.error(event.error.message)
}
},
},
},
},
},
initial: 'selecting',
entry: 'sendSelectionEvent',
exit: ['clearContext'],
},
enteringTranslateFlow: {
states: {
selecting: {
on: {
selected: {
target: 'prepareTranslateCommand',
reenter: true,
},
},
},
done: {
always: '#featureTree.idle',
},
prepareTranslateCommand: {
invoke: {
src: 'prepareTranslateCommand',
input: ({ context }) => {
const artifact = context.targetSourceRange
? (getArtifactFromRange(
context.targetSourceRange,
kclManager.artifactGraph
) ?? undefined)
: undefined
return {
// currentOperation is guaranteed to be defined here
operation: context.currentOperation!,
artifact,
commandBarSend: commandBarActor.send,
}
},
onDone: {
target: 'done',
reenter: true,
},
onError: {
target: 'done',
reenter: true,
actions: ({ event }) => {
if ('error' in event && err(event.error)) {
toast.error(event.error.message)
}
},
},
},
},
},
initial: 'selecting',
entry: 'sendSelectionEvent',
exit: ['clearContext'],
},
enteringRotateFlow: {
states: {
selecting: {
on: {
selected: {
target: 'prepareRotateCommand',
reenter: true,
},
},
},
done: {
always: '#featureTree.idle',
},
prepareRotateCommand: {
invoke: {
src: 'prepareRotateCommand',
input: ({ context }) => {
const artifact = context.targetSourceRange
? (getArtifactFromRange(
context.targetSourceRange,
kclManager.artifactGraph
) ?? undefined)
: undefined
return {
// currentOperation is guaranteed to be defined here
operation: context.currentOperation!,
artifact,
commandBarSend: commandBarActor.send,
}
},
onDone: {
target: 'done',
reenter: true,
},
onError: {
target: 'done',
reenter: true,
actions: ({ event }) => {
if ('error' in event && err(event.error)) {
toast.error(event.error.message)
}
},
},
},
},
},
initial: 'selecting',
entry: 'sendSelectionEvent',
exit: ['clearContext'],
},
enteringCloneFlow: {
states: {
selecting: {
on: {
selected: {
target: 'prepareCloneCommand',
reenter: true,
},
},
},
done: {
always: '#featureTree.idle',
},
prepareCloneCommand: {
invoke: {
src: 'prepareCloneCommand',
input: ({ context }) => {
const artifact = context.targetSourceRange
? (getArtifactFromRange(
context.targetSourceRange,
kclManager.artifactGraph
) ?? undefined)
: undefined
return {
// currentOperation is guaranteed to be defined here
operation: context.currentOperation!,
artifact,
commandBarSend: commandBarActor.send,
}
},
onDone: {
target: 'done',
reenter: true,
},
onError: {
target: 'done',
reenter: true,
actions: ({ event }) => {
if ('error' in event && err(event.error)) {
toast.error(event.error.message)
}
},
},
},
},
},
initial: 'selecting',
entry: 'sendSelectionEvent',
exit: ['clearContext'],
},
deletingOperation: {
states: {
selecting: {
on: {
selected: {
target: 'deletingSelection',
reenter: true,
},
},
},
done: {
always: '#featureTree.idle',
},
deletingSelection: {
invoke: {
src: 'sendDeleteCommand',
input: ({ context }) => {
const artifact = context.targetSourceRange
? (getArtifactFromRange(
context.targetSourceRange,
kclManager.artifactGraph
) ?? undefined)
: undefined
return {
artifact,
targetSourceRange: context.targetSourceRange,
}
},
onDone: {
target: 'done',
reenter: true,
},
onError: {
target: 'done',
reenter: true,
actions: ({ event }) => {
if ('error' in event && err(event.error)) {
toast.error(event.error.message)
}
},
},
},
},
},
initial: 'selecting',
entry: 'sendSelectionEvent',
exit: ['clearContext'],
},
goingToError: {
states: {
openingCodePane: {
entry: 'openCodePane',
on: {
codePaneOpened: 'done',
},
},
done: {
entry: 'scrollToError',
always: '#featureTree.idle',
},
},
initial: 'openingCodePane',
},
},
initial: 'idle',
})