ArtifactMap should be processed at the end of an execution (PART 2) (#3121)

* seperate out artifactmap functions into seperate file, change types quiet a bit with e2e still passing

* more type changes

* another increment

* cull artifact map

* remove excessive parentIds

* rename props

* final clean up

* unused vars
This commit is contained in:
Kurt Hutten
2024-07-25 19:03:56 +10:00
committed by GitHub
parent c184a7d4d8
commit 95781143eb
12 changed files with 363 additions and 290 deletions

View File

@ -26,7 +26,7 @@ import * as TOML from '@iarna/toml'
import { LineInputsType } from 'lang/std/sketchcombos'
import { Coords2d } from 'lang/std/sketch'
import { KCL_DEFAULT_LENGTH } from 'lib/constants'
import { EngineCommand } from 'lang/std/engineConnection'
import { EngineCommand } from 'lang/std/artifactMap'
import { onboardingPaths } from 'routes/Onboarding/paths'
import { bracket } from 'lib/exampleKcl'

View File

@ -1,5 +1,5 @@
import { test, expect, Page, Download } from '@playwright/test'
import { EngineCommand } from '../../src/lang/std/engineConnection'
import { expect, Page, Download } from '@playwright/test'
import { EngineCommand } from 'lang/std/artifactMap'
import os from 'os'
import fsp from 'fs/promises'
import pixelMatch from 'pixelmatch'

View File

@ -2,7 +2,7 @@ import { MouseEventHandler, useEffect, useMemo, useRef } from 'react'
import { uuidv4 } from 'lib/utils'
import { useHotKeyListener } from './hooks/useHotKeyListener'
import { Stream } from './components/Stream'
import { EngineCommand } from './lang/std/engineConnection'
import { EngineCommand } from 'lang/std/artifactMap'
import { throttle } from './lib/utils'
import { AppHeader } from './components/AppHeader'
import { useHotkeys } from 'react-hotkeys-hook'

View File

@ -17,11 +17,11 @@ import {
ZOOM_MAGIC_NUMBER,
} from './sceneInfra'
import {
EngineCommand,
Subscription,
EngineCommandManager,
UnreliableSubscription,
} from 'lang/std/engineConnection'
import { EngineCommand } from 'lang/std/artifactMap'
import { uuidv4 } from 'lib/utils'
import { deg2Rad } from 'lib/utils2d'
import { isReducedMotion, roundOff, throttle } from 'lib/utils'

View File

@ -95,10 +95,7 @@ import { createGridHelper, orthoScale, perspScale } from './helpers'
import { Models } from '@kittycad/lib'
import { uuidv4 } from 'lib/utils'
import { SegmentOverlayPayload, SketchDetails } from 'machines/modelingMachine'
import {
ArtifactMapCommand,
EngineCommandManager,
} from 'lang/std/engineConnection'
import { EngineCommandManager } from 'lang/std/engineConnection'
import {
getRectangleCallExpressions,
updateRectangleSketch,
@ -1562,25 +1559,24 @@ export class SceneEntities {
// to get the sketch profile's face ID. If we clicked on an endcap,
// we already have it.
const targetId =
'additionalData' in artifact &&
artifact.additionalData?.type === 'cap'
? _entity_id
: artifact.parentId
artifact?.type === 'extrudeWall' || artifact?.type === 'extrudeCap'
? artifact.segmentId
: ''
// tsc cannot infer that target can have extrusions
// from the commandType (why?) so we need to cast it
const target = this.engineCommandManager.artifactMap?.[
targetId || ''
] as ArtifactMapCommand & { extrusions?: string[] }
const target = this.engineCommandManager.artifactMap?.[targetId || '']
const extrusionId =
target?.type === 'startPath' ? target.extrusions[0] : ''
// TODO: We get the first extrusion command ID,
// which is fine while backend systems only support one extrusion.
// but we need to more robustly handle resolving to the correct extrusion
// if there are multiple.
const extrusions =
this.engineCommandManager.artifactMap?.[target?.extrusions?.[0] || '']
const extrusions = this.engineCommandManager.artifactMap?.[extrusionId]
if (artifact?.commandType !== 'solid3d_get_extrusion_face_info') return
if (artifact?.type !== 'extrudeCap' && artifact?.type !== 'extrudeWall')
return
const faceInfo = await getFaceDetails(_entity_id)
if (!faceInfo?.origin || !faceInfo?.z_axis || !faceInfo?.y_axis) return
@ -1606,7 +1602,7 @@ export class SceneEntities {
sketchPathToNode,
extrudePathToNode,
cap:
artifact?.additionalData?.type === 'cap'
artifact.type === 'extrudeCap'
? artifact.additionalData.info
: 'none',
faceId: _entity_id,

View File

@ -5,7 +5,6 @@ import { uuidv4 } from 'lib/utils'
import { EngineCommandManager } from './std/engineConnection'
import { err } from 'lib/trap'
import { deferExecution } from 'lib/utils'
import {
CallExpression,
initPromise,

280
src/lang/std/artifactMap.ts Normal file
View File

@ -0,0 +1,280 @@
import { PathToNode, Program, SourceRange } from 'lang/wasm'
import { Models } from '@kittycad/lib'
import { getNodePathFromSourceRange } from 'lang/queryAst'
interface CommonCommandProperties {
range: SourceRange
pathToNode: PathToNode
}
interface ExtrudeArtifact extends CommonCommandProperties {
type: 'extrude'
target: string
}
export interface StartPathArtifact extends CommonCommandProperties {
type: 'startPath'
extrusions: string[]
}
export interface SegmentArtifact extends CommonCommandProperties {
type: 'segment'
subType: 'segment' | 'closeSegment'
pathId: string
}
interface ExtrudeCapArtifact extends CommonCommandProperties {
type: 'extrudeCap'
additionalData: {
type: 'cap'
info: 'start' | 'end'
}
segmentId: string
}
interface ExtrudeWallArtifact extends CommonCommandProperties {
type: 'extrudeWall'
segmentId: string
}
interface PatternInstance extends CommonCommandProperties {
type: 'patternInstance'
}
export type ArtifactMapCommand =
| ExtrudeArtifact
| StartPathArtifact
| ExtrudeCapArtifact
| ExtrudeWallArtifact
| SegmentArtifact
| PatternInstance
export type EngineCommand = Models['WebSocketRequest_type']
type OkWebSocketResponseData = Models['OkWebSocketResponseData_type']
/**
* The ArtifactMap is a client-side representation of the artifacts that
* have been sent to the server-side engine. It is used to keep track of
* the state of each command, and to resolve the promise that was returned.
* It is also used to keep track of what entities are in the engine scene,
* so that we can associate IDs returned from the engine with the
* lines of KCL code that generated them.
*/
export interface ArtifactMap {
[commandId: string]: ArtifactMapCommand
}
export interface ResponseMap {
[commandId: string]: OkWebSocketResponseData
}
export interface OrderedCommand {
command: EngineCommand
range: SourceRange
}
export function createArtifactMap({
orderedCommands,
responseMap,
ast,
}: {
orderedCommands: Array<OrderedCommand>
responseMap: ResponseMap
ast: Program
}): ArtifactMap {
const artifactMap: ArtifactMap = {}
orderedCommands.forEach(({ command, range }) => {
// expect all to be `modeling_cmd_req` as batch commands have
// already been expanded before being added to orderedCommands
if (command.type !== 'modeling_cmd_req') return
const id = command.cmd_id
const response = responseMap[id]
const artifacts = handleIndividualResponse({
id,
pendingMsg: {
command,
range,
},
response,
ast,
prevArtifactMap: artifactMap,
})
artifacts.forEach(({ commandId, artifact }) => {
artifactMap[commandId] = artifact
})
})
return artifactMap
}
function handleIndividualResponse({
id,
pendingMsg,
response,
ast,
prevArtifactMap,
}: {
id: string
pendingMsg: {
command: EngineCommand
range: SourceRange
}
response: OkWebSocketResponseData
ast: Program
prevArtifactMap: ArtifactMap
}): Array<{
commandId: string
artifact: ArtifactMapCommand
}> {
const command = pendingMsg
if (command?.command?.type !== 'modeling_cmd_req') return []
if (response?.type !== 'modeling') return []
const command2 = command.command.cmd
const range = command.range
const pathToNode = getNodePathFromSourceRange(ast, range)
const modelingResponse = response.data.modeling_response
const artifacts: Array<{
commandId: string
artifact: ArtifactMapCommand
}> = []
if (command) {
if (
command2.type !== 'extrude' &&
command2.type !== 'extend_path' &&
command2.type !== 'solid3d_get_extrusion_face_info' &&
command2.type !== 'start_path' &&
command2.type !== 'close_path'
) {
}
if (command2.type === 'extrude') {
artifacts.push({
commandId: id,
artifact: {
type: 'extrude',
range,
pathToNode,
target: command2.target,
},
})
const targetArtifact = { ...prevArtifactMap[command2.target] }
if (targetArtifact?.type === 'startPath') {
artifacts.push({
commandId: command2.target,
artifact: {
...targetArtifact,
type: 'startPath',
range: targetArtifact.range,
pathToNode: targetArtifact.pathToNode,
extrusions: targetArtifact?.extrusions
? [...targetArtifact?.extrusions, id]
: [id],
},
})
}
}
if (command2.type === 'extend_path') {
artifacts.push({
commandId: id,
artifact: {
type: 'segment',
subType: 'segment',
range,
pathToNode,
pathId: command2.path,
},
})
}
if (command2.type === 'close_path')
artifacts.push({
commandId: id,
artifact: {
type: 'segment',
subType: 'closeSegment',
range,
pathToNode,
pathId: command2.path_id,
},
})
if (command2.type === 'start_path') {
artifacts.push({
commandId: id,
artifact: {
type: 'startPath',
range,
pathToNode,
extrusions: [],
},
})
}
if (
(command2.type === 'entity_linear_pattern' &&
modelingResponse.type === 'entity_linear_pattern') ||
(command2.type === 'entity_circular_pattern' &&
modelingResponse.type === 'entity_circular_pattern')
) {
// TODO this is not working perfectly, maybe it's like a selection filter issue
// but when clicking on a instance it does put the cursor somewhat relevant but
// edges and what not do not highlight the correct segment.
const entities = modelingResponse.data.entity_ids
entities?.forEach((entity: string) => {
artifacts.push({
commandId: entity,
artifact: {
range: range,
pathToNode,
type: 'patternInstance',
},
})
})
}
if (
command2.type === 'solid3d_get_extrusion_face_info' &&
modelingResponse.type === 'solid3d_get_extrusion_face_info'
) {
const edgeArtifact = prevArtifactMap[command2.edge_id]
const parent =
edgeArtifact?.type === 'segment'
? prevArtifactMap[edgeArtifact.pathId]
: null
modelingResponse.data.faces.forEach((face) => {
if (
face.cap !== 'none' &&
face.face_id &&
parent?.type === 'startPath'
) {
artifacts.push({
commandId: face.face_id,
artifact: {
// ...parent,
type: 'extrudeCap',
additionalData: {
type: 'cap',
info: face.cap === 'bottom' ? 'start' : 'end',
},
range: parent.range,
pathToNode: parent.pathToNode,
segmentId:
edgeArtifact?.type === 'segment' ? edgeArtifact.pathId : '',
},
})
}
const curveArtifact = prevArtifactMap[face?.curve_id || '']
if (curveArtifact?.type === 'segment' && face?.face_id) {
artifacts.push({
commandId: face.face_id,
artifact: {
...curveArtifact,
type: 'extrudeWall',
range: curveArtifact.range,
pathToNode: curveArtifact.pathToNode,
segmentId: curveArtifact.pathId,
},
})
}
})
}
}
return artifacts
}

View File

@ -1,101 +1,29 @@
import { PathToNode, Program, SourceRange } from 'lang/wasm'
import { Program, SourceRange } from 'lang/wasm'
import { VITE_KC_API_WS_MODELING_URL } from 'env'
import { Models } from '@kittycad/lib'
import { exportSave } from 'lib/exportSave'
import { uuidv4 } from 'lib/utils'
import { getNodePathFromSourceRange } from 'lang/queryAst'
import { Themes, getThemeColorForEngine, getOppositeTheme } from 'lib/theme'
import { DefaultPlanes } from 'wasm-lib/kcl/bindings/DefaultPlanes'
import {
ArtifactMap,
EngineCommand,
OrderedCommand,
ResponseMap,
createArtifactMap,
} from 'lang/std/artifactMap'
// TODO(paultag): This ought to be tweakable.
const pingIntervalMs = 10000
type CommandTypes = Models['ModelingCmd_type']['type'] | 'batch'
type CommandInfo =
| {
commandType: 'extrude'
// commandType: CommandTypes
range: SourceRange
pathToNode: PathToNode
/// uuid of the entity to extrude
target: string
parentId?: string
}
| {
commandType: 'start_path'
// commandType: CommandTypes
range: SourceRange
pathToNode: PathToNode
/// uuid of the entity that have been extruded
extrusions: string[]
parentId?: string
}
| {
commandType: CommandTypes
range: SourceRange
pathToNode: PathToNode
parentId?: string
additionalData?:
| {
type: 'cap'
info: 'start' | 'end'
}
| {
type: 'batch-ids'
ids: string[]
info?: null
}
}
function isHighlightSetEntity_type(
data: any
): data is Models['HighlightSetEntity_type'] {
return data.entity_id && data.sequence
}
type WebSocketResponse = Models['WebSocketResponse_type']
type OkWebSocketResponseData = Models['OkWebSocketResponseData_type']
type ResultCommand = CommandInfo & {
type: 'result'
data: any
raw: WebSocketResponse
headVertexId?: string
}
type FailedCommand = CommandInfo & {
type: 'failed'
errors: Models['FailureWebSocketResponse_type']['errors']
}
interface ResolveCommand {
id: string
commandType: CommandTypes
range: SourceRange
// We ALWAYS need the raw response because we pass it back to the rust side.
raw: WebSocketResponse
data?: Models['OkModelingCmdResponse_type']
errors?: Models['FailureWebSocketResponse_type']['errors']
}
type PendingCommand = CommandInfo & {
type: 'pending'
promise: Promise<any>
resolve: (val: ResolveCommand) => void
}
export type ArtifactMapCommand = ResultCommand | PendingCommand | FailedCommand
/**
* The ArtifactMap is a client-side representation of the artifacts that
* have been sent to the server-side engine. It is used to keep track of
* the state of each command, and to resolve the promise that was returned.
* It is also used to keep track of what entities are in the engine scene,
* so that we can associate IDs returned from the engine with the
* lines of KCL code that generated them.
*/
export interface ArtifactMap {
[commandId: string]: ArtifactMapCommand
}
interface NewTrackArgs {
conn: EngineConnection
mediaStream: MediaStream
@ -877,7 +805,7 @@ class EngineConnection extends EventTarget {
this.engineCommandManager.artifactMap[message.request_id]
console.error(
`Error in response to request ${message.request_id}:\n${errorsString}
failed cmd type was ${artifactThatFailed?.commandType}`
failed cmd type was ${artifactThatFailed?.type}`
)
} else {
console.error(`Error from server:\n${errorsString}`)
@ -1109,7 +1037,6 @@ class EngineConnection extends EventTarget {
}
}
export type EngineCommand = Models['WebSocketRequest_type']
type ModelTypes = Models['OkModelingCmdResponse_type']['type']
type UnreliableResponses = Extract<
@ -1196,15 +1123,12 @@ export class EngineCommandManager extends EventTarget {
* The orderedCommands array of all the the commands sent to the engine, un-folded from batches, and made into one long
* list of the individual commands, this is used to process all the commands into the artifactMap
*/
orderedCommands: {
command: EngineCommand
range: SourceRange
}[] = []
orderedCommands: Array<OrderedCommand> = []
/**
* A map of the responses to the @this.orderedCommands, when processing the commands into the artifactMap, this response map allow
* us to look up the response by command id
*/
responseMap: { [commandId: string]: OkWebSocketResponseData } = {}
responseMap: ResponseMap = {}
/**
* The client-side representation of the scene command artifacts that have been sent to the server;
* that is, the *non-modeling* commands and corresponding artifacts.
@ -1515,9 +1439,9 @@ export class EngineCommandManager extends EventTarget {
}
})
Object.entries(message.resp.data.responses).forEach(
([key, response]) => {
([commandId, response]) => {
if (!('response' in response)) return
const command = individualPendingResponses[key]
const command = individualPendingResponses[commandId]
if (!command) return
if (command.type === 'modeling_cmd_req')
this.addCommandLog({
@ -1528,11 +1452,11 @@ export class EngineCommandManager extends EventTarget {
modeling_response: response.response,
},
},
id: key,
id: commandId,
cmd_type: command?.cmd?.type,
})
this.responseMap[key] = {
this.responseMap[commandId] = {
type: 'modeling',
data: {
modeling_response: response.response,
@ -1569,106 +1493,6 @@ export class EngineCommandManager extends EventTarget {
this.onEngineConnectionStarted
)
}
handleIndividualResponse({
id,
pendingMsg,
response,
}: {
id: string
pendingMsg: {
command: EngineCommand
range: SourceRange
}
response: OkWebSocketResponseData
}) {
const command = pendingMsg
if (command?.command?.type !== 'modeling_cmd_req') return
if (response?.type !== 'modeling') return
const command2 = command.command.cmd
const range = command.range
const pathToNode = getNodePathFromSourceRange(this.getAst(), range)
const getParentId = (): string | undefined => {
if (command2.type === 'extend_path') return command2.path
if (command2.type === 'solid3d_get_extrusion_face_info') {
const edgeArtifact = this.artifactMap[command2.edge_id]
// edges's parent id is to the original "start_path" artifact
if (edgeArtifact && edgeArtifact.parentId) {
return edgeArtifact.parentId
}
}
if (command2.type === 'close_path') return command2.path_id
if (command2.type === 'extrude') return command2.target
// handle other commands that have a parent here
}
const modelingResponse = response.data.modeling_response
if (command) {
const parentId = getParentId()
const artifact = {
type: 'result',
range: range,
pathToNode,
commandType: command.command.cmd.type,
parentId: parentId,
} as ArtifactMapCommand & { extrusions?: string[] }
this.artifactMap[id] = artifact
if (command2.type === 'extrude') {
;(artifact as any).target = command2.target
if (this.artifactMap[command2.target]?.commandType === 'start_path') {
if ((this.artifactMap[command2.target] as any)?.extrusions?.length) {
;(this.artifactMap[command2.target] as any).extrusions.push(id)
} else {
;(this.artifactMap[command2.target] as any).extrusions = [id]
}
}
}
this.artifactMap[id] = artifact
if (
(command2.type === 'entity_linear_pattern' &&
modelingResponse.type === 'entity_linear_pattern') ||
(command2.type === 'entity_circular_pattern' &&
modelingResponse.type === 'entity_circular_pattern')
) {
const entities = modelingResponse.data.entity_ids
entities?.forEach((entity: string) => {
this.artifactMap[entity] = artifact
})
}
if (
command2.type === 'solid3d_get_extrusion_face_info' &&
modelingResponse.type === 'solid3d_get_extrusion_face_info'
) {
const parent = this.artifactMap[parentId || '']
modelingResponse.data.faces.forEach((face) => {
if (face.cap !== 'none' && face.face_id && parent) {
this.artifactMap[face.face_id] = {
...parent,
commandType: 'solid3d_get_extrusion_face_info',
additionalData: {
type: 'cap',
info: face.cap === 'bottom' ? 'start' : 'end',
},
}
}
const curveArtifact = this.artifactMap[face?.curve_id || '']
if (curveArtifact && face?.face_id) {
this.artifactMap[face.face_id] = {
...curveArtifact,
commandType: 'solid3d_get_extrusion_face_info',
}
}
})
}
} else if (command) {
this.artifactMap[id] = {
type: 'result',
commandType: command2.type,
range,
pathToNode,
} as ArtifactMapCommand & { extrusions?: string[] }
}
}
handleResize({
streamWidth,
@ -1965,29 +1789,11 @@ export class EngineCommandManager extends EventTarget {
* When this is done when we build the artifact map synchronously.
*/
async waitForAllCommands() {
const pendingCommands = Object.values(this.artifactMap).filter(
({ type }) => type === 'pending'
) as PendingCommand[]
const proms = pendingCommands.map(({ promise }) => promise)
const otherPending = Object.values(this.pendingCommands).map(
(a) => a.promise
)
await Promise.all([...proms, otherPending])
this.orderedCommands.forEach(({ command, range }) => {
// expect all to be `modeling_cmd_req` as batch commands have
// already been expanded before being added to orderedCommands
if (command.type !== 'modeling_cmd_req') return
const id = command.cmd_id
const response = this.responseMap[id]
this.handleIndividualResponse({
id,
pendingMsg: {
command,
range,
},
response,
})
await Promise.all(Object.values(this.pendingCommands).map((a) => a.promise))
this.artifactMap = createArtifactMap({
orderedCommands: this.orderedCommands,
responseMap: this.responseMap,
ast: this.getAst(),
})
}
private async initPlanes() {
@ -2047,13 +1853,11 @@ export class EngineCommandManager extends EventTarget {
): string | undefined {
const values = Object.entries(this.artifactMap)
for (const [id, data] of values) {
if (data.type !== 'result') continue
// Our range selection seems to just select the cursor position, so either
// of these can be right...
// // Our range selection seems to just select the cursor position, so either
// // of these can be right...
if (
(data.range[0] === range[0] || data.range[1] === range[1]) &&
data.commandType === commandTypeToTarget
data.type === commandTypeToTarget
)
return id
}

View File

@ -1,7 +1,12 @@
import { Selections } from 'lib/selections'
import { Program, PathToNode } from './wasm'
import { getNodeFromPath } from './queryAst'
import { ArtifactMap } from './std/engineConnection'
import {
ArtifactMap,
ArtifactMapCommand,
SegmentArtifact,
StartPathArtifact,
} from 'lang/std/artifactMap'
import { isOverlap } from 'lib/utils'
import { err } from 'lib/trap'
@ -49,24 +54,22 @@ export function isCursorInSketchCommandRange(
artifactMap: ArtifactMap,
selectionRanges: Selections
): string | false {
const overlapingEntries: [string, ArtifactMap[string]][] = Object.entries(
artifactMap
).filter(([id, artifact]: [string, ArtifactMap[string]]) =>
const overlappingEntries = Object.entries(artifactMap).filter(
([id, artifact]: [string, ArtifactMapCommand]) =>
selectionRanges.codeBasedSelections.some(
(selection) =>
Array.isArray(selection?.range) &&
Array.isArray(artifact?.range) &&
isOverlap(selection.range, artifact.range) &&
(artifact.commandType === 'start_path' ||
artifact.commandType === 'extend_path' ||
artifact.commandType === 'close_path')
(artifact.type === 'startPath' || artifact.type === 'segment')
)
)
let result =
overlapingEntries.length && overlapingEntries[0][1].parentId
? overlapingEntries[0][1].parentId
: overlapingEntries.find(
([, artifact]) => artifact.commandType === 'start_path'
) as [string, StartPathArtifact | SegmentArtifact][]
const secondEntry = overlappingEntries?.[0]?.[1]
const parentId = secondEntry?.type === 'segment' ? secondEntry.pathId : false
let result = parentId
? parentId
: overlappingEntries.find(
([, artifact]) => artifact.type === 'startPath'
)?.[0] || false
return result
}

View File

@ -108,8 +108,7 @@ export async function getEventForSelectWithPoint(
}
const sourceRange = _artifact?.range
if (_artifact) {
if (_artifact.commandType === 'solid3d_get_extrusion_face_info') {
if (_artifact?.additionalData)
if (_artifact.type === 'extrudeCap')
return {
type: 'Set selection',
data: {
@ -123,6 +122,7 @@ export async function getEventForSelectWithPoint(
},
},
}
if (_artifact.type === 'extrudeWall')
return {
type: 'Set selection',
data: {
@ -130,7 +130,6 @@ export async function getEventForSelectWithPoint(
selection: { range: sourceRange, type: 'extrude-wall' },
},
}
}
return {
type: 'Set selection',
data: {
@ -519,16 +518,13 @@ function codeToIdSelections(
let bestCandidate
entriesWithOverlap.forEach((entry) => {
if (!entry) return
if (
type === 'default' &&
entry.artifact.commandType === 'extend_path'
) {
if (type === 'default' && entry.artifact.type === 'segment') {
bestCandidate = entry
return
}
if (
type === 'start-cap' &&
entry.artifact.commandType === 'solid3d_get_extrusion_face_info' &&
entry.artifact.type === 'extrudeCap' &&
entry?.artifact?.additionalData?.info === 'start'
) {
bestCandidate = entry
@ -536,16 +532,13 @@ function codeToIdSelections(
}
if (
type === 'end-cap' &&
entry.artifact.commandType === 'solid3d_get_extrusion_face_info' &&
entry.artifact.type === 'extrudeCap' &&
entry?.artifact?.additionalData?.info === 'end'
) {
bestCandidate = entry
return
}
if (
type === 'extrude-wall' &&
entry.artifact.commandType === 'solid3d_get_extrusion_face_info'
) {
if (type === 'extrude-wall' && entry.artifact.type === 'extrudeWall') {
bestCandidate = entry
return
}

View File

@ -1,8 +1,6 @@
import { Program, ProgramMemory, _executor, SourceRange } from '../lang/wasm'
import {
EngineCommandManager,
EngineCommand,
} from '../lang/std/engineConnection'
import { EngineCommandManager } from 'lang/std/engineConnection'
import { EngineCommand } from 'lang/std/artifactMap'
import { Models } from '@kittycad/lib'
import { v4 as uuidv4 } from 'uuid'
import { DefaultPlanes } from 'wasm-lib/kcl/bindings/DefaultPlanes'

View File

@ -1142,7 +1142,7 @@ export const modelingMachine = createMachine(
const sketchGroup = kclManager.programMemory.get(sketchVar)
if (sketchGroup?.type !== 'SketchGroup') return
const idArtifact = engineCommandManager.artifactMap[sketchGroup.id]
if (idArtifact.commandType !== 'start_path') return
if (idArtifact.type !== 'startPath') return
const extrusionArtifactId = (idArtifact as any)?.extrusions?.[0]
if (typeof extrusionArtifactId !== 'string') return
const extrusionArtifact = (engineCommandManager.artifactMap as any)[