diff --git a/e2e/playwright/flow-tests.spec.ts b/e2e/playwright/flow-tests.spec.ts index acd57f176..192feb4ab 100644 --- a/e2e/playwright/flow-tests.spec.ts +++ b/e2e/playwright/flow-tests.spec.ts @@ -4,6 +4,7 @@ import { EngineCommand } from '../../src/lang/std/engineConnection' import { v4 as uuidv4 } from 'uuid' import { getUtils } from './test-utils' import waitOn from 'wait-on' +import { Themes } from '../../src/lib/theme' /* debug helper: unfortunately we do rely on exact coord mouse clicks in a few places @@ -631,3 +632,46 @@ test('Selections work on fresh and edited sketch', async ({ page }) => { // hover again and check it works await selectionSequence() }) + +test('Command bar works and can change a setting', async ({ page }) => { + // Brief boilerplate + const u = getUtils(page) + await page.setViewportSize({ width: 1200, height: 500 }) + await page.goto('/') + await u.waitForAuthSkipAppStart() + + let cmdSearchBar = page.getByPlaceholder('Search commands') + + // First try opening the command bar and closing it + await page.getByRole('button', { name: '⌘K' }).click() + await expect(cmdSearchBar).toBeVisible() + await page.keyboard.press('Escape') + await expect(cmdSearchBar).not.toBeVisible() + + // Now try the same, but with the keyboard shortcut, check focus + await page.keyboard.press('Meta+K') + await expect(cmdSearchBar).toBeVisible() + await expect(cmdSearchBar).toBeFocused() + + // Try typing in the command bar + await page.keyboard.type('theme') + const themeOption = page.getByRole('option', { name: 'Set Theme' }) + await expect(themeOption).toBeVisible() + await themeOption.click() + const themeInput = page.getByPlaceholder(Themes.System) + await expect(themeInput).toBeVisible() + await expect(themeInput).toBeFocused() + // Select dark theme + await page.keyboard.press('ArrowDown') + await page.keyboard.press('ArrowUp') + await expect(page.getByRole('option', { name: Themes.Dark })).toHaveAttribute( + 'data-headlessui-state', + 'active' + ) + await page.keyboard.press('Enter') + + // Check the toast appeared + await expect(page.getByText(`Set Theme to "${Themes.Dark}"`)).toBeVisible() + // Check that the theme changed + await expect(page.locator('body')).toHaveClass(`body-bg ${Themes.Dark}`) +}) diff --git a/e2e/playwright/snapshot-tests.spec.ts-snapshots/change-camera-show-planes-1-Google-Chrome-linux.png b/e2e/playwright/snapshot-tests.spec.ts-snapshots/change-camera-show-planes-1-Google-Chrome-linux.png index d693b74b4..b05191d7d 100644 Binary files a/e2e/playwright/snapshot-tests.spec.ts-snapshots/change-camera-show-planes-1-Google-Chrome-linux.png and b/e2e/playwright/snapshot-tests.spec.ts-snapshots/change-camera-show-planes-1-Google-Chrome-linux.png differ diff --git a/e2e/playwright/snapshot-tests.spec.ts-snapshots/change-camera-show-planes-2-Google-Chrome-linux.png b/e2e/playwright/snapshot-tests.spec.ts-snapshots/change-camera-show-planes-2-Google-Chrome-linux.png index 43ee467ca..caa4f8793 100644 Binary files a/e2e/playwright/snapshot-tests.spec.ts-snapshots/change-camera-show-planes-2-Google-Chrome-linux.png and b/e2e/playwright/snapshot-tests.spec.ts-snapshots/change-camera-show-planes-2-Google-Chrome-linux.png differ diff --git a/e2e/playwright/snapshot-tests.spec.ts-snapshots/change-camera-show-planes-3-Google-Chrome-linux.png b/e2e/playwright/snapshot-tests.spec.ts-snapshots/change-camera-show-planes-3-Google-Chrome-linux.png index 9fd5080b0..f63be3c6d 100644 Binary files a/e2e/playwright/snapshot-tests.spec.ts-snapshots/change-camera-show-planes-3-Google-Chrome-linux.png and b/e2e/playwright/snapshot-tests.spec.ts-snapshots/change-camera-show-planes-3-Google-Chrome-linux.png differ diff --git a/public/Icon/Icon/Projects/Create File.svg b/public/Icon/Icon/Projects/Create File.svg deleted file mode 100644 index 757ee17b3..000000000 --- a/public/Icon/Icon/Projects/Create File.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/public/Icon/Icon/Projects/Create Folder.svg b/public/Icon/Icon/Projects/Create Folder.svg deleted file mode 100644 index a0a23431d..000000000 --- a/public/Icon/Icon/Projects/Create Folder.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/public/Icon/Icon/Projects/File.svg b/public/Icon/Icon/Projects/File.svg deleted file mode 100644 index 2b8287a46..000000000 --- a/public/Icon/Icon/Projects/File.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/Toolbar.module.css b/src/Toolbar.module.css deleted file mode 100644 index d807a6831..000000000 --- a/src/Toolbar.module.css +++ /dev/null @@ -1,106 +0,0 @@ -.toolbarWrapper { - @apply relative; -} - -.toolbar { - @apply flex gap-4 items-center rounded-full; - @apply border border-cool-20/30 bg-cool-10/50; -} - -:global(.dark) .toolbar { - @apply border-cool-100/50 bg-cool-120/50; -} - -:global(.sketch) .toolbar { - @apply border-fern-20/20 bg-fern-10/20; -} - -:global(.dark .sketch) .toolbar { - @apply border-fern-120/50 bg-fern-100/30; -} - -.toolbarCap { - @apply text-sm font-bold; - @apply bg-cool-20/50 text-cool-100; -} - -:global(.dark) .toolbarCap { - @apply bg-cool-90/50 text-cool-30; -} - -:global(.sketch) .toolbarCap { - @apply bg-fern-20/50 text-fern-100; -} - -:global(.dark .sketch) .toolbarCap { - @apply bg-fern-90/50 text-fern-30; -} - -.label { - @apply self-stretch flex items-center px-4 py-1; - @apply rounded-l-full; -} - -.popoverToggle { - @apply self-stretch m-0 flex items-center px-4 py-1; - @apply rounded-r-full border-none; - @apply hover:bg-cool-20; -} - -.toolbarButtons::-webkit-scrollbar { - @apply h-0.5; -} - -.toolbarButtons { - @apply flex items-center overflow-x-auto; - scrollbar-width: thin; -} - -.toolbarButtons button { - @apply text-chalkboard-90 bg-chalkboard-10/50 border-chalkboard-50 whitespace-nowrap; - display: inline-flex; - align-items: center; - justify-content: center; - @apply gap-1.5 p-0.5 pr-1; - @apply rounded-sm; -} -:global(.dark) .toolbarButtons button { - @apply text-chalkboard-30 bg-chalkboard-90/50 border-chalkboard-50; -} -.toolbarButtons button:hover { - @apply text-cool-90 bg-cool-10; -} -:global(.sketch) .toolbarButtons button:hover { - @apply text-fern-90 bg-fern-10; -} -.toolbarButtons button:disabled { - @apply text-chalkboard-70 bg-chalkboard-30; -} -.toolbarButtons button:disabled:hover { - @apply !bg-inherit !text-inherit cursor-not-allowed; -} - -:global(.dark) .toolbarButtons button { - @apply text-chalkboard-20 border-chalkboard-50; -} -:global(.dark) .toolbarButtons button:hover { - @apply text-cool-10 border-chalkboard-50 bg-cool-90; -} -:global(.dark .sketch) .toolbarButtons button:hover { - @apply text-fern-10 border-chalkboard-50 bg-fern-90; -} -:global(.dark) .toolbarButtons button:disabled { - @apply text-chalkboard-40 bg-chalkboard-80; -} - -:global(.dark) .popoverToggle { - @apply hover:bg-cool-90; -} - -:global(.sketch) .popoverToggle { - @apply hover:bg-fern-20; -} - -:global(.dark .sketch) .popoverToggle { - @apply hover:bg-fern-90; -} diff --git a/src/Toolbar.tsx b/src/Toolbar.tsx index f1b57d806..4c6d9712b 100644 --- a/src/Toolbar.tsx +++ b/src/Toolbar.tsx @@ -1,22 +1,16 @@ -import { Fragment, WheelEvent, useRef, useMemo } from 'react' -import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' -import { faSearch, faX } from '@fortawesome/free-solid-svg-icons' -import { Popover, Transition } from '@headlessui/react' -import styles from './Toolbar.module.css' +import { WheelEvent, useRef, useMemo } from 'react' import { isCursorInSketchCommandRange } from 'lang/util' -import { ActionIcon } from 'components/ActionIcon' import { engineCommandManager } from './lang/std/engineConnection' import { useModelingContext } from 'hooks/useModelingContext' - -export const sketchButtonClassnames = { - background: - 'bg-chalkboard-100 group-hover:bg-chalkboard-90 hover:bg-chalkboard-90 dark:bg-fern-20 dark:group-hover:bg-fern-10 dark:hover:bg-fern-10 group-disabled:bg-chalkboard-50 dark:group-disabled:bg-chalkboard-60 group-hover:group-disabled:bg-chalkboard-50 dark:group-hover:group-disabled:bg-chalkboard-50', - icon: 'text-fern-20 h-auto group-hover:text-fern-10 hover:text-fern-10 dark:text-chalkboard-100 dark:group-hover:text-chalkboard-100 dark:hover:text-chalkboard-100 group-disabled:bg-chalkboard-60 hover:group-disabled:text-inherit', -} +import { useCommandsContext } from 'hooks/useCommandsContext' +import { ActionButton } from 'components/ActionButton' export const Toolbar = () => { + const { setCommandBarOpen } = useCommandsContext() const { state, send, context } = useModelingContext() - const toolbarButtonsRef = useRef(null) + const toolbarButtonsRef = useRef(null) + const bgClassName = + 'group-enabled:group-hover:bg-energy-10 group-pressed:bg-energy-10 dark:group-enabled:group-hover:bg-chalkboard-80 dark:group-pressed:bg-chalkboard-80' const pathId = useMemo( () => isCursorInSketchCommandRange( @@ -35,72 +29,102 @@ export const Toolbar = () => { span.scrollLeft = span.scrollLeft += ev.deltaY } - function ToolbarButtons({ className }: React.HTMLAttributes) { + function ToolbarButtons({ + className = '', + ...props + }: React.HTMLAttributes) { return ( - {state.nextEvents.includes('Enter sketch') && ( - +
  • + send({ type: 'Enter sketch' })} + icon={{ + icon: 'sketch', + bgClassName, + }} + > + Start Sketch + +
  • )} {state.nextEvents.includes('Enter sketch') && pathId && ( - +
  • + send({ type: 'Enter sketch' })} + icon={{ + icon: 'sketch', + bgClassName, + }} + > + Edit Sketch + +
  • )} {state.nextEvents.includes('Cancel') && !state.matches('idle') && ( - +
  • + send({ type: 'Cancel' })} + icon={{ + icon: 'arrowLeft', + bgClassName, + }} + > + Exit Sketch + +
  • )} {state.matches('Sketch') && !state.matches('idle') && ( - +
  • + + state.matches('Sketch.Line Tool') + ? send('CancelSketch') + : send('Equip tool') + } + aria-pressed={state.matches('Sketch.Line Tool')} + className="pressed:bg-energy-10/20 dark:pressed:bg-energy-80" + icon={{ + icon: 'line', + bgClassName, + }} + > + Line + +
  • )} {state.matches('Sketch') && ( - +
  • + + state.matches('Sketch.Move Tool') + ? send('CancelSketch') + : send('Equip move tool') + } + aria-pressed={state.matches('Sketch.Move Tool')} + className="pressed:bg-energy-10/20 dark:pressed:bg-energy-80" + icon={{ + icon: 'move', + bgClassName, + }} + > + Move + +
  • )} {state.matches('Sketch.SketchIdle') && state.nextEvents @@ -125,102 +149,66 @@ export const Toolbar = () => { return 0 }) .map((eventName) => ( - +
  • + send(eventName)} + disabled={ + !state.nextEvents + .filter((event) => state.can(event as any)) + .includes(eventName) + } + title={eventName} + icon={{ + icon: 'line', + bgClassName, + }} + > + {eventName + .replace('Make segment ', '') + .replace('Constrain ', '')} + +
  • ))} {state.matches('idle') && ( - +
  • + send('extrude intent')} + disabled={!state.can('extrude intent')} + title={ + state.can('extrude intent') + ? 'extrude' + : 'sketches need to be closed, or not already extruded' + } + icon={{ + icon: 'extrude', + bgClassName, + }} + > + Extrude + +
  • )} -
    + ) } return ( - -
    - - {state.matches('Sketch') ? '2D' : '3D'} - - - - - - - -
    - + + + + setCommandBarOpen(true)} + className="rounded-r-full pr-4 self-stretch border-energy-10 hover:border-energy-10 dark:border-chalkboard-80 bg-energy-10/50 hover:bg-energy-10 dark:bg-chalkboard-80 dark:text-energy-10" > - - - - -
    -

    - You're in {state.matches('Sketch') ? '2D' : '3D'} -

    - - - -
    -
    - -
    -
    -
    -
    + ⌘K + + ) } diff --git a/src/components/ActionButton.tsx b/src/components/ActionButton.tsx index 1cca9f939..e79c76b63 100644 --- a/src/components/ActionButton.tsx +++ b/src/components/ActionButton.tsx @@ -39,16 +39,16 @@ type ActionButtonProps = | ActionButtonAsElement export const ActionButton = (props: ActionButtonProps) => { - const classNames = `group mono text-base flex items-center gap-2 rounded-sm border border-chalkboard-40 dark:border-chalkboard-60 hover:border-liquid-40 dark:hover:bg-chalkboard-90 p-[3px] text-chalkboard-110 dark:text-chalkboard-10 hover:text-chalkboard-110 hover:dark:text-chalkboard-10 ${ + const classNames = `action-button m-0 group mono text-sm flex items-center gap-2 rounded-sm border-solid border border-chalkboard-30 hover:border-chalkboard-40 dark:border-chalkboard-70 dark:hover:border-chalkboard-60 dark:bg-chalkboard-90/50 p-[3px] text-chalkboard-100 dark:text-chalkboard-10 ${ props.icon ? 'pr-2' : 'px-2' - } ${props.className || ''}` + } ${props.className ? props.className : ''}` switch (props.Element) { case 'button': { // Note we have to destructure 'className' and 'Element' out of props // because we don't want to pass them to the button element; // the same is true for the other cases below. - const { Element, icon, children, className, ...rest } = props + const { Element, icon, children, className: _className, ...rest } = props return (