import { App } from './App' import { createBrowserRouter, Outlet, redirect, RouterProvider, } from 'react-router-dom' import { ErrorPage } from './components/ErrorPage' import { Settings } from './routes/Settings' import Onboarding, { onboardingRoutes } from './routes/Onboarding' import SignIn from './routes/SignIn' import { Auth } from './Auth' 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' import { metadata } from 'tauri-plugin-fs-extra-api' import DownloadAppBanner from './components/DownloadAppBanner' import { WasmErrBanner } from './components/WasmErrBanner' import { GlobalStateProvider } from './components/GlobalStateProvider' import { SETTINGS_PERSIST_KEY, settingsMachine, } from './machines/settingsMachine' import { ContextFrom } from 'xstate' import CommandBarProvider, { CommandBar, } from 'components/CommandBar/CommandBar' import ModelingMachineProvider from 'components/ModelingMachineProvider' import { KclContextProvider, kclManager } from 'lang/KclSingleton' import FileMachineProvider from 'components/FileMachineProvider' import { sep } from '@tauri-apps/api/path' import { paths } from 'lib/paths' import { IndexLoaderData, HomeLoaderData } from 'lib/types' import { fileSystemManager } from 'lang/std/fileSystemManager' export const BROWSER_FILE_NAME = 'new' type CreateBrowserRouterArg = Parameters[0] const addGlobalContextToElements = ( routes: CreateBrowserRouterArg ): CreateBrowserRouterArg => routes.map((route) => 'element' in route ? { ...route, element: ( {route.element} ), } : route ) const router = createBrowserRouter( addGlobalContextToElements([ { path: paths.INDEX, loader: () => isTauri() ? redirect(paths.HOME) : redirect(paths.FILE + '/' + BROWSER_FILE_NAME), errorElement: , }, { path: paths.FILE + '/:id', element: ( {!isTauri() && import.meta.env.PROD && } ), id: paths.FILE, loader: async ({ request, params, }): Promise => { const fetchedStorage = localStorage?.getItem(SETTINGS_PERSIST_KEY) const persistedSettings = JSON.parse(fetchedStorage || '{}') as Partial< ContextFrom > 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) { return redirect( makeUrlPathRelative(paths.ONBOARDING.INDEX) + status.slice(1) ) } const defaultDir = persistedSettings.defaultDirectory || '' if (params.id && params.id !== BROWSER_FILE_NAME) { const decodedId = decodeURIComponent(params.id) const projectAndFile = decodedId.replace(defaultDir + sep, '') const firstSlashIndex = projectAndFile.indexOf(sep) const projectName = projectAndFile.slice(0, firstSlashIndex) const projectPath = defaultDir + sep + projectName const currentFileName = projectAndFile.slice(firstSlashIndex + 1) if (firstSlashIndex === -1 || !currentFileName) return redirect( `${paths.FILE}/${encodeURIComponent( `${params.id}${sep}${PROJECT_ENTRYPOINT}` )}` ) // Note that PROJECT_ENTRYPOINT is hardcoded until we support multiple files const code = await readTextFile(decodedId) const entrypointMetadata = await metadata( projectPath + sep + PROJECT_ENTRYPOINT ) const children = await readDir(projectPath, { recursive: true }) kclManager.setCodeAndExecute(code, false) // Set the file system manager to the project path // So that WASM gets an updated path for operations fileSystemManager.dir = projectPath return { code, project: { name: projectName, path: projectPath, children, entrypointMetadata, }, file: { name: currentFileName, path: params.id, }, } } return { code: '', } }, children: [ { path: makeUrlPathRelative(paths.SETTINGS), element: , }, { path: makeUrlPathRelative(paths.ONBOARDING.INDEX), element: , children: onboardingRoutes, }, ], }, { path: paths.HOME, element: ( ), loader: async (): Promise => { if (!isTauri()) { return redirect(paths.FILE + '/' + BROWSER_FILE_NAME) } const fetchedStorage = localStorage?.getItem(SETTINGS_PERSIST_KEY) const persistedSettings = JSON.parse(fetchedStorage || '{}') as Partial< ContextFrom > const projectDir = await initializeProjectDirectory( persistedSettings.defaultDirectory || '' ) let newDefaultDirectory: string | undefined = undefined if (projectDir !== persistedSettings.defaultDirectory) { localStorage.setItem( SETTINGS_PERSIST_KEY, JSON.stringify({ ...persistedSettings, defaultDirectory: projectDir, }) ) 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, })) ) return { projects, newDefaultDirectory, } }, children: [ { path: makeUrlPathRelative(paths.SETTINGS), element: , }, ], }, { path: paths.SIGN_IN, element: , }, ]) ) /** * All routes in the app, used in src/index.tsx * @returns RouterProvider */ export const Router = () => { return }