diff --git a/app-icon.png b/app-icon.png index 36c205d06..4e21b2070 100644 Binary files a/app-icon.png and b/app-icon.png differ diff --git a/public/favicon.ico b/public/favicon.ico index 8d12c47e0..196017cb2 100644 Binary files a/public/favicon.ico and b/public/favicon.ico differ diff --git a/public/kittycad-logomark-light.svg b/public/kittycad-logomark-light.svg index 5c5a02c41..3f87c7ecc 100644 --- a/public/kittycad-logomark-light.svg +++ b/public/kittycad-logomark-light.svg @@ -1,26 +1,45 @@ - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/kittycad-logomark.svg b/public/kittycad-logomark.svg index 4ea6340d1..5e6c2ab29 100644 --- a/public/kittycad-logomark.svg +++ b/public/kittycad-logomark.svg @@ -1,26 +1,45 @@ - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/logo192.png b/public/logo192.png index c5eb2e8e1..2330436c4 100644 Binary files a/public/logo192.png and b/public/logo192.png differ diff --git a/public/logo512.png b/public/logo512.png index 89bb9667e..82d06279e 100644 Binary files a/public/logo512.png and b/public/logo512.png differ diff --git a/src-tauri/icons/128x128.png b/src-tauri/icons/128x128.png index 3bc92963c..8793d0b0f 100644 Binary files a/src-tauri/icons/128x128.png and b/src-tauri/icons/128x128.png differ diff --git a/src-tauri/icons/128x128@2x.png b/src-tauri/icons/128x128@2x.png index fcdcf67ab..8c5785b8a 100644 Binary files a/src-tauri/icons/128x128@2x.png and b/src-tauri/icons/128x128@2x.png differ diff --git a/src-tauri/icons/32x32.png b/src-tauri/icons/32x32.png index 46cc8ee9d..d7178ff14 100644 Binary files a/src-tauri/icons/32x32.png and b/src-tauri/icons/32x32.png differ diff --git a/src-tauri/icons/Square107x107Logo.png b/src-tauri/icons/Square107x107Logo.png index 28416ea23..ae77b0b10 100644 Binary files a/src-tauri/icons/Square107x107Logo.png and b/src-tauri/icons/Square107x107Logo.png differ diff --git a/src-tauri/icons/Square142x142Logo.png b/src-tauri/icons/Square142x142Logo.png index 15ef4002e..e6a1ae05d 100644 Binary files a/src-tauri/icons/Square142x142Logo.png and b/src-tauri/icons/Square142x142Logo.png differ diff --git a/src-tauri/icons/Square150x150Logo.png b/src-tauri/icons/Square150x150Logo.png index 662b2a515..820820b6f 100644 Binary files a/src-tauri/icons/Square150x150Logo.png and b/src-tauri/icons/Square150x150Logo.png differ diff --git a/src-tauri/icons/Square284x284Logo.png b/src-tauri/icons/Square284x284Logo.png index 41a38561d..9b62aa8e5 100644 Binary files a/src-tauri/icons/Square284x284Logo.png and b/src-tauri/icons/Square284x284Logo.png differ diff --git a/src-tauri/icons/Square30x30Logo.png b/src-tauri/icons/Square30x30Logo.png index 9b6289469..bf558d768 100644 Binary files a/src-tauri/icons/Square30x30Logo.png and b/src-tauri/icons/Square30x30Logo.png differ diff --git a/src-tauri/icons/Square310x310Logo.png b/src-tauri/icons/Square310x310Logo.png index 9b2c0a0a4..ce9a9df0f 100644 Binary files a/src-tauri/icons/Square310x310Logo.png and b/src-tauri/icons/Square310x310Logo.png differ diff --git a/src-tauri/icons/Square44x44Logo.png b/src-tauri/icons/Square44x44Logo.png index 855a982af..d1a94c672 100644 Binary files a/src-tauri/icons/Square44x44Logo.png and b/src-tauri/icons/Square44x44Logo.png differ diff --git a/src-tauri/icons/Square71x71Logo.png b/src-tauri/icons/Square71x71Logo.png index 0b6400996..3840f642f 100644 Binary files a/src-tauri/icons/Square71x71Logo.png and b/src-tauri/icons/Square71x71Logo.png differ diff --git a/src-tauri/icons/Square89x89Logo.png b/src-tauri/icons/Square89x89Logo.png index 53f5b9ad9..847044bd9 100644 Binary files a/src-tauri/icons/Square89x89Logo.png and b/src-tauri/icons/Square89x89Logo.png differ diff --git a/src-tauri/icons/StoreLogo.png b/src-tauri/icons/StoreLogo.png index a8c91b734..4f6e11cf3 100644 Binary files a/src-tauri/icons/StoreLogo.png and b/src-tauri/icons/StoreLogo.png differ diff --git a/src-tauri/icons/icon.icns b/src-tauri/icons/icon.icns index 27b84ff71..cb7b6ac53 100644 Binary files a/src-tauri/icons/icon.icns and b/src-tauri/icons/icon.icns differ diff --git a/src-tauri/icons/icon.ico b/src-tauri/icons/icon.ico index 4e8a76bdc..70605d0e7 100644 Binary files a/src-tauri/icons/icon.ico and b/src-tauri/icons/icon.ico differ diff --git a/src-tauri/icons/icon.png b/src-tauri/icons/icon.png index fad36a15a..9630ad889 100644 Binary files a/src-tauri/icons/icon.png and b/src-tauri/icons/icon.png differ diff --git a/src/App.tsx b/src/App.tsx index 74f72a901..d23e3d0af 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -45,12 +45,12 @@ import { getSystemTheme } from './lib/getSystemTheme' import { isTauri } from './lib/isTauri' import { useLoaderData, useParams } from 'react-router-dom' import { writeTextFile } from '@tauri-apps/api/fs' -import { FILE_EXT, PROJECT_ENTRYPOINT } from './lib/tauriFS' +import { PROJECT_ENTRYPOINT } from './lib/tauriFS' import { IndexLoaderData } from './Router' import { toast } from 'react-hot-toast' export function App() { - const { code: loadedCode } = useLoaderData() as IndexLoaderData + const { code: loadedCode, project } = useLoaderData() as IndexLoaderData const pathParams = useParams() const streamRef = useRef(null) useHotKeyListener() @@ -89,6 +89,7 @@ export function App() { openPanes, setOpenPanes, onboardingStatus, + didDragInStream, setDidDragInStream, setStreamDimensions, streamDimensions, @@ -130,6 +131,7 @@ export function App() { openPanes: s.openPanes, setOpenPanes: s.setOpenPanes, onboardingStatus: s.onboardingStatus, + didDragInStream: s.didDragInStream, setDidDragInStream: s.setDidDragInStream, setStreamDimensions: s.setStreamDimensions, streamDimensions: s.streamDimensions, @@ -154,7 +156,7 @@ export function App() { const paneOpacity = onboardingStatus === 'camera' ? 'opacity-20' - : isMouseDownInStream + : didDragInStream ? 'opacity-40' : '' @@ -444,11 +446,8 @@ export function App() { paneOpacity + (isMouseDownInStream ? ' pointer-events-none' : '') } - filename={ - pathParams.id - ?.slice(pathParams.id.lastIndexOf('/') + 1) - .replace(FILE_EXT, '') || '' - } + project={project} + enableMenu={true} /> ) => (prepend: string) => { @@ -47,6 +48,7 @@ export const paths = { export type IndexLoaderData = { code: string | null + project?: ProjectWithEntryPointMetadata } export type ProjectWithEntryPointMetadata = FileEntry & { @@ -68,9 +70,11 @@ const router = createBrowserRouter([ + {!isTauri() && import.meta.env.PROD && } ), errorElement: , + id: paths.FILE, loader: async ({ request, params, @@ -98,9 +102,19 @@ const router = createBrowserRouter([ if (params.id && params.id !== 'new') { // Note that PROJECT_ENTRYPOINT is hardcoded until we support multiple files const code = await readTextFile(params.id + '/' + PROJECT_ENTRYPOINT) + const entrypoint_metadata = await metadata( + params.id + '/' + PROJECT_ENTRYPOINT + ) + const children = await readDir(params.id) return { code, + project: { + name: params.id.slice(params.id.lastIndexOf('/') + 1), + path: params.id, + children, + entrypoint_metadata, + }, } } diff --git a/src/Toolbar.tsx b/src/Toolbar.tsx index d5450e227..62f4ab252 100644 --- a/src/Toolbar.tsx +++ b/src/Toolbar.tsx @@ -32,7 +32,6 @@ export const Toolbar = () => { return (
- {guiMode.mode === 'default' && ( + + {children || 'Export'} + -
-

Export your design

-
-

- -

-

+

Export your design

+ +
+ {(type === 'gltf' || type === 'ply' || type === 'stl') && ( - <> -

- {' '} - -

-

- -

- + )} +
-
- -
-
{ > Close + + Export +
-
+
) diff --git a/src/components/ProjectCard.tsx b/src/components/ProjectCard.tsx index 2294207fb..104637a27 100644 --- a/src/components/ProjectCard.tsx +++ b/src/components/ProjectCard.tsx @@ -79,11 +79,11 @@ function ProjectCard({
{project.name?.replace(FILE_EXT, '')} - + Edited {getDisplayedTime(project.entrypoint_metadata.modifiedAt)}
diff --git a/src/components/ProjectSidebarMenu.test.tsx b/src/components/ProjectSidebarMenu.test.tsx new file mode 100644 index 000000000..973c450f9 --- /dev/null +++ b/src/components/ProjectSidebarMenu.test.tsx @@ -0,0 +1,82 @@ +import { fireEvent, render, screen } from '@testing-library/react' +import { User } from '../useStore' +import { BrowserRouter } from 'react-router-dom' +import ProjectSidebarMenu from './ProjectSidebarMenu' +import { ProjectWithEntryPointMetadata } from '../Router' + +const now = new Date() +const projectWellFormed = { + name: 'Simple Box', + path: '/some/path/Simple Box', + children: [ + { + name: 'main.kcl', + path: '/some/path/Simple Box/main.kcl', + }, + ], + entrypoint_metadata: { + accessedAt: now, + blksize: 32, + blocks: 32, + createdAt: now, + dev: 1, + gid: 1, + ino: 1, + isDir: false, + isFile: true, + isSymlink: false, + mode: 1, + modifiedAt: now, + nlink: 1, + permissions: { readonly: false, mode: 1 }, + rdev: 1, + size: 32, + uid: 1, + }, +} satisfies ProjectWithEntryPointMetadata + +describe('ProjectSidebarMenu tests', () => { + test('Renders the project name', () => { + render( + + + + ) + + fireEvent.click(screen.getByTestId('project-sidebar-toggle')) + + expect(screen.getByTestId('projectName')).toHaveTextContent( + projectWellFormed.name + ) + expect(screen.getByTestId('createdAt')).toHaveTextContent( + `Created ${now.toLocaleDateString()}` + ) + }) + + test('Renders app name if given no project', () => { + render( + + + + ) + + fireEvent.click(screen.getByTestId('project-sidebar-toggle')) + + expect(screen.getByTestId('projectName')).toHaveTextContent( + 'KittyCAD Modeling App' + ) + }) + + test('Renders as a link if set to do so', () => { + render( + + + + ) + + expect(screen.getByTestId('project-sidebar-link')).toBeInTheDocument() + expect(screen.getByTestId('project-sidebar-link-name')).toHaveTextContent( + projectWellFormed.name + ) + }) +}) diff --git a/src/components/ProjectSidebarMenu.tsx b/src/components/ProjectSidebarMenu.tsx new file mode 100644 index 000000000..5278fa86e --- /dev/null +++ b/src/components/ProjectSidebarMenu.tsx @@ -0,0 +1,101 @@ +import { Popover } from '@headlessui/react' +import { ActionButton } from './ActionButton' +import { faHome } from '@fortawesome/free-solid-svg-icons' +import { ProjectWithEntryPointMetadata, paths } from '../Router' +import { isTauri } from '../lib/isTauri' +import { Link } from 'react-router-dom' +import { ExportButton } from './ExportButton' + +const ProjectSidebarMenu = ({ + project, + renderAsLink = false, +}: { + renderAsLink?: boolean + project?: Partial +}) => { + return renderAsLink ? ( + + KittyCAD App + + {project?.name ? project.name : 'KittyCAD Modeling App'} + + + ) : ( + + + KittyCAD App + + {isTauri() && project?.name ? project.name : 'KittyCAD Modeling App'} + + + + + +
+ KittyCAD App + +
+

+ {project?.name ? project.name : 'KittyCAD Modeling App'} +

+ {project?.entrypoint_metadata && ( +

+ Created{' '} + {project?.entrypoint_metadata.createdAt.toLocaleDateString()} +

+ )} +
+
+
+ + Export Model + + {isTauri() && ( + + Go to Home + + )} +
+
+
+ ) +} + +export default ProjectSidebarMenu diff --git a/src/components/Toggle/Toggle.module.css b/src/components/Toggle/Toggle.module.css index 611b734f3..299b89248 100644 --- a/src/components/Toggle/Toggle.module.css +++ b/src/components/Toggle/Toggle.module.css @@ -13,13 +13,13 @@ } .toggle > span { - @apply relative rounded border border-chalkboard-110; + @apply relative rounded border border-chalkboard-110 hover:border-chalkboard-100 cursor-pointer; width: calc(2 * (var(--toggle-size) + var(--padding))); height: calc(var(--toggle-size) + var(--padding)); } :global(.dark) .toggle > span { - @apply border-chalkboard-40; + @apply border-chalkboard-40 hover:border-chalkboard-30; } .toggle > span::after { diff --git a/src/components/UserSidebarMenu.tsx b/src/components/UserSidebarMenu.tsx index 75c7952a3..0370e301a 100644 --- a/src/components/UserSidebarMenu.tsx +++ b/src/components/UserSidebarMenu.tsx @@ -7,6 +7,7 @@ import { useNavigate } from 'react-router-dom' import { useState } from 'react' import { paths } from '../Router' import makeUrlPathRelative from '../lib/makeUrlPathRelative' +import { ExportButton } from './ExportButton' const UserSidebarMenu = ({ user }: { user?: User }) => { const displayedName = getDisplayName(user) @@ -35,10 +36,10 @@ const UserSidebarMenu = ({ user }: { user?: User }) => { {user?.image && !imageLoadFailed ? ( -
+
{user?.name { Menu )} - + - + {({ close }) => ( <> {user && ( diff --git a/src/routes/Home.tsx b/src/routes/Home.tsx index 11146fb60..0db1a582c 100644 --- a/src/routes/Home.tsx +++ b/src/routes/Home.tsx @@ -40,6 +40,8 @@ const Home = () => { defaultProjectName: s.defaultProjectName, })) + const modifiedSelected = sort?.includes('modified') || !sort || sort === null + const refreshProjects = useCallback( async (projectDir = defaultDir) => { const readProjects = ( @@ -184,11 +186,19 @@ const Home = () => {
setSearchParams(getNextSearchParams('name'))} icon={{ icon: getSortIcon('name'), bgClassName: !sort?.includes('name') - ? 'bg-liquid-30 dark:bg-liquid-70' + ? 'bg-liquid-50 dark:bg-liquid-70' + : '', + iconClassName: !sort?.includes('name') + ? 'text-liquid-80 dark:text-liquid-30' : '', }} > @@ -196,15 +206,19 @@ const Home = () => { setSearchParams(getNextSearchParams('modified'))} icon={{ icon: sort ? getSortIcon('modified') : faArrowDown, - bgClassName: !( - sort?.includes('modified') || - !sort || - sort === null - ) - ? 'bg-liquid-30 dark:bg-liquid-70' + bgClassName: !modifiedSelected + ? 'bg-liquid-50 dark:bg-liquid-70' + : '', + iconClassName: !modifiedSelected + ? 'text-liquid-80 dark:text-liquid-30' : '', }} > diff --git a/src/routes/Onboarding/Camera.tsx b/src/routes/Onboarding/Camera.tsx index 39a8ecdd4..704cfa02c 100644 --- a/src/routes/Onboarding/Camera.tsx +++ b/src/routes/Onboarding/Camera.tsx @@ -27,7 +27,7 @@ export default function Units() {
dismiss('../../')} icon={{ icon: faXmark, bgClassName: 'bg-destroy-80', diff --git a/src/routes/Onboarding/Introduction.tsx b/src/routes/Onboarding/Introduction.tsx index c0fc20339..c9c96b0c3 100644 --- a/src/routes/Onboarding/Introduction.tsx +++ b/src/routes/Onboarding/Introduction.tsx @@ -23,7 +23,7 @@ export default function Introduction() {
dismiss('../')} icon={{ icon: faXmark, bgClassName: 'bg-destroy-80', diff --git a/src/routes/Onboarding/Sketching.tsx b/src/routes/Onboarding/Sketching.tsx index 3c33357ff..521c5c8b6 100644 --- a/src/routes/Onboarding/Sketching.tsx +++ b/src/routes/Onboarding/Sketching.tsx @@ -15,7 +15,7 @@ export default function Sketching() {
dismiss('../../')} icon={{ icon: faXmark, bgClassName: 'bg-destroy-80', @@ -28,7 +28,7 @@ export default function Sketching() { dismiss('../../')} icon={{ icon: faArrowRight }} > Finish diff --git a/src/routes/Onboarding/Units.tsx b/src/routes/Onboarding/Units.tsx index e659362c5..e9ac6e270 100644 --- a/src/routes/Onboarding/Units.tsx +++ b/src/routes/Onboarding/Units.tsx @@ -68,7 +68,7 @@ export default function Units() {
dismiss('../../')} icon={{ icon: faXmark, bgClassName: 'bg-destroy-80', diff --git a/src/routes/Onboarding/index.tsx b/src/routes/Onboarding/index.tsx index 14996d4d9..03e7e6faa 100644 --- a/src/routes/Onboarding/index.tsx +++ b/src/routes/Onboarding/index.tsx @@ -54,15 +54,18 @@ export function useDismiss() { })) const navigate = useNavigate() - return useCallback(() => { - setOnboardingStatus('dismissed') - navigate(paths.INDEX) - }, [setOnboardingStatus, navigate]) + return useCallback( + (path: string) => { + setOnboardingStatus('dismissed') + navigate(path) + }, + [setOnboardingStatus, navigate] + ) } const Onboarding = () => { const dismiss = useDismiss() - useHotkeys('esc', dismiss) + useHotkeys('esc', () => dismiss('../')) return ( <> diff --git a/src/routes/Settings.tsx b/src/routes/Settings.tsx index 8d187b3fa..bf41e9988 100644 --- a/src/routes/Settings.tsx +++ b/src/routes/Settings.tsx @@ -10,11 +10,12 @@ import { Themes, baseUnits, useStore } from '../useStore' import { useRef } from 'react' import { toast } from 'react-hot-toast' import { Toggle } from '../components/Toggle/Toggle' -import { useNavigate } from 'react-router-dom' +import { useNavigate, useRouteLoaderData } from 'react-router-dom' import { useHotkeys } from 'react-hotkeys-hook' -import { paths } from '../Router' +import { IndexLoaderData, paths } from '../Router' export const Settings = () => { + const loaderData = useRouteLoaderData(paths.FILE) as IndexLoaderData const navigate = useNavigate() useHotkeys('esc', () => navigate('../')) const { @@ -63,7 +64,7 @@ export const Settings = () => { return (
- + { us prioritize what to build next.

{(window as any).__TAURI__ && ( - -
+ <> + +
+ { + setDefaultDir({ + base: defaultDir.base, + dir: e.target.value, + }) + }} + onBlur={() => { + ogDefaultDir.current.dir !== defaultDir.dir && + toast.success('Default directory updated') + ogDefaultDir.current.dir = defaultDir.dir + }} + /> + + Choose a folder + +
+
+ { - setDefaultDir({ - base: defaultDir.base, - dir: e.target.value, - }) + setDefaultProjectName(e.target.value) }} onBlur={() => { - ogDefaultDir.current.dir !== defaultDir.dir && - toast.success('Default directory updated') - ogDefaultDir.current.dir = defaultDir.dir + ogDefaultProjectName.current !== defaultProjectName && + toast.success('Default project name updated') + ogDefaultProjectName.current = defaultProjectName }} /> - - Choose a folder - -
-
+ + )} - - { - setDefaultProjectName(e.target.value) - }} - onBlur={() => { - ogDefaultProjectName.current !== defaultProjectName && - toast.success('Default project name updated') - ogDefaultProjectName.current = defaultProjectName - }} - /> -