diff --git a/e2e/playwright/fixtures/cmdBarFixture.ts b/e2e/playwright/fixtures/cmdBarFixture.ts
index d21ed6908..0b88f4e68 100644
--- a/e2e/playwright/fixtures/cmdBarFixture.ts
+++ b/e2e/playwright/fixtures/cmdBarFixture.ts
@@ -47,6 +47,8 @@ export class CmdBarFixture {
private _serialiseCmdBar = async (): Promise => {
if (!(await this.page.getByTestId('command-bar-wrapper').isVisible())) {
return { stage: 'commandBarClosed' }
+ } else if (await this.page.getByTestId('cmd-bar-search').isVisible()) {
+ return { stage: 'pickCommand' }
}
const reviewForm = this.page.locator('#review-form')
const getHeaderArgs = async () => {
diff --git a/e2e/playwright/point-click.spec.ts b/e2e/playwright/point-click.spec.ts
index 5f67b20de..1e0afb78a 100644
--- a/e2e/playwright/point-click.spec.ts
+++ b/e2e/playwright/point-click.spec.ts
@@ -2877,23 +2877,41 @@ extrude001 = extrude(profile001, length = 100)
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 enterAppearanceFlow = async (stepName: string) =>
+ test.step(stepName, async () => {
+ 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',
+ })
+ })
+
+ await enterAppearanceFlow(`Open Set Appearance flow`)
+
+ await test.step(`Validate hidden argument "nodeToEdit" can't be reached with Backspace`, async () => {
+ await page.keyboard.press('Backspace')
+ await cmdBar.expectState({
+ stage: 'pickCommand',
+ })
+ await page.keyboard.press('Escape')
+ await cmdBar.expectState({
+ stage: 'commandBarClosed',
+ })
})
+
+ await enterAppearanceFlow(`Restart Appearance flow`)
const item = page.getByText(option, { exact: true })
await item.click()
await cmdBar.expectState({
diff --git a/src/components/CommandBar/CommandBar.tsx b/src/components/CommandBar/CommandBar.tsx
index 93fd576d4..23589c46e 100644
--- a/src/components/CommandBar/CommandBar.tsx
+++ b/src/components/CommandBar/CommandBar.tsx
@@ -43,9 +43,10 @@ export const CommandBar = () => {
if (commandBarState.matches('Review')) {
const entries = Object.entries(selectedCommand?.args || {}).filter(
([_, argConfig]) =>
- typeof argConfig.required === 'function'
+ !argConfig.hidden &&
+ (typeof argConfig.required === 'function'
? argConfig.required(commandBarState.context)
- : argConfig.required
+ : argConfig.required)
)
const currentArgName = entries[entries.length - 1][0]
@@ -64,7 +65,9 @@ export const CommandBar = () => {
commandBarActor.send({ type: 'Deselect command' })
}
} else {
- const entries = Object.entries(selectedCommand?.args || {})
+ const entries = Object.entries(selectedCommand?.args || {}).filter(
+ (a) => !a[1].hidden
+ )
const index = entries.findIndex(
([key, _]) => key === currentArgument.name
)
diff --git a/src/components/CommandBar/CommandBarHeader.tsx b/src/components/CommandBar/CommandBarHeader.tsx
index 1fe4ebb8a..f02f82a25 100644
--- a/src/components/CommandBar/CommandBarHeader.tsx
+++ b/src/components/CommandBar/CommandBarHeader.tsx
@@ -1,5 +1,5 @@
import { CustomIcon } from '../CustomIcon'
-import React, { useState } from 'react'
+import React, { useMemo, useState } from 'react'
import { ActionButton } from '../ActionButton'
import { Selections, getSelectionTypeDisplayText } from 'lib/selections'
import { useHotkeys } from 'react-hotkeys-hook'
@@ -13,6 +13,14 @@ function CommandBarHeader({ children }: React.PropsWithChildren<{}>) {
const {
context: { selectedCommand, currentArgument, argumentsToSubmit },
} = commandBarState
+ const nonHiddenArgs = useMemo(() => {
+ if (!selectedCommand?.args) return undefined
+ const s = { ...selectedCommand.args }
+ for (const [name, arg] of Object.entries(s)) {
+ if (arg.hidden) delete s[name]
+ }
+ return s
+ }, [selectedCommand])
const isReviewing = commandBarState.matches('Review')
const [showShortcuts, setShowShortcuts] = useState(false)
@@ -43,11 +51,9 @@ function CommandBarHeader({ children }: React.PropsWithChildren<{}>) {
],
(_, b) => {
if (b.keys && !Number.isNaN(parseInt(b.keys[0], 10))) {
- if (!selectedCommand?.args) return
- const argName = Object.keys(selectedCommand.args)[
- parseInt(b.keys[0], 10) - 1
- ]
- const arg = selectedCommand?.args[argName]
+ if (!nonHiddenArgs) return
+ const argName = Object.keys(nonHiddenArgs)[parseInt(b.keys[0], 10) - 1]
+ const arg = nonHiddenArgs[argName]
if (!argName || !arg) return
commandBarActor.send({
type: 'Change current argument',
@@ -78,7 +84,7 @@ function CommandBarHeader({ children }: React.PropsWithChildren<{}>) {
{selectedCommand.displayName || selectedCommand.name}
- {Object.entries(selectedCommand?.args || {})
+ {Object.entries(nonHiddenArgs || {})
.filter(([_, argConfig]) =>
typeof argConfig.required === 'function'
? argConfig.required(commandBarState.context)
diff --git a/src/lib/commandBarConfigs/modelingCommandConfig.ts b/src/lib/commandBarConfigs/modelingCommandConfig.ts
index a9f0d54e3..191ea0b41 100644
--- a/src/lib/commandBarConfigs/modelingCommandConfig.ts
+++ b/src/lib/commandBarConfigs/modelingCommandConfig.ts
@@ -311,6 +311,7 @@ export const modelingMachineCommandConfig: StateMachineCommandSetConfig<
skip: true,
inputType: 'text',
required: false,
+ hidden: true,
},
selection: {
inputType: 'selection',
@@ -454,6 +455,7 @@ export const modelingMachineCommandConfig: StateMachineCommandSetConfig<
skip: true,
inputType: 'text',
required: false,
+ hidden: true,
},
plane: {
inputType: 'selection',
@@ -481,6 +483,7 @@ export const modelingMachineCommandConfig: StateMachineCommandSetConfig<
skip: true,
inputType: 'text',
required: false,
+ hidden: true,
},
revolutions: {
inputType: 'kcl',
@@ -702,6 +705,7 @@ export const modelingMachineCommandConfig: StateMachineCommandSetConfig<
skip: true,
inputType: 'text',
required: false,
+ hidden: true,
},
color: {
inputType: 'options',
diff --git a/src/lib/commandTypes.ts b/src/lib/commandTypes.ts
index 0bb8ee579..791f78d23 100644
--- a/src/lib/commandTypes.ts
+++ b/src/lib/commandTypes.ts
@@ -119,6 +119,8 @@ export type CommandArgumentConfig<
machineContext?: C
) => boolean)
warningMessage?: string
+ /** If `true`, arg is used as passed-through data, never for user input */
+ hidden?: boolean
skip?: boolean
/** For showing a summary display of the current value, such as in
* the command bar's header
@@ -233,6 +235,8 @@ export type CommandArgument<
commandBarContext: { argumentsToSubmit: Record }, // Should be the commandbarMachine's context, but it creates a circular dependency
machineContext?: ContextFrom
) => boolean)
+ /** If `true`, arg is used as passed-through data, never for user input */
+ hidden?: boolean
skip?: boolean
machineActor?: Actor
warningMessage?: string
diff --git a/src/lib/createMachineCommand.ts b/src/lib/createMachineCommand.ts
index c89cdd448..2e3fdaa24 100644
--- a/src/lib/createMachineCommand.ts
+++ b/src/lib/createMachineCommand.ts
@@ -162,6 +162,7 @@ export function buildCommandArgument<
const baseCommandArgument = {
description: arg.description,
required: arg.required,
+ hidden: arg.hidden,
skip: arg.skip,
machineActor,
valueSummary: arg.valueSummary,
diff --git a/src/machines/commandBarMachine.ts b/src/machines/commandBarMachine.ts
index 95e91600b..aa6673ce8 100644
--- a/src/machines/commandBarMachine.ts
+++ b/src/machines/commandBarMachine.ts
@@ -132,14 +132,15 @@ export const commandBarMachine = setup({
// Find the first argument that is not to be skipped:
// that is, the first argument that is not already in the argumentsToSubmit
- // 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
+ const nonHiddenArgs = Object.entries(selectedCommand.args).filter(
+ (a) => !a[1].hidden
+ )
let argIndex = 0
- while (argIndex < Object.keys(selectedCommand.args).length) {
- const [argName, argConfig] = Object.entries(selectedCommand.args)[
- argIndex
- ]
+ while (argIndex < nonHiddenArgs.length) {
+ const [argName, argConfig] = nonHiddenArgs[argIndex]
const argIsRequired =
typeof argConfig.required === 'function'
? argConfig.required(context)
@@ -155,7 +156,7 @@ export const commandBarMachine = setup({
if (
mustNotSkipArg === true ||
- argIndex + 1 === Object.keys(selectedCommand.args).length
+ argIndex + 1 === Object.keys(nonHiddenArgs).length
) {
// If we have reached the end of the arguments and none are skippable,
// return the last argument.
@@ -259,7 +260,7 @@ export const commandBarMachine = setup({
},
'All arguments are skippable': ({ context }) => {
return Object.values(context.selectedCommand!.args!).every(
- (argConfig) => argConfig.skip
+ (argConfig) => argConfig.skip || argConfig.hidden
)
},
'Has selected command': ({ context }) => !!context.selectedCommand,