From 55e61f923dcfd4071e52a10bd35c883d7cb66a67 Mon Sep 17 00:00:00 2001 From: Frank Noirot Date: Thu, 1 May 2025 23:13:46 -0400 Subject: [PATCH 1/7] Make "experimental" a valid status for toolbar and commands --- src/lib/commandTypes.ts | 5 +++-- src/lib/toolbar.ts | 7 ++++--- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/lib/commandTypes.ts b/src/lib/commandTypes.ts index 6b58f1d85cf..e438fc1bce1 100644 --- a/src/lib/commandTypes.ts +++ b/src/lib/commandTypes.ts @@ -40,7 +40,7 @@ export interface KclExpressionWithVariable extends KclExpression { } export type KclCommandValue = KclExpression | KclExpressionWithVariable export type CommandInputType = INPUT_TYPE[number] - +type CommandStatus = 'active' | 'development' | 'inactive' | 'experimental' export type FileFilter = { name: string extensions: string[] @@ -103,6 +103,7 @@ export type Command< icon?: Icon hide?: PLATFORM[number] hideFromSearch?: boolean + status?: CommandStatus } export type CommandConfig< @@ -115,7 +116,7 @@ export type CommandConfig< 'name' | 'groupId' | 'onSubmit' | 'onCancel' | 'args' | 'needsReview' > & { needsReview?: boolean - status?: 'active' | 'development' | 'inactive' + status?: CommandStatus args?: { [ArgName in keyof CommandSchema]: CommandArgumentConfig< CommandSchema[ArgName], diff --git a/src/lib/toolbar.ts b/src/lib/toolbar.ts index 1357072208f..0462f3917d3 100644 --- a/src/lib/toolbar.ts +++ b/src/lib/toolbar.ts @@ -10,6 +10,7 @@ import { isEditingExistingSketch, pipeHasCircle, } from '@src/machines/modelingMachine' +import { IS_ML_EXPERIMENTAL } from './constants' export type ToolbarModeName = 'modeling' | 'sketching' @@ -36,7 +37,7 @@ export type ToolbarItem = { icon?: CustomIconName iconColor?: string alwaysDark?: true - status: 'available' | 'unavailable' | 'kcl-only' + status: 'available' | 'unavailable' | 'kcl-only' | 'experimental' disabled?: (state: StateFrom) => boolean disableHotkey?: (state: StateFrom) => boolean title: string | ((props: ToolbarItemCallbackProps) => string) @@ -440,7 +441,7 @@ export const toolbarConfig: Record = { icon: 'sparkles', iconColor: '#29FFA4', alwaysDark: true, - status: 'available', + status: IS_ML_EXPERIMENTAL ? 'experimental' : 'available', title: 'Create with Zoo Text-to-CAD', description: 'Create geometry with AI / ML.', links: [ @@ -460,7 +461,7 @@ export const toolbarConfig: Record = { icon: 'sparkles', iconColor: '#29FFA4', alwaysDark: true, - status: 'available', + status: IS_ML_EXPERIMENTAL ? 'experimental' : 'available', title: 'Modify with Zoo Text-to-CAD', description: 'Edit geometry with AI / ML.', links: [], From ecb8f5d2f63dc65ebd6cda2339073cb0c0922a01 Mon Sep 17 00:00:00 2001 From: Frank Noirot Date: Thu, 1 May 2025 23:14:44 -0400 Subject: [PATCH 2/7] Wire up status through createMachineCommand --- src/lib/createMachineCommand.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/lib/createMachineCommand.ts b/src/lib/createMachineCommand.ts index 969135840cb..a1f99ffefde 100644 --- a/src/lib/createMachineCommand.ts +++ b/src/lib/createMachineCommand.ts @@ -123,6 +123,9 @@ export function createMachineCommand< if ('reviewMessage' in commandConfig) { command.reviewMessage = commandConfig.reviewMessage } + if ('status' in commandConfig) { + command.status = commandConfig.status + } return command } From 44dca011ebeb531fa3d88c3abcb39fa2cde23259 Mon Sep 17 00:00:00 2001 From: Frank Noirot Date: Thu, 1 May 2025 23:14:51 -0400 Subject: [PATCH 3/7] Add beaker icon --- src/components/CustomIcon.tsx | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/components/CustomIcon.tsx b/src/components/CustomIcon.tsx index c69f7fe4c4a..9880e399f7c 100644 --- a/src/components/CustomIcon.tsx +++ b/src/components/CustomIcon.tsx @@ -106,6 +106,14 @@ const CustomIconMap = { /> ), + beaker: ( + + + + ), booleanExclude: ( Date: Thu, 1 May 2025 23:15:16 -0400 Subject: [PATCH 4/7] Show UI elements if status is experimental --- src/Toolbar.tsx | 72 ++++++++++++++++--------- src/components/ActionButtonDropdown.tsx | 5 +- src/components/CommandComboBox.tsx | 6 +++ 3 files changed, 58 insertions(+), 25 deletions(-) diff --git a/src/Toolbar.tsx b/src/Toolbar.tsx index 872fc9d96e1..5629fcad758 100644 --- a/src/Toolbar.tsx +++ b/src/Toolbar.tsx @@ -95,7 +95,7 @@ export function Toolbar({ const tooltipContentClassName = !showRichContent ? '' - : '!text-left text-wrap !text-xs !p-0 !pb-2 flex gap-2 !max-w-none !w-72 flex-col items-stretch' + : '!text-left text-wrap !text-xs !p-0 !pb-2 flex !max-w-none !w-72 flex-col items-stretch' const richContentTimeout = useRef(null) const richContentClearTimeout = useRef(null) // On mouse enter, show rich content after a 1s delay @@ -155,9 +155,12 @@ export function Toolbar({ maybeIconConfig: ToolbarItem, dropdownId?: string ): ToolbarItemResolved { + const isConfiguredAvailable = ['available', 'experimental'].includes( + maybeIconConfig.status + ) const isDisabled = disableAllButtons || - maybeIconConfig.status !== 'available' || + !isConfiguredAvailable || maybeIconConfig.disabled?.(state) === true return { @@ -248,7 +251,9 @@ export function Toolbar({ onClick: () => itemConfig.onClick(configCallbackProps), disabled: disableAllButtons || - itemConfig.status !== 'available' || + !['available', 'experimental'].includes( + itemConfig.status + ) || itemConfig.disabled === true, status: itemConfig.status, }))} @@ -276,7 +281,9 @@ export function Toolbar({ aria-pressed={selectedIcon.isActive} disabled={ disableAllButtons || - selectedIcon.status !== 'available' || + !['available', 'experimental'].includes( + selectedIcon.status + ) || selectedIcon.disabled } name={selectedIcon.title} @@ -347,7 +354,7 @@ export function Toolbar({ aria-pressed={itemConfig.isActive} disabled={ disableAllButtons || - itemConfig.status !== 'available' || + !['available', 'experimental'].includes(itemConfig.status) || itemConfig.disabled } onClick={() => itemConfig.onClick(configCallbackProps)} @@ -409,7 +416,7 @@ const ToolbarItemTooltip = memo(function ToolbarItemContents({ }, { enabled: - itemConfig.status === 'available' && + ['available', 'experimental'].includes(itemConfig.status) && !!itemConfig.hotkey && !itemConfig.disabled && !itemConfig.disableHotkey, @@ -444,18 +451,28 @@ const ToolbarItemTooltipShortContent = ({ title: string hotkey?: string | string[] }) => ( - - {title} - {hotkey && ( - - {displayHotkeys(hotkey)} - + {status === 'experimental' && ( +
+ + Experimental +
)} -
+
+ {title} + {hotkey && ( + + {displayHotkeys(hotkey)} + + )} +
+ ) const ToolbarItemTooltipRichContent = ({ @@ -463,9 +480,18 @@ const ToolbarItemTooltipRichContent = ({ }: { itemConfig: ToolbarItemResolved }) => { + const shouldBeEnabled = ['available', 'experimental'].includes( + itemConfig.status + ) const { state } = useModelingContext() return ( <> + {itemConfig.status === 'experimental' && ( +
+ + Experimental +
+ )}
{itemConfig.icon && ( )} - {itemConfig.title} - - {itemConfig.status === 'available' && itemConfig.hotkey ? ( +
+ {shouldBeEnabled && itemConfig.hotkey ? ( {displayHotkeys(itemConfig.hotkey)} @@ -511,12 +535,12 @@ const ToolbarItemTooltipRichContent = ({ ) )} -

{itemConfig.description}

+

{itemConfig.description}

{/* Add disabled reason if item is disabled */} {itemConfig.disabled && itemConfig.disabledReason && ( <>
-

+

{typeof itemConfig.disabledReason === 'function' ? itemConfig.disabledReason(state) : itemConfig.disabledReason} diff --git a/src/components/ActionButtonDropdown.tsx b/src/components/ActionButtonDropdown.tsx index 15bb7d2c1c0..a9bb4c7f6cb 100644 --- a/src/components/ActionButtonDropdown.tsx +++ b/src/components/ActionButtonDropdown.tsx @@ -13,7 +13,7 @@ type ActionButtonSplitProps = ActionButtonProps & { Element: 'button' } & { shortcut?: string onClick: () => void disabled?: boolean - status?: 'available' | 'unavailable' | 'kcl-only' + status?: 'available' | 'unavailable' | 'kcl-only' | 'experimental' }[] } @@ -101,6 +101,9 @@ export function ActionButtonDropdown({ {item.shortcut} ) : null} + {item.status === 'experimental' ? ( + + ) : null} ))} diff --git a/src/components/CommandComboBox.tsx b/src/components/CommandComboBox.tsx index 7c3905ea610..40ce3e39c56 100644 --- a/src/components/CommandComboBox.tsx +++ b/src/components/CommandComboBox.tsx @@ -102,6 +102,12 @@ function CommandComboBox({

)} + {option.status === 'experimental' && ( +
+ + Experimental +
+ )} ))} From 350a589895bf9d8c1a53d9bcdd959973db7d6e3b Mon Sep 17 00:00:00 2001 From: Frank Noirot Date: Thu, 1 May 2025 23:15:43 -0400 Subject: [PATCH 5/7] Make ML operations experimental, powered by a flag --- src/lib/commandBarConfigs/applicationCommandConfig.ts | 4 +++- src/lib/commandBarConfigs/modelingCommandConfig.ts | 4 +++- src/lib/constants.ts | 3 +++ 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/lib/commandBarConfigs/applicationCommandConfig.ts b/src/lib/commandBarConfigs/applicationCommandConfig.ts index 8352a464629..3cc3304de6d 100644 --- a/src/lib/commandBarConfigs/applicationCommandConfig.ts +++ b/src/lib/commandBarConfigs/applicationCommandConfig.ts @@ -5,7 +5,7 @@ import { SystemIOMachineEvents } from '@src/machines/systemIO/utils' import { isDesktop } from '@src/lib/isDesktop' import { kclSamplesManifestWithNoMultipleFiles } from '@src/lib/kclSamples' import { getUniqueProjectName } from '@src/lib/desktopFS' -import { FILE_EXT } from '@src/lib/constants' +import { FILE_EXT, IS_ML_EXPERIMENTAL } from '@src/lib/constants' import toast from 'react-hot-toast' import { reportRejection } from '@src/lib/trap' import { relevantFileExtensions } from '@src/lang/wasmUtils' @@ -21,6 +21,7 @@ export function createApplicationCommands({ displayName: `Text to CAD`, groupId: 'application', needsReview: false, + status: IS_ML_EXPERIMENTAL ? 'experimental' : 'active', icon: 'sparkles', onSubmit: (record) => { if (record) { @@ -91,6 +92,7 @@ export function createApplicationCommands({ description: 'Add KCL file, Zoo sample, or 3D model to new or existing project.', needsReview: false, + status: IS_ML_EXPERIMENTAL ? 'experimental' : 'active', icon: 'importFile', groupId: 'application', onSubmit(data) { diff --git a/src/lib/commandBarConfigs/modelingCommandConfig.ts b/src/lib/commandBarConfigs/modelingCommandConfig.ts index 5ea302cca47..a7e72a7e179 100644 --- a/src/lib/commandBarConfigs/modelingCommandConfig.ts +++ b/src/lib/commandBarConfigs/modelingCommandConfig.ts @@ -23,6 +23,7 @@ import type { StateMachineCommandSetConfig, } from '@src/lib/commandTypes' import { + IS_ML_EXPERIMENTAL, KCL_DEFAULT_CONSTANT_PREFIXES, KCL_DEFAULT_DEGREE, KCL_DEFAULT_LENGTH, @@ -965,7 +966,8 @@ export const modelingMachineCommandConfig: StateMachineCommandSetConfig< }, 'Prompt-to-edit': { description: 'Use Zoo AI to edit your kcl', - icon: 'chat', + icon: 'sparkles', + status: IS_ML_EXPERIMENTAL ? 'experimental' : 'active', args: { selection: { inputType: 'selectionMixed', diff --git a/src/lib/constants.ts b/src/lib/constants.ts index b438fd9dd26..003bd40010c 100644 --- a/src/lib/constants.ts +++ b/src/lib/constants.ts @@ -190,3 +190,6 @@ export type ExecutionType = /** Key for setting window.localStorage.setItem and .getItem to determine if the runtime is playwright for browsers */ export const IS_PLAYWRIGHT_KEY = 'playwright' + +/** Should we mark all the ML features as "beta"? */ +export const IS_ML_EXPERIMENTAL = true From 28ebb552b4fd0d1c625fb8fa5a3b0b1da99b69c2 Mon Sep 17 00:00:00 2001 From: Frank Noirot Date: Fri, 2 May 2025 00:05:42 -0400 Subject: [PATCH 6/7] Update command descriptions --- src/lib/commandBarConfigs/applicationCommandConfig.ts | 3 ++- src/lib/commandBarConfigs/modelingCommandConfig.ts | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/lib/commandBarConfigs/applicationCommandConfig.ts b/src/lib/commandBarConfigs/applicationCommandConfig.ts index 3cc3304de6d..eac54281642 100644 --- a/src/lib/commandBarConfigs/applicationCommandConfig.ts +++ b/src/lib/commandBarConfigs/applicationCommandConfig.ts @@ -17,7 +17,8 @@ export function createApplicationCommands({ }) { const textToCADCommand: Command = { name: 'Text-to-CAD', - description: 'Use the Zoo Text-to-CAD API to generate part starters.', + description: + 'Generate parts from text prompts. This feature is experimental and undergoing constant improvment, stay tuned for updates.', displayName: `Text to CAD`, groupId: 'application', needsReview: false, diff --git a/src/lib/commandBarConfigs/modelingCommandConfig.ts b/src/lib/commandBarConfigs/modelingCommandConfig.ts index a7e72a7e179..e587afd51cd 100644 --- a/src/lib/commandBarConfigs/modelingCommandConfig.ts +++ b/src/lib/commandBarConfigs/modelingCommandConfig.ts @@ -965,7 +965,8 @@ export const modelingMachineCommandConfig: StateMachineCommandSetConfig< }, }, 'Prompt-to-edit': { - description: 'Use Zoo AI to edit your kcl', + description: + 'Use Zoo AI to edit your kcl. This feature is experimental and undergoing constant improvment, stay tuned for updates.', icon: 'sparkles', status: IS_ML_EXPERIMENTAL ? 'experimental' : 'active', args: { From c88daa145011b30753eab7f3b1cf64e2e2d04f37 Mon Sep 17 00:00:00 2001 From: Frank Noirot Date: Fri, 2 May 2025 00:05:54 -0400 Subject: [PATCH 7/7] Add tooltip to home page Text-to-CAD button --- src/routes/Home.tsx | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/routes/Home.tsx b/src/routes/Home.tsx index 475c05ed0d1..9f5c3ab864d 100644 --- a/src/routes/Home.tsx +++ b/src/routes/Home.tsx @@ -39,6 +39,8 @@ import { } from '@src/machines/systemIO/utils' import type { WebContentSendPayload } from '@src/menu/channels' import { openExternalBrowserIfDesktop } from '@src/lib/openWindow' +import { CustomIcon } from '@src/components/CustomIcon' +import Tooltip from '@src/components/Tooltip' type ReadWriteProjectState = { value: boolean @@ -250,6 +252,18 @@ const Home = () => { data-testid="home-text-to-cad" > Generate with Text-to-CAD + +
+
+ + Experimental +
+

+ This feature is experimental and undergoing constant + improvment, stay tuned for updates. +

+
+