Big time clean up and bringing clone on for the ride

This commit is contained in:
Pierre Jacquier
2025-07-04 08:51:56 -04:00
parent 65168eb139
commit c3c8b727bd
8 changed files with 274 additions and 677 deletions

View File

@ -64,13 +64,28 @@ export const FeatureTreePane = () => {
scrollToError: () => {
editorManager.scrollToFirstErrorDiagnosticIfExists()
},
sendTranslateCommand: ({ context }) => {
sendTranslateCommand: () => {
commandBarActor.send({
type: 'Find and select command',
data: {
name: 'Translate',
groupId: 'modeling',
},
data: { name: 'Translate', groupId: 'modeling' },
})
},
sendRotateCommand: () => {
commandBarActor.send({
type: 'Find and select command',
data: { name: 'Rotate', groupId: 'modeling' },
})
},
sendScaleCommand: () => {
commandBarActor.send({
type: 'Find and select command',
data: { name: 'Scale', groupId: 'modeling' },
})
},
sendCloneCommand: () => {
commandBarActor.send({
type: 'Find and select command',
data: { name: 'Clone', groupId: 'modeling' },
})
},
sendSelectionEvent: ({ context }) => {
@ -537,7 +552,7 @@ const OperationItem = (props: {
!stdLibMap[props.item.name]?.supportsTransform
}
>
Set translate
Translate
</ContextMenuItem>,
<ContextMenuItem
onClick={enterRotateFlow}
@ -547,7 +562,7 @@ const OperationItem = (props: {
!stdLibMap[props.item.name]?.supportsTransform
}
>
Set rotate
Rotate
</ContextMenuItem>,
<ContextMenuItem
onClick={enterScaleFlow}
@ -557,7 +572,7 @@ const OperationItem = (props: {
!stdLibMap[props.item.name]?.supportsTransform
}
>
Set scale
Scale
</ContextMenuItem>,
<ContextMenuItem
onClick={enterCloneFlow}

View File

@ -655,39 +655,6 @@ export function addHelix({
}
}
/**
* Add clone statement
*/
export function addClone({
ast,
geometryName,
variableName,
}: {
ast: Node<Program>
geometryName: string
variableName: string
}): { modifiedAst: Node<Program>; pathToNode: PathToNode } {
const modifiedAst = structuredClone(ast)
const variable = createVariableDeclaration(
variableName,
createCallExpressionStdLibKw('clone', createLocalName(geometryName), [])
)
modifiedAst.body.push(variable)
const insertAt = modifiedAst.body.length - 1
const pathToNode: PathToNode = [
['body', ''],
[insertAt, 'index'],
['declaration', 'VariableDeclaration'],
['init', 'VariableDeclarator'],
]
return {
modifiedAst,
pathToNode,
}
}
/**
* Return a modified clone of an AST with a named constant inserted into the body
*/
@ -1252,7 +1219,8 @@ export function setCallInAst(
ast: Node<Program>,
call: Node<CallExpressionKw>,
nodeToEdit?: PathToNode,
lastPathToNode?: PathToNode
lastPathToNode?: PathToNode,
toFirstKwarg?: boolean
): Error | PathToNode {
let pathToNode: PathToNode | undefined
if (nodeToEdit) {
@ -1283,7 +1251,7 @@ export function setCallInAst(
const name = findUniqueName(ast, call.callee.name.name)
const declaration = createVariableDeclaration(name, call)
ast.body.push(declaration)
pathToNode = createPathToNodeForLastVariable(ast)
pathToNode = createPathToNodeForLastVariable(ast, toFirstKwarg)
}
}

View File

@ -11,10 +11,6 @@ import {
} from '@src/lang/queryAst'
import type { ArtifactGraph, PathToNode, Program } from '@src/lang/wasm'
import { err } from '@src/lib/trap'
import {
findAllChildrenAndOrderByPlaceInCode,
getLastVariable,
} from '@src/lang/modifyAst/boolean'
import type { Selections } from '@src/lib/selections'
import type { KclCommandValue } from '@src/lib/commandTypes'
import {
@ -208,33 +204,53 @@ export function addScale({
})
}
export function retrievePathToNodeFromTransformSelection(
selection: Selections,
artifactGraph: ArtifactGraph,
export function addClone({
ast,
artifactGraph,
objects,
nodeToEdit,
}: {
ast: Node<Program>
): PathToNode | Error {
const error = new Error(
"Couldn't retrieve selection. If you're trying to transform an import, use the feature tree."
)
const hasPathToNode = !!selection.graphSelections[0].codeRef.pathToNode.length
const artifact = selection.graphSelections[0].artifact
let pathToNode: PathToNode | undefined
if (hasPathToNode && artifact) {
const children = findAllChildrenAndOrderByPlaceInCode(
artifact,
artifactGraph
)
const variable = getLastVariable(children, ast)
if (!variable) {
return error
}
artifactGraph: ArtifactGraph
objects: Selections
nodeToEdit?: PathToNode
}): Error | { modifiedAst: Node<Program>; pathToNode: PathToNode } {
// 1. Clone the ast so we can edit it
const modifiedAst = structuredClone(ast)
pathToNode = variable.pathToNode
} else if (hasPathToNode) {
pathToNode = selection.graphSelections[0].codeRef.pathToNode
} else {
return error
// 2. Prepare unlabeled arguments
// Map the sketches selection into a list of kcl expressions to be passed as unlabelled argument
const variableExpressions = getVariableExprsFromSelection(
objects,
modifiedAst,
nodeToEdit,
true,
artifactGraph
)
if (err(variableExpressions)) {
return variableExpressions
}
return pathToNode
const objectsExpr = createVariableExpressionsArray(variableExpressions.exprs)
const call = createCallExpressionStdLibKw('clone', objectsExpr, [])
// 3. If edit, we assign the new function call declaration to the existing node,
// otherwise just push to the end
const lastPath = variableExpressions.paths.pop() // TODO: check if this is correct
const toFirstKwarg = false
const pathToNode = setCallInAst(
modifiedAst,
call,
nodeToEdit,
lastPath,
toFirstKwarg
)
if (err(pathToNode)) {
return pathToNode
}
return {
modifiedAst,
pathToNode,
}
}

View File

@ -1,7 +1,6 @@
import type { Models } from '@kittycad/lib'
import { angleLengthInfo } from '@src/components/Toolbar/angleLengthInfo'
import { findUniqueName } from '@src/lang/create'
import { getNodeFromPath } from '@src/lang/queryAst'
import { getVariableDeclaration } from '@src/lang/queryAst/getVariableDeclaration'
import { getNodePathFromSourceRange } from '@src/lang/queryAstNodePathUtils'
@ -19,7 +18,6 @@ import type {
} from '@src/lib/commandTypes'
import {
IS_ML_EXPERIMENTAL,
KCL_DEFAULT_CONSTANT_PREFIXES,
KCL_DEFAULT_DEGREE,
KCL_DEFAULT_LENGTH,
KCL_DEFAULT_TRANSFORM,
@ -210,8 +208,7 @@ export type ModelingCommandSchema = {
}
Clone: {
nodeToEdit?: PathToNode
selection: Selections
variableName: string
objects: Selections
}
'Boolean Subtract': {
solids: Selections
@ -1211,39 +1208,14 @@ export const modelingMachineCommandConfig: StateMachineCommandSetConfig<
nodeToEdit: {
...nodeToEditProps,
},
selection: {
objects: {
// selectionMixed allows for feature tree selection of module imports
inputType: 'selectionMixed',
multiple: false,
required: true,
skip: true,
selectionTypes: ['path'],
selectionTypes: ['path', 'sweep'],
selectionFilter: ['object'],
hidden: (context) => Boolean(context.argumentsToSubmit.nodeToEdit),
},
variableName: {
inputType: 'string',
multiple: true,
required: true,
defaultValue: () => {
return findUniqueName(
kclManager.ast,
KCL_DEFAULT_CONSTANT_PREFIXES.CLONE
)
},
validation: async ({
data,
}: {
data: string
}) => {
// Be conservative and error out if there is an item or module with the same name.
const variableExists =
kclManager.variables[data] || kclManager.variables['__mod_' + data]
if (variableExists) {
return 'This variable name is already in use.'
}
return true
},
hidden: (context) => Boolean(context.argumentsToSubmit.nodeToEdit),
},
},
},

View File

@ -1,11 +1,6 @@
import { executeAstMock } from '@src/lang/langHelpers'
import {
type CallExpressionKw,
formatNumberValue,
parse,
resultIsOk,
} from '@src/lang/wasm'
import type { KclCommandValue, KclExpression } from '@src/lib/commandTypes'
import { formatNumberValue, parse, resultIsOk } from '@src/lang/wasm'
import type { KclExpression } from '@src/lib/commandTypes'
import { rustContext } from '@src/lib/singletons'
import { err } from '@src/lib/trap'

View File

@ -1525,11 +1525,10 @@ async function prepareToEditTranslate({ operation }: EnterEditFlowProps) {
name: 'Translate',
groupId: 'modeling',
}
const isModuleImport = operation.type === 'GroupBegin'
const isSupportedStdLibCall =
operation.type === 'StdLibCall' &&
stdLibMap[operation.name]?.supportsTransform
if (!isModuleImport && !isSupportedStdLibCall) {
if (!isSupportedStdLibCall) {
return {
reason: 'Unsupported operation type. Please edit in the code editor.',
}
@ -1549,53 +1548,51 @@ async function prepareToEditTranslate({ operation }: EnterEditFlowProps) {
let y: KclCommandValue | undefined = undefined
let z: KclCommandValue | undefined = undefined
let global: boolean | undefined
if (isSupportedStdLibCall) {
if (operation.labeledArgs.x) {
const result = await stringToKclExpression(
codeManager.code.slice(
operation.labeledArgs.x.sourceRange[0],
operation.labeledArgs.x.sourceRange[1]
)
if (operation.labeledArgs.x) {
const result = await stringToKclExpression(
codeManager.code.slice(
operation.labeledArgs.x.sourceRange[0],
operation.labeledArgs.x.sourceRange[1]
)
if (err(result) || 'errors' in result) {
return { reason: "Couldn't retrieve x argument" }
}
x = result
)
if (err(result) || 'errors' in result) {
return { reason: "Couldn't retrieve x argument" }
}
x = result
}
if (operation.labeledArgs.y) {
const result = await stringToKclExpression(
codeManager.code.slice(
operation.labeledArgs.y.sourceRange[0],
operation.labeledArgs.y.sourceRange[1]
)
if (operation.labeledArgs.y) {
const result = await stringToKclExpression(
codeManager.code.slice(
operation.labeledArgs.y.sourceRange[0],
operation.labeledArgs.y.sourceRange[1]
)
if (err(result) || 'errors' in result) {
return { reason: "Couldn't retrieve y argument" }
}
y = result
)
if (err(result) || 'errors' in result) {
return { reason: "Couldn't retrieve y argument" }
}
y = result
}
if (operation.labeledArgs.z) {
const result = await stringToKclExpression(
codeManager.code.slice(
operation.labeledArgs.z.sourceRange[0],
operation.labeledArgs.z.sourceRange[1]
)
if (operation.labeledArgs.z) {
const result = await stringToKclExpression(
codeManager.code.slice(
operation.labeledArgs.z.sourceRange[0],
operation.labeledArgs.z.sourceRange[1]
)
if (err(result) || 'errors' in result) {
return { reason: "Couldn't retrieve z argument" }
}
z = result
)
if (err(result) || 'errors' in result) {
return { reason: "Couldn't retrieve z argument" }
}
z = result
}
if (operation.labeledArgs.global) {
global =
codeManager.code.slice(
operation.labeledArgs.global.sourceRange[0],
operation.labeledArgs.global.sourceRange[1]
) === 'true'
}
if (operation.labeledArgs.global) {
global =
codeManager.code.slice(
operation.labeledArgs.global.sourceRange[0],
operation.labeledArgs.global.sourceRange[1]
) === 'true'
}
// 3. Assemble the default argument values for the command,
@ -1615,30 +1612,15 @@ async function prepareToEditTranslate({ operation }: EnterEditFlowProps) {
}
}
export async function enterTranslateFlow({
operation,
}: EnterEditFlowProps): Promise<Error | CommandBarMachineEvent> {
const data = await prepareToEditTranslate({ operation })
if ('reason' in data) {
return new Error(data.reason)
}
return {
type: 'Find and select command',
data,
}
}
async function prepareToEditScale({ operation }: EnterEditFlowProps) {
const baseCommand = {
name: 'Scale',
groupId: 'modeling',
}
const isModuleImport = operation.type === 'GroupBegin'
const isSupportedStdLibCall =
operation.type === 'StdLibCall' &&
stdLibMap[operation.name]?.supportsTransform
if (!isModuleImport && !isSupportedStdLibCall) {
if (!isSupportedStdLibCall) {
return {
reason: 'Unsupported operation type. Please edit in the code editor.',
}
@ -1658,53 +1640,51 @@ async function prepareToEditScale({ operation }: EnterEditFlowProps) {
let y: KclCommandValue | undefined = undefined
let z: KclCommandValue | undefined = undefined
let global: boolean | undefined
if (isSupportedStdLibCall) {
if (operation.labeledArgs.x) {
const result = await stringToKclExpression(
codeManager.code.slice(
operation.labeledArgs.x.sourceRange[0],
operation.labeledArgs.x.sourceRange[1]
)
if (operation.labeledArgs.x) {
const result = await stringToKclExpression(
codeManager.code.slice(
operation.labeledArgs.x.sourceRange[0],
operation.labeledArgs.x.sourceRange[1]
)
if (err(result) || 'errors' in result) {
return { reason: "Couldn't retrieve x argument" }
}
x = result
)
if (err(result) || 'errors' in result) {
return { reason: "Couldn't retrieve x argument" }
}
x = result
}
if (operation.labeledArgs.y) {
const result = await stringToKclExpression(
codeManager.code.slice(
operation.labeledArgs.y.sourceRange[0],
operation.labeledArgs.y.sourceRange[1]
)
if (operation.labeledArgs.y) {
const result = await stringToKclExpression(
codeManager.code.slice(
operation.labeledArgs.y.sourceRange[0],
operation.labeledArgs.y.sourceRange[1]
)
if (err(result) || 'errors' in result) {
return { reason: "Couldn't retrieve y argument" }
}
y = result
)
if (err(result) || 'errors' in result) {
return { reason: "Couldn't retrieve y argument" }
}
y = result
}
if (operation.labeledArgs.z) {
const result = await stringToKclExpression(
codeManager.code.slice(
operation.labeledArgs.z.sourceRange[0],
operation.labeledArgs.z.sourceRange[1]
)
if (operation.labeledArgs.z) {
const result = await stringToKclExpression(
codeManager.code.slice(
operation.labeledArgs.z.sourceRange[0],
operation.labeledArgs.z.sourceRange[1]
)
if (err(result) || 'errors' in result) {
return { reason: "Couldn't retrieve z argument" }
}
z = result
)
if (err(result) || 'errors' in result) {
return { reason: "Couldn't retrieve z argument" }
}
z = result
}
if (operation.labeledArgs.global) {
global =
codeManager.code.slice(
operation.labeledArgs.global.sourceRange[0],
operation.labeledArgs.global.sourceRange[1]
) === 'true'
}
if (operation.labeledArgs.global) {
global =
codeManager.code.slice(
operation.labeledArgs.global.sourceRange[0],
operation.labeledArgs.global.sourceRange[1]
) === 'true'
}
// 3. Assemble the default argument values for the command,
@ -1724,30 +1704,15 @@ async function prepareToEditScale({ operation }: EnterEditFlowProps) {
}
}
export async function enterScaleFlow({
operation,
}: EnterEditFlowProps): Promise<Error | CommandBarMachineEvent> {
const data = await prepareToEditScale({ operation })
if ('reason' in data) {
return new Error(data.reason)
}
return {
type: 'Find and select command',
data,
}
}
async function prepareToEditRotate({ operation }: EnterEditFlowProps) {
const baseCommand = {
name: 'Rotate',
groupId: 'modeling',
}
const isModuleImport = operation.type === 'GroupBegin'
const isSupportedStdLibCall =
operation.type === 'StdLibCall' &&
stdLibMap[operation.name]?.supportsTransform
if (!isModuleImport && !isSupportedStdLibCall) {
if (!isSupportedStdLibCall) {
return {
reason: 'Unsupported operation type. Please edit in the code editor.',
}
@ -1767,53 +1732,51 @@ async function prepareToEditRotate({ operation }: EnterEditFlowProps) {
let pitch: KclCommandValue | undefined = undefined
let yaw: KclCommandValue | undefined = undefined
let global: boolean | undefined
if (isSupportedStdLibCall) {
if (operation.labeledArgs.roll) {
const result = await stringToKclExpression(
codeManager.code.slice(
operation.labeledArgs.roll.sourceRange[0],
operation.labeledArgs.roll.sourceRange[1]
)
if (operation.labeledArgs.roll) {
const result = await stringToKclExpression(
codeManager.code.slice(
operation.labeledArgs.roll.sourceRange[0],
operation.labeledArgs.roll.sourceRange[1]
)
if (err(result) || 'errors' in result) {
return { reason: "Couldn't retrieve roll argument" }
}
roll = result
)
if (err(result) || 'errors' in result) {
return { reason: "Couldn't retrieve roll argument" }
}
roll = result
}
if (operation.labeledArgs.pitch) {
const result = await stringToKclExpression(
codeManager.code.slice(
operation.labeledArgs.pitch.sourceRange[0],
operation.labeledArgs.pitch.sourceRange[1]
)
if (operation.labeledArgs.pitch) {
const result = await stringToKclExpression(
codeManager.code.slice(
operation.labeledArgs.pitch.sourceRange[0],
operation.labeledArgs.pitch.sourceRange[1]
)
if (err(result) || 'errors' in result) {
return { reason: "Couldn't retrieve pitch argument" }
}
pitch = result
)
if (err(result) || 'errors' in result) {
return { reason: "Couldn't retrieve pitch argument" }
}
pitch = result
}
if (operation.labeledArgs.yaw) {
const result = await stringToKclExpression(
codeManager.code.slice(
operation.labeledArgs.yaw.sourceRange[0],
operation.labeledArgs.yaw.sourceRange[1]
)
if (operation.labeledArgs.yaw) {
const result = await stringToKclExpression(
codeManager.code.slice(
operation.labeledArgs.yaw.sourceRange[0],
operation.labeledArgs.yaw.sourceRange[1]
)
if (err(result) || 'errors' in result) {
return { reason: "Couldn't retrieve yaw argument" }
}
yaw = result
)
if (err(result) || 'errors' in result) {
return { reason: "Couldn't retrieve yaw argument" }
}
yaw = result
}
if (operation.labeledArgs.global) {
global =
codeManager.code.slice(
operation.labeledArgs.global.sourceRange[0],
operation.labeledArgs.global.sourceRange[1]
) === 'true'
}
if (operation.labeledArgs.global) {
global =
codeManager.code.slice(
operation.labeledArgs.global.sourceRange[0],
operation.labeledArgs.global.sourceRange[1]
) === 'true'
}
// 3. Assemble the default argument values for the command,
@ -1832,45 +1795,3 @@ async function prepareToEditRotate({ operation }: EnterEditFlowProps) {
argDefaultValues,
}
}
export async function enterRotateFlow({
operation,
}: EnterEditFlowProps): Promise<Error | CommandBarMachineEvent> {
const data = await prepareToEditRotate({ operation })
if ('reason' in data) {
return new Error(data.reason)
}
return {
type: 'Find and select command',
data,
}
}
export async function enterCloneFlow({
operation,
}: EnterEditFlowProps): Promise<Error | CommandBarMachineEvent> {
const isModuleImport = operation.type === 'GroupBegin'
const isSupportedStdLibCall =
operation.type === 'StdLibCall' &&
stdLibMap[operation.name]?.supportsTransform
if (!isModuleImport && !isSupportedStdLibCall) {
return new Error(
'Unsupported operation type. Please edit in the code editor.'
)
}
const nodeToEdit = pathToNodeFromRustNodePath(operation.nodePath)
// Won't be used since we provide nodeToEdit
const selection: Selections = { graphSelections: [], otherSelections: [] }
const argDefaultValues = { nodeToEdit, selection }
return {
type: 'Find and select command',
data: {
name: 'Clone',
groupId: 'modeling',
argDefaultValues,
},
}
}

View File

@ -12,14 +12,7 @@ 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,
enterScaleFlow,
} from '@src/lib/operations'
import { enterAppearanceFlow, enterEditFlow } from '@src/lib/operations'
import { kclManager } from '@src/lib/singletons'
import { err } from '@src/lib/trap'
import { commandBarActor } from '@src/lib/singletons'
@ -131,98 +124,6 @@ export const featureTreeMachine = setup({
})
}
),
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)
})
}
),
prepareScaleCommand: fromPromise(
({
input,
}: {
input: EnterEditFlowProps & {
commandBarSend: (typeof commandBarActor)['send']
}
}) => {
return new Promise((resolve, reject) => {
const { commandBarSend, ...editFlowProps } = input
enterScaleFlow(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,
@ -281,6 +182,9 @@ export const featureTreeMachine = setup({
}),
sendSelectionEvent: () => {},
sendTranslateCommand: () => {},
sendRotateCommand: () => {},
sendScaleCommand: () => {},
sendCloneCommand: () => {},
openCodePane: () => {},
scrollToError: () => {},
},
@ -313,7 +217,7 @@ export const featureTreeMachine = setup({
},
enterTranslateFlow: {
target: 'enteringTranslateFlow2',
target: 'enteringTranslateFlow',
actions: [
'saveTargetSourceRange',
'saveCurrentOperation',
@ -323,17 +227,29 @@ export const featureTreeMachine = setup({
enterRotateFlow: {
target: 'enteringRotateFlow',
actions: ['saveTargetSourceRange', 'saveCurrentOperation'],
actions: [
'saveTargetSourceRange',
'saveCurrentOperation',
'sendSelectionEvent',
],
},
enterScaleFlow: {
target: 'enteringScaleFlow',
actions: ['saveTargetSourceRange', 'saveCurrentOperation'],
actions: [
'saveTargetSourceRange',
'saveCurrentOperation',
'sendSelectionEvent',
],
},
enterCloneFlow: {
target: 'enteringCloneFlow',
actions: ['saveTargetSourceRange', 'saveCurrentOperation'],
actions: [
'saveTargetSourceRange',
'saveCurrentOperation',
'sendSelectionEvent',
],
},
deleteOperation: {
@ -393,9 +309,9 @@ export const featureTreeMachine = setup({
initial: 'selecting',
},
enteringTranslateFlow2: {
enteringTranslateFlow: {
states: {
enteringTranslateFlow2: {
enteringTranslateFlow: {
on: {
selected: 'done',
},
@ -409,7 +325,64 @@ export const featureTreeMachine = setup({
},
},
initial: 'enteringTranslateFlow2',
initial: 'enteringTranslateFlow',
},
enteringRotateFlow: {
states: {
enteringRotateFlow: {
on: {
selected: 'done',
},
entry: 'sendRotateCommand',
},
done: {
always: '#featureTree.idle',
entry: 'clearContext',
},
},
initial: 'enteringRotateFlow',
},
enteringScaleFlow: {
states: {
enteringScaleFlow: {
on: {
selected: 'done',
},
entry: 'sendScaleCommand',
},
done: {
always: '#featureTree.idle',
entry: 'clearContext',
},
},
initial: 'enteringScaleFlow',
},
enteringCloneFlow: {
states: {
enteringCloneFlow: {
on: {
selected: 'done',
},
entry: 'sendCloneCommand',
},
done: {
always: '#featureTree.idle',
entry: 'clearContext',
},
},
initial: 'enteringCloneFlow',
},
enteringEditFlow: {
@ -520,222 +493,6 @@ export const featureTreeMachine = setup({
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'],
},
enteringScaleFlow: {
states: {
selecting: {
on: {
selected: {
target: 'prepareScaleCommand',
reenter: true,
},
},
},
done: {
always: '#featureTree.idle',
},
prepareScaleCommand: {
invoke: {
src: 'prepareScaleCommand',
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: {

View File

@ -47,7 +47,6 @@ import { angleLengthInfo } from '@src/components/Toolbar/angleLengthInfo'
import { createLiteral, createLocalName } from '@src/lang/create'
import { updateModelingState } from '@src/lang/modelingWorkflows'
import {
addClone,
addHelix,
addOffsetPlane,
addShell,
@ -86,7 +85,7 @@ import {
addTranslate,
addRotate,
addScale,
retrievePathToNodeFromTransformSelection,
addClone,
} from '@src/lang/modifyAst/transforms'
import {
getNodeFromPath,
@ -112,7 +111,6 @@ import type {
Literal,
Name,
PathToNode,
PipeExpression,
Program,
VariableDeclaration,
VariableDeclarator,
@ -140,7 +138,6 @@ import {
import type { ToolbarModeName } from '@src/lib/toolbar'
import { err, reportRejection, trap } from '@src/lib/trap'
import { uuidv4 } from '@src/lib/utils'
import type { ImportStatement } from '@rust/kcl-lib/bindings/ImportStatement'
import { isDesktop } from '@src/lib/isDesktop'
import {
crossProduct,
@ -3420,58 +3417,14 @@ export const modelingMachine = setup({
}
const ast = kclManager.ast
const { nodeToEdit, selection, variableName } = input
let pathToNode = nodeToEdit
if (!(pathToNode && typeof pathToNode[1][0] === 'number')) {
const result = retrievePathToNodeFromTransformSelection(
selection,
kclManager.artifactGraph,
ast
)
if (err(result)) {
return Promise.reject(result)
}
pathToNode = result
}
const returnEarly = true
const geometryNode = getNodeFromPath<
VariableDeclaration | ImportStatement | PipeExpression
>(
ast,
pathToNode,
['VariableDeclaration', 'ImportStatement', 'PipeExpression'],
returnEarly
)
if (err(geometryNode)) {
return Promise.reject(
new Error("Couldn't find corresponding path to node")
)
}
let geometryName: string | undefined
if (geometryNode.node.type === 'VariableDeclaration') {
geometryName = geometryNode.node.declaration.id.name
} else if (
geometryNode.node.type === 'ImportStatement' &&
geometryNode.node.selector.type === 'None' &&
geometryNode.node.selector.alias
) {
geometryName = geometryNode.node.selector.alias?.name
} else {
return Promise.reject(
new Error("Couldn't find corresponding geometry")
)
}
const artifactGraph = kclManager.artifactGraph
const result = addClone({
...input,
ast,
geometryName,
variableName,
artifactGraph,
})
if (err(result)) {
return Promise.reject(err(result))
return Promise.reject(result)
}
await updateModelingState(