diff --git a/e2e/playwright/code-pane-and-errors.spec.ts b/e2e/playwright/code-pane-and-errors.spec.ts
index 0915a2835..be6ca7992 100644
--- a/e2e/playwright/code-pane-and-errors.spec.ts
+++ b/e2e/playwright/code-pane-and-errors.spec.ts
@@ -134,8 +134,6 @@ extrude001 = extrude(sketch001, length = 5)`
await page.setBodyDimensions({ width: 1200, height: 500 })
await homePage.goToModelingScene()
- await page.waitForTimeout(1000)
-
// Ensure badge is present
const codePaneButtonHolder = page.locator('#code-button-holder')
await expect(codePaneButtonHolder).toContainText('notification', {
@@ -183,7 +181,7 @@ extrude001 = extrude(sketch001, length = 5)`
await page.setBodyDimensions({ width: 1200, height: 500 })
await homePage.goToModelingScene()
- await scene.settled(cmdBar)
+ // await scene.settled(cmdBar)
// Ensure badge is present
const codePaneButtonHolder = page.locator('#code-button-holder')
diff --git a/e2e/playwright/editor-tests.spec.ts b/e2e/playwright/editor-tests.spec.ts
index db5777689..727764944 100644
--- a/e2e/playwright/editor-tests.spec.ts
+++ b/e2e/playwright/editor-tests.spec.ts
@@ -1533,7 +1533,6 @@ sketch001 = startSketchOn(XZ)
await homePage.goToModelingScene()
await scene.connectionEstablished()
- await scene.settled(cmdBar)
await scene.expectPixelColor(
TEST_COLORS.DARK_MODE_BKGD,
diff --git a/e2e/playwright/point-click.spec.ts b/e2e/playwright/point-click.spec.ts
index 302ef46c8..de5320ef7 100644
--- a/e2e/playwright/point-click.spec.ts
+++ b/e2e/playwright/point-click.spec.ts
@@ -4943,4 +4943,34 @@ path001 = startProfile(sketch001, at = [0, 0])
)
})
})
+
+ test(`Point and click codemods can't run on KCL errors`, async ({
+ context,
+ page,
+ homePage,
+ scene,
+ editor,
+ toolbar,
+ cmdBar,
+ }) => {
+ const badCode = `sketch001 = startSketchOn(XZ)
+profile001 = circle(sketch001, center = [0, 0], radius = 1)
+extrude001 = extrude(profile001 length = 1)`
+ await context.addInitScript((initialCode) => {
+ localStorage.setItem('persistCode', initialCode)
+ }, badCode)
+ await page.setBodyDimensions({ width: 1000, height: 500 })
+ await homePage.goToModelingScene()
+ await scene.connectionEstablished()
+
+ await test.step(`Start Sketch is disabled`, async () => {
+ await expect(toolbar.startSketchBtn).not.toBeEnabled()
+ await editor.expectEditor.toContain(badCode, { shouldNormalise: true })
+ })
+
+ await test.step(`Helix is disabled`, async () => {
+ await expect(toolbar.helixButton).not.toBeEnabled()
+ await editor.expectEditor.toContain(badCode, { shouldNormalise: true })
+ })
+ })
})
diff --git a/e2e/playwright/regression-tests.spec.ts b/e2e/playwright/regression-tests.spec.ts
index 3f0555689..6308322cd 100644
--- a/e2e/playwright/regression-tests.spec.ts
+++ b/e2e/playwright/regression-tests.spec.ts
@@ -19,11 +19,12 @@ test.describe('Regression tests', () => {
context,
page,
homePage,
+ scene,
}) => {
// because the model has `line([0,0]..` it is valid code, but the model is invalid
// regression test for https://github.com/KittyCAD/modeling-app/issues/3251
// Since the bad model also found as issue with the artifact graph, which in tern blocked the editor diognostics
- const u = await getUtils(page)
+ // const u = await getUtils(page)
await context.addInitScript(async () => {
localStorage.setItem(
'persistCode',
@@ -40,7 +41,8 @@ test.describe('Regression tests', () => {
await page.setBodyDimensions({ width: 1000, height: 500 })
await homePage.goToModelingScene()
- await u.waitForPageLoad()
+ await scene.connectionEstablished()
+ // await u.waitForPageLoad()
// error in guter
await expect(page.locator('.cm-lint-marker-error')).toBeVisible()
@@ -188,8 +190,8 @@ extrude001 = extrude(sketch001, length = 50)
page.locator('.pretty-json-container >> text=myVar:"67')
).toBeVisible()
})
- test('ProgramMemory can be serialised', async ({ page, homePage }) => {
- const u = await getUtils(page)
+ test('ProgramMemory can be serialised', async ({ page, homePage, scene }) => {
+ // const u = await getUtils(page)
await page.addInitScript(async () => {
localStorage.setItem(
'persistCode',
@@ -214,11 +216,12 @@ extrude001 = extrude(sketch001, length = 50)
// Listen for all console events and push the message text to an array
page.on('console', (message) => messages.push(message.text()))
await homePage.goToModelingScene()
- await u.waitForPageLoad()
+ // await u.waitForPageLoad()
+ await scene.connectionEstablished()
// wait for execution done
- await u.openDebugPanel()
- await u.expectCmdLog('[data-message-type="execution-done"]')
+ // await u.openDebugPanel()
+ // await u.expectCmdLog('[data-message-type="execution-done"]')
const forbiddenMessages = ['cannot serialize tagged newtype variant']
forbiddenMessages.forEach((forbiddenMessage) => {
@@ -232,6 +235,7 @@ extrude001 = extrude(sketch001, length = 50)
context,
page,
homePage,
+ scene,
}) => {
const u = await getUtils(page)
// const PUR = 400 / 37.5 //pixeltoUnitRatio
@@ -250,11 +254,10 @@ extrude001 = extrude(sketch001, length = 50)
shell(exampleSketch, faces = ['end'], thickness = 0.25)`
)
})
+ await homePage.goToModelingScene()
+ await scene.connectionEstablished()
await expect(async () => {
- await homePage.goToModelingScene()
- await u.waitForPageLoad()
-
// error in guter
await expect(page.locator('.cm-lint-marker-error')).toBeVisible({
timeout: 1_000,
diff --git a/e2e/playwright/sketch-tests.spec.ts b/e2e/playwright/sketch-tests.spec.ts
index d02ddd808..e6f256980 100644
--- a/e2e/playwright/sketch-tests.spec.ts
+++ b/e2e/playwright/sketch-tests.spec.ts
@@ -1365,18 +1365,18 @@ solid001 = subtract([extrude001], tools = [extrude002])
await page.addInitScript(async () => {
localStorage.setItem(
'persistCode',
- `fn in2mm = (inches) => {
+ `fn in2mm(@inches) {
return inches * 25.4
}
- const railTop = in2mm(.748)
- const railSide = in2mm(.024)
- const railBaseWidth = in2mm(.612)
- const railWideWidth = in2mm(.835)
- const railBaseLength = in2mm(.200)
- const railClampable = in2mm(.200)
+ railTop = in2mm(.748)
+ railSide = in2mm(.024)
+ railBaseWidth = in2mm(.612)
+ railWideWidth = in2mm(.835)
+ railBaseLength = in2mm(.200)
+ railClampable = in2mm(.200)
- const rail = startSketchOn(XZ)
+ rail = startSketchOn(XZ)
|> startProfile(at = [-railTop / 2, railClampable + railBaseLength])
|> line(endAbsolute = [
railTop / 2,
@@ -3540,7 +3540,6 @@ profile001 = startProfile(sketch001, at = [127.56, 179.02])
await homePage.openProject('multi-file-sketch-test')
await scene.connectionEstablished()
- await scene.settled(cmdBar)
await u.closeDebugPanel()
@@ -3555,9 +3554,6 @@ profile001 = startProfile(sketch001, at = [127.56, 179.02])
await toolbar.openFile('error.kcl')
- // Ensure filetree is populated
- await scene.settled(cmdBar)
-
await expect(
toolbar.featureTreePane.getByRole('button', { name: 'Sketch' })
).toHaveCount(0)
diff --git a/src/Toolbar.tsx b/src/Toolbar.tsx
index 424f89f56..2f46a76df 100644
--- a/src/Toolbar.tsx
+++ b/src/Toolbar.tsx
@@ -158,7 +158,8 @@ export function Toolbar({
const isDisabled =
disableAllButtons ||
!isConfiguredAvailable ||
- maybeIconConfig.disabled?.(state) === true
+ maybeIconConfig.disabled?.(state) === true ||
+ kclManager.hasErrors()
return {
...maybeIconConfig,
@@ -444,6 +445,15 @@ const ToolbarItemTooltip = memo(function ToolbarItemContents({
contentClassName={contentClassName}
>
{children}
+ {kclManager.hasErrors() && (
+
+
+ Fix KCL errors to enable tools
+
+ )}
)
})
diff --git a/src/components/CommandComboBox.tsx b/src/components/CommandComboBox.tsx
index 0eaac2839..f8de2142e 100644
--- a/src/components/CommandComboBox.tsx
+++ b/src/components/CommandComboBox.tsx
@@ -136,8 +136,9 @@ function optionIsDisabled(option: Command): boolean {
option.disabled ||
('machineActor' in option &&
option.machineActor !== undefined &&
- !getActorNextEvents(option.machineActor.getSnapshot()).includes(
+ (!getActorNextEvents(option.machineActor.getSnapshot()).includes(
option.name
- ))
+ ) ||
+ !option.machineActor?.getSnapshot().can({ type: option.name })))
)
}
diff --git a/src/components/ModelingMachineProvider.tsx b/src/components/ModelingMachineProvider.tsx
index ca31281e8..89e28022a 100644
--- a/src/components/ModelingMachineProvider.tsx
+++ b/src/components/ModelingMachineProvider.tsx
@@ -580,24 +580,23 @@ export const ModelingMachineProvider = ({
selectionRanges
)
},
- 'Has exportable geometry': () => {
- if (!kclManager.hasErrors() && kclManager.ast.body.length > 0)
- return true
- else {
- let errorMessage = 'Unable to Export '
- if (kclManager.hasErrors()) errorMessage += 'due to KCL Errors'
- else if (kclManager.ast.body.length === 0)
- errorMessage += 'due to Empty Scene'
- console.error(errorMessage)
- toast.error(errorMessage)
- return false
- }
- },
+ 'Has exportable geometry': () =>
+ !kclManager.hasErrors() && kclManager.ast.body.length > 0,
},
actors: {
exportFromEngine: fromPromise(
async ({ input }: { input?: ModelingCommandSchema['Export'] }) => {
- if (!input) {
+ if (kclManager.hasErrors() || kclManager.ast.body.length === 0) {
+ let errorMessage = 'Unable to Export '
+ if (kclManager.hasErrors()) {
+ errorMessage += 'due to KCL Errors'
+ } else if (kclManager.ast.body.length === 0) {
+ errorMessage += 'due to Empty Scene'
+ }
+ console.error(errorMessage)
+ toast.error(errorMessage)
+ return new Error(errorMessage)
+ } else if (!input) {
return new Error('No input provided')
}
diff --git a/src/machines/modelingMachine.ts b/src/machines/modelingMachine.ts
index 2c6a4b36e..acaaaa87e 100644
--- a/src/machines/modelingMachine.ts
+++ b/src/machines/modelingMachine.ts
@@ -566,6 +566,8 @@ export const modelingMachineDefaultContext: ModelingMachineContext = {
planesInitialized: false,
}
+const NO_INPUT_PROVIDED_MESSAGE = 'No input provided'
+
export const modelingMachine = setup({
types: {
context: {} as ModelingMachineContext,
@@ -1357,6 +1359,8 @@ export const modelingMachine = setup({
},
// end actions
actors: {
+ /* Below are all the do-constrain sketch actors,
+ * which aren't using updateModelingState and don't have the 'no kcl errors' guard yet */
'do-constrain-remove-constraint': fromPromise(
async ({
input: { selectionRanges, sketchDetails, data },
@@ -1694,6 +1698,9 @@ export const modelingMachine = setup({
}
}
),
+
+ /* Below are actors being defined in src/components/ModelingMachineProvider.tsx
+ * which aren't using updateModelingState and don't have the 'no kcl errors' guard yet */
'Get vertical info': fromPromise(
async (_: {
input: Pick
@@ -1775,76 +1782,167 @@ export const modelingMachine = setup({
return {} as SetSelections
}
),
- extrudeAstMod: fromPromise<
- unknown,
- ModelingCommandSchema['Extrude'] | undefined
- >(async ({ input }) => {
- if (!input) return Promise.reject(new Error('No input provided'))
- const { nodeToEdit, sketches, length } = input
- const { ast } = kclManager
- const astResult = addExtrude({
- ast,
- sketches,
- length,
- nodeToEdit,
- })
- if (err(astResult)) {
- return Promise.reject(new Error("Couldn't add extrude statement"))
- }
-
- const { modifiedAst, pathToNode } = astResult
- await updateModelingState(
- modifiedAst,
- EXECUTION_TYPE_REAL,
- {
- kclManager,
- editorManager,
- codeManager,
- },
- {
- focusPath: [pathToNode],
+ 'set-up-draft-circle': fromPromise(
+ async (_: {
+ input: Pick & {
+ data: [x: number, y: number]
}
- )
- }),
- sweepAstMod: fromPromise<
- unknown,
- ModelingCommandSchema['Sweep'] | undefined
- >(async ({ input }) => {
- if (!input) return Promise.reject(new Error('No input provided'))
- const { nodeToEdit, sketches, path, sectional } = input
- const { ast } = kclManager
- const astResult = addSweep({
- ast,
- sketches,
- path,
- sectional,
- nodeToEdit,
- })
- if (err(astResult)) {
- return Promise.reject(astResult)
+ }) => {
+ return {} as SketchDetailsUpdate
}
-
- const { modifiedAst, pathToNode } = astResult
- await updateModelingState(
- modifiedAst,
- EXECUTION_TYPE_REAL,
- {
- kclManager,
- editorManager,
- codeManager,
- },
- {
- focusPath: [pathToNode],
+ ),
+ 'set-up-draft-circle-three-point': fromPromise(
+ async (_: {
+ input: Pick & {
+ data: { p1: [x: number, y: number]; p2: [x: number, y: number] }
}
- )
- }),
+ }) => {
+ return {} as SketchDetailsUpdate
+ }
+ ),
+ 'set-up-draft-rectangle': fromPromise(
+ async (_: {
+ input: Pick & {
+ data: [x: number, y: number]
+ }
+ }) => {
+ return {} as SketchDetailsUpdate
+ }
+ ),
+ 'set-up-draft-center-rectangle': fromPromise(
+ async (_: {
+ input: Pick & {
+ data: [x: number, y: number]
+ }
+ }) => {
+ return {} as SketchDetailsUpdate
+ }
+ ),
+ 'set-up-draft-arc': fromPromise(
+ async (_: {
+ input: Pick & {
+ data: [x: number, y: number]
+ }
+ }) => {
+ return {} as SketchDetailsUpdate
+ }
+ ),
+ 'set-up-draft-arc-three-point': fromPromise(
+ async (_: {
+ input: Pick & {
+ data: [x: number, y: number]
+ }
+ }) => {
+ return {} as SketchDetailsUpdate
+ }
+ ),
+ 'setup-client-side-sketch-segments': fromPromise(
+ async (_: {
+ input: Pick
+ }) => {
+ return undefined
+ }
+ ),
+ 'split-sketch-pipe-if-needed': fromPromise(
+ async (_: { input: Pick }) => {
+ return {} as SketchDetailsUpdate
+ }
+ ),
+ 'submit-prompt-edit': fromPromise(
+ async ({
+ input,
+ }: {
+ input: ModelingCommandSchema['Prompt-to-edit']
+ }) => {}
+ ),
+
+ /* Below are recent modeling codemods that are using updateModelinState,
+ * trigger toastError on Error, and have the 'no kcl errors' guard yet */
+ extrudeAstMod: fromPromise(
+ async ({
+ input,
+ }: {
+ input: ModelingCommandSchema['Extrude'] | undefined
+ }) => {
+ if (!input) {
+ return Promise.reject(new Error(NO_INPUT_PROVIDED_MESSAGE))
+ }
+
+ const { nodeToEdit, sketches, length } = input
+ const { ast } = kclManager
+ const astResult = addExtrude({
+ ast,
+ sketches,
+ length,
+ nodeToEdit,
+ })
+ if (err(astResult)) {
+ return Promise.reject(new Error("Couldn't add extrude statement"))
+ }
+
+ const { modifiedAst, pathToNode } = astResult
+ await updateModelingState(
+ modifiedAst,
+ EXECUTION_TYPE_REAL,
+ {
+ kclManager,
+ editorManager,
+ codeManager,
+ },
+ {
+ focusPath: [pathToNode],
+ }
+ )
+ }
+ ),
+ sweepAstMod: fromPromise(
+ async ({
+ input,
+ }: {
+ input: ModelingCommandSchema['Sweep'] | undefined
+ }) => {
+ if (!input) {
+ return Promise.reject(new Error(NO_INPUT_PROVIDED_MESSAGE))
+ }
+
+ const { nodeToEdit, sketches, path, sectional } = input
+ const { ast } = kclManager
+ const astResult = addSweep({
+ ast,
+ sketches,
+ path,
+ sectional,
+ nodeToEdit,
+ })
+ if (err(astResult)) {
+ return Promise.reject(astResult)
+ }
+
+ const { modifiedAst, pathToNode } = astResult
+ await updateModelingState(
+ modifiedAst,
+ EXECUTION_TYPE_REAL,
+ {
+ kclManager,
+ editorManager,
+ codeManager,
+ },
+ {
+ focusPath: [pathToNode],
+ }
+ )
+ }
+ ),
loftAstMod: fromPromise(
async ({
input,
}: {
input: ModelingCommandSchema['Loft'] | undefined
}) => {
- if (!input) return Promise.reject(new Error('No input provided'))
+ if (!input) {
+ return Promise.reject(new Error(NO_INPUT_PROVIDED_MESSAGE))
+ }
+
const { sketches } = input
const { ast } = kclManager
const astResult = addLoft({ ast, sketches })
@@ -1867,47 +1965,56 @@ export const modelingMachine = setup({
)
}
),
- revolveAstMod: fromPromise<
- unknown,
- ModelingCommandSchema['Revolve'] | undefined
- >(async ({ input }) => {
- if (!input) return Promise.reject(new Error('No input provided'))
- const { nodeToEdit, sketches, angle, axis, edge, axisOrEdge } = input
- const { ast } = kclManager
- const astResult = addRevolve({
- ast,
- sketches,
- angle,
- axisOrEdge,
- axis,
- edge,
- nodeToEdit,
- })
- if (err(astResult)) {
- return Promise.reject(astResult)
- }
-
- const { modifiedAst, pathToNode } = astResult
- await updateModelingState(
- modifiedAst,
- EXECUTION_TYPE_REAL,
- {
- kclManager,
- editorManager,
- codeManager,
- },
- {
- focusPath: [pathToNode],
+ revolveAstMod: fromPromise(
+ async ({
+ input,
+ }: {
+ input: ModelingCommandSchema['Revolve'] | undefined
+ }) => {
+ if (!input) {
+ return Promise.reject(new Error(NO_INPUT_PROVIDED_MESSAGE))
}
- )
- }),
+
+ const { nodeToEdit, sketches, angle, axis, edge, axisOrEdge } = input
+ const { ast } = kclManager
+ const astResult = addRevolve({
+ ast,
+ sketches,
+ angle,
+ axisOrEdge,
+ axis,
+ edge,
+ nodeToEdit,
+ })
+ if (err(astResult)) {
+ return Promise.reject(astResult)
+ }
+
+ const { modifiedAst, pathToNode } = astResult
+ await updateModelingState(
+ modifiedAst,
+ EXECUTION_TYPE_REAL,
+ {
+ kclManager,
+ editorManager,
+ codeManager,
+ },
+ {
+ focusPath: [pathToNode],
+ }
+ )
+ }
+ ),
offsetPlaneAstMod: fromPromise(
async ({
input,
}: {
input: ModelingCommandSchema['Offset plane'] | undefined
}) => {
- if (!input) return new Error('No input provided')
+ if (!input) {
+ return Promise.reject(new Error(NO_INPUT_PROVIDED_MESSAGE))
+ }
+
// Extract inputs
const ast = kclManager.ast
const { plane: selection, distance, nodeToEdit } = input
@@ -1989,9 +2096,10 @@ export const modelingMachine = setup({
}: {
input: ModelingCommandSchema['Helix'] | undefined
}) => {
- if (!input) return new Error('No input provided')
+ if (!input) {
+ return Promise.reject(new Error(NO_INPUT_PROVIDED_MESSAGE))
+ }
// Extract inputs
- console.log('input', input)
const ast = kclManager.ast
const {
mode,
@@ -2043,7 +2151,7 @@ export const modelingMachine = setup({
cylinder.graphSelections[0].artifact?.type === 'wall'
)
) {
- return new Error('Cylinder argument not valid')
+ return Promise.reject(new Error('Cylinder argument not valid'))
}
const clonedAstForGetExtrude = structuredClone(ast)
const extrudeLookupResult = getPathToExtrudeForSegmentSelection(
@@ -2052,7 +2160,7 @@ export const modelingMachine = setup({
kclManager.artifactGraph
)
if (err(extrudeLookupResult)) {
- return extrudeLookupResult
+ return Promise.reject(extrudeLookupResult)
}
const extrudeNode = getNodeFromPath(
ast,
@@ -2060,18 +2168,20 @@ export const modelingMachine = setup({
'VariableDeclaration'
)
if (err(extrudeNode)) {
- return extrudeNode
+ return Promise.reject(extrudeNode)
}
cylinderDeclarator = extrudeNode.node.declaration
} else if (mode === 'Axis' || mode === 'Edge') {
const getAxisResult = getAxisExpressionAndIndex(mode, axis, edge, ast)
if (err(getAxisResult)) {
- return getAxisResult
+ return Promise.reject(getAxisResult)
}
axisExpression = getAxisResult.generatedAxis
} else {
- return new Error(
- 'Generated axis or cylinder declarator selection is missing.'
+ return Promise.reject(
+ new Error(
+ 'Generated axis or cylinder declarator selection is missing.'
+ )
)
}
@@ -2132,7 +2242,7 @@ export const modelingMachine = setup({
input: ModelingCommandSchema['Shell'] | undefined
}) => {
if (!input) {
- return new Error('No input provided')
+ return Promise.reject(new Error(NO_INPUT_PROVIDED_MESSAGE))
}
// Extract inputs
@@ -2173,9 +2283,11 @@ export const modelingMachine = setup({
kclManager.artifactGraph
)
if (err(extrudeLookupResult)) {
- return new Error(
- "Couldn't find extrude paths from getPathToExtrudeForSegmentSelection",
- { cause: extrudeLookupResult }
+ return Promise.reject(
+ new Error(
+ "Couldn't find extrude paths from getPathToExtrudeForSegmentSelection",
+ { cause: extrudeLookupResult }
+ )
)
}
@@ -2196,9 +2308,11 @@ export const modelingMachine = setup({
'VariableDeclaration'
)
if (err(segmentNode)) {
- return new Error("Couldn't find segment node from selection", {
- cause: segmentNode,
- })
+ return Promise.reject(
+ new Error("Couldn't find segment node from selection", {
+ cause: segmentNode,
+ })
+ )
}
if (extrudeNode.node.declaration.init.type === 'CallExpressionKw') {
@@ -2208,15 +2322,17 @@ export const modelingMachine = setup({
) {
pathToExtrudeNode = extrudeLookupResult.pathToSegmentNode
} else {
- return new Error(
- "Couldn't find extrude node that was either a call expression or a pipe",
- { cause: segmentNode }
+ return Promise.reject(
+ new Error(
+ "Couldn't find extrude node that was either a call expression or a pipe",
+ { cause: segmentNode }
+ )
)
}
const selectedArtifact = graphSelection.artifact
if (!selectedArtifact) {
- return new Error('Bad artifact from selection')
+ return Promise.reject(new Error('Bad artifact from selection'))
}
// Check on the selection, and handle the wall vs cap cases
@@ -2229,20 +2345,22 @@ export const modelingMachine = setup({
extrudeLookupResult.pathToSegmentNode
)
if (err(tagResult)) {
- return tagResult
+ return Promise.reject(tagResult)
}
const { tag } = tagResult
expr = createLocalName(tag)
} else {
- return new Error('Artifact is neither a cap nor a wall')
+ return Promise.reject(
+ new Error('Artifact is neither a cap nor a wall')
+ )
}
faces.push(expr)
}
if (!pathToExtrudeNode) {
- return new Error('No path to extrude node found')
+ return Promise.reject(new Error('No path to extrude node found'))
}
const extrudeNode = getNodeFromPath(
@@ -2251,9 +2369,11 @@ export const modelingMachine = setup({
'VariableDeclarator'
)
if (err(extrudeNode)) {
- return new Error("Couldn't find extrude node", {
- cause: extrudeNode,
- })
+ return Promise.reject(
+ new Error("Couldn't find extrude node", {
+ cause: extrudeNode,
+ })
+ )
}
// Perform the shell op
@@ -2308,7 +2428,7 @@ export const modelingMachine = setup({
input: ModelingCommandSchema['Fillet'] | undefined
}) => {
if (!input) {
- return new Error('No input provided')
+ return Promise.reject(new Error(NO_INPUT_PROVIDED_MESSAGE))
}
// Extract inputs
@@ -2401,7 +2521,7 @@ export const modelingMachine = setup({
input: ModelingCommandSchema['Chamfer'] | undefined
}) => {
if (!input) {
- return Promise.reject(new Error('No input provided'))
+ return Promise.reject(new Error(NO_INPUT_PROVIDED_MESSAGE))
}
// Extract inputs
@@ -2492,10 +2612,13 @@ export const modelingMachine = setup({
}: {
input: ModelingCommandSchema['event.parameter.create'] | undefined
}) => {
- if (!input) return new Error('No input provided')
+ if (!input) {
+ return Promise.reject(new Error(NO_INPUT_PROVIDED_MESSAGE))
+ }
+
const { value } = input
if (!('variableName' in value)) {
- return new Error('variable name is required')
+ return Promise.reject(new Error('variable name is required'))
}
const newAst = insertNamedConstant({
node: kclManager.ast,
@@ -2514,7 +2637,10 @@ export const modelingMachine = setup({
}: {
input: ModelingCommandSchema['event.parameter.edit'] | undefined
}) => {
- if (!input) return new Error('No input provided')
+ if (!input) {
+ return Promise.reject(new Error(NO_INPUT_PROVIDED_MESSAGE))
+ }
+
// Get the variable AST node to edit
const { nodeToEdit, value } = input
const newAst = structuredClone(kclManager.ast)
@@ -2528,7 +2654,7 @@ export const modelingMachine = setup({
variableNode.node.type !== 'VariableDeclarator' ||
!variableNode.node
) {
- return new Error('No variable found, this is a bug')
+ return Promise.reject(new Error('No variable found, this is a bug'))
}
// Mutate the variable's value
@@ -2541,79 +2667,6 @@ export const modelingMachine = setup({
})
}
),
- 'set-up-draft-circle': fromPromise(
- async (_: {
- input: Pick & {
- data: [x: number, y: number]
- }
- }) => {
- return {} as SketchDetailsUpdate
- }
- ),
- 'set-up-draft-circle-three-point': fromPromise(
- async (_: {
- input: Pick & {
- data: { p1: [x: number, y: number]; p2: [x: number, y: number] }
- }
- }) => {
- return {} as SketchDetailsUpdate
- }
- ),
- 'set-up-draft-rectangle': fromPromise(
- async (_: {
- input: Pick & {
- data: [x: number, y: number]
- }
- }) => {
- return {} as SketchDetailsUpdate
- }
- ),
- 'set-up-draft-center-rectangle': fromPromise(
- async (_: {
- input: Pick & {
- data: [x: number, y: number]
- }
- }) => {
- return {} as SketchDetailsUpdate
- }
- ),
- 'set-up-draft-arc': fromPromise(
- async (_: {
- input: Pick & {
- data: [x: number, y: number]
- }
- }) => {
- return {} as SketchDetailsUpdate
- }
- ),
- 'set-up-draft-arc-three-point': fromPromise(
- async (_: {
- input: Pick & {
- data: [x: number, y: number]
- }
- }) => {
- return {} as SketchDetailsUpdate
- }
- ),
- 'setup-client-side-sketch-segments': fromPromise(
- async (_: {
- input: Pick
- }) => {
- return undefined
- }
- ),
- 'split-sketch-pipe-if-needed': fromPromise(
- async (_: { input: Pick }) => {
- return {} as SketchDetailsUpdate
- }
- ),
- 'submit-prompt-edit': fromPromise(
- async ({
- input,
- }: {
- input: ModelingCommandSchema['Prompt-to-edit']
- }) => {}
- ),
deleteSelectionAstMod: fromPromise(
({
input: { selectionRanges },
@@ -2648,12 +2701,15 @@ export const modelingMachine = setup({
}: {
input: ModelingCommandSchema['Appearance'] | undefined
}) => {
- if (!input) return new Error('No input provided')
+ if (!input) {
+ return Promise.reject(new Error(NO_INPUT_PROVIDED_MESSAGE))
+ }
+
// Extract inputs
const ast = kclManager.ast
const { color, nodeToEdit } = input
if (!(nodeToEdit && typeof nodeToEdit[1][0] === 'number')) {
- return new Error('Appearance is only an edit flow')
+ return Promise.reject(new Error('Appearance is only an edit flow'))
}
const result = setAppearance({
@@ -2663,7 +2719,7 @@ export const modelingMachine = setup({
})
if (err(result)) {
- return err(result)
+ return Promise.reject(err(result))
}
await updateModelingState(
@@ -2686,7 +2742,10 @@ export const modelingMachine = setup({
}: {
input: ModelingCommandSchema['Translate'] | undefined
}) => {
- if (!input) return Promise.reject(new Error('No input provided'))
+ if (!input) {
+ return Promise.reject(new Error(NO_INPUT_PROVIDED_MESSAGE))
+ }
+
const ast = kclManager.ast
const modifiedAst = structuredClone(ast)
const { x, y, z, nodeToEdit, selection } = input
@@ -2764,7 +2823,10 @@ export const modelingMachine = setup({
}: {
input: ModelingCommandSchema['Rotate'] | undefined
}) => {
- if (!input) return Promise.reject(new Error('No input provided'))
+ if (!input) {
+ return Promise.reject(new Error(NO_INPUT_PROVIDED_MESSAGE))
+ }
+
const ast = kclManager.ast
const modifiedAst = structuredClone(ast)
const { roll, pitch, yaw, nodeToEdit, selection } = input
@@ -2842,7 +2904,10 @@ export const modelingMachine = setup({
}: {
input: ModelingCommandSchema['Clone'] | undefined
}) => {
- if (!input) return Promise.reject(new Error('No input provided'))
+ if (!input) {
+ return Promise.reject(new Error(NO_INPUT_PROVIDED_MESSAGE))
+ }
+
const ast = kclManager.ast
const { nodeToEdit, selection, variableName } = input
let pathToNode = nodeToEdit
@@ -2933,15 +2998,17 @@ export const modelingMachine = setup({
input: ModelingCommandSchema['Boolean Subtract'] | undefined
}) => {
if (!input) {
- return new Error('No input provided')
+ return Promise.reject(new Error(NO_INPUT_PROVIDED_MESSAGE))
}
+
const { target, tool } = input
if (
!target.graphSelections[0].artifact ||
!tool.graphSelections[0].artifact
) {
- return new Error('No artifact in selections found')
+ return Promise.reject(new Error('No artifact in selections found'))
}
+
await applySubtractFromTargetOperatorSelections(
target.graphSelections[0],
tool.graphSelections[0],
@@ -2961,12 +3028,14 @@ export const modelingMachine = setup({
input: ModelingCommandSchema['Boolean Union'] | undefined
}) => {
if (!input) {
- return new Error('No input provided')
+ return Promise.reject(new Error(NO_INPUT_PROVIDED_MESSAGE))
}
+
const { solids } = input
if (!solids.graphSelections[0].artifact) {
- return new Error('No artifact in selections found')
+ return Promise.reject(new Error('No artifact in selections found'))
}
+
await applyUnionFromTargetOperatorSelections(solids, {
kclManager,
codeManager,
@@ -2982,12 +3051,14 @@ export const modelingMachine = setup({
input: ModelingCommandSchema['Boolean Union'] | undefined
}) => {
if (!input) {
- return new Error('No input provided')
+ return Promise.reject(new Error(NO_INPUT_PROVIDED_MESSAGE))
}
+
const { solids } = input
if (!solids.graphSelections[0].artifact) {
- return new Error('No artifact in selections found')
+ return Promise.reject(new Error('No artifact in selections found'))
}
+
await applyIntersectFromTargetOperatorSelections(solids, {
kclManager,
codeManager,
@@ -2996,6 +3067,8 @@ export const modelingMachine = setup({
})
}
),
+
+ /* Pierre: looks like somewhat of a one-off */
'reeval-node-paths': fromPromise(
async ({
input: { sketchDetails },
@@ -3096,50 +3169,74 @@ export const modelingMachine = setup({
target: 'animating to existing sketch',
guard: 'Selection is on face',
},
- 'Sketch no face',
+ {
+ target: 'Sketch no face',
+ guard: 'no kcl errors',
+ },
],
Extrude: {
target: 'Applying extrude',
reenter: true,
- },
-
- Revolve: {
- target: 'Applying revolve',
- reenter: true,
+ guard: 'no kcl errors',
},
Sweep: {
target: 'Applying sweep',
reenter: true,
+ guard: 'no kcl errors',
},
Loft: {
target: 'Applying loft',
reenter: true,
+ guard: 'no kcl errors',
+ },
+
+ Revolve: {
+ target: 'Applying revolve',
+ reenter: true,
+ guard: 'no kcl errors',
+ },
+
+ 'Offset plane': {
+ target: 'Applying offset plane',
+ reenter: true,
+ guard: 'no kcl errors',
+ },
+
+ Helix: {
+ target: 'Applying helix',
+ reenter: true,
+ guard: 'no kcl errors',
},
Shell: {
target: 'Applying shell',
reenter: true,
+ guard: 'no kcl errors',
},
Fillet: {
target: 'Applying fillet',
reenter: true,
+ guard: 'no kcl errors',
},
Chamfer: {
target: 'Applying chamfer',
reenter: true,
+ guard: 'no kcl errors',
},
'event.parameter.create': {
target: '#Modeling.parameter.creating',
+ guard: 'no kcl errors',
},
'event.parameter.edit': {
target: '#Modeling.parameter.editing',
+ guard: 'no kcl errors',
},
Export: {
@@ -3164,41 +3261,44 @@ export const modelingMachine = setup({
actions: ['Submit to Text-to-CAD API'],
},
- 'Offset plane': {
- target: 'Applying offset plane',
- reenter: true,
- },
-
- Helix: {
- target: 'Applying helix',
- reenter: true,
- },
-
'Prompt-to-edit': 'Applying Prompt-to-edit',
Appearance: {
target: 'Applying appearance',
reenter: true,
+ guard: 'no kcl errors',
},
Translate: {
target: 'Applying translate',
reenter: true,
+ guard: 'no kcl errors',
},
Rotate: {
target: 'Applying rotate',
reenter: true,
+ guard: 'no kcl errors',
},
Clone: {
target: 'Applying clone',
reenter: true,
+ guard: 'no kcl errors',
},
- 'Boolean Subtract': 'Boolean subtracting',
- 'Boolean Union': 'Boolean uniting',
- 'Boolean Intersect': 'Boolean intersecting',
+ 'Boolean Subtract': {
+ target: 'Boolean subtracting',
+ guard: 'no kcl errors',
+ },
+ 'Boolean Union': {
+ target: 'Boolean uniting',
+ guard: 'no kcl errors',
+ },
+ 'Boolean Intersect': {
+ target: 'Boolean intersecting',
+ guard: 'no kcl errors',
+ },
},
entry: 'reset client scene mouse handlers',
@@ -4415,6 +4515,38 @@ export const modelingMachine = setup({
},
},
+ 'Applying sweep': {
+ invoke: {
+ src: 'sweepAstMod',
+ id: 'sweepAstMod',
+ input: ({ event }) => {
+ if (event.type !== 'Sweep') return undefined
+ return event.data
+ },
+ onDone: ['idle'],
+ onError: {
+ target: 'idle',
+ actions: 'toastError',
+ },
+ },
+ },
+
+ 'Applying loft': {
+ invoke: {
+ src: 'loftAstMod',
+ id: 'loftAstMod',
+ input: ({ event }) => {
+ if (event.type !== 'Loft') return undefined
+ return event.data
+ },
+ onDone: ['idle'],
+ onError: {
+ target: 'idle',
+ actions: 'toastError',
+ },
+ },
+ },
+
'Applying revolve': {
invoke: {
src: 'revolveAstMod',
@@ -4440,7 +4572,10 @@ export const modelingMachine = setup({
return event.data
},
onDone: ['idle'],
- onError: ['idle'],
+ onError: {
+ target: 'idle',
+ actions: 'toastError',
+ },
},
},
@@ -4453,33 +4588,10 @@ export const modelingMachine = setup({
return event.data
},
onDone: ['idle'],
- onError: ['idle'],
- },
- },
-
- 'Applying sweep': {
- invoke: {
- src: 'sweepAstMod',
- id: 'sweepAstMod',
- input: ({ event }) => {
- if (event.type !== 'Sweep') return undefined
- return event.data
+ onError: {
+ target: 'idle',
+ actions: 'toastError',
},
- onDone: ['idle'],
- onError: ['idle'],
- },
- },
-
- 'Applying loft': {
- invoke: {
- src: 'loftAstMod',
- id: 'loftAstMod',
- input: ({ event }) => {
- if (event.type !== 'Loft') return undefined
- return event.data
- },
- onDone: ['idle'],
- onError: ['idle'],
},
},
@@ -4492,7 +4604,10 @@ export const modelingMachine = setup({
return event.data
},
onDone: ['idle'],
- onError: ['idle'],
+ onError: {
+ target: 'idle',
+ actions: 'toastError',
+ },
},
},
@@ -4505,7 +4620,10 @@ export const modelingMachine = setup({
return event.data
},
onDone: ['idle'],
- onError: ['idle'],
+ onError: {
+ target: 'idle',
+ actions: 'toastError',
+ },
},
},
@@ -4518,7 +4636,10 @@ export const modelingMachine = setup({
return event.data
},
onDone: ['idle'],
- onError: ['idle'],
+ onError: {
+ target: 'idle',
+ actions: 'toastError',
+ },
},
},
@@ -4535,7 +4656,10 @@ export const modelingMachine = setup({
return event.data
},
onDone: ['#Modeling.idle'],
- onError: ['#Modeling.idle'],
+ onError: {
+ target: '#Modeling.idle',
+ actions: 'toastError',
+ },
},
},
editing: {
@@ -4547,7 +4671,10 @@ export const modelingMachine = setup({
return event.data
},
onDone: ['#Modeling.idle'],
- onError: ['#Modeling.idle'],
+ onError: {
+ target: '#Modeling.idle',
+ actions: 'toastError',
+ },
},
},
},
@@ -4604,7 +4731,10 @@ export const modelingMachine = setup({
return event.data
},
onDone: ['idle'],
- onError: ['idle'],
+ onError: {
+ target: 'idle',
+ actions: 'toastError',
+ },
},
},
@@ -4617,7 +4747,10 @@ export const modelingMachine = setup({
return event.data
},
onDone: ['idle'],
- onError: ['idle'],
+ onError: {
+ target: 'idle',
+ actions: 'toastError',
+ },
},
},
@@ -4630,7 +4763,10 @@ export const modelingMachine = setup({
return event.data
},
onDone: ['idle'],
- onError: ['idle'],
+ onError: {
+ target: 'idle',
+ actions: 'toastError',
+ },
},
},
@@ -4686,7 +4822,10 @@ export const modelingMachine = setup({
input: ({ event }) =>
event.type !== 'Boolean Subtract' ? undefined : event.data,
onDone: 'idle',
- onError: 'idle',
+ onError: {
+ target: 'idle',
+ actions: 'toastError',
+ },
},
},
@@ -4697,7 +4836,10 @@ export const modelingMachine = setup({
input: ({ event }) =>
event.type !== 'Boolean Union' ? undefined : event.data,
onDone: 'idle',
- onError: 'idle',
+ onError: {
+ target: 'idle',
+ actions: 'toastError',
+ },
},
},
@@ -4708,7 +4850,10 @@ export const modelingMachine = setup({
input: ({ event }) =>
event.type !== 'Boolean Intersect' ? undefined : event.data,
onDone: 'idle',
- onError: 'idle',
+ onError: {
+ target: 'idle',
+ actions: 'toastError',
+ },
},
},
},