Big time clean up and bringing clone on for the ride
This commit is contained in:
		@ -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}
 | 
			
		||||
 | 
			
		||||
@ -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)
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -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,
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -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),
 | 
			
		||||
      },
 | 
			
		||||
    },
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
@ -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'
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -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,
 | 
			
		||||
    },
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -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: {
 | 
			
		||||
 | 
			
		||||
@ -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(
 | 
			
		||||
 | 
			
		||||
		Reference in New Issue
	
	Block a user