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 +
+ )} ))} 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: ( { if (record) { @@ -91,6 +93,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..e587afd51cd 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, @@ -964,8 +965,10 @@ export const modelingMachineCommandConfig: StateMachineCommandSetConfig< }, }, 'Prompt-to-edit': { - description: 'Use Zoo AI to edit your kcl', - icon: 'chat', + 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: { selection: { inputType: 'selectionMixed', 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/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 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 } 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: [], 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. +

+
+