Compare commits
6 Commits
api-deux-p
...
franknoiro
Author | SHA1 | Date | |
---|---|---|---|
c64c4705ef | |||
a122128e0b | |||
0fe79ea7cb | |||
021e4b9565 | |||
e57783483a | |||
e952148cd2 |
@ -274,7 +274,6 @@ test.describe('Feature Tree pane', () => {
|
|||||||
currentArgKey: 'distance',
|
currentArgKey: 'distance',
|
||||||
currentArgValue: initialInput,
|
currentArgValue: initialInput,
|
||||||
headerArguments: {
|
headerArguments: {
|
||||||
Selection: '1 face',
|
|
||||||
Distance: initialInput,
|
Distance: initialInput,
|
||||||
},
|
},
|
||||||
highlightedHeaderArg: 'distance',
|
highlightedHeaderArg: 'distance',
|
||||||
@ -291,7 +290,6 @@ test.describe('Feature Tree pane', () => {
|
|||||||
await cmdBar.expectState({
|
await cmdBar.expectState({
|
||||||
stage: 'review',
|
stage: 'review',
|
||||||
headerArguments: {
|
headerArguments: {
|
||||||
Selection: '1 face',
|
|
||||||
// The calculated value is shown in the argument summary
|
// The calculated value is shown in the argument summary
|
||||||
Distance: initialInput,
|
Distance: initialInput,
|
||||||
},
|
},
|
||||||
|
@ -17,7 +17,12 @@ function CommandBarHeader({ children }: React.PropsWithChildren<{}>) {
|
|||||||
if (!selectedCommand?.args) return undefined
|
if (!selectedCommand?.args) return undefined
|
||||||
const s = { ...selectedCommand.args }
|
const s = { ...selectedCommand.args }
|
||||||
for (const [name, arg] of Object.entries(s)) {
|
for (const [name, arg] of Object.entries(s)) {
|
||||||
if (arg.hidden) delete s[name]
|
if (
|
||||||
|
typeof arg.hidden === 'function'
|
||||||
|
? arg.hidden(commandBarState.context)
|
||||||
|
: arg.hidden
|
||||||
|
)
|
||||||
|
delete s[name]
|
||||||
}
|
}
|
||||||
return s
|
return s
|
||||||
}, [selectedCommand])
|
}, [selectedCommand])
|
||||||
|
@ -7,7 +7,7 @@ import toast from 'react-hot-toast'
|
|||||||
import { editorManager } from 'lib/singletons'
|
import { editorManager } from 'lib/singletons'
|
||||||
import { Annotation, Transaction } from '@codemirror/state'
|
import { Annotation, Transaction } from '@codemirror/state'
|
||||||
import { EditorView, KeyBinding } from '@codemirror/view'
|
import { EditorView, KeyBinding } from '@codemirror/view'
|
||||||
import { recast, Program, parse } from 'lang/wasm'
|
import { recast, Program, parse, SourceRange } from 'lang/wasm'
|
||||||
import { err, reportRejection } from 'lib/trap'
|
import { err, reportRejection } from 'lib/trap'
|
||||||
import { Compartment } from '@codemirror/state'
|
import { Compartment } from '@codemirror/state'
|
||||||
import { history } from '@codemirror/commands'
|
import { history } from '@codemirror/commands'
|
||||||
@ -57,6 +57,10 @@ export default class CodeManager {
|
|||||||
return this._code
|
return this._code
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getCodeAtRange(range: SourceRange) {
|
||||||
|
return this._code.slice(range[0], range[1])
|
||||||
|
}
|
||||||
|
|
||||||
localStoragePersistCode(): string {
|
localStoragePersistCode(): string {
|
||||||
return safeLSGetItem(PERSIST_CODE_KEY) || ''
|
return safeLSGetItem(PERSIST_CODE_KEY) || ''
|
||||||
}
|
}
|
||||||
|
@ -69,6 +69,8 @@ export type ModelingCommandSchema = {
|
|||||||
edge: Selections
|
edge: Selections
|
||||||
}
|
}
|
||||||
Fillet: {
|
Fillet: {
|
||||||
|
// Enables editing workflow
|
||||||
|
nodeToEdit?: PathToNode
|
||||||
selection: Selections
|
selection: Selections
|
||||||
radius: KclCommandValue
|
radius: KclCommandValue
|
||||||
}
|
}
|
||||||
@ -319,6 +321,7 @@ export const modelingMachineCommandConfig: StateMachineCommandSetConfig<
|
|||||||
multiple: false, // TODO: multiple selection
|
multiple: false, // TODO: multiple selection
|
||||||
required: true,
|
required: true,
|
||||||
skip: true,
|
skip: true,
|
||||||
|
hidden: (context) => Boolean(context.argumentsToSubmit.nodeToEdit),
|
||||||
},
|
},
|
||||||
// result: {
|
// result: {
|
||||||
// inputType: 'options',
|
// inputType: 'options',
|
||||||
@ -407,6 +410,7 @@ export const modelingMachineCommandConfig: StateMachineCommandSetConfig<
|
|||||||
multiple: false, // TODO: multiple selection
|
multiple: false, // TODO: multiple selection
|
||||||
required: true,
|
required: true,
|
||||||
skip: true,
|
skip: true,
|
||||||
|
hidden: (context) => Boolean(context.argumentsToSubmit.nodeToEdit),
|
||||||
},
|
},
|
||||||
axisOrEdge: {
|
axisOrEdge: {
|
||||||
inputType: 'options',
|
inputType: 'options',
|
||||||
@ -416,6 +420,7 @@ export const modelingMachineCommandConfig: StateMachineCommandSetConfig<
|
|||||||
{ name: 'Axis', isCurrent: true, value: 'Axis' },
|
{ name: 'Axis', isCurrent: true, value: 'Axis' },
|
||||||
{ name: 'Edge', isCurrent: false, value: 'Edge' },
|
{ name: 'Edge', isCurrent: false, value: 'Edge' },
|
||||||
],
|
],
|
||||||
|
hidden: (context) => Boolean(context.argumentsToSubmit.nodeToEdit),
|
||||||
},
|
},
|
||||||
axis: {
|
axis: {
|
||||||
required: (commandContext) =>
|
required: (commandContext) =>
|
||||||
@ -437,6 +442,7 @@ export const modelingMachineCommandConfig: StateMachineCommandSetConfig<
|
|||||||
selectionTypes: ['segment', 'sweepEdge', 'edgeCutEdge'],
|
selectionTypes: ['segment', 'sweepEdge', 'edgeCutEdge'],
|
||||||
multiple: false,
|
multiple: false,
|
||||||
validation: revolveAxisValidator,
|
validation: revolveAxisValidator,
|
||||||
|
hidden: (context) => Boolean(context.argumentsToSubmit.nodeToEdit),
|
||||||
},
|
},
|
||||||
angle: {
|
angle: {
|
||||||
inputType: 'kcl',
|
inputType: 'kcl',
|
||||||
@ -534,12 +540,21 @@ export const modelingMachineCommandConfig: StateMachineCommandSetConfig<
|
|||||||
status: 'development',
|
status: 'development',
|
||||||
needsReview: true,
|
needsReview: true,
|
||||||
args: {
|
args: {
|
||||||
|
nodeToEdit: {
|
||||||
|
description:
|
||||||
|
'Path to the node in the AST to edit. Never shown to the user.',
|
||||||
|
skip: true,
|
||||||
|
inputType: 'text',
|
||||||
|
required: false,
|
||||||
|
hidden: true,
|
||||||
|
},
|
||||||
selection: {
|
selection: {
|
||||||
inputType: 'selection',
|
inputType: 'selection',
|
||||||
selectionTypes: ['segment', 'sweepEdge', 'edgeCutEdge'],
|
selectionTypes: ['segment', 'sweepEdge', 'edgeCutEdge'],
|
||||||
multiple: true,
|
multiple: true,
|
||||||
required: true,
|
required: true,
|
||||||
skip: false,
|
skip: false,
|
||||||
|
hidden: (context) => Boolean(context.argumentsToSubmit.nodeToEdit),
|
||||||
warningMessage:
|
warningMessage:
|
||||||
'Fillets cannot touch other fillets yet. This is under development.',
|
'Fillets cannot touch other fillets yet. This is under development.',
|
||||||
},
|
},
|
||||||
|
@ -120,7 +120,12 @@ export type CommandArgumentConfig<
|
|||||||
) => boolean)
|
) => boolean)
|
||||||
warningMessage?: string
|
warningMessage?: string
|
||||||
/** If `true`, arg is used as passed-through data, never for user input */
|
/** If `true`, arg is used as passed-through data, never for user input */
|
||||||
hidden?: boolean
|
hidden?:
|
||||||
|
| boolean
|
||||||
|
| ((
|
||||||
|
commandBarContext: { argumentsToSubmit: Record<string, unknown> }, // Should be the commandbarMachine's context, but it creates a circular dependency
|
||||||
|
machineContext?: C
|
||||||
|
) => boolean)
|
||||||
skip?: boolean
|
skip?: boolean
|
||||||
/** For showing a summary display of the current value, such as in
|
/** For showing a summary display of the current value, such as in
|
||||||
* the command bar's header
|
* the command bar's header
|
||||||
@ -236,7 +241,12 @@ export type CommandArgument<
|
|||||||
machineContext?: ContextFrom<T>
|
machineContext?: ContextFrom<T>
|
||||||
) => boolean)
|
) => boolean)
|
||||||
/** If `true`, arg is used as passed-through data, never for user input */
|
/** If `true`, arg is used as passed-through data, never for user input */
|
||||||
hidden?: boolean
|
hidden?:
|
||||||
|
| boolean
|
||||||
|
| ((
|
||||||
|
commandBarContext: { argumentsToSubmit: Record<string, unknown> }, // Should be the commandbarMachine's context, but it creates a circular dependency
|
||||||
|
machineContext?: ContextFrom<T>
|
||||||
|
) => boolean)
|
||||||
skip?: boolean
|
skip?: boolean
|
||||||
machineActor?: Actor<T>
|
machineActor?: Actor<T>
|
||||||
warningMessage?: string
|
warningMessage?: string
|
||||||
|
@ -1,5 +1,10 @@
|
|||||||
import { CustomIconName } from 'components/CustomIcon'
|
import { CustomIconName } from 'components/CustomIcon'
|
||||||
import { Artifact, getArtifactOfTypes } from 'lang/std/artifactGraph'
|
import {
|
||||||
|
Artifact,
|
||||||
|
getArtifactByPredicate,
|
||||||
|
getArtifactOfTypes,
|
||||||
|
getArtifactsOfTypes,
|
||||||
|
} from 'lang/std/artifactGraph'
|
||||||
import { Operation } from '@rust/kcl-lib/bindings/Operation'
|
import { Operation } from '@rust/kcl-lib/bindings/Operation'
|
||||||
import { codeManager, engineCommandManager, kclManager } from './singletons'
|
import { codeManager, engineCommandManager, kclManager } from './singletons'
|
||||||
import { err } from './trap'
|
import { err } from './trap'
|
||||||
@ -293,6 +298,84 @@ const prepareToEditHelix: PrepareToEditCallback = async ({ operation }) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const prepareToEditFillet: PrepareToEditCallback = async ({
|
||||||
|
artifact,
|
||||||
|
operation,
|
||||||
|
}) => {
|
||||||
|
const baseCommand = {
|
||||||
|
name: 'Fillet',
|
||||||
|
groupId: 'modeling',
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
operation.type !== 'StdLibCall' ||
|
||||||
|
!operation.labeledArgs ||
|
||||||
|
operation.name !== 'fillet' ||
|
||||||
|
!('tags' in operation.labeledArgs) ||
|
||||||
|
!('radius' in operation.labeledArgs) ||
|
||||||
|
!operation.labeledArgs.radius ||
|
||||||
|
!artifact ||
|
||||||
|
artifact.type !== 'edgeCut' ||
|
||||||
|
artifact.subType !== 'fillet'
|
||||||
|
) {
|
||||||
|
return baseCommand
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
!operation.labeledArgs.tags ||
|
||||||
|
operation.labeledArgs.tags.value.type !== 'Array'
|
||||||
|
) {
|
||||||
|
return baseCommand
|
||||||
|
}
|
||||||
|
const edgeIds = operation.labeledArgs.tags.value.value.map((tag) =>
|
||||||
|
tag.type === 'Uuid' ? tag.value : ''
|
||||||
|
)
|
||||||
|
if (!edgeIds.every((id) => id)) {
|
||||||
|
return baseCommand
|
||||||
|
}
|
||||||
|
|
||||||
|
const edges = getArtifactsOfTypes(
|
||||||
|
{
|
||||||
|
keys: edgeIds,
|
||||||
|
types: ['sweepEdge', 'segment'],
|
||||||
|
},
|
||||||
|
engineCommandManager.artifactGraph
|
||||||
|
)
|
||||||
|
const selection: ModelingCommandSchema['Fillet']['selection'] = {
|
||||||
|
graphSelections: edges
|
||||||
|
.values()
|
||||||
|
.map((edge, index) => ({
|
||||||
|
artifact: edge,
|
||||||
|
codeRef: {
|
||||||
|
range: operation.labeledArgs.tags?.sourceRange ?? [0, 0, 0],
|
||||||
|
pathToNode: getNodePathFromSourceRange(
|
||||||
|
kclManager.ast,
|
||||||
|
operation.labeledArgs.tags?.sourceRange ?? [0, 0, 0]
|
||||||
|
),
|
||||||
|
},
|
||||||
|
}))
|
||||||
|
.toArray(),
|
||||||
|
otherSelections: [],
|
||||||
|
}
|
||||||
|
const maybeRadius = await stringToKclExpression(
|
||||||
|
codeManager.getCodeAtRange(operation.labeledArgs.radius.sourceRange)
|
||||||
|
)
|
||||||
|
if (err(maybeRadius) || 'errors' in maybeRadius) return baseCommand
|
||||||
|
|
||||||
|
const argDefaultValues: ModelingCommandSchema['Fillet'] = {
|
||||||
|
nodeToEdit: getNodePathFromSourceRange(
|
||||||
|
kclManager.ast,
|
||||||
|
sourceRangeFromRust(operation.sourceRange)
|
||||||
|
),
|
||||||
|
radius: maybeRadius,
|
||||||
|
selection,
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
...baseCommand,
|
||||||
|
argDefaultValues,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A map of standard library calls to their corresponding information
|
* A map of standard library calls to their corresponding information
|
||||||
* for use in the feature tree UI.
|
* for use in the feature tree UI.
|
||||||
@ -312,6 +395,7 @@ export const stdLibMap: Record<string, StdLibCallInfo> = {
|
|||||||
fillet: {
|
fillet: {
|
||||||
label: 'Fillet',
|
label: 'Fillet',
|
||||||
icon: 'fillet3d',
|
icon: 'fillet3d',
|
||||||
|
prepareToEdit: prepareToEditFillet,
|
||||||
},
|
},
|
||||||
helix: {
|
helix: {
|
||||||
label: 'Helix',
|
label: 'Helix',
|
||||||
|
@ -134,8 +134,10 @@ export const commandBarMachine = setup({
|
|||||||
// that is, the first argument that is not already in the argumentsToSubmit
|
// that is, the first argument that is not already in the argumentsToSubmit
|
||||||
// or hidden, or that is not undefined, or that is not marked as "skippable".
|
// or hidden, or that is not undefined, or that is not marked as "skippable".
|
||||||
// TODO validate the type of the existing arguments
|
// TODO validate the type of the existing arguments
|
||||||
const nonHiddenArgs = Object.entries(selectedCommand.args).filter(
|
const nonHiddenArgs = Object.entries(selectedCommand.args).filter((a) =>
|
||||||
(a) => !a[1].hidden
|
a[1].hidden && typeof a[1].hidden === 'function'
|
||||||
|
? !a[1].hidden(context)
|
||||||
|
: !a[1].hidden
|
||||||
)
|
)
|
||||||
let argIndex = 0
|
let argIndex = 0
|
||||||
|
|
||||||
@ -260,7 +262,11 @@ export const commandBarMachine = setup({
|
|||||||
},
|
},
|
||||||
'All arguments are skippable': ({ context }) => {
|
'All arguments are skippable': ({ context }) => {
|
||||||
return Object.values(context.selectedCommand!.args!).every(
|
return Object.values(context.selectedCommand!.args!).every(
|
||||||
(argConfig) => argConfig.skip || argConfig.hidden
|
(argConfig) =>
|
||||||
|
argConfig.skip ||
|
||||||
|
(typeof argConfig.hidden === 'function'
|
||||||
|
? argConfig.hidden(context)
|
||||||
|
: argConfig.hidden)
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
'Has selected command': ({ context }) => !!context.selectedCommand,
|
'Has selected command': ({ context }) => !!context.selectedCommand,
|
||||||
|
Reference in New Issue
Block a user