Use platform-agnostic file separators (#890)
* Use platform-agnostic path separators * Fix file settings by fixing absolute file path * Fix missing home link in AppHeader * Found so many more instances of raw "/" characters * Tiny Settings style fix * Clean up onboarding behavior for XState and multi-file
This commit is contained in:
@ -44,6 +44,7 @@ import * as Sentry from '@sentry/react'
|
|||||||
import ModelingMachineProvider from 'components/ModelingMachineProvider'
|
import ModelingMachineProvider from 'components/ModelingMachineProvider'
|
||||||
import { KclContextProvider } from 'lang/KclSinglton'
|
import { KclContextProvider } from 'lang/KclSinglton'
|
||||||
import FileMachineProvider from 'components/FileMachineProvider'
|
import FileMachineProvider from 'components/FileMachineProvider'
|
||||||
|
import { sep } from '@tauri-apps/api/path'
|
||||||
|
|
||||||
if (VITE_KC_SENTRY_DSN && !TEST) {
|
if (VITE_KC_SENTRY_DSN && !TEST) {
|
||||||
Sentry.init({
|
Sentry.init({
|
||||||
@ -145,10 +146,10 @@ const router = createBrowserRouter(
|
|||||||
path: paths.FILE + '/:id',
|
path: paths.FILE + '/:id',
|
||||||
element: (
|
element: (
|
||||||
<Auth>
|
<Auth>
|
||||||
<Outlet />
|
|
||||||
<FileMachineProvider>
|
<FileMachineProvider>
|
||||||
<KclContextProvider>
|
<KclContextProvider>
|
||||||
<ModelingMachineProvider>
|
<ModelingMachineProvider>
|
||||||
|
<Outlet />
|
||||||
<App />
|
<App />
|
||||||
</ModelingMachineProvider>
|
</ModelingMachineProvider>
|
||||||
<WasmErrBanner />
|
<WasmErrBanner />
|
||||||
@ -187,23 +188,23 @@ const router = createBrowserRouter(
|
|||||||
|
|
||||||
if (params.id && params.id !== BROWSER_FILE_NAME) {
|
if (params.id && params.id !== BROWSER_FILE_NAME) {
|
||||||
const decodedId = decodeURIComponent(params.id)
|
const decodedId = decodeURIComponent(params.id)
|
||||||
const projectAndFile = decodedId.replace(defaultDir + '/', '')
|
const projectAndFile = decodedId.replace(defaultDir + sep, '')
|
||||||
const firstSlashIndex = projectAndFile.indexOf('/')
|
const firstSlashIndex = projectAndFile.indexOf(sep)
|
||||||
const projectName = projectAndFile.slice(0, firstSlashIndex)
|
const projectName = projectAndFile.slice(0, firstSlashIndex)
|
||||||
const projectPath = defaultDir + '/' + projectName
|
const projectPath = defaultDir + sep + projectName
|
||||||
const currentFileName = projectAndFile.slice(firstSlashIndex + 1)
|
const currentFileName = projectAndFile.slice(firstSlashIndex + 1)
|
||||||
|
|
||||||
if (firstSlashIndex === -1 || !currentFileName)
|
if (firstSlashIndex === -1 || !currentFileName)
|
||||||
return redirect(
|
return redirect(
|
||||||
`${paths.FILE}/${encodeURIComponent(
|
`${paths.FILE}/${encodeURIComponent(
|
||||||
`${params.id}/${PROJECT_ENTRYPOINT}`
|
`${params.id}${sep}${PROJECT_ENTRYPOINT}`
|
||||||
)}`
|
)}`
|
||||||
)
|
)
|
||||||
|
|
||||||
// Note that PROJECT_ENTRYPOINT is hardcoded until we support multiple files
|
// Note that PROJECT_ENTRYPOINT is hardcoded until we support multiple files
|
||||||
const code = await readTextFile(decodedId)
|
const code = await readTextFile(decodedId)
|
||||||
const entrypointMetadata = await metadata(
|
const entrypointMetadata = await metadata(
|
||||||
projectPath + '/' + PROJECT_ENTRYPOINT
|
projectPath + sep + PROJECT_ENTRYPOINT
|
||||||
)
|
)
|
||||||
const children = await readDir(projectPath, { recursive: true })
|
const children = await readDir(projectPath, { recursive: true })
|
||||||
|
|
||||||
@ -270,9 +271,9 @@ const router = createBrowserRouter(
|
|||||||
isProjectDirectory
|
isProjectDirectory
|
||||||
)
|
)
|
||||||
const projects = await Promise.all(
|
const projects = await Promise.all(
|
||||||
projectsNoMeta.map(async (p) => ({
|
projectsNoMeta.map(async (p: FileEntry) => ({
|
||||||
entrypointMetadata: await metadata(
|
entrypointMetadata: await metadata(
|
||||||
p.path + '/' + PROJECT_ENTRYPOINT
|
p.path + sep + PROJECT_ENTRYPOINT
|
||||||
),
|
),
|
||||||
...p,
|
...p,
|
||||||
}))
|
}))
|
||||||
|
@ -32,13 +32,11 @@ export const AppHeader = ({
|
|||||||
className
|
className
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
{project && (
|
|
||||||
<ProjectSidebarMenu
|
<ProjectSidebarMenu
|
||||||
renderAsLink={!enableMenu}
|
renderAsLink={!enableMenu}
|
||||||
project={project.project}
|
project={project?.project}
|
||||||
file={project.file}
|
file={project?.file}
|
||||||
/>
|
/>
|
||||||
)}
|
|
||||||
{/* Toolbar if the context deems it */}
|
{/* Toolbar if the context deems it */}
|
||||||
{showToolbar && (
|
{showToolbar && (
|
||||||
<div className="max-w-lg md:max-w-xl lg:max-w-2xl xl:max-w-4xl 2xl:max-w-5xl">
|
<div className="max-w-lg md:max-w-xl lg:max-w-2xl xl:max-w-4xl 2xl:max-w-5xl">
|
||||||
@ -47,7 +45,7 @@ export const AppHeader = ({
|
|||||||
)}
|
)}
|
||||||
{/* If there are children, show them, otherwise show User menu */}
|
{/* If there are children, show them, otherwise show User menu */}
|
||||||
{children || (
|
{children || (
|
||||||
<div className="ml-auto flex items-center gap-1">
|
<div className="flex items-center gap-1 ml-auto">
|
||||||
<NetworkHealthIndicator />
|
<NetworkHealthIndicator />
|
||||||
<UserSidebarMenu user={user} />
|
<UserSidebarMenu user={user} />
|
||||||
</div>
|
</div>
|
||||||
|
@ -22,6 +22,7 @@ import {
|
|||||||
} from '@tauri-apps/api/fs'
|
} from '@tauri-apps/api/fs'
|
||||||
import { FILE_EXT, readProject } from 'lib/tauriFS'
|
import { FILE_EXT, readProject } from 'lib/tauriFS'
|
||||||
import { isTauri } from 'lib/isTauri'
|
import { isTauri } from 'lib/isTauri'
|
||||||
|
import { sep } from '@tauri-apps/api/path'
|
||||||
|
|
||||||
type MachineContext<T extends AnyStateMachine> = {
|
type MachineContext<T extends AnyStateMachine> = {
|
||||||
state: StateFrom<T>
|
state: StateFrom<T>
|
||||||
@ -56,7 +57,7 @@ export const FileMachineProvider = ({
|
|||||||
setCommandBarOpen(false)
|
setCommandBarOpen(false)
|
||||||
navigate(
|
navigate(
|
||||||
`${paths.FILE}/${encodeURIComponent(
|
`${paths.FILE}/${encodeURIComponent(
|
||||||
context.selectedDirectory + '/' + event.data.name
|
context.selectedDirectory + sep + event.data.name
|
||||||
)}`
|
)}`
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -82,11 +83,11 @@ export const FileMachineProvider = ({
|
|||||||
let name = event.data.name.trim() || DEFAULT_FILE_NAME
|
let name = event.data.name.trim() || DEFAULT_FILE_NAME
|
||||||
|
|
||||||
if (event.data.makeDir) {
|
if (event.data.makeDir) {
|
||||||
await createDir(context.selectedDirectory.path + '/' + name)
|
await createDir(context.selectedDirectory.path + sep + name)
|
||||||
} else {
|
} else {
|
||||||
await writeFile(
|
await writeFile(
|
||||||
context.selectedDirectory.path +
|
context.selectedDirectory.path +
|
||||||
'/' +
|
sep +
|
||||||
name +
|
name +
|
||||||
(name.endsWith(FILE_EXT) ? '' : FILE_EXT),
|
(name.endsWith(FILE_EXT) ? '' : FILE_EXT),
|
||||||
''
|
''
|
||||||
@ -103,9 +104,9 @@ export const FileMachineProvider = ({
|
|||||||
let name = newName ? newName : DEFAULT_FILE_NAME
|
let name = newName ? newName : DEFAULT_FILE_NAME
|
||||||
|
|
||||||
await renameFile(
|
await renameFile(
|
||||||
context.selectedDirectory.path + '/' + oldName,
|
context.selectedDirectory.path + sep + oldName,
|
||||||
context.selectedDirectory.path +
|
context.selectedDirectory.path +
|
||||||
'/' +
|
sep +
|
||||||
name +
|
name +
|
||||||
(name.endsWith(FILE_EXT) || isDir ? '' : FILE_EXT)
|
(name.endsWith(FILE_EXT) || isDir ? '' : FILE_EXT)
|
||||||
)
|
)
|
||||||
|
@ -7,6 +7,7 @@ import { Link } from 'react-router-dom'
|
|||||||
import { ExportButton } from './ExportButton'
|
import { ExportButton } from './ExportButton'
|
||||||
import { Fragment } from 'react'
|
import { Fragment } from 'react'
|
||||||
import { FileTree } from './FileTree'
|
import { FileTree } from './FileTree'
|
||||||
|
import { sep } from '@tauri-apps/api/path'
|
||||||
|
|
||||||
const ProjectSidebarMenu = ({
|
const ProjectSidebarMenu = ({
|
||||||
project,
|
project,
|
||||||
@ -26,10 +27,10 @@ const ProjectSidebarMenu = ({
|
|||||||
<img
|
<img
|
||||||
src="/kitt-8bit-winking.svg"
|
src="/kitt-8bit-winking.svg"
|
||||||
alt="KittyCAD App"
|
alt="KittyCAD App"
|
||||||
className="h-9 w-auto"
|
className="w-auto h-9"
|
||||||
/>
|
/>
|
||||||
<span
|
<span
|
||||||
className="text-sm text-chalkboard-110 dark:text-chalkboard-20 whitespace-nowrap hidden lg:block"
|
className="hidden text-sm text-chalkboard-110 dark:text-chalkboard-20 whitespace-nowrap lg:block"
|
||||||
data-testid="project-sidebar-link-name"
|
data-testid="project-sidebar-link-name"
|
||||||
>
|
>
|
||||||
{project?.name ? project.name : 'KittyCAD Modeling App'}
|
{project?.name ? project.name : 'KittyCAD Modeling App'}
|
||||||
@ -44,16 +45,16 @@ const ProjectSidebarMenu = ({
|
|||||||
<img
|
<img
|
||||||
src="/kitt-8bit-winking.svg"
|
src="/kitt-8bit-winking.svg"
|
||||||
alt="KittyCAD App"
|
alt="KittyCAD App"
|
||||||
className="h-full w-auto"
|
className="w-auto h-full"
|
||||||
/>
|
/>
|
||||||
<div className="flex flex-col items-start py-0.5">
|
<div className="flex flex-col items-start py-0.5">
|
||||||
<span className="text-sm text-chalkboard-110 dark:text-chalkboard-20 whitespace-nowrap hidden lg:block">
|
<span className="hidden text-sm text-chalkboard-110 dark:text-chalkboard-20 whitespace-nowrap lg:block">
|
||||||
{isTauri() && file?.name
|
{isTauri() && file?.name
|
||||||
? file.name.slice(file.name.lastIndexOf('/') + 1)
|
? file.name.slice(file.name.lastIndexOf(sep) + 1)
|
||||||
: 'KittyCAD Modeling App'}
|
: 'KittyCAD Modeling App'}
|
||||||
</span>
|
</span>
|
||||||
{isTauri() && project?.name && (
|
{isTauri() && project?.name && (
|
||||||
<span className="text-xs text-chalkboard-70 dark:text-chalkboard-40 whitespace-nowrap hidden lg:block">
|
<span className="hidden text-xs text-chalkboard-70 dark:text-chalkboard-40 whitespace-nowrap lg:block">
|
||||||
{project.name}
|
{project.name}
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
@ -68,7 +69,7 @@ const ProjectSidebarMenu = ({
|
|||||||
leaveTo="opacity-0"
|
leaveTo="opacity-0"
|
||||||
as={Fragment}
|
as={Fragment}
|
||||||
>
|
>
|
||||||
<Popover.Overlay className="fixed z-20 inset-0 bg-chalkboard-110/50" />
|
<Popover.Overlay className="fixed inset-0 z-20 bg-chalkboard-110/50" />
|
||||||
</Transition>
|
</Transition>
|
||||||
|
|
||||||
<Transition
|
<Transition
|
||||||
@ -81,7 +82,7 @@ const ProjectSidebarMenu = ({
|
|||||||
as={Fragment}
|
as={Fragment}
|
||||||
>
|
>
|
||||||
<Popover.Panel
|
<Popover.Panel
|
||||||
className="fixed inset-0 right-auto z-30 w-64 h-screen max-h-screen grid grid-cols-1 bg-chalkboard-10 dark:bg-chalkboard-100 border border-energy-100 dark:border-energy-100/50 shadow-md rounded-r-lg"
|
className="fixed inset-0 right-auto z-30 grid w-64 h-screen max-h-screen grid-cols-1 border rounded-r-lg shadow-md bg-chalkboard-10 dark:bg-chalkboard-100 border-energy-100 dark:border-energy-100/50"
|
||||||
style={{ gridTemplateRows: 'auto 1fr auto' }}
|
style={{ gridTemplateRows: 'auto 1fr auto' }}
|
||||||
>
|
>
|
||||||
{({ close }) => (
|
{({ close }) => (
|
||||||
@ -90,7 +91,7 @@ const ProjectSidebarMenu = ({
|
|||||||
<img
|
<img
|
||||||
src="/kitt-8bit-winking.svg"
|
src="/kitt-8bit-winking.svg"
|
||||||
alt="KittyCAD App"
|
alt="KittyCAD App"
|
||||||
className="h-9 w-auto"
|
className="w-auto h-9"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
@ -102,7 +103,7 @@ const ProjectSidebarMenu = ({
|
|||||||
</p>
|
</p>
|
||||||
{project?.entrypointMetadata && (
|
{project?.entrypointMetadata && (
|
||||||
<p
|
<p
|
||||||
className="m-0 text-chalkboard-100 dark:text-energy-40 text-xs"
|
className="m-0 text-xs text-chalkboard-100 dark:text-energy-40"
|
||||||
data-testid="createdAt"
|
data-testid="createdAt"
|
||||||
>
|
>
|
||||||
Created{' '}
|
Created{' '}
|
||||||
@ -120,7 +121,7 @@ const ProjectSidebarMenu = ({
|
|||||||
) : (
|
) : (
|
||||||
<div className="flex-1 overflow-hidden" />
|
<div className="flex-1 overflow-hidden" />
|
||||||
)}
|
)}
|
||||||
<div className="p-4 flex flex-col gap-2 bg-energy-10/25 dark:bg-energy-110">
|
<div className="flex flex-col gap-2 p-4 bg-energy-10/25 dark:bg-energy-110">
|
||||||
<ExportButton
|
<ExportButton
|
||||||
className={{
|
className={{
|
||||||
button:
|
button:
|
||||||
|
@ -7,6 +7,6 @@ export function useAbsoluteFilePath() {
|
|||||||
return (
|
return (
|
||||||
paths.FILE +
|
paths.FILE +
|
||||||
'/' +
|
'/' +
|
||||||
encodeURIComponent(routeData?.project?.path || BROWSER_FILE_NAME)
|
encodeURIComponent(routeData?.file?.path || BROWSER_FILE_NAME)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -5,10 +5,11 @@ import {
|
|||||||
readDir,
|
readDir,
|
||||||
writeTextFile,
|
writeTextFile,
|
||||||
} from '@tauri-apps/api/fs'
|
} from '@tauri-apps/api/fs'
|
||||||
import { documentDir, homeDir } from '@tauri-apps/api/path'
|
import { documentDir, homeDir, sep } from '@tauri-apps/api/path'
|
||||||
import { isTauri } from './isTauri'
|
import { isTauri } from './isTauri'
|
||||||
import { ProjectWithEntryPointMetadata } from '../Router'
|
import { ProjectWithEntryPointMetadata } from '../Router'
|
||||||
import { metadata } from 'tauri-plugin-fs-extra-api'
|
import { metadata } from 'tauri-plugin-fs-extra-api'
|
||||||
|
import { bracket } from './exampleKcl'
|
||||||
|
|
||||||
const PROJECT_FOLDER = 'kittycad-modeling-projects'
|
const PROJECT_FOLDER = 'kittycad-modeling-projects'
|
||||||
export const FILE_EXT = '.kcl'
|
export const FILE_EXT = '.kcl'
|
||||||
@ -70,7 +71,7 @@ export async function getProjectsInDir(projectDir: string) {
|
|||||||
|
|
||||||
const projectsWithMetadata = await Promise.all(
|
const projectsWithMetadata = await Promise.all(
|
||||||
readProjects.map(async (p) => ({
|
readProjects.map(async (p) => ({
|
||||||
entrypointMetadata: await metadata(p.path + '/' + PROJECT_ENTRYPOINT),
|
entrypointMetadata: await metadata(p.path + sep + PROJECT_ENTRYPOINT),
|
||||||
...p,
|
...p,
|
||||||
}))
|
}))
|
||||||
)
|
)
|
||||||
@ -224,7 +225,7 @@ export async function createNewProject(
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
await writeTextFile(path + '/' + PROJECT_ENTRYPOINT, '').catch((err) => {
|
await writeTextFile(path + sep + PROJECT_ENTRYPOINT, bracket).catch((err) => {
|
||||||
console.error('Error creating new file:', err)
|
console.error('Error creating new file:', err)
|
||||||
throw err
|
throw err
|
||||||
})
|
})
|
||||||
@ -232,13 +233,13 @@ export async function createNewProject(
|
|||||||
const m = await metadata(path)
|
const m = await metadata(path)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
name: path.slice(path.lastIndexOf('/') + 1),
|
name: path.slice(path.lastIndexOf(sep) + 1),
|
||||||
path: path,
|
path: path,
|
||||||
entrypointMetadata: m,
|
entrypointMetadata: m,
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
name: PROJECT_ENTRYPOINT,
|
name: PROJECT_ENTRYPOINT,
|
||||||
path: path + '/' + PROJECT_ENTRYPOINT,
|
path: path + sep + PROJECT_ENTRYPOINT,
|
||||||
children: [],
|
children: [],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
@ -29,6 +29,7 @@ import useStateMachineCommands from '../hooks/useStateMachineCommands'
|
|||||||
import { useGlobalStateContext } from 'hooks/useGlobalStateContext'
|
import { useGlobalStateContext } from 'hooks/useGlobalStateContext'
|
||||||
import { useCommandsContext } from 'hooks/useCommandsContext'
|
import { useCommandsContext } from 'hooks/useCommandsContext'
|
||||||
import { DEFAULT_PROJECT_NAME } from 'machines/settingsMachine'
|
import { DEFAULT_PROJECT_NAME } from 'machines/settingsMachine'
|
||||||
|
import { sep } from '@tauri-apps/api/path'
|
||||||
|
|
||||||
// This route only opens in the Tauri desktop context for now,
|
// This route only opens in the Tauri desktop context for now,
|
||||||
// as defined in Router.tsx, so we can use the Tauri APIs and types.
|
// as defined in Router.tsx, so we can use the Tauri APIs and types.
|
||||||
@ -58,7 +59,7 @@ const Home = () => {
|
|||||||
setCommandBarOpen(false)
|
setCommandBarOpen(false)
|
||||||
navigate(
|
navigate(
|
||||||
`${paths.FILE}/${encodeURIComponent(
|
`${paths.FILE}/${encodeURIComponent(
|
||||||
context.defaultDirectory + '/' + event.data.name
|
context.defaultDirectory + sep + event.data.name
|
||||||
)}`
|
)}`
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -91,7 +92,7 @@ const Home = () => {
|
|||||||
name = interpolateProjectNameWithIndex(name, nextIndex)
|
name = interpolateProjectNameWithIndex(name, nextIndex)
|
||||||
}
|
}
|
||||||
|
|
||||||
await createNewProject(context.defaultDirectory + '/' + name)
|
await createNewProject(context.defaultDirectory + sep + name)
|
||||||
|
|
||||||
if (shouldUpdateDefaultProjectName) {
|
if (shouldUpdateDefaultProjectName) {
|
||||||
sendToSettings({
|
sendToSettings({
|
||||||
@ -114,8 +115,8 @@ const Home = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
await renameFile(
|
await renameFile(
|
||||||
context.defaultDirectory + '/' + oldName,
|
context.defaultDirectory + sep + oldName,
|
||||||
context.defaultDirectory + '/' + name
|
context.defaultDirectory + sep + name
|
||||||
)
|
)
|
||||||
return `Successfully renamed "${oldName}" to "${name}"`
|
return `Successfully renamed "${oldName}" to "${name}"`
|
||||||
},
|
},
|
||||||
@ -123,7 +124,7 @@ const Home = () => {
|
|||||||
context: ContextFrom<typeof homeMachine>,
|
context: ContextFrom<typeof homeMachine>,
|
||||||
event: EventFrom<typeof homeMachine, 'Delete project'>
|
event: EventFrom<typeof homeMachine, 'Delete project'>
|
||||||
) => {
|
) => {
|
||||||
await removeDir(context.defaultDirectory + '/' + event.data.name, {
|
await removeDir(context.defaultDirectory + sep + event.data.name, {
|
||||||
recursive: true,
|
recursive: true,
|
||||||
})
|
})
|
||||||
return `Successfully deleted "${event.data.name}"`
|
return `Successfully deleted "${event.data.name}"`
|
||||||
@ -172,9 +173,9 @@ const Home = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="h-screen overflow-hidden relative flex flex-col">
|
<div className="relative flex flex-col h-screen overflow-hidden">
|
||||||
<AppHeader showToolbar={false} />
|
<AppHeader showToolbar={false} />
|
||||||
<div className="my-24 px-4 lg:px-0 overflow-y-auto max-w-5xl w-full mx-auto">
|
<div className="w-full max-w-5xl px-4 mx-auto my-24 overflow-y-auto lg:px-0">
|
||||||
<section className="flex justify-between">
|
<section className="flex justify-between">
|
||||||
<h1 className="text-3xl text-bold">Your Projects</h1>
|
<h1 className="text-3xl text-bold">Your Projects</h1>
|
||||||
<div className="flex">
|
<div className="flex">
|
||||||
@ -235,7 +236,7 @@ const Home = () => {
|
|||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
{projects.length > 0 ? (
|
{projects.length > 0 ? (
|
||||||
<ul className="my-8 w-full grid grid-cols-4 gap-4">
|
<ul className="grid w-full grid-cols-4 gap-4 my-8">
|
||||||
{projects.sort(getSortFunction(sort)).map((project) => (
|
{projects.sort(getSortFunction(sort)).map((project) => (
|
||||||
<ProjectCard
|
<ProjectCard
|
||||||
key={project.name}
|
key={project.name}
|
||||||
@ -246,7 +247,7 @@ const Home = () => {
|
|||||||
))}
|
))}
|
||||||
</ul>
|
</ul>
|
||||||
) : (
|
) : (
|
||||||
<p className="rounded my-8 border border-dashed border-chalkboard-30 dark:border-chalkboard-70 p-4">
|
<p className="p-4 my-8 border border-dashed rounded border-chalkboard-30 dark:border-chalkboard-70">
|
||||||
No Projects found, ready to make your first one?
|
No Projects found, ready to make your first one?
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
|
@ -24,8 +24,15 @@ export default function Export() {
|
|||||||
Try opening the project menu and clicking "Export Model".
|
Try opening the project menu and clicking "Export Model".
|
||||||
</p>
|
</p>
|
||||||
<p className="my-4">
|
<p className="my-4">
|
||||||
KittyCAD Modeling App uses our open-source extension proposal for
|
KittyCAD Modeling App uses{' '}
|
||||||
the GLTF file format.{' '}
|
<a
|
||||||
|
href="https://kittycad.io/gltf-format-extension"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
target="_blank"
|
||||||
|
>
|
||||||
|
our open-source extension proposal
|
||||||
|
</a>{' '}
|
||||||
|
for the GLTF file format.{' '}
|
||||||
<a
|
<a
|
||||||
href="https://kittycad.io/docs/api/convert-cad-file"
|
href="https://kittycad.io/docs/api/convert-cad-file"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
|
@ -4,13 +4,23 @@ import { useDismiss } from '.'
|
|||||||
import { useEffect } from 'react'
|
import { useEffect } from 'react'
|
||||||
import { bracket } from 'lib/exampleKcl'
|
import { bracket } from 'lib/exampleKcl'
|
||||||
import { kclManager } from 'lang/KclSinglton'
|
import { kclManager } from 'lang/KclSinglton'
|
||||||
|
import { useModelingContext } from 'hooks/useModelingContext'
|
||||||
|
|
||||||
export default function FutureWork() {
|
export default function FutureWork() {
|
||||||
|
const { send } = useModelingContext()
|
||||||
const dismiss = useDismiss()
|
const dismiss = useDismiss()
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
if (kclManager.engineCommandManager.engineConnection?.isReady()) {
|
||||||
|
// If the engine is ready, promptly execute the loaded code
|
||||||
|
kclManager.setCodeAndExecute(bracket)
|
||||||
|
} else {
|
||||||
|
// Otherwise, just set the code and wait for the connection to complete
|
||||||
kclManager.setCode(bracket)
|
kclManager.setCode(bracket)
|
||||||
}, [kclManager.setCode])
|
}
|
||||||
|
|
||||||
|
send({ type: 'Cancel' }) // in case the user hit 'Next' while still in sketch mode
|
||||||
|
}, [send])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="fixed grid justify-center items-center inset-0 bg-chalkboard-100/50 z-50">
|
<div className="fixed grid justify-center items-center inset-0 bg-chalkboard-100/50 z-50">
|
||||||
|
@ -10,6 +10,7 @@ import { useGlobalStateContext } from 'hooks/useGlobalStateContext'
|
|||||||
import { Themes, getSystemTheme } from 'lib/theme'
|
import { Themes, getSystemTheme } from 'lib/theme'
|
||||||
import { bracket } from 'lib/exampleKcl'
|
import { bracket } from 'lib/exampleKcl'
|
||||||
import {
|
import {
|
||||||
|
PROJECT_ENTRYPOINT,
|
||||||
createNewProject,
|
createNewProject,
|
||||||
getNextProjectIndex,
|
getNextProjectIndex,
|
||||||
getProjectsInDir,
|
getProjectsInDir,
|
||||||
@ -20,6 +21,7 @@ import { useNavigate } from 'react-router-dom'
|
|||||||
import { paths } from 'Router'
|
import { paths } from 'Router'
|
||||||
import { useEffect } from 'react'
|
import { useEffect } from 'react'
|
||||||
import { kclManager } from 'lang/KclSinglton'
|
import { kclManager } from 'lang/KclSinglton'
|
||||||
|
import { sep } from '@tauri-apps/api/path'
|
||||||
|
|
||||||
function OnboardingWithNewFile() {
|
function OnboardingWithNewFile() {
|
||||||
const navigate = useNavigate()
|
const navigate = useNavigate()
|
||||||
@ -41,12 +43,16 @@ function OnboardingWithNewFile() {
|
|||||||
ONBOARDING_PROJECT_NAME,
|
ONBOARDING_PROJECT_NAME,
|
||||||
nextIndex
|
nextIndex
|
||||||
)
|
)
|
||||||
const newFile = await createNewProject(defaultDirectory + '/' + name)
|
const newFile = await createNewProject(defaultDirectory + sep + name)
|
||||||
navigate(`${paths.FILE}/${encodeURIComponent(newFile.path)}`)
|
navigate(
|
||||||
|
`${paths.FILE}/${encodeURIComponent(
|
||||||
|
newFile.path + sep + PROJECT_ENTRYPOINT
|
||||||
|
)}${paths.ONBOARDING.INDEX}`
|
||||||
|
)
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<div className="fixed grid place-content-center inset-0 bg-chalkboard-110/50 z-50">
|
<div className="fixed inset-0 z-50 grid place-content-center bg-chalkboard-110/50">
|
||||||
<div className="max-w-3xl bg-chalkboard-10 dark:bg-chalkboard-90 p-8 rounded">
|
<div className="max-w-3xl p-8 rounded bg-chalkboard-10 dark:bg-chalkboard-90">
|
||||||
{!isTauri() ? (
|
{!isTauri() ? (
|
||||||
<>
|
<>
|
||||||
<h1 className="text-2xl font-bold text-warn-80 dark:text-warn-10">
|
<h1 className="text-2xl font-bold text-warn-80 dark:text-warn-10">
|
||||||
@ -84,7 +90,7 @@ function OnboardingWithNewFile() {
|
|||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<h1 className="text-2xl font-bold flex gap-4 flex-wrap items-center">
|
<h1 className="flex flex-wrap items-center gap-4 text-2xl font-bold">
|
||||||
Would you like to create a new project?
|
Would you like to create a new project?
|
||||||
</h1>
|
</h1>
|
||||||
<section className="my-12">
|
<section className="my-12">
|
||||||
@ -110,7 +116,11 @@ function OnboardingWithNewFile() {
|
|||||||
</ActionButton>
|
</ActionButton>
|
||||||
<ActionButton
|
<ActionButton
|
||||||
Element="button"
|
Element="button"
|
||||||
onClick={createAndOpenNewProject}
|
onClick={() => {
|
||||||
|
createAndOpenNewProject()
|
||||||
|
kclManager.setCode(bracket)
|
||||||
|
dismiss()
|
||||||
|
}}
|
||||||
icon={{ icon: faArrowRight }}
|
icon={{ icon: faArrowRight }}
|
||||||
>
|
>
|
||||||
Make a new project
|
Make a new project
|
||||||
@ -138,21 +148,22 @@ export default function Introduction() {
|
|||||||
: ''
|
: ''
|
||||||
const dismiss = useDismiss()
|
const dismiss = useDismiss()
|
||||||
const next = useNextClick(onboardingPaths.CAMERA)
|
const next = useNextClick(onboardingPaths.CAMERA)
|
||||||
|
const isStarterCode = kclManager.code === '' || kclManager.code === bracket
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (kclManager.code === '') kclManager.setCode(bracket)
|
if (kclManager.code === '') kclManager.setCode(bracket)
|
||||||
}, [kclManager.code, kclManager.setCode])
|
}, [])
|
||||||
|
|
||||||
return !(kclManager.code !== '' && kclManager.code !== bracket) ? (
|
return isStarterCode ? (
|
||||||
<div className="fixed grid place-content-center inset-0 bg-chalkboard-110/50 z-50">
|
<div className="fixed inset-0 z-50 grid place-content-center bg-chalkboard-110/50">
|
||||||
<div className="max-w-3xl bg-chalkboard-10 dark:bg-chalkboard-90 p-8 rounded">
|
<div className="max-w-3xl p-8 rounded bg-chalkboard-10 dark:bg-chalkboard-90">
|
||||||
<h1 className="text-2xl font-bold flex gap-4 flex-wrap items-center">
|
<h1 className="flex flex-wrap items-center gap-4 text-2xl font-bold">
|
||||||
<img
|
<img
|
||||||
src={`/kcma-logomark${getLogoTheme()}.svg`}
|
src={`/kcma-logomark${getLogoTheme()}.svg`}
|
||||||
alt="KittyCAD Modeling App"
|
alt="KittyCAD Modeling App"
|
||||||
className="max-w-full h-20"
|
className="h-20 max-w-full"
|
||||||
/>
|
/>
|
||||||
<span className="bg-energy-10 text-energy-80 px-3 py-1 rounded-full text-base">
|
<span className="px-3 py-1 text-base rounded-full bg-energy-10 text-energy-80">
|
||||||
Alpha
|
Alpha
|
||||||
</span>
|
</span>
|
||||||
</h1>
|
</h1>
|
||||||
|
@ -11,7 +11,13 @@ export default function Sketching() {
|
|||||||
const next = useNextClick(onboardingPaths.FUTURE_WORK)
|
const next = useNextClick(onboardingPaths.FUTURE_WORK)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
if (kclManager.engineCommandManager.engineConnection?.isReady()) {
|
||||||
|
// If the engine is ready, promptly execute the loaded code
|
||||||
|
kclManager.setCodeAndExecute('')
|
||||||
|
} else {
|
||||||
|
// Otherwise, just set the code and wait for the connection to complete
|
||||||
kclManager.setCode('')
|
kclManager.setCode('')
|
||||||
|
}
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -31,6 +31,7 @@ import {
|
|||||||
interpolateProjectNameWithIndex,
|
interpolateProjectNameWithIndex,
|
||||||
} from 'lib/tauriFS'
|
} from 'lib/tauriFS'
|
||||||
import { ONBOARDING_PROJECT_NAME } from './Onboarding'
|
import { ONBOARDING_PROJECT_NAME } from './Onboarding'
|
||||||
|
import { sep } from '@tauri-apps/api/path'
|
||||||
|
|
||||||
export const Settings = () => {
|
export const Settings = () => {
|
||||||
const loaderData =
|
const loaderData =
|
||||||
@ -95,7 +96,7 @@ export const Settings = () => {
|
|||||||
ONBOARDING_PROJECT_NAME,
|
ONBOARDING_PROJECT_NAME,
|
||||||
nextIndex
|
nextIndex
|
||||||
)
|
)
|
||||||
const newFile = await createNewProject(defaultDirectory + '/' + name)
|
const newFile = await createNewProject(defaultDirectory + sep + name)
|
||||||
navigate(`${paths.FILE}/${encodeURIComponent(newFile.path)}`)
|
navigate(`${paths.FILE}/${encodeURIComponent(newFile.path)}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -116,7 +117,7 @@ export const Settings = () => {
|
|||||||
Close
|
Close
|
||||||
</ActionButton>
|
</ActionButton>
|
||||||
</AppHeader>
|
</AppHeader>
|
||||||
<div className="max-w-5xl mx-auto my-24">
|
<div className="max-w-5xl mx-5 lg:mx-auto my-24">
|
||||||
<h1 className="text-4xl font-bold">User Settings</h1>
|
<h1 className="text-4xl font-bold">User Settings</h1>
|
||||||
<p className="max-w-2xl mt-6">
|
<p className="max-w-2xl mt-6">
|
||||||
Don't see the feature you want? Check to see if it's on{' '}
|
Don't see the feature you want? Check to see if it's on{' '}
|
||||||
|
Reference in New Issue
Block a user