2023-07-27 18:59:40 -04:00
|
|
|
import { App } from './App'
|
2023-08-10 13:30:32 -04:00
|
|
|
import {
|
|
|
|
createBrowserRouter,
|
|
|
|
Outlet,
|
|
|
|
redirect,
|
|
|
|
RouterProvider,
|
|
|
|
} from 'react-router-dom'
|
2023-07-27 18:59:40 -04:00
|
|
|
import { ErrorPage } from './components/ErrorPage'
|
|
|
|
import { Settings } from './routes/Settings'
|
2024-02-11 12:59:00 +11:00
|
|
|
import Onboarding, { onboardingRoutes } from './routes/Onboarding'
|
2023-07-27 18:59:40 -04:00
|
|
|
import SignIn from './routes/SignIn'
|
|
|
|
import { Auth } from './Auth'
|
2023-08-15 21:56:24 -04:00
|
|
|
import { isTauri } from './lib/isTauri'
|
|
|
|
import Home from './routes/Home'
|
|
|
|
import { FileEntry, readDir, readTextFile } from '@tauri-apps/api/fs'
|
|
|
|
import makeUrlPathRelative from './lib/makeUrlPathRelative'
|
|
|
|
import {
|
|
|
|
initializeProjectDirectory,
|
|
|
|
isProjectDirectory,
|
|
|
|
PROJECT_ENTRYPOINT,
|
|
|
|
} from './lib/tauriFS'
|
2024-02-11 12:59:00 +11:00
|
|
|
import { metadata } from 'tauri-plugin-fs-extra-api'
|
2023-08-18 10:27:01 -04:00
|
|
|
import DownloadAppBanner from './components/DownloadAppBanner'
|
2023-10-17 12:07:48 +11:00
|
|
|
import { WasmErrBanner } from './components/WasmErrBanner'
|
2024-03-11 20:26:13 -04:00
|
|
|
import { SettingsAuthProvider } from './components/SettingsAuthProvider'
|
2024-03-12 10:37:35 -04:00
|
|
|
import { settingsMachine } from './machines/settingsMachine'
|
|
|
|
import { SETTINGS_PERSIST_KEY } from './lib/settings'
|
2023-08-28 20:31:49 -04:00
|
|
|
import { ContextFrom } from 'xstate'
|
2024-02-23 11:24:22 -05:00
|
|
|
import CommandBarProvider, {
|
|
|
|
CommandBar,
|
|
|
|
} from 'components/CommandBar/CommandBar'
|
2023-10-11 13:36:54 +11:00
|
|
|
import ModelingMachineProvider from 'components/ModelingMachineProvider'
|
2024-02-11 12:59:00 +11:00
|
|
|
import { KclContextProvider, kclManager } from 'lang/KclSingleton'
|
2023-10-16 13:28:41 -04:00
|
|
|
import FileMachineProvider from 'components/FileMachineProvider'
|
2023-10-17 12:31:14 -04:00
|
|
|
import { sep } from '@tauri-apps/api/path'
|
2024-02-11 12:59:00 +11:00
|
|
|
import { paths } from 'lib/paths'
|
|
|
|
import { IndexLoaderData, HomeLoaderData } from 'lib/types'
|
2024-02-12 12:18:37 -08:00
|
|
|
import { fileSystemManager } from 'lang/std/fileSystemManager'
|
2024-03-11 17:50:31 -07:00
|
|
|
import LspProvider from 'components/LspProvider'
|
2023-08-30 10:34:14 -04:00
|
|
|
|
2023-10-04 18:00:55 -04:00
|
|
|
export const BROWSER_FILE_NAME = 'new'
|
|
|
|
|
2023-08-22 05:34:20 +10:00
|
|
|
type CreateBrowserRouterArg = Parameters<typeof createBrowserRouter>[0]
|
2023-08-10 13:30:32 -04:00
|
|
|
|
2023-08-22 05:34:20 +10:00
|
|
|
const addGlobalContextToElements = (
|
|
|
|
routes: CreateBrowserRouterArg
|
|
|
|
): CreateBrowserRouterArg =>
|
|
|
|
routes.map((route) =>
|
|
|
|
'element' in route
|
|
|
|
? {
|
|
|
|
...route,
|
2023-08-28 20:31:49 -04:00
|
|
|
element: (
|
2023-08-29 10:48:55 -04:00
|
|
|
<CommandBarProvider>
|
2024-03-11 17:50:31 -07:00
|
|
|
<SettingsAuthProvider>
|
|
|
|
<LspProvider>{route.element}</LspProvider>
|
|
|
|
</SettingsAuthProvider>
|
2023-08-29 10:48:55 -04:00
|
|
|
</CommandBarProvider>
|
2023-08-28 20:31:49 -04:00
|
|
|
),
|
2023-07-27 18:59:40 -04:00
|
|
|
}
|
2023-08-22 05:34:20 +10:00
|
|
|
: route
|
|
|
|
)
|
2023-08-15 21:56:24 -04:00
|
|
|
|
2023-08-22 05:34:20 +10:00
|
|
|
const router = createBrowserRouter(
|
|
|
|
addGlobalContextToElements([
|
|
|
|
{
|
|
|
|
path: paths.INDEX,
|
|
|
|
loader: () =>
|
2023-10-04 18:00:55 -04:00
|
|
|
isTauri()
|
|
|
|
? redirect(paths.HOME)
|
|
|
|
: redirect(paths.FILE + '/' + BROWSER_FILE_NAME),
|
2023-09-19 14:06:56 -04:00
|
|
|
errorElement: <ErrorPage />,
|
2023-08-22 05:34:20 +10:00
|
|
|
},
|
|
|
|
{
|
|
|
|
path: paths.FILE + '/:id',
|
|
|
|
element: (
|
2024-02-11 12:59:00 +11:00
|
|
|
<KclContextProvider>
|
|
|
|
<Auth>
|
|
|
|
<FileMachineProvider>
|
2023-10-16 13:28:41 -04:00
|
|
|
<ModelingMachineProvider>
|
2023-10-17 12:31:14 -04:00
|
|
|
<Outlet />
|
2023-10-16 13:28:41 -04:00
|
|
|
<App />
|
2024-02-23 11:24:22 -05:00
|
|
|
<CommandBar />
|
2023-10-16 13:28:41 -04:00
|
|
|
</ModelingMachineProvider>
|
2023-10-17 12:07:48 +11:00
|
|
|
<WasmErrBanner />
|
2024-02-11 12:59:00 +11:00
|
|
|
</FileMachineProvider>
|
|
|
|
{!isTauri() && import.meta.env.PROD && <DownloadAppBanner />}
|
|
|
|
</Auth>
|
|
|
|
</KclContextProvider>
|
2023-08-22 05:34:20 +10:00
|
|
|
),
|
|
|
|
id: paths.FILE,
|
|
|
|
loader: async ({
|
|
|
|
request,
|
|
|
|
params,
|
|
|
|
}): Promise<IndexLoaderData | Response> => {
|
2023-08-28 20:31:49 -04:00
|
|
|
const fetchedStorage = localStorage?.getItem(SETTINGS_PERSIST_KEY)
|
|
|
|
const persistedSettings = JSON.parse(fetchedStorage || '{}') as Partial<
|
|
|
|
ContextFrom<typeof settingsMachine>
|
|
|
|
>
|
2023-08-15 21:56:24 -04:00
|
|
|
|
2023-08-28 20:31:49 -04:00
|
|
|
const status = persistedSettings.onboardingStatus || ''
|
|
|
|
const notEnRouteToOnboarding = !request.url.includes(
|
|
|
|
paths.ONBOARDING.INDEX
|
|
|
|
)
|
|
|
|
// '' is the initial state, 'done' and 'dismissed' are the final states
|
|
|
|
const hasValidOnboardingStatus =
|
|
|
|
status.length === 0 || !(status === 'done' || status === 'dismissed')
|
|
|
|
const shouldRedirectToOnboarding =
|
|
|
|
notEnRouteToOnboarding && hasValidOnboardingStatus
|
|
|
|
|
|
|
|
if (shouldRedirectToOnboarding) {
|
2023-08-31 10:41:24 -04:00
|
|
|
return redirect(
|
|
|
|
makeUrlPathRelative(paths.ONBOARDING.INDEX) + status.slice(1)
|
|
|
|
)
|
2023-08-15 21:56:24 -04:00
|
|
|
}
|
|
|
|
|
2023-10-16 13:28:41 -04:00
|
|
|
const defaultDir = persistedSettings.defaultDirectory || ''
|
|
|
|
|
2023-10-04 18:00:55 -04:00
|
|
|
if (params.id && params.id !== BROWSER_FILE_NAME) {
|
2023-10-16 13:28:41 -04:00
|
|
|
const decodedId = decodeURIComponent(params.id)
|
2023-10-17 12:31:14 -04:00
|
|
|
const projectAndFile = decodedId.replace(defaultDir + sep, '')
|
|
|
|
const firstSlashIndex = projectAndFile.indexOf(sep)
|
2023-10-16 13:28:41 -04:00
|
|
|
const projectName = projectAndFile.slice(0, firstSlashIndex)
|
2023-10-17 12:31:14 -04:00
|
|
|
const projectPath = defaultDir + sep + projectName
|
2023-10-16 13:28:41 -04:00
|
|
|
const currentFileName = projectAndFile.slice(firstSlashIndex + 1)
|
|
|
|
|
|
|
|
if (firstSlashIndex === -1 || !currentFileName)
|
|
|
|
return redirect(
|
|
|
|
`${paths.FILE}/${encodeURIComponent(
|
2023-10-17 12:31:14 -04:00
|
|
|
`${params.id}${sep}${PROJECT_ENTRYPOINT}`
|
2023-10-16 13:28:41 -04:00
|
|
|
)}`
|
|
|
|
)
|
|
|
|
|
2023-08-22 05:34:20 +10:00
|
|
|
// Note that PROJECT_ENTRYPOINT is hardcoded until we support multiple files
|
2023-10-16 13:28:41 -04:00
|
|
|
const code = await readTextFile(decodedId)
|
|
|
|
const entrypointMetadata = await metadata(
|
2023-10-17 12:31:14 -04:00
|
|
|
projectPath + sep + PROJECT_ENTRYPOINT
|
2023-08-22 05:34:20 +10:00
|
|
|
)
|
2023-10-16 13:28:41 -04:00
|
|
|
const children = await readDir(projectPath, { recursive: true })
|
2023-11-06 11:49:13 +11:00
|
|
|
kclManager.setCodeAndExecute(code, false)
|
2023-08-15 21:56:24 -04:00
|
|
|
|
2024-02-12 12:18:37 -08:00
|
|
|
// Set the file system manager to the project path
|
|
|
|
// So that WASM gets an updated path for operations
|
|
|
|
fileSystemManager.dir = projectPath
|
|
|
|
|
2023-08-22 05:34:20 +10:00
|
|
|
return {
|
|
|
|
code,
|
|
|
|
project: {
|
2023-10-16 13:28:41 -04:00
|
|
|
name: projectName,
|
|
|
|
path: projectPath,
|
2023-08-22 05:34:20 +10:00
|
|
|
children,
|
2023-10-16 13:28:41 -04:00
|
|
|
entrypointMetadata,
|
|
|
|
},
|
|
|
|
file: {
|
|
|
|
name: currentFileName,
|
|
|
|
path: params.id,
|
2023-08-22 05:34:20 +10:00
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
2023-08-15 21:56:24 -04:00
|
|
|
|
2023-08-22 05:34:20 +10:00
|
|
|
return {
|
|
|
|
code: '',
|
|
|
|
}
|
|
|
|
},
|
|
|
|
children: [
|
|
|
|
{
|
|
|
|
path: makeUrlPathRelative(paths.SETTINGS),
|
|
|
|
element: <Settings />,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
path: makeUrlPathRelative(paths.ONBOARDING.INDEX),
|
|
|
|
element: <Onboarding />,
|
|
|
|
children: onboardingRoutes,
|
|
|
|
},
|
|
|
|
],
|
2023-08-15 21:56:24 -04:00
|
|
|
},
|
2023-08-22 05:34:20 +10:00
|
|
|
{
|
|
|
|
path: paths.HOME,
|
|
|
|
element: (
|
|
|
|
<Auth>
|
|
|
|
<Outlet />
|
|
|
|
<Home />
|
2024-02-23 11:24:22 -05:00
|
|
|
<CommandBar />
|
2023-08-22 05:34:20 +10:00
|
|
|
</Auth>
|
|
|
|
),
|
2024-02-11 12:59:00 +11:00
|
|
|
loader: async (): Promise<HomeLoaderData | Response> => {
|
2023-08-22 05:34:20 +10:00
|
|
|
if (!isTauri()) {
|
2023-10-04 18:00:55 -04:00
|
|
|
return redirect(paths.FILE + '/' + BROWSER_FILE_NAME)
|
2023-08-22 05:34:20 +10:00
|
|
|
}
|
2023-08-28 20:31:49 -04:00
|
|
|
const fetchedStorage = localStorage?.getItem(SETTINGS_PERSIST_KEY)
|
|
|
|
const persistedSettings = JSON.parse(fetchedStorage || '{}') as Partial<
|
|
|
|
ContextFrom<typeof settingsMachine>
|
|
|
|
>
|
|
|
|
const projectDir = await initializeProjectDirectory(
|
|
|
|
persistedSettings.defaultDirectory || ''
|
|
|
|
)
|
2024-01-04 04:54:07 -05:00
|
|
|
let newDefaultDirectory: string | undefined = undefined
|
2024-02-16 09:09:58 -05:00
|
|
|
if (projectDir !== persistedSettings.defaultDirectory) {
|
|
|
|
localStorage.setItem(
|
|
|
|
SETTINGS_PERSIST_KEY,
|
|
|
|
JSON.stringify({
|
|
|
|
...persistedSettings,
|
|
|
|
defaultDirectory: projectDir,
|
|
|
|
})
|
2023-08-28 20:31:49 -04:00
|
|
|
)
|
2024-02-16 09:09:58 -05:00
|
|
|
newDefaultDirectory = projectDir
|
|
|
|
}
|
|
|
|
const projectsNoMeta = (await readDir(projectDir)).filter(
|
|
|
|
isProjectDirectory
|
|
|
|
)
|
|
|
|
const projects = await Promise.all(
|
|
|
|
projectsNoMeta.map(async (p: FileEntry) => ({
|
|
|
|
entrypointMetadata: await metadata(
|
|
|
|
p.path + sep + PROJECT_ENTRYPOINT
|
|
|
|
),
|
|
|
|
...p,
|
|
|
|
}))
|
|
|
|
)
|
2023-08-22 05:34:20 +10:00
|
|
|
|
2024-02-16 09:09:58 -05:00
|
|
|
return {
|
|
|
|
projects,
|
|
|
|
newDefaultDirectory,
|
2023-08-22 05:34:20 +10:00
|
|
|
}
|
2023-08-15 21:56:24 -04:00
|
|
|
},
|
2023-08-22 05:34:20 +10:00
|
|
|
children: [
|
|
|
|
{
|
|
|
|
path: makeUrlPathRelative(paths.SETTINGS),
|
|
|
|
element: <Settings />,
|
|
|
|
},
|
|
|
|
],
|
|
|
|
},
|
|
|
|
{
|
|
|
|
path: paths.SIGN_IN,
|
|
|
|
element: <SignIn />,
|
|
|
|
},
|
|
|
|
])
|
|
|
|
)
|
2023-07-27 18:59:40 -04:00
|
|
|
|
|
|
|
/**
|
|
|
|
* All routes in the app, used in src/index.tsx
|
|
|
|
* @returns RouterProvider
|
|
|
|
*/
|
|
|
|
export const Router = () => {
|
|
|
|
return <RouterProvider router={router} />
|
|
|
|
}
|