diff --git a/e2e/playwright/flow-tests.spec.ts b/e2e/playwright/flow-tests.spec.ts index c49bd8e51..e6c834350 100644 --- a/e2e/playwright/flow-tests.spec.ts +++ b/e2e/playwright/flow-tests.spec.ts @@ -2213,3 +2213,105 @@ test('Extrude from command bar selects extrude line after', async ({ ` |> extrude(${KCL_DEFAULT_LENGTH}, %)` ) }) + +test('Basic default modeling and sketch hotkeys work', async ({ page }) => { + // This test can run long if it takes a little too long to load + // the engine. + test.setTimeout(90000) + // This test has a weird bug on ubuntu + test.skip( + process.platform === 'linux', + 'weird playwright bug on ubuntu https://github.com/KittyCAD/modeling-app/issues/2444' + ) + // Load the app with the code pane open + await page.addInitScript(async () => { + localStorage.setItem( + 'store', + JSON.stringify({ + state: { + openPanes: ['code'], + }, + version: 0, + }) + ) + }) + + // Wait for the app to be ready for use + const u = getUtils(page) + await page.setViewportSize({ width: 1200, height: 500 }) + await page.goto('/') + await u.waitForAuthSkipAppStart() + await u.openDebugPanel() + await u.expectCmdLog('[data-message-type="execution-done"]') + await u.closeDebugPanel() + + const codePane = page.getByRole('textbox').locator('div') + const codePaneButton = page.getByRole('tab', { name: 'KCL Code' }) + const lineButton = page.getByRole('button', { name: 'Line' }) + const arcButton = page.getByRole('button', { name: 'Tangential Arc' }) + const extrudeButton = page.getByRole('button', { name: 'Extrude' }) + + // Test that the hotkeys do nothing when + // focus is on the code pane + await codePane.click() + await page.keyboard.press('s') + await page.keyboard.press('l') + await page.keyboard.press('a') + await page.keyboard.press('e') + await expect(page.locator('.cm-content')).toHaveText('slae') + await page.keyboard.press('Meta+/') + + // Test these hotkeys perform actions when + // focus is on the canvas + await page.mouse.move(600, 250) + await page.mouse.click(600, 250) + // Start a sketch + await page.keyboard.press('s') + await page.mouse.move(800, 300) + await page.mouse.click(800, 300) + await page.waitForTimeout(1000) + await expect(lineButton).toHaveAttribute('aria-pressed', 'true') + /** + * TODO: There is a bug somewhere that causes this test to fail + * if you toggle the codePane closed before your trigger the + * start of the sketch. + */ + await codePaneButton.click() + + // Draw a line + await page.mouse.move(700, 200, { steps: 5 }) + await page.mouse.click(700, 200) + await page.mouse.move(800, 250, { steps: 5 }) + await page.mouse.click(800, 250) + // Unequip line tool + await page.keyboard.press('l') + await expect(lineButton).not.toHaveAttribute('aria-pressed', 'true') + // Equip arc tool + await page.keyboard.press('a') + await expect(arcButton).toHaveAttribute('aria-pressed', 'true') + await page.mouse.move(1000, 100, { steps: 5 }) + await page.mouse.click(1000, 100) + await page.keyboard.press('Escape') + await page.keyboard.press('l') + await expect(lineButton).toHaveAttribute('aria-pressed', 'true') + // Close profile + await page.mouse.move(700, 200, { steps: 5 }) + await page.mouse.click(700, 200) + // Unequip line tool + await page.keyboard.press('Escape') + // Exit sketch + await page.keyboard.press('Escape') + + // Extrude + await page.mouse.click(750, 150) + await expect(extrudeButton).not.toBeDisabled() + await page.keyboard.press('e') + await page.mouse.move(850, 180, { steps: 5 }) + await page.mouse.click(850, 180) + await page.waitForTimeout(100) + await page.getByRole('button', { name: 'Continue' }).click() + await page.getByRole('button', { name: 'Submit command' }).click() + + await codePaneButton.click() + await expect(page.locator('.cm-content')).toContainText('extrude(') +}) diff --git a/e2e/playwright/snapshot-tests.spec.ts-snapshots/Client-side-scene-scale-should-match-engine-scale-Inch-scale-1-Google-Chrome-linux.png b/e2e/playwright/snapshot-tests.spec.ts-snapshots/Client-side-scene-scale-should-match-engine-scale-Inch-scale-1-Google-Chrome-linux.png index f071addc8..db836bb56 100644 Binary files a/e2e/playwright/snapshot-tests.spec.ts-snapshots/Client-side-scene-scale-should-match-engine-scale-Inch-scale-1-Google-Chrome-linux.png and b/e2e/playwright/snapshot-tests.spec.ts-snapshots/Client-side-scene-scale-should-match-engine-scale-Inch-scale-1-Google-Chrome-linux.png differ diff --git a/e2e/playwright/snapshot-tests.spec.ts-snapshots/Client-side-scene-scale-should-match-engine-scale-Millimeter-scale-1-Google-Chrome-linux.png b/e2e/playwright/snapshot-tests.spec.ts-snapshots/Client-side-scene-scale-should-match-engine-scale-Millimeter-scale-1-Google-Chrome-linux.png index c69ea0377..2f5aa7db9 100644 Binary files a/e2e/playwright/snapshot-tests.spec.ts-snapshots/Client-side-scene-scale-should-match-engine-scale-Millimeter-scale-1-Google-Chrome-linux.png and b/e2e/playwright/snapshot-tests.spec.ts-snapshots/Client-side-scene-scale-should-match-engine-scale-Millimeter-scale-1-Google-Chrome-linux.png differ diff --git a/src/App.tsx b/src/App.tsx index 641eaf150..76c7270e9 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -58,9 +58,8 @@ export function App() { const { app: { onboardingStatus }, } = settings.context - const { state, send } = useModelingContext() + const { state } = useModelingContext() - useHotkeys('esc', () => send('Cancel')) useHotkeys('backspace', (e) => { e.preventDefault() }) diff --git a/src/Toolbar.tsx b/src/Toolbar.tsx index 46a4765fb..d13758d9b 100644 --- a/src/Toolbar.tsx +++ b/src/Toolbar.tsx @@ -12,6 +12,8 @@ import { } from 'components/NetworkHealthIndicator' import { useStore } from 'useStore' import { ActionButtonDropdown } from 'components/ActionButtonDropdown' +import { useHotkeys } from 'react-hotkeys-hook' +import Tooltip from 'components/Tooltip' export const Toolbar = () => { const { commandBarSend } = useCommandsContext() @@ -40,6 +42,56 @@ export const Toolbar = () => { const disableAllButtons = overallState !== NetworkHealthState.Ok || isExecuting || !isStreamReady + useHotkeys( + 'l', + () => + state.matches('Sketch.Line tool') + ? send('CancelSketch') + : send('Equip Line tool'), + { enabled: !disableAllButtons, scopes: ['sketch'] } + ) + useHotkeys( + 'a', + () => + state.matches('Sketch.Tangential arc to') + ? send('CancelSketch') + : send('Equip tangential arc to'), + { enabled: !disableAllButtons, scopes: ['sketch'] } + ) + useHotkeys( + 'r', + () => + state.matches('Sketch.Rectangle tool') + ? send('CancelSketch') + : send('Equip rectangle tool'), + { enabled: !disableAllButtons, scopes: ['sketch'] } + ) + useHotkeys( + 's', + () => + state.nextEvents.includes('Enter sketch') && pathId + ? send({ type: 'Enter sketch' }) + : send({ type: 'Enter sketch', data: { forceNewSketch: true } }), + { enabled: !disableAllButtons, scopes: ['modeling'] } + ) + useHotkeys( + 'esc', + () => + state.matches('Sketch.SketchIdle') + ? send('Cancel') + : send('CancelSketch'), + { enabled: !disableAllButtons, scopes: ['sketch'] } + ) + useHotkeys( + 'e', + () => + commandBarSend({ + type: 'Find and select command', + data: { name: 'Extrude', ownerMachine: 'modeling' }, + }), + { enabled: !disableAllButtons, scopes: ['modeling'] } + ) + function handleToolbarButtonsWheelEvent(ev: WheelEvent) { const span = toolbarButtonsRef.current if (!span) { @@ -77,6 +129,13 @@ export const Toolbar = () => { disabled={disableAllButtons} > Start Sketch + + Shortcut: S + )} @@ -94,6 +153,13 @@ export const Toolbar = () => { disabled={disableAllButtons} > Edit Sketch + + Shortcut: S + )} @@ -111,6 +177,13 @@ export const Toolbar = () => { disabled={disableAllButtons} > Exit Sketch + + Shortcut: Esc + )} @@ -134,6 +207,13 @@ export const Toolbar = () => { disabled={disableAllButtons} > Line + + Shortcut: L +
  • @@ -158,6 +238,13 @@ export const Toolbar = () => { } > Tangential Arc + + Shortcut: A +
  • @@ -187,6 +274,13 @@ export const Toolbar = () => { } > Rectangle + + Shortcut: R +
  • @@ -264,6 +358,13 @@ export const Toolbar = () => { }} > Extrude + + Shortcut: E + )} diff --git a/src/components/ModelingMachineProvider.tsx b/src/components/ModelingMachineProvider.tsx index 99a7dd62d..7dda81099 100644 --- a/src/components/ModelingMachineProvider.tsx +++ b/src/components/ModelingMachineProvider.tsx @@ -290,7 +290,7 @@ export const ModelingMachineProvider = ({ kclManager.ast, sketchDetails?.sketchPathToNode || [], 'VariableDeclaration' - )?.node?.declarations[0]?.init.type !== 'PipeExpression', + )?.node?.declarations?.[0]?.init.type !== 'PipeExpression', 'Selection is on face': ({ selectionRanges }, { data }) => { if (data?.forceNewSketch) return false if (!isSingleCursorInPipe(selectionRanges, kclManager.ast))