From 18f4a1303c83a93a1cfd06581761ec3dc4badef4 Mon Sep 17 00:00:00 2001 From: Kurt Hutten Date: Wed, 26 Feb 2025 14:06:51 +1100 Subject: [PATCH] Multiple prompt-to-edit selection, plus direct editor selections (#5478) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add multiple selections and editor selections for promptToEdit * remove unused * re-enable prompt to edit tests * add test for manual code selection * at test for multi-selection * clean up * A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores) * typo --------- Co-authored-by: github-actions[bot] --- e2e/playwright/fixtures/editorFixture.ts | 18 ++ e2e/playwright/prompt-to-edit.spec.ts | 154 +++++++++++++++++- ...ode-color-goober-1-Google-Chrome-linux.png | Bin 148918 -> 148932 bytes src/components/CommandBar/CommandBar.tsx | 4 +- .../CommandBar/CommandBarArgument.tsx | 9 + .../CommandBar/CommandBarHeader.tsx | 3 +- .../CommandBarSelectionMixedInput.tsx | 135 +++++++++++++++ .../modelingCommandConfig.ts | 6 +- src/lib/commandTypes.ts | 35 ++++ src/lib/createMachineCommand.ts | 10 ++ src/lib/promptToEdit.ts | 38 ++++- src/lib/selections.ts | 4 +- src/machines/commandBarMachine.ts | 3 +- 13 files changed, 409 insertions(+), 10 deletions(-) create mode 100644 src/components/CommandBar/CommandBarSelectionMixedInput.tsx diff --git a/e2e/playwright/fixtures/editorFixture.ts b/e2e/playwright/fixtures/editorFixture.ts index ecd768c8e..b8c5e2d82 100644 --- a/e2e/playwright/fixtures/editorFixture.ts +++ b/e2e/playwright/fixtures/editorFixture.ts @@ -171,4 +171,22 @@ export class EditorFixture { { text, placeCursor } ) } + async selectText(text: string) { + // First make sure the code pane is open + const wasPaneOpen = await this.checkIfPaneIsOpen() + if (!wasPaneOpen) { + await this.openPane() + } + + // Use Playwright's built-in text selection on the code content + // it seems to only select whole divs, which works out to align with syntax highlighting + // for code mirror, so you can probably select "sketch002 = startSketchOn('XZ')" + // but less so for exactly "sketch002 = startS" + await this.codeContent.getByText(text).first().selectText() + + // Reset pane state if needed + if (!wasPaneOpen) { + await this.closePane() + } + } } diff --git a/e2e/playwright/prompt-to-edit.spec.ts b/e2e/playwright/prompt-to-edit.spec.ts index 23d6de648..7950b2d17 100644 --- a/e2e/playwright/prompt-to-edit.spec.ts +++ b/e2e/playwright/prompt-to-edit.spec.ts @@ -36,7 +36,7 @@ extrude003 = extrude(sketch003, length = 20) ` test.describe('Prompt-to-edit tests', { tag: '@skipWin' }, () => { - test.fixme('Check the happy path, for basic changing color', () => { + test.describe('Check the happy path, for basic changing color', () => { const cases = [ { desc: 'User accepts change', @@ -70,7 +70,7 @@ test.describe('Prompt-to-edit tests', { tag: '@skipWin' }, () => { body1CapCoords.y ) const yellow: [number, number, number] = [179, 179, 131] - const green: [number, number, number] = [108, 152, 75] + const green: [number, number, number] = [128, 194, 88] const notGreen: [number, number, number] = [132, 132, 132] const body2NotGreen: [number, number, number] = [88, 88, 88] const submittingToast = page.getByText( @@ -109,7 +109,7 @@ test.describe('Prompt-to-edit tests', { tag: '@skipWin' }, () => { }) await test.step('verify initial change', async () => { - await scene.expectPixelColor(green, greenCheckCoords, 15) + await scene.expectPixelColor(green, greenCheckCoords, 20) await scene.expectPixelColor(body2NotGreen, body2WallCoords, 15) await editor.expectEditor.toContain('appearance(') }) @@ -142,7 +142,7 @@ test.describe('Prompt-to-edit tests', { tag: '@skipWin' }, () => { } }) - test(`bad edit prompt`, async ({ + test('bad edit prompt', async ({ context, homePage, cmdBar, @@ -195,4 +195,150 @@ test.describe('Prompt-to-edit tests', { tag: '@skipWin' }, () => { await expect(failToast).toBeVisible() }) }) + + test(`manual code selection rename`, async ({ + context, + homePage, + cmdBar, + editor, + page, + scene, + }) => { + const body1CapCoords = { x: 571, y: 351 } + + await context.addInitScript((file) => { + localStorage.setItem('persistCode', file) + }, file) + await homePage.goToModelingScene() + await scene.waitForExecutionDone() + + const submittingToast = page.getByText('Submitting to Text-to-CAD API...') + const successToast = page.getByText('Prompt to edit successful') + const acceptBtn = page.getByRole('button', { name: 'checkmark Accept' }) + + await test.step('wait for scene to load and select code in editor', async () => { + // Find and select the text "sketch002" in the editor + await editor.selectText('sketch002') + + // Verify the selection was made + await editor.expectState({ + highlightedCode: '', + activeLines: ["sketch002 = startSketchOn('XZ')"], + diagnostics: [], + }) + }) + + await test.step('fire off edit prompt', async () => { + await scene.expectPixelColor([134, 134, 134], body1CapCoords, 15) + await cmdBar.openCmdBar('promptToEdit') + await page + .getByTestId('cmd-bar-arg-value') + .fill('Please rename to mySketch') + await page.waitForTimeout(100) + await cmdBar.progressCmdBar() + await expect(submittingToast).toBeVisible() + await expect(submittingToast).not.toBeVisible({ + timeout: 2 * 60_000, + }) + await expect(successToast).toBeVisible() + }) + + await test.step('verify rename change and accept it', async () => { + await editor.expectEditor.toContain('mySketch = startSketchOn') + await editor.expectEditor.not.toContain('sketch002 = startSketchOn') + await editor.expectEditor.toContain( + 'extrude002 = extrude(mySketch, length = 50)' + ) + + await acceptBtn.click() + await expect(successToast).not.toBeVisible() + }) + }) + + test('multiple body selections', async ({ + context, + homePage, + cmdBar, + editor, + page, + scene, + }) => { + const body1CapCoords = { x: 571, y: 351 } + const body2WallCoords = { x: 620, y: 152 } + const [clickBody1Cap] = scene.makeMouseHelpers( + body1CapCoords.x, + body1CapCoords.y + ) + const [clickBody2Cap] = scene.makeMouseHelpers( + body2WallCoords.x, + body2WallCoords.y + ) + const grey: [number, number, number] = [132, 132, 132] + + await context.addInitScript((file) => { + localStorage.setItem('persistCode', file) + }, file) + await homePage.goToModelingScene() + await scene.waitForExecutionDone() + + const submittingToast = page.getByText('Submitting to Text-to-CAD API...') + const successToast = page.getByText('Prompt to edit successful') + const acceptBtn = page.getByRole('button', { name: 'checkmark Accept' }) + + await test.step('select multiple bodies and fire prompt', async () => { + // Initial color check + await scene.expectPixelColor(grey, body1CapCoords, 15) + + // Open command bar first (without selection) + await cmdBar.openCmdBar('promptToEdit') + + // Select first body + await page.waitForTimeout(100) + await clickBody1Cap() + + // Hold shift and select second body + await editor.expectState({ + highlightedCode: '', + activeLines: ['|>startProfileAt([-73.64,-42.89],%)'], + diagnostics: [], + }) + await page.keyboard.down('Shift') + await page.waitForTimeout(100) + await clickBody2Cap() + await editor.expectState({ + highlightedCode: + 'line(end=[121.13,56.63],tag=$seg02)extrude(profile001,length=200)', + activeLines: [ + '|>line(end=[121.13,56.63],tag=$seg02)', + '|>startProfileAt([-73.64,-42.89],%)', + ], + diagnostics: [], + }) + await page.keyboard.up('Shift') + await page.waitForTimeout(100) + await cmdBar.progressCmdBar() + + // Enter prompt and submit + await page + .getByTestId('cmd-bar-arg-value') + .fill('make these neon green please, use #39FF14') + await page.waitForTimeout(100) + await cmdBar.progressCmdBar() + + // Wait for API response + await expect(submittingToast).toBeVisible() + await expect(submittingToast).not.toBeVisible({ + timeout: 2 * 60_000, + }) + await expect(successToast).toBeVisible() + }) + + await test.step('verify code changed', async () => { + await editor.expectEditor.toContain('appearance(') + + // Accept changes + await acceptBtn.click() + await expect(successToast).not.toBeVisible() + }) + }) }) diff --git a/e2e/playwright/snapshot-tests.spec.ts-snapshots/code-color-goober-code-color-goober-1-Google-Chrome-linux.png b/e2e/playwright/snapshot-tests.spec.ts-snapshots/code-color-goober-code-color-goober-1-Google-Chrome-linux.png index 15105d98a93d9f6bb661d5fc690583b98b9069b9..51f2107b1cbf4b60cd9374a6a819ea79a85ae7be 100644 GIT binary patch delta 26475 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 wvo63I$K;$VaHY?fD`RF?nVb}|k delta 26419 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 G<^KbWqZ0N2 diff --git a/src/components/CommandBar/CommandBar.tsx b/src/components/CommandBar/CommandBar.tsx index 8523f3e3f..93fd576d4 100644 --- a/src/components/CommandBar/CommandBar.tsx +++ b/src/components/CommandBar/CommandBar.tsx @@ -17,7 +17,9 @@ export const CommandBar = () => { const { context: { selectedCommand, currentArgument, commands }, } = commandBarState - const isSelectionArgument = currentArgument?.inputType === 'selection' + const isSelectionArgument = + currentArgument?.inputType === 'selection' || + currentArgument?.inputType === 'selectionMixed' const WrapperComponent = isSelectionArgument ? Popover : Dialog // Close the command bar when navigating diff --git a/src/components/CommandBar/CommandBarArgument.tsx b/src/components/CommandBar/CommandBarArgument.tsx index 792c45f62..2251ac629 100644 --- a/src/components/CommandBar/CommandBarArgument.tsx +++ b/src/components/CommandBar/CommandBarArgument.tsx @@ -1,6 +1,7 @@ import CommandArgOptionInput from './CommandArgOptionInput' import CommandBarBasicInput from './CommandBarBasicInput' import CommandBarSelectionInput from './CommandBarSelectionInput' +import CommandBarSelectionMixedInput from './CommandBarSelectionMixedInput' import { CommandArgument } from 'lib/commandTypes' import CommandBarHeader from './CommandBarHeader' import CommandBarKclInput from './CommandBarKclInput' @@ -84,6 +85,14 @@ function ArgumentInput({ onSubmit={onSubmit} /> ) + case 'selectionMixed': + return ( + + ) case 'kcl': return ( diff --git a/src/components/CommandBar/CommandBarHeader.tsx b/src/components/CommandBar/CommandBarHeader.tsx index 0fbf018df..1fe4ebb8a 100644 --- a/src/components/CommandBar/CommandBarHeader.tsx +++ b/src/components/CommandBar/CommandBarHeader.tsx @@ -124,7 +124,8 @@ function CommandBarHeader({ children }: React.PropsWithChildren<{}>) { {argValue ? ( - arg.inputType === 'selection' ? ( + arg.inputType === 'selection' || + arg.inputType === 'selectionMixed' ? ( getSelectionTypeDisplayText(argValue as Selections) ) : arg.inputType === 'kcl' ? ( roundOff( diff --git a/src/components/CommandBar/CommandBarSelectionMixedInput.tsx b/src/components/CommandBar/CommandBarSelectionMixedInput.tsx new file mode 100644 index 000000000..5c46a0d48 --- /dev/null +++ b/src/components/CommandBar/CommandBarSelectionMixedInput.tsx @@ -0,0 +1,135 @@ +import { useEffect, useMemo, useRef, useState } from 'react' +import { CommandArgument } from 'lib/commandTypes' +import { + Selections, + canSubmitSelectionArg, + getSelectionCountByType, + getSelectionTypeDisplayText, +} from 'lib/selections' +import { useSelector } from '@xstate/react' +import { commandBarActor, useCommandBarState } from 'machines/commandBarMachine' + +const selectionSelector = (snapshot: any) => snapshot?.context.selectionRanges + +export default function CommandBarSelectionMixedInput({ + arg, + stepBack, + onSubmit, +}: { + arg: CommandArgument & { inputType: 'selectionMixed'; name: string } + stepBack: () => void + onSubmit: (data: unknown) => void +}) { + const inputRef = useRef(null) + const commandBarState = useCommandBarState() + const [hasSubmitted, setHasSubmitted] = useState(false) + const [hasAutoSkipped, setHasAutoSkipped] = useState(false) + const selection: Selections = useSelector(arg.machineActor, selectionSelector) + + const selectionsByType = useMemo(() => { + return getSelectionCountByType(selection) + }, [selection]) + + const canSubmitSelection = useMemo(() => { + if (!selection) return false + const isNonZeroRange = selection.graphSelections.some((sel) => { + const range = sel.codeRef.range + return range[1] - range[0] !== 0 // Non-zero range is always valid + }) + if (isNonZeroRange) return true + return canSubmitSelectionArg(selectionsByType, arg) + }, [selectionsByType, selection]) + + useEffect(() => { + inputRef.current?.focus() + }, [selection, inputRef]) + + // Only auto-skip on initial mount if we have a valid selection + // different from the component CommandBarSelectionInput in the the dependency array + // is empty + useEffect(() => { + if (!hasAutoSkipped && canSubmitSelection && arg.skip) { + const argValue = commandBarState.context.argumentsToSubmit[arg.name] + if (argValue === undefined) { + handleSubmit() + setHasAutoSkipped(true) + } + } + }, []) + + function handleChange() { + inputRef.current?.focus() + } + + function handleSubmit(e?: React.FormEvent) { + e?.preventDefault() + + if (!canSubmitSelection) { + setHasSubmitted(true) + return + } + + onSubmit(selection) + } + + const isMixedSelection = arg.inputType === 'selectionMixed' + const allowNoSelection = isMixedSelection && arg.allowNoSelection + const showSceneSelection = + isMixedSelection && arg.selectionSource?.allowSceneSelection + + return ( +
+ +
+ ) +} diff --git a/src/lib/commandBarConfigs/modelingCommandConfig.ts b/src/lib/commandBarConfigs/modelingCommandConfig.ts index 5b16b1d05..a9f0d54e3 100644 --- a/src/lib/commandBarConfigs/modelingCommandConfig.ts +++ b/src/lib/commandBarConfigs/modelingCommandConfig.ts @@ -666,7 +666,7 @@ export const modelingMachineCommandConfig: StateMachineCommandSetConfig< icon: 'chat', args: { selection: { - inputType: 'selection', + inputType: 'selectionMixed', selectionTypes: [ 'solid2d', 'segment', @@ -678,6 +678,10 @@ export const modelingMachineCommandConfig: StateMachineCommandSetConfig< ], multiple: true, required: true, + selectionSource: { + allowSceneSelection: true, + allowCodeSelection: true, + }, skip: true, }, prompt: { diff --git a/src/lib/commandTypes.ts b/src/lib/commandTypes.ts index ce99e42d2..0bb8ee579 100644 --- a/src/lib/commandTypes.ts +++ b/src/lib/commandTypes.ts @@ -16,6 +16,7 @@ const INPUT_TYPES = [ 'text', 'kcl', 'selection', + 'selectionMixed', 'boolean', ] as const export interface KclExpression { @@ -156,6 +157,23 @@ export type CommandArgumentConfig< context: CommandBarContext }) => Promise } + | { + inputType: 'selectionMixed' + selectionTypes: Artifact['type'][] + multiple: boolean + allowNoSelection?: boolean + validation?: ({ + data, + context, + }: { + data: any + context: CommandBarContext + }) => Promise + selectionSource?: { + allowSceneSelection?: boolean + allowCodeSelection?: boolean + } + } | { inputType: 'kcl' createVariableByDefault?: boolean @@ -252,6 +270,23 @@ export type CommandArgument< context: CommandBarContext }) => Promise } + | { + inputType: 'selectionMixed' + selectionTypes: Artifact['type'][] + multiple: boolean + allowNoSelection?: boolean + validation?: ({ + data, + context, + }: { + data: any + context: CommandBarContext + }) => Promise + selectionSource?: { + allowSceneSelection?: boolean + allowCodeSelection?: boolean + } + } | { inputType: 'kcl' createVariableByDefault?: boolean diff --git a/src/lib/createMachineCommand.ts b/src/lib/createMachineCommand.ts index 126f51164..c89cdd448 100644 --- a/src/lib/createMachineCommand.ts +++ b/src/lib/createMachineCommand.ts @@ -187,6 +187,16 @@ export function buildCommandArgument< selectionTypes: arg.selectionTypes, validation: arg.validation, } satisfies CommandArgument & { inputType: 'selection' } + } else if (arg.inputType === 'selectionMixed') { + return { + inputType: arg.inputType, + ...baseCommandArgument, + multiple: arg.multiple, + selectionTypes: arg.selectionTypes, + validation: arg.validation, + allowNoSelection: arg.allowNoSelection, + selectionSource: arg.selectionSource, + } satisfies CommandArgument & { inputType: 'selectionMixed' } } else if (arg.inputType === 'kcl') { return { inputType: arg.inputType, diff --git a/src/lib/promptToEdit.ts b/src/lib/promptToEdit.ts index bb3eb025d..1578a428e 100644 --- a/src/lib/promptToEdit.ts +++ b/src/lib/promptToEdit.ts @@ -43,15 +43,33 @@ export async function submitPromptToEditToQueue({ projectName, }: { prompt: string - selections: Selections + selections: Selections | null code: string projectName: string token?: string artifactGraph: ArtifactGraph }): Promise { + // If no selection, use whole file + if (selections === null) { + const body: Models['TextToCadIterationBody_type'] = { + original_source_code: code, + prompt, + source_ranges: [], // Empty ranges indicates whole file + project_name: + projectName !== '' && projectName !== 'browser' + ? projectName + : undefined, + kcl_version: kclManager.kclVersion, + } + return submitToApi(body, token) + } + + // Handle manual code selections and artifact selections differently const ranges: Models['TextToCadIterationBody_type']['source_ranges'] = selections.graphSelections.flatMap((selection) => { const artifact = selection.artifact + + // For artifact selections, add context const prompts: Models['TextToCadIterationBody_type']['source_ranges'] = [] if (artifact?.type === 'cap') { @@ -153,8 +171,17 @@ See later source ranges for more context. about the sweep`, } } } + if (!artifact) { + // manually selected code is more likely to not have an artifact + // an example might be highlighting the variable name only in a variable declaration + prompts.push({ + prompt: '', + range: convertAppRangeToApiRange(selection.codeRef.range, code), + }) + } return prompts }) + const body: Models['TextToCadIterationBody_type'] = { original_source_code: code, prompt, @@ -163,6 +190,15 @@ See later source ranges for more context. about the sweep`, projectName !== '' && projectName !== 'browser' ? projectName : undefined, kcl_version: kclManager.kclVersion, } + + return submitToApi(body, token) +} + +// Helper function to handle API submission +async function submitToApi( + body: Models['TextToCadIterationBody_type'], + token?: string +): Promise { const url = VITE_KC_API_BASE_URL + '/ml/text-to-cad/iteration' const data: Models['TextToCadIteration_type'] | Error = await crossPlatformFetch( diff --git a/src/lib/selections.ts b/src/lib/selections.ts index 4c9f94520..308abdcc8 100644 --- a/src/lib/selections.ts +++ b/src/lib/selections.ts @@ -481,7 +481,9 @@ export function getSelectionTypeDisplayText( export function canSubmitSelectionArg( selectionsByType: 'none' | Map, - argument: CommandArgument & { inputType: 'selection' } + argument: CommandArgument & { + inputType: 'selection' | 'selectionMixed' + } ) { return ( selectionsByType !== 'none' && diff --git a/src/machines/commandBarMachine.ts b/src/machines/commandBarMachine.ts index e2fe6089f..95e91600b 100644 --- a/src/machines/commandBarMachine.ts +++ b/src/machines/commandBarMachine.ts @@ -295,7 +295,8 @@ export const commandBarMachine = setup({ if ( context.currentArgument && context.selectedCommand && - argConfig?.inputType === 'selection' && + (argConfig?.inputType === 'selection' || + argConfig?.inputType === 'selectionMixed') && argConfig?.validation ) { argConfig