Set apperance in feature tree context menu (#5439)
* Revert "Revert multi-profile (#4812)" This reverts commitefe8089b08
. * fix poor 1000ms wait UX * A snapshot a day keeps the bugs away! 📷🐛 (OS: ubuntu-latest-8-cores) * trigger CI * Add Rust side artifacts for startSketchOn face or plane (#4834) * Add Rust side artifacts for startSketchOn face or plane * move ast digging --------- Co-authored-by: Kurt Hutten Irev-Dev <k.hutten@protonmail.ch> * lint * lint * A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-macos-8-cores) * A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores) * A snapshot a day keeps the bugs away! 📷🐛 (OS: windows-16-cores) * trigger CI * chore: disabled file watcher which prevents faster file write (#4835) * A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores) * partial fixes * A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores) * Trigger CI * A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores) * Trigger CI * Fix up all the tests * Fix partial execution * wip * WIP * wip * rust changes to make three point confrom to same as others since we're not ready with name params yet * most of the fix for 3 point circle * get overlays working for circle three point * fmt * fix types * cargo fmt * add face codef ref for walls and caps * fix sketch on face after updates to rust side artifact graph * some things needed for multi-profile tests * bad attempts at fixing rust * more * more * fix rust * more rust fixes * overlay fix * remove duplicate test * A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores) * lint and typing * maybe fix a unit test * small thing * WIP: Add Delete right click menu item to Feature Tree Copying code around Fixes #5090 * I don't know why it works * WIP * fix circ dep * fix unit test * fix some tests * A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores) * A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores) * Working deletion machine loo * Working helix deletion * Extend deletion to more things * A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores) * A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores) * fix sweep point-and-click test * fix more tests and add a fix me * fix more tests * fix electron specific test * tsc * more test tweaks * update docs * commint snaps? * is clippy happy now? * clippy again * test works now without me changing anything big-fixed-itself * small bug * make three point have cross hair to make it consistent with othe rtools * fix up state diagram * fmt * add draft point for first click of three point circ * 1 test for three point circle * 2 test for three point circle * clean up * A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores) * A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores) * remove bad doc comment * remove test skip * remove onboarding test changes * Update src/lang/modifyAst.ts Co-authored-by: Jonathan Tran <jonnytran@gmail.com> * Update output from simulation tests * Fix to use correct source ranges This also reduces cloning. * Change back to skipping face cap none and both * Update output after changing back to skipping none and both * Fix clippy warning * fix profile start snap bug * WIP: migrate to actor * add path ids to cap * fix going into edit sketch * make other startSketchOn's work * fix snapshot test * explain function name * Update src/lib/rectangleTool.ts Co-authored-by: Frank Noirot <frank@zoo.dev> * rename error * remove file tree from diff * Update src/clientSideScene/segments.ts Co-authored-by: Frank Noirot <frank@zoo.dev> * nit * Continue actor migration * Prevent double write to KCL code on revolve * Clean up * Update output after adding cap-to-path graph edge * Clean up * Update machine diag * Update context menu hotkey class * Fix edit/select sketch-on-cap via feature tree * clean up for face codeRef * fix changing tools part way through circle/rect tools * fix delete of circle profile * fix close profiles * fix closing profile bug (tangentArcTo being ignored) * remove stale comment * Delete paths associated with sketch when the sketch plane is deleted * Add support for deleting sketches on caps (not walls) * get delet working for walls * make delet of extrusions work for multi profile * A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores) * Delete the sketch statement too on the cap and wall cases * Don't write to file in `split-sketch-pipe-if-needed` unless necessary * Don't wait for file write to complete within `updateEditorWithAstAndWriteToFile` It is already debounced internally. If we await it, we will have to wait for a debounced timeout * Fix bad conflict resolution * Fix a few things post merge * Add guard back, fixing tests * A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores) * A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores) * Add e2e test * WIP: Add Set apperance right click menu item to Feature Tree Fixes #5372 * Working cheap implementation * Unset appearance via Default option * More colors * Add basic test * Add test * Lint * 🔪 them timers * Increase color matching threshold on appearance test * Fix colors in e2e * Move Set apperance down in the menu * Revert "Move Set apperance down in the menu" This reverts commiteb1d2e2c1c
. * Attempt at fixing dual extrude for role option search in test --------- Co-authored-by: Kurt Hutten Irev-Dev <k.hutten@protonmail.ch> Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> Co-authored-by: Jonathan Tran <jonnytran@gmail.com> Co-authored-by: Kevin Nadro <nadr0@users.noreply.github.com> Co-authored-by: 49lf <ircsurfer33@gmail.com> Co-authored-by: Frank Noirot <frank@zoo.dev> Co-authored-by: Frank Noirot <frankjohnson1993@gmail.com>
This commit is contained in:
@ -219,7 +219,11 @@ test.describe('Command bar tests', { tag: ['@skipWin'] }, () => {
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
test('Can extrude from the command bar', async ({ page, homePage }) => {
|
test('Can extrude from the command bar', async ({
|
||||||
|
page,
|
||||||
|
homePage,
|
||||||
|
cmdBar,
|
||||||
|
}) => {
|
||||||
await page.addInitScript(async () => {
|
await page.addInitScript(async () => {
|
||||||
localStorage.setItem(
|
localStorage.setItem(
|
||||||
'persistCode',
|
'persistCode',
|
||||||
@ -254,7 +258,7 @@ test.describe('Command bar tests', { tag: ['@skipWin'] }, () => {
|
|||||||
await expect(cmdSearchBar).toBeVisible()
|
await expect(cmdSearchBar).toBeVisible()
|
||||||
|
|
||||||
// Search for extrude command and choose it
|
// Search for extrude command and choose it
|
||||||
await page.getByRole('option', { name: 'Extrude' }).click()
|
await cmdBar.cmdOptions.getByText('Extrude').click()
|
||||||
|
|
||||||
// Assert that we're on the selection step
|
// Assert that we're on the selection step
|
||||||
await expect(page.getByRole('button', { name: 'selection' })).toBeDisabled()
|
await expect(page.getByRole('button', { name: 'selection' })).toBeDisabled()
|
||||||
|
@ -2796,4 +2796,107 @@ radius = 8.69
|
|||||||
expect(editor.expectEditor.toContain(newCodeToFind)).toBeTruthy()
|
expect(editor.expectEditor.toContain(newCodeToFind)).toBeTruthy()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test(`Set appearance`, async ({
|
||||||
|
context,
|
||||||
|
page,
|
||||||
|
homePage,
|
||||||
|
scene,
|
||||||
|
editor,
|
||||||
|
toolbar,
|
||||||
|
cmdBar,
|
||||||
|
}) => {
|
||||||
|
const initialCode = `sketch001 = startSketchOn('XZ')
|
||||||
|
profile001 = circle({
|
||||||
|
center = [0, 0],
|
||||||
|
radius = 100
|
||||||
|
}, sketch001)
|
||||||
|
extrude001 = extrude(profile001, length = 100)
|
||||||
|
`
|
||||||
|
await context.addInitScript((initialCode) => {
|
||||||
|
localStorage.setItem('persistCode', initialCode)
|
||||||
|
}, initialCode)
|
||||||
|
await page.setBodyDimensions({ width: 1000, height: 500 })
|
||||||
|
await homePage.goToModelingScene()
|
||||||
|
await scene.waitForExecutionDone()
|
||||||
|
|
||||||
|
// One dumb hardcoded screen pixel value
|
||||||
|
const testPoint = { x: 500, y: 250 }
|
||||||
|
const initialColor: [number, number, number] = [135, 135, 135]
|
||||||
|
|
||||||
|
await test.step(`Confirm extrude exists with default appearance`, async () => {
|
||||||
|
await toolbar.closePane('code')
|
||||||
|
await scene.expectPixelColor(initialColor, testPoint, 15)
|
||||||
|
})
|
||||||
|
|
||||||
|
async function setApperanceAndCheck(
|
||||||
|
option: string,
|
||||||
|
hex: string,
|
||||||
|
shapeColor: [number, number, number]
|
||||||
|
) {
|
||||||
|
await toolbar.openPane('feature-tree')
|
||||||
|
const operationButton = await toolbar.getFeatureTreeOperation(
|
||||||
|
'Extrude',
|
||||||
|
0
|
||||||
|
)
|
||||||
|
await operationButton.click({ button: 'right' })
|
||||||
|
const menuButton = page.getByTestId('context-menu-set-appearance')
|
||||||
|
await menuButton.click()
|
||||||
|
await cmdBar.expectState({
|
||||||
|
commandName: 'Appearance',
|
||||||
|
currentArgKey: 'color',
|
||||||
|
currentArgValue: '',
|
||||||
|
headerArguments: {
|
||||||
|
Color: '',
|
||||||
|
},
|
||||||
|
highlightedHeaderArg: 'color',
|
||||||
|
stage: 'arguments',
|
||||||
|
})
|
||||||
|
const item = page.getByText(option, { exact: true })
|
||||||
|
await item.click()
|
||||||
|
await cmdBar.expectState({
|
||||||
|
commandName: 'Appearance',
|
||||||
|
headerArguments: {
|
||||||
|
Color: hex,
|
||||||
|
},
|
||||||
|
stage: 'review',
|
||||||
|
})
|
||||||
|
await cmdBar.progressCmdBar()
|
||||||
|
await toolbar.closePane('feature-tree')
|
||||||
|
await scene.expectPixelColor(shapeColor, testPoint, 40)
|
||||||
|
await toolbar.openPane('code')
|
||||||
|
if (hex === 'default') {
|
||||||
|
const anyAppearanceDeclaration = `|> appearance(`
|
||||||
|
await editor.expectEditor.not.toContain(anyAppearanceDeclaration)
|
||||||
|
} else {
|
||||||
|
const declaration = `|> appearance(%, color = '${hex}')`
|
||||||
|
await editor.expectEditor.toContain(declaration)
|
||||||
|
// TODO: fix selection range after appearance update
|
||||||
|
// await editor.expectState({
|
||||||
|
// diagnostics: [],
|
||||||
|
// activeLines: [declaration],
|
||||||
|
// highlightedCode: '',
|
||||||
|
// })
|
||||||
|
}
|
||||||
|
await toolbar.closePane('code')
|
||||||
|
}
|
||||||
|
|
||||||
|
await test.step(`Go through the Set Appearance flow for all options`, async () => {
|
||||||
|
await setApperanceAndCheck('Red', '#FF0000', [180, 0, 0])
|
||||||
|
await setApperanceAndCheck('Green', '#00FF00', [0, 180, 0])
|
||||||
|
await setApperanceAndCheck('Blue', '#0000FF', [0, 0, 180])
|
||||||
|
await setApperanceAndCheck('Turquoise', '#00FFFF', [0, 180, 180])
|
||||||
|
await setApperanceAndCheck('Purple', '#FF00FF', [180, 0, 180])
|
||||||
|
await setApperanceAndCheck('Yellow', '#FFFF00', [180, 180, 0])
|
||||||
|
await setApperanceAndCheck('Black', '#000000', [0, 0, 0])
|
||||||
|
await setApperanceAndCheck('Dark Grey', '#080808', [10, 10, 10])
|
||||||
|
await setApperanceAndCheck('Light Grey', '#D3D3D3', [190, 190, 190])
|
||||||
|
await setApperanceAndCheck('White', '#FFFFFF', [200, 200, 200])
|
||||||
|
await setApperanceAndCheck(
|
||||||
|
'Default (clear appearance)',
|
||||||
|
'default',
|
||||||
|
initialColor
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
Binary file not shown.
Before Width: | Height: | Size: 76 KiB After Width: | Height: | Size: 76 KiB |
Binary file not shown.
Before Width: | Height: | Size: 51 KiB After Width: | Height: | Size: 51 KiB |
@ -324,6 +324,18 @@ const OperationItem = (props: {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function enterAppearanceFlow() {
|
||||||
|
if (props.item.type === 'StdLibCall') {
|
||||||
|
props.send({
|
||||||
|
type: 'enterAppearanceFlow',
|
||||||
|
data: {
|
||||||
|
targetSourceRange: sourceRangeFromRust(props.item.sourceRange),
|
||||||
|
currentOperation: props.item,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function deleteOperation() {
|
function deleteOperation() {
|
||||||
if (
|
if (
|
||||||
props.item.type === 'StdLibCall' ||
|
props.item.type === 'StdLibCall' ||
|
||||||
@ -380,6 +392,13 @@ const OperationItem = (props: {
|
|||||||
: []),
|
: []),
|
||||||
...(props.item.type === 'StdLibCall'
|
...(props.item.type === 'StdLibCall'
|
||||||
? [
|
? [
|
||||||
|
<ContextMenuItem
|
||||||
|
disabled={!stdLibMap[props.item.name]?.supportsAppearance}
|
||||||
|
onClick={enterAppearanceFlow}
|
||||||
|
data-testid="context-menu-set-appearance"
|
||||||
|
>
|
||||||
|
Set appearance
|
||||||
|
</ContextMenuItem>,
|
||||||
<ContextMenuItem
|
<ContextMenuItem
|
||||||
disabled={!stdLibMap[props.item.name]?.prepareToEdit}
|
disabled={!stdLibMap[props.item.name]?.prepareToEdit}
|
||||||
onClick={enterEditFlow}
|
onClick={enterEditFlow}
|
||||||
|
@ -397,10 +397,10 @@ export function getEdgeTagCall(
|
|||||||
return tagCall
|
return tagCall
|
||||||
}
|
}
|
||||||
|
|
||||||
function locateExtrudeDeclarator(
|
export function locateExtrudeDeclarator(
|
||||||
node: Program,
|
node: Program,
|
||||||
pathToExtrudeNode: PathToNode
|
pathToExtrudeNode: PathToNode
|
||||||
): { extrudeDeclarator: VariableDeclarator } | Error {
|
): { extrudeDeclarator: VariableDeclarator; shallowPath: PathToNode } | Error {
|
||||||
const nodeOfExtrudeCall = getNodeFromPath<VariableDeclaration>(
|
const nodeOfExtrudeCall = getNodeFromPath<VariableDeclaration>(
|
||||||
node,
|
node,
|
||||||
pathToExtrudeNode,
|
pathToExtrudeNode,
|
||||||
@ -427,7 +427,7 @@ function locateExtrudeDeclarator(
|
|||||||
return new Error('Extrude must be a PipeExpression or CallExpression')
|
return new Error('Extrude must be a PipeExpression or CallExpression')
|
||||||
}
|
}
|
||||||
|
|
||||||
return { extrudeDeclarator }
|
return { extrudeDeclarator, shallowPath: nodeOfExtrudeCall.shallowPath }
|
||||||
}
|
}
|
||||||
|
|
||||||
function getPathToNodeOfEdgeTreatmentLiteral(
|
function getPathToNodeOfEdgeTreatmentLiteral(
|
||||||
|
70
src/lang/modifyAst/setAppearance.ts
Normal file
70
src/lang/modifyAst/setAppearance.ts
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
import { PathToNode, Program } from 'lang/wasm'
|
||||||
|
import { Node } from 'wasm-lib/kcl/bindings/Node'
|
||||||
|
import { locateExtrudeDeclarator } from './addEdgeTreatment'
|
||||||
|
import { err } from 'lib/trap'
|
||||||
|
import {
|
||||||
|
createCallExpressionStdLibKw,
|
||||||
|
createLabeledArg,
|
||||||
|
createLiteral,
|
||||||
|
createPipeExpression,
|
||||||
|
} from 'lang/modifyAst'
|
||||||
|
import { createPipeSubstitution } from 'lang/modifyAst'
|
||||||
|
import { COMMAND_APPEARANCE_COLOR_DEFAULT } from 'lib/commandBarConfigs/modelingCommandConfig'
|
||||||
|
|
||||||
|
export function setAppearance({
|
||||||
|
ast,
|
||||||
|
nodeToEdit,
|
||||||
|
color,
|
||||||
|
}: {
|
||||||
|
ast: Node<Program>
|
||||||
|
nodeToEdit: PathToNode
|
||||||
|
color: string
|
||||||
|
}): Error | { modifiedAst: Node<Program>; pathToNode: PathToNode } {
|
||||||
|
const modifiedAst = structuredClone(ast)
|
||||||
|
|
||||||
|
// Locate the call (not necessarily an extrude here)
|
||||||
|
const result = locateExtrudeDeclarator(modifiedAst, nodeToEdit)
|
||||||
|
if (err(result)) {
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
const declarator = result.extrudeDeclarator
|
||||||
|
const call = createCallExpressionStdLibKw(
|
||||||
|
'appearance',
|
||||||
|
createPipeSubstitution(),
|
||||||
|
[createLabeledArg('color', createLiteral(color))]
|
||||||
|
)
|
||||||
|
// Modify the expression
|
||||||
|
if (
|
||||||
|
declarator.init.type === 'CallExpression' ||
|
||||||
|
declarator.init.type === 'CallExpressionKw'
|
||||||
|
) {
|
||||||
|
// 1. case when no appearance exists, mutate in place
|
||||||
|
declarator.init = createPipeExpression([declarator.init, call])
|
||||||
|
} else if (declarator.init.type === 'PipeExpression') {
|
||||||
|
// 2. case when appearance exists or extrude in sketch pipe
|
||||||
|
const existingIndex = declarator.init.body.findIndex(
|
||||||
|
(v) =>
|
||||||
|
v.type === 'CallExpressionKw' &&
|
||||||
|
v.callee.type === 'Identifier' &&
|
||||||
|
v.callee.name === 'appearance'
|
||||||
|
)
|
||||||
|
if (existingIndex > -1) {
|
||||||
|
if (color === COMMAND_APPEARANCE_COLOR_DEFAULT) {
|
||||||
|
// Special case of unsetting the appearance aka deleting the node
|
||||||
|
declarator.init.body.splice(existingIndex, 1)
|
||||||
|
} else {
|
||||||
|
declarator.init.body[existingIndex] = call
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
declarator.init.body.push(call)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return new Error('Unsupported operation type.')
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
modifiedAst,
|
||||||
|
pathToNode: result.shallowPath,
|
||||||
|
}
|
||||||
|
}
|
@ -3,7 +3,11 @@ import { angleLengthInfo } from 'components/Toolbar/setAngleLength'
|
|||||||
import { transformAstSketchLines } from 'lang/std/sketchcombos'
|
import { transformAstSketchLines } from 'lang/std/sketchcombos'
|
||||||
import { PathToNode } from 'lang/wasm'
|
import { PathToNode } from 'lang/wasm'
|
||||||
import { StateMachineCommandSetConfig, KclCommandValue } from 'lib/commandTypes'
|
import { StateMachineCommandSetConfig, KclCommandValue } from 'lib/commandTypes'
|
||||||
import { KCL_DEFAULT_LENGTH, KCL_DEFAULT_DEGREE } from 'lib/constants'
|
import {
|
||||||
|
KCL_DEFAULT_LENGTH,
|
||||||
|
KCL_DEFAULT_DEGREE,
|
||||||
|
KCL_DEFAULT_COLOR,
|
||||||
|
} from 'lib/constants'
|
||||||
import { components } from 'lib/machine-api'
|
import { components } from 'lib/machine-api'
|
||||||
import { Selections } from 'lib/selections'
|
import { Selections } from 'lib/selections'
|
||||||
import { kclManager } from 'lib/singletons'
|
import { kclManager } from 'lib/singletons'
|
||||||
@ -28,6 +32,8 @@ export const EXTRUSION_RESULTS = [
|
|||||||
'intersect',
|
'intersect',
|
||||||
] as const
|
] as const
|
||||||
|
|
||||||
|
export const COMMAND_APPEARANCE_COLOR_DEFAULT = 'default'
|
||||||
|
|
||||||
export type ModelingCommandSchema = {
|
export type ModelingCommandSchema = {
|
||||||
'Enter sketch': {}
|
'Enter sketch': {}
|
||||||
Export: {
|
Export: {
|
||||||
@ -107,6 +113,10 @@ export type ModelingCommandSchema = {
|
|||||||
selection: Selections
|
selection: Selections
|
||||||
}
|
}
|
||||||
'Delete selection': {}
|
'Delete selection': {}
|
||||||
|
Appearance: {
|
||||||
|
nodeToEdit?: PathToNode
|
||||||
|
color: string
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const modelingMachineCommandConfig: StateMachineCommandSetConfig<
|
export const modelingMachineCommandConfig: StateMachineCommandSetConfig<
|
||||||
@ -664,4 +674,40 @@ export const modelingMachineCommandConfig: StateMachineCommandSetConfig<
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
Appearance: {
|
||||||
|
description:
|
||||||
|
'Set the appearance of a solid. This only works on solids, not sketches or individual paths.',
|
||||||
|
icon: 'extrude',
|
||||||
|
needsReview: true,
|
||||||
|
args: {
|
||||||
|
nodeToEdit: {
|
||||||
|
description:
|
||||||
|
'Path to the node in the AST to edit. Never shown to the user.',
|
||||||
|
skip: true,
|
||||||
|
inputType: 'text',
|
||||||
|
required: false,
|
||||||
|
},
|
||||||
|
color: {
|
||||||
|
inputType: 'options',
|
||||||
|
required: true,
|
||||||
|
options: [
|
||||||
|
{ name: 'Red', value: '#FF0000' },
|
||||||
|
{ name: 'Green', value: '#00FF00' },
|
||||||
|
{ name: 'Blue', value: '#0000FF' },
|
||||||
|
{ name: 'Turquoise', value: '#00FFFF' },
|
||||||
|
{ name: 'Purple', value: '#FF00FF' },
|
||||||
|
{ name: 'Yellow', value: '#FFFF00' },
|
||||||
|
{ name: 'Black', value: '#000000' },
|
||||||
|
{ name: 'Dark Grey', value: '#080808' },
|
||||||
|
{ name: 'Light Grey', value: '#D3D3D3' },
|
||||||
|
{ name: 'White', value: '#FFFFFF' },
|
||||||
|
{
|
||||||
|
name: 'Default (clear appearance)',
|
||||||
|
value: COMMAND_APPEARANCE_COLOR_DEFAULT,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
// Add more fields
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
@ -66,6 +66,9 @@ export const KCL_DEFAULT_LENGTH = `5`
|
|||||||
/** The default KCL degree expression */
|
/** The default KCL degree expression */
|
||||||
export const KCL_DEFAULT_DEGREE = `360`
|
export const KCL_DEFAULT_DEGREE = `360`
|
||||||
|
|
||||||
|
/** The default KCL color expression */
|
||||||
|
export const KCL_DEFAULT_COLOR = `#3c73ff`
|
||||||
|
|
||||||
/** localStorage key for the playwright test-specific app settings file */
|
/** localStorage key for the playwright test-specific app settings file */
|
||||||
export const TEST_SETTINGS_FILE_KEY = 'playwright-test-settings'
|
export const TEST_SETTINGS_FILE_KEY = 'playwright-test-settings'
|
||||||
|
|
||||||
|
@ -33,6 +33,7 @@ interface StdLibCallInfo {
|
|||||||
| ExecuteCommandEventPayload
|
| ExecuteCommandEventPayload
|
||||||
| PrepareToEditCallback
|
| PrepareToEditCallback
|
||||||
| PrepareToEditFailurePayload
|
| PrepareToEditFailurePayload
|
||||||
|
supportsAppearance?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -204,6 +205,7 @@ export const stdLibMap: Record<string, StdLibCallInfo> = {
|
|||||||
label: 'Extrude',
|
label: 'Extrude',
|
||||||
icon: 'extrude',
|
icon: 'extrude',
|
||||||
prepareToEdit: prepareToEditExtrude,
|
prepareToEdit: prepareToEditExtrude,
|
||||||
|
supportsAppearance: true,
|
||||||
},
|
},
|
||||||
fillet: {
|
fillet: {
|
||||||
label: 'Fillet',
|
label: 'Fillet',
|
||||||
@ -228,6 +230,7 @@ export const stdLibMap: Record<string, StdLibCallInfo> = {
|
|||||||
loft: {
|
loft: {
|
||||||
label: 'Loft',
|
label: 'Loft',
|
||||||
icon: 'loft',
|
icon: 'loft',
|
||||||
|
supportsAppearance: true,
|
||||||
},
|
},
|
||||||
offsetPlane: {
|
offsetPlane: {
|
||||||
label: 'Offset Plane',
|
label: 'Offset Plane',
|
||||||
@ -253,10 +256,12 @@ export const stdLibMap: Record<string, StdLibCallInfo> = {
|
|||||||
revolve: {
|
revolve: {
|
||||||
label: 'Revolve',
|
label: 'Revolve',
|
||||||
icon: 'revolve',
|
icon: 'revolve',
|
||||||
|
supportsAppearance: true,
|
||||||
},
|
},
|
||||||
shell: {
|
shell: {
|
||||||
label: 'Shell',
|
label: 'Shell',
|
||||||
icon: 'shell',
|
icon: 'shell',
|
||||||
|
supportsAppearance: true,
|
||||||
},
|
},
|
||||||
startSketchOn: {
|
startSketchOn: {
|
||||||
label: 'Sketch',
|
label: 'Sketch',
|
||||||
@ -280,6 +285,7 @@ export const stdLibMap: Record<string, StdLibCallInfo> = {
|
|||||||
sweep: {
|
sweep: {
|
||||||
label: 'Sweep',
|
label: 'Sweep',
|
||||||
icon: 'sweep',
|
icon: 'sweep',
|
||||||
|
supportsAppearance: true,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -432,3 +438,37 @@ export async function enterEditFlow({
|
|||||||
'Feature tree editing not yet supported for this operation. Please edit in the code editor.'
|
'Feature tree editing not yet supported for this operation. Please edit in the code editor.'
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function enterAppearanceFlow({
|
||||||
|
operation,
|
||||||
|
artifact,
|
||||||
|
}: EnterEditFlowProps): Promise<Error | CommandBarMachineEvent> {
|
||||||
|
if (operation.type !== 'StdLibCall') {
|
||||||
|
return new Error(
|
||||||
|
'Appearance setting not yet supported for user-defined functions. Please edit in the code editor.'
|
||||||
|
)
|
||||||
|
}
|
||||||
|
const stdLibInfo = stdLibMap[operation.name]
|
||||||
|
|
||||||
|
if (stdLibInfo && stdLibInfo.supportsAppearance) {
|
||||||
|
const argDefaultValues = {
|
||||||
|
nodeToEdit: getNodePathFromSourceRange(
|
||||||
|
kclManager.ast,
|
||||||
|
sourceRangeFromRust(operation.sourceRange)
|
||||||
|
),
|
||||||
|
}
|
||||||
|
console.log('argDefaultValues', argDefaultValues)
|
||||||
|
return {
|
||||||
|
type: 'Find and select command',
|
||||||
|
data: {
|
||||||
|
name: 'Appearance',
|
||||||
|
groupId: 'modeling',
|
||||||
|
argDefaultValues,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Error(
|
||||||
|
'Appearance setting not yet supported for this operation. Please edit in the code editor.'
|
||||||
|
)
|
||||||
|
}
|
||||||
|
File diff suppressed because one or more lines are too long
@ -88,6 +88,7 @@ import {
|
|||||||
import { getPathsFromPlaneArtifact } from 'lang/std/artifactGraph'
|
import { getPathsFromPlaneArtifact } from 'lang/std/artifactGraph'
|
||||||
import { createProfileStartHandle } from 'clientSideScene/segments'
|
import { createProfileStartHandle } from 'clientSideScene/segments'
|
||||||
import { DRAFT_POINT } from 'clientSideScene/sceneInfra'
|
import { DRAFT_POINT } from 'clientSideScene/sceneInfra'
|
||||||
|
import { setAppearance } from 'lang/modifyAst/setAppearance'
|
||||||
|
|
||||||
export const MODELING_PERSIST_KEY = 'MODELING_PERSIST_KEY'
|
export const MODELING_PERSIST_KEY = 'MODELING_PERSIST_KEY'
|
||||||
|
|
||||||
@ -314,6 +315,7 @@ export type ModelingMachineEvent =
|
|||||||
type: 'Delete selection'
|
type: 'Delete selection'
|
||||||
data: ModelingCommandSchema['Delete selection']
|
data: ModelingCommandSchema['Delete selection']
|
||||||
}
|
}
|
||||||
|
| { type: 'Appearance'; data: ModelingCommandSchema['Appearance'] }
|
||||||
| {
|
| {
|
||||||
type: 'Add rectangle origin'
|
type: 'Add rectangle origin'
|
||||||
data: [x: number, y: number]
|
data: [x: number, y: number]
|
||||||
@ -2172,6 +2174,47 @@ export const modelingMachine = setup({
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
|
appearanceAstMod: fromPromise(
|
||||||
|
async ({
|
||||||
|
input,
|
||||||
|
}: {
|
||||||
|
input: ModelingCommandSchema['Appearance'] | undefined
|
||||||
|
}) => {
|
||||||
|
if (!input) return new Error('No input provided')
|
||||||
|
// 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')
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = setAppearance({
|
||||||
|
ast,
|
||||||
|
nodeToEdit,
|
||||||
|
color,
|
||||||
|
})
|
||||||
|
|
||||||
|
if (err(result)) {
|
||||||
|
return err(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
const updateAstResult = await kclManager.updateAst(
|
||||||
|
result.modifiedAst,
|
||||||
|
true,
|
||||||
|
{
|
||||||
|
focusPath: [result.pathToNode],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
await codeManager.updateEditorWithAstAndWriteToFile(
|
||||||
|
updateAstResult.newAst
|
||||||
|
)
|
||||||
|
|
||||||
|
if (updateAstResult?.selections) {
|
||||||
|
editorManager.selectRange(updateAstResult?.selections)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
),
|
||||||
},
|
},
|
||||||
// end actors
|
// end actors
|
||||||
}).createMachine({
|
}).createMachine({
|
||||||
@ -2267,6 +2310,11 @@ export const modelingMachine = setup({
|
|||||||
},
|
},
|
||||||
|
|
||||||
'Prompt-to-edit': 'Applying Prompt-to-edit',
|
'Prompt-to-edit': 'Applying Prompt-to-edit',
|
||||||
|
|
||||||
|
Appearance: {
|
||||||
|
target: 'Applying appearance',
|
||||||
|
reenter: true,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
entry: 'reset client scene mouse handlers',
|
entry: 'reset client scene mouse handlers',
|
||||||
@ -3389,6 +3437,19 @@ export const modelingMachine = setup({
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
'Applying appearance': {
|
||||||
|
invoke: {
|
||||||
|
src: 'appearanceAstMod',
|
||||||
|
id: 'appearanceAstMod',
|
||||||
|
input: ({ event }) => {
|
||||||
|
if (event.type !== 'Appearance') return undefined
|
||||||
|
return event.data
|
||||||
|
},
|
||||||
|
onDone: ['idle'],
|
||||||
|
onError: ['idle'],
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
initial: 'idle',
|
initial: 'idle',
|
||||||
|
Reference in New Issue
Block a user