Compare commits
1 Commits
nrc-std-ax
...
frank-xsta
Author | SHA1 | Date | |
---|---|---|---|
ac64b9fa45 |
@ -26,7 +26,7 @@
|
||||
"@types/react-dom": "^18.0.0",
|
||||
"@uiw/react-codemirror": "^4.21.20",
|
||||
"@xstate/inspect": "^0.8.0",
|
||||
"@xstate/react": "^3.2.2",
|
||||
"@xstate/react": "^4.1.0",
|
||||
"crypto-js": "^4.2.0",
|
||||
"debounce-promise": "^3.1.2",
|
||||
"formik": "^2.4.3",
|
||||
@ -50,7 +50,7 @@
|
||||
"three": "^0.160.0",
|
||||
"toml": "^3.0.0",
|
||||
"ts-node": "^10.9.1",
|
||||
"typescript": "^5.2.2",
|
||||
"typescript": "^5.4.2",
|
||||
"uuid": "^9.0.1",
|
||||
"vitest": "^1.3.1",
|
||||
"vscode-jsonrpc": "^8.1.0",
|
||||
@ -58,7 +58,7 @@
|
||||
"wasm-pack": "^0.12.1",
|
||||
"web-vitals": "^3.5.0",
|
||||
"ws": "^8.13.0",
|
||||
"xstate": "^4.38.2",
|
||||
"xstate": "^5.9.1",
|
||||
"zustand": "^4.4.5"
|
||||
},
|
||||
"scripts": {
|
||||
|
@ -1,12 +1,11 @@
|
||||
import { AppMachineContext } from 'machines/appMachine'
|
||||
import Loading from './components/Loading'
|
||||
import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext'
|
||||
|
||||
// Wrapper around protected routes, used in src/Router.tsx
|
||||
export const Auth = ({ children }: React.PropsWithChildren) => {
|
||||
const { auth } = useSettingsAuthContext()
|
||||
const isLoggingIn = auth?.state.matches('checkIfLoggedIn')
|
||||
const isAuthenticating = AppMachineContext.useSelector(s => s.matches('Loading'))
|
||||
|
||||
return isLoggingIn ? (
|
||||
return isAuthenticating ? (
|
||||
<Loading>
|
||||
<span data-testid="initial-load">Loading Modeling App...</span>
|
||||
</Loading>
|
||||
|
@ -16,8 +16,6 @@ import makeUrlPathRelative from './lib/makeUrlPathRelative'
|
||||
import DownloadAppBanner from 'components/DownloadAppBanner'
|
||||
import { WasmErrBanner } from 'components/WasmErrBanner'
|
||||
import { CommandBar } from 'components/CommandBar/CommandBar'
|
||||
import ModelingMachineProvider from 'components/ModelingMachineProvider'
|
||||
import FileMachineProvider from 'components/FileMachineProvider'
|
||||
import { paths } from 'lib/paths'
|
||||
import {
|
||||
fileLoader,
|
||||
@ -25,10 +23,7 @@ import {
|
||||
indexLoader,
|
||||
onboardingRedirectLoader,
|
||||
} from 'lib/routeLoaders'
|
||||
import { CommandBarProvider } from 'components/CommandBar/CommandBarProvider'
|
||||
import SettingsAuthProvider from 'components/SettingsAuthProvider'
|
||||
import LspProvider from 'components/LspProvider'
|
||||
import { KclContextProvider } from 'lang/KclSingleton'
|
||||
import { AppMachineProvider } from 'components/AppMachineProvider'
|
||||
|
||||
export const BROWSER_FILE_NAME = 'new'
|
||||
|
||||
@ -37,15 +32,9 @@ const router = createBrowserRouter([
|
||||
loader: indexLoader,
|
||||
id: paths.INDEX,
|
||||
element: (
|
||||
<CommandBarProvider>
|
||||
<KclContextProvider>
|
||||
<SettingsAuthProvider>
|
||||
<LspProvider>
|
||||
<Outlet />
|
||||
</LspProvider>
|
||||
</SettingsAuthProvider>
|
||||
</KclContextProvider>
|
||||
</CommandBarProvider>
|
||||
<AppMachineProvider>
|
||||
<Outlet />
|
||||
</AppMachineProvider>
|
||||
),
|
||||
children: [
|
||||
{
|
||||
@ -61,14 +50,10 @@ const router = createBrowserRouter([
|
||||
id: paths.FILE,
|
||||
element: (
|
||||
<Auth>
|
||||
<FileMachineProvider>
|
||||
<ModelingMachineProvider>
|
||||
<Outlet />
|
||||
<App />
|
||||
<CommandBar />
|
||||
</ModelingMachineProvider>
|
||||
<WasmErrBanner />
|
||||
</FileMachineProvider>
|
||||
<Outlet />
|
||||
<App />
|
||||
<CommandBar />
|
||||
<WasmErrBanner />
|
||||
{!isTauri() && import.meta.env.PROD && <DownloadAppBanner />}
|
||||
</Auth>
|
||||
),
|
||||
|
21
src/components/AppMachineProvider.tsx
Normal file
21
src/components/AppMachineProvider.tsx
Normal file
@ -0,0 +1,21 @@
|
||||
import { AppMachineContext } from 'machines/appMachine'
|
||||
import { PropsWithChildren } from 'react'
|
||||
import { useLocation, useNavigate } from 'react-router-dom'
|
||||
|
||||
export const AppMachineProvider = ({ children }: PropsWithChildren) => {
|
||||
const navigate = useNavigate()
|
||||
const location = useLocation()
|
||||
|
||||
return (
|
||||
<AppMachineContext.Provider
|
||||
options={{
|
||||
input: {
|
||||
navigate,
|
||||
location,
|
||||
},
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</AppMachineContext.Provider>
|
||||
)
|
||||
}
|
@ -13,6 +13,7 @@ import {
|
||||
} from 'components/NetworkHealthIndicator'
|
||||
import { useKclContext } from 'lang/KclSingleton'
|
||||
import { useStore } from 'useStore'
|
||||
import { AppMachineContext } from 'machines/appMachine'
|
||||
|
||||
// This might not be necessary, AnyStateMachine from xstate is working
|
||||
export type AllMachines =
|
||||
@ -46,7 +47,7 @@ export default function useStateMachineCommands<
|
||||
allCommandsRequireNetwork = false,
|
||||
onCancel,
|
||||
}: UseStateMachineCommandsArgs<T, S>) {
|
||||
const { commandBarSend } = useCommandsContext()
|
||||
const commandsActorRef = AppMachineContext.useSelector(s => s.children.commands)
|
||||
const { overallState } = useNetworkStatus()
|
||||
const { isExecuting } = useKclContext()
|
||||
const { isStreamReady } = useStore((s) => ({
|
||||
@ -72,10 +73,10 @@ export default function useStateMachineCommands<
|
||||
)
|
||||
.filter((c) => c !== null) as Command[] // TS isn't smart enough to know this filter removes nulls
|
||||
|
||||
commandBarSend({ type: 'Add commands', data: { commands: newCommands } })
|
||||
commandsActorRef?.send({ type: 'Add commands', data: { commands: newCommands } })
|
||||
|
||||
return () => {
|
||||
commandBarSend({
|
||||
commandsActorRef?.send({
|
||||
type: 'Remove commands',
|
||||
data: { commands: newCommands },
|
||||
})
|
||||
|
264
src/machines/appMachine.ts
Normal file
264
src/machines/appMachine.ts
Normal file
@ -0,0 +1,264 @@
|
||||
import { ProjectWithEntryPointMetadata } from 'lib/types'
|
||||
import { assign, fromCallback, fromPromise, setup } from 'xstate'
|
||||
import { settingsMachine } from './settingsMachine'
|
||||
import { commandBarMachine } from './commandBarMachine'
|
||||
import { homeMachine } from './homeMachine'
|
||||
import { modelingMachine } from './modelingMachine'
|
||||
import { fileMachine } from './fileMachine'
|
||||
import { useLocation, useNavigate } from 'react-router-dom'
|
||||
import { TOKEN_PERSIST_KEY, getUser, persistedToken } from './authMachine'
|
||||
import { Models } from '@kittycad/lib/dist/types/src'
|
||||
import { createActorContext } from '@xstate/react'
|
||||
import { paths } from 'lib/paths'
|
||||
import { isTauri } from 'lib/isTauri'
|
||||
import { sep } from '@tauri-apps/api/path'
|
||||
|
||||
export const appMachine = setup({
|
||||
types: {
|
||||
context: {} as {
|
||||
token?: string
|
||||
user?: Models['User_type']
|
||||
currentProject?: ProjectWithEntryPointMetadata
|
||||
currentFile?: string
|
||||
navigate: ReturnType<typeof useNavigate>
|
||||
location: ReturnType<typeof useLocation>
|
||||
},
|
||||
events: {} as
|
||||
| { type: 'Sign out' }
|
||||
| {
|
||||
type: 'Open project'
|
||||
data: {
|
||||
project: ProjectWithEntryPointMetadata
|
||||
file: string
|
||||
}
|
||||
}
|
||||
| { type: 'Close project' }
|
||||
| { type: 'AuthActor.done'; output: Models['User_type'] },
|
||||
input: {} as {
|
||||
navigate: ReturnType<typeof useNavigate>
|
||||
location: ReturnType<typeof useLocation>
|
||||
},
|
||||
},
|
||||
actions: {
|
||||
'Clear currentProject and currentFile': assign({
|
||||
currentProject: undefined,
|
||||
currentFile: undefined,
|
||||
}),
|
||||
'Set currentProject': assign({
|
||||
currentProject: ({ event, context }) =>
|
||||
event.type === 'Open project'
|
||||
? event.data.project
|
||||
: context.currentProject,
|
||||
}),
|
||||
'Set currentFile': assign({
|
||||
currentFile: ({ event, context }) =>
|
||||
event.type === 'Open project' ? event.data.file : context.currentFile,
|
||||
}),
|
||||
'Delete auth state': () => {
|
||||
localStorage.removeItem(TOKEN_PERSIST_KEY)
|
||||
assign({ token: undefined })
|
||||
},
|
||||
'Go to sign in page': ({ context }) => {
|
||||
context.navigate(paths.SIGN_IN)
|
||||
},
|
||||
'Navigate home': ({ context }) => {
|
||||
context.navigate(paths.INDEX)
|
||||
},
|
||||
'Navigate to file': ({ context, event }) => {
|
||||
if (event.type !== 'Open project') return
|
||||
context.navigate(
|
||||
paths.FILE +
|
||||
event.data.project.path +
|
||||
(isTauri() ? sep : '/') +
|
||||
event.data.file
|
||||
)
|
||||
},
|
||||
'Assign user': assign({
|
||||
user: ({ event, context }) =>
|
||||
event.type === 'AuthActor.done' ? event.output : context.user,
|
||||
}),
|
||||
'Persist auth state': ({ context }) => {
|
||||
localStorage.setItem(TOKEN_PERSIST_KEY, context.token || '')
|
||||
},
|
||||
},
|
||||
actors: {
|
||||
SettingsActor: settingsMachine,
|
||||
EngineConnectionActor: fromCallback(({ sendBack, receive, input }) => {
|
||||
// TODO implement actor
|
||||
}),
|
||||
CommandBarMachine: commandBarMachine,
|
||||
LspActor: fromCallback(({ sendBack, receive }) => {
|
||||
// TODO implement actor
|
||||
}),
|
||||
HomeMachine: homeMachine,
|
||||
ModelingMachine: modelingMachine,
|
||||
ClientSideSceneActor: fromCallback(({ sendBack, receive }) => {
|
||||
// TODO implement actor
|
||||
}),
|
||||
ProjectMachine: fileMachine,
|
||||
KclActor: fromCallback(({ sendBack, receive }) => {
|
||||
// TODO implement actor
|
||||
}),
|
||||
AuthActor: fromPromise(
|
||||
({ input }: { input: { token: string | undefined } }) =>
|
||||
getUser(input.token)
|
||||
),
|
||||
},
|
||||
schemas: {
|
||||
events: {
|
||||
'Sign out': {
|
||||
type: 'object',
|
||||
properties: {},
|
||||
},
|
||||
'Open project': {
|
||||
type: 'object',
|
||||
properties: {},
|
||||
},
|
||||
'Close project': {
|
||||
type: 'object',
|
||||
properties: {},
|
||||
},
|
||||
'': {
|
||||
type: 'object',
|
||||
properties: {},
|
||||
},
|
||||
},
|
||||
},
|
||||
}).createMachine({
|
||||
/** @xstate-layout N4IgpgJg5mDOIC5QEEAOqAEBZAhgYwAsBLAOzADoBlIqMiDUgYmtowHsBXAFwG0AGALqJQqNrCJcibEsJAAPRADYA7AEZyAFgCcG1QGZVygKyKAHMq3KANCACeiAExH1WvUY2Lnejct0a9AL4BNmiYuISkFCx0DCTkABJsALZgjADyqGAkGKgATmwAVmB4vIKyouKS0rIKCNp85A7KTnxqGq3m5jb2CMqKeuSWWqYjfKoeRmNBIejY+MRkVDQxpOQACvlFJeyZJIwAwgA2YmA5m8WlQkggFRJSMte19ZqqRsp6fHqKfIqq-UbdJRmcjKPjOZymVRaMHvaYgUJzCKLAAybBwEFIUEYEGkFFIADc2ABrCg4bgEfhXERiO7VR6IPTecimLRGYbDIymTyqUyAhAORSKcitVw8zx6Bx6ZpwhHhBYUVHozGMMC5fK5cioQ44LgAMzYuSS5DJXApZWutyqD1AtSMDgc5E8fW0ouUfQcfIcP00GhZpl90PeziCwRAJDYEDgsll80i5RpVpqiAAtEZGhpJkY3F8+KZc74+ao+Bphe1-VL-Vm+PaZbM5ZElrRILF45V7km6g7jNoPB49FooaoBXyNA5TMKVHxi99jO5VLWwrHFtFm6tEilW7TrfJENCQe4dIo+wOB3o+YphsLWu0s8NfK4F4j5Y2VnENoULjsspvE-Tev2r3GAV70FFRz3aRpWiPHwtCcBxVHnUMYyRKJlmbTguB-ds-yHBDyCze1OhUe1jE9PN8OcdpvFeBCL0fesUTRDESCgLC6RtRBJkGJoswQ0EuXaaw7EQQdyC+KVQW+b4Pg0EMAiAA */
|
||||
context: ({ input }) => ({
|
||||
token: persistedToken,
|
||||
navigate: input.navigate,
|
||||
location: input.location,
|
||||
}),
|
||||
id: 'App Machine',
|
||||
initial: 'Loading',
|
||||
states: {
|
||||
'Signed in': {
|
||||
initial: 'Home',
|
||||
on: {
|
||||
'Sign out': {
|
||||
target: 'Signed out',
|
||||
actions: [
|
||||
{
|
||||
type: 'Delete auth state',
|
||||
},
|
||||
{
|
||||
type: 'Go to sign in page',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
invoke: [
|
||||
{
|
||||
id: 'settings',
|
||||
systemId: 'settings',
|
||||
input: {},
|
||||
src: 'SettingsActor',
|
||||
},
|
||||
{
|
||||
id: 'engine',
|
||||
systemId: 'engine',
|
||||
input: {},
|
||||
src: 'EngineConnectionActor',
|
||||
},
|
||||
{
|
||||
id: 'commands',
|
||||
systemId: 'commands',
|
||||
input: {},
|
||||
src: 'CommandBarMachine',
|
||||
},
|
||||
{
|
||||
id: 'lsp',
|
||||
systemId: 'lsp',
|
||||
input: {},
|
||||
src: 'LspActor',
|
||||
},
|
||||
],
|
||||
states: {
|
||||
Home: {
|
||||
on: {
|
||||
'Open project': {
|
||||
target: 'Project open',
|
||||
actions: {
|
||||
type: 'Navigate to file',
|
||||
},
|
||||
},
|
||||
},
|
||||
entry: {
|
||||
type: 'Clear currentProject and currentFile',
|
||||
},
|
||||
invoke: {
|
||||
id: 'home',
|
||||
input: {},
|
||||
src: 'HomeMachine',
|
||||
},
|
||||
},
|
||||
'Project open': {
|
||||
on: {
|
||||
'Close project': {
|
||||
target: 'Home',
|
||||
actions: {
|
||||
type: 'Navigate home',
|
||||
},
|
||||
},
|
||||
},
|
||||
entry: [
|
||||
{
|
||||
type: 'Set currentProject',
|
||||
},
|
||||
{
|
||||
type: 'Set currentFile',
|
||||
},
|
||||
],
|
||||
invoke: [
|
||||
{
|
||||
id: 'modeling',
|
||||
systemId: 'modeling',
|
||||
input: {},
|
||||
src: 'ModelingMachine',
|
||||
},
|
||||
{
|
||||
input: {},
|
||||
src: 'ClientSideSceneActor',
|
||||
},
|
||||
{
|
||||
id: 'project',
|
||||
systemId: 'project',
|
||||
input: {},
|
||||
src: 'ProjectMachine',
|
||||
},
|
||||
{
|
||||
input: {},
|
||||
src: 'KclActor',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
'Signed out': {
|
||||
type: 'final',
|
||||
},
|
||||
|
||||
Loading: {
|
||||
invoke: {
|
||||
src: 'AuthActor',
|
||||
id: 'auth',
|
||||
input: ({ context: { token } }) => ({ token }),
|
||||
onDone: {
|
||||
target: 'Signed in',
|
||||
actions: [
|
||||
assign({
|
||||
user: ({ event }) => event.output,
|
||||
}),
|
||||
'Persist auth state',
|
||||
],
|
||||
},
|
||||
onError: 'Signed out',
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
export const AppMachineContext = createActorContext(appMachine)
|
@ -1,4 +1,4 @@
|
||||
import { createMachine, assign } from 'xstate'
|
||||
import { createMachine, assign, setup, fromPromise } from 'xstate'
|
||||
import { Models } from '@kittycad/lib'
|
||||
import withBaseURL from '../lib/withBaseURL'
|
||||
import { isTauri } from 'lib/isTauri'
|
||||
@ -39,86 +39,82 @@ export type Events =
|
||||
}
|
||||
|
||||
export const TOKEN_PERSIST_KEY = 'TOKEN_PERSIST_KEY'
|
||||
const persistedToken =
|
||||
export const persistedToken =
|
||||
localStorage?.getItem(TOKEN_PERSIST_KEY) ||
|
||||
getCookie('__Secure-next-auth.session-token') ||
|
||||
''
|
||||
undefined
|
||||
|
||||
export const authMachine = createMachine<UserContext, Events>(
|
||||
{
|
||||
id: 'Auth',
|
||||
initial: 'checkIfLoggedIn',
|
||||
states: {
|
||||
checkIfLoggedIn: {
|
||||
id: 'check-if-logged-in',
|
||||
invoke: {
|
||||
src: 'getUser',
|
||||
id: 'check-logged-in',
|
||||
onDone: [
|
||||
{
|
||||
target: 'loggedIn',
|
||||
actions: assign({
|
||||
user: (context, event) => event.data,
|
||||
}),
|
||||
},
|
||||
],
|
||||
onError: [
|
||||
{
|
||||
target: 'loggedOut',
|
||||
actions: assign({
|
||||
user: () => undefined,
|
||||
}),
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
loggedIn: {
|
||||
entry: ['goToIndexPage'],
|
||||
on: {
|
||||
'Log out': {
|
||||
target: 'loggedOut',
|
||||
},
|
||||
},
|
||||
},
|
||||
loggedOut: {
|
||||
entry: ['goToSignInPage'],
|
||||
on: {
|
||||
'Log in': {
|
||||
target: 'checkIfLoggedIn',
|
||||
export const authMachine = setup({
|
||||
types: {
|
||||
events: {} as { type: 'Log out' } | { type: 'Log in' },
|
||||
context: {} as UserContext,
|
||||
},
|
||||
actors: {
|
||||
fetchUser: fromPromise(({ input }: { input: { token: string | undefined }}) => getUser(input.token)),
|
||||
},
|
||||
actions: {
|
||||
goToIndexPage: () => ({}),
|
||||
goToSignInPage: () => ({}),
|
||||
}
|
||||
}).createMachine({
|
||||
id: 'Auth',
|
||||
preserveActionOrder: true,
|
||||
initial: 'checkIfLoggedIn',
|
||||
context: {
|
||||
token: persistedToken,
|
||||
},
|
||||
states: {
|
||||
checkIfLoggedIn: {
|
||||
id: 'check-if-logged-in',
|
||||
invoke: {
|
||||
id: 'getUser',
|
||||
src: 'fetchUser',
|
||||
input: ({ context: { token } }) => ({ token }),
|
||||
onDone: [
|
||||
{
|
||||
target: 'loggedIn',
|
||||
actions: assign({
|
||||
token: (_, event) => {
|
||||
const token = event.token || ''
|
||||
localStorage.setItem(TOKEN_PERSIST_KEY, token)
|
||||
return token
|
||||
},
|
||||
user: ({ event }) => event.output,
|
||||
}),
|
||||
},
|
||||
],
|
||||
onError: [
|
||||
{
|
||||
target: 'loggedOut',
|
||||
actions: assign({
|
||||
user: () => undefined,
|
||||
}),
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
loggedIn: {
|
||||
entry: ['goToIndexPage'],
|
||||
on: {
|
||||
'Log out': {
|
||||
target: 'loggedOut',
|
||||
},
|
||||
},
|
||||
},
|
||||
schema: { events: {} as { type: 'Log out' } | { type: 'Log in' } },
|
||||
predictableActionArguments: true,
|
||||
preserveActionOrder: true,
|
||||
context: {
|
||||
token: persistedToken,
|
||||
loggedOut: {
|
||||
entry: { type: 'goToIndexPage' },
|
||||
on: {
|
||||
'Log in': {
|
||||
target: 'checkIfLoggedIn',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
actions: {},
|
||||
services: { getUser },
|
||||
guards: {},
|
||||
delays: {},
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
async function getUser(context: UserContext) {
|
||||
export async function getUser(token?: string) {
|
||||
const url = withBaseURL('/user')
|
||||
const headers: { [key: string]: string } = {
|
||||
'Content-Type': 'application/json',
|
||||
}
|
||||
|
||||
if (!context.token && isTauri()) throw new Error('No token found')
|
||||
if (context.token) headers['Authorization'] = `Bearer ${context.token}`
|
||||
if (!token && isTauri()) throw new Error('No token found')
|
||||
if (token) headers['Authorization'] = `Bearer ${token}`
|
||||
if (SKIP_AUTH) return LOCAL_USER
|
||||
|
||||
const userPromise = !isTauri()
|
||||
@ -130,7 +126,7 @@ async function getUser(context: UserContext) {
|
||||
.then((res) => res.json())
|
||||
.catch((err) => console.error('error from Browser getUser', err))
|
||||
: invoke<Models['User_type'] | Record<'error_code', unknown>>('get_user', {
|
||||
token: context.token,
|
||||
token: token,
|
||||
hostname: VITE_KC_API_BASE_URL,
|
||||
}).catch((err) => console.error('error from Tauri getUser', err))
|
||||
|
||||
@ -138,10 +134,10 @@ async function getUser(context: UserContext) {
|
||||
|
||||
if ('error_code' in user) throw new Error(user.message)
|
||||
|
||||
return user
|
||||
return user as Models['User_type']
|
||||
}
|
||||
|
||||
function getCookie(cname: string): string {
|
||||
export function getCookie(cname: string): string {
|
||||
if (isTauri()) {
|
||||
return ''
|
||||
}
|
||||
|
@ -1,28 +1,54 @@
|
||||
import { assign, createMachine } from 'xstate'
|
||||
import { assign, createMachine, fromPromise, setup } from 'xstate'
|
||||
import { type ProjectWithEntryPointMetadata } from 'lib/types'
|
||||
import { HomeCommandSchema } from 'lib/commandBarConfigs/homeCommandConfig'
|
||||
import { useNavigate } from 'react-router-dom'
|
||||
|
||||
export const homeMachine = createMachine(
|
||||
export const homeMachine = setup({
|
||||
types: {
|
||||
context: {} as {
|
||||
projects: ProjectWithEntryPointMetadata[]
|
||||
defaultProjectName: string
|
||||
defaultDirectory: string
|
||||
navigate: ReturnType<typeof useNavigate>
|
||||
},
|
||||
events: {} as
|
||||
| { type: 'Open project'; data: HomeCommandSchema['Open project'] }
|
||||
| { type: 'Rename project'; data: HomeCommandSchema['Rename project'] }
|
||||
| { type: 'Create project'; data: HomeCommandSchema['Create project'] }
|
||||
| { type: 'Delete project'; data: HomeCommandSchema['Delete project'] },
|
||||
input: {} as {
|
||||
navigate: ReturnType<typeof useNavigate>
|
||||
defaultProjectName: string
|
||||
defaultDirectory: string
|
||||
},
|
||||
},
|
||||
actions: {} as { toastSuccess: () => void; toastError: () => void },
|
||||
actors: {
|
||||
readProjects: fromPromise(async () => {
|
||||
return []
|
||||
}),
|
||||
createProject: fromPromise(async () => {
|
||||
return
|
||||
}),
|
||||
renameProject: fromPromise(async () => {
|
||||
return
|
||||
}),
|
||||
deleteProject: fromPromise(async () => {
|
||||
return
|
||||
}),
|
||||
},
|
||||
}).createMachine(
|
||||
{
|
||||
/** @xstate-layout N4IgpgJg5mDOIC5QAkD2BbMACdBDAxgBYCWAdmAHTK6xampYAOATqgFZj4AusAxAMLMwuLthbtOXANoAGALqJQjVLGJdiqUopAAPRAHYAbPooAWABwBGUwE5zAJgeGArM-MAaEAE9EN0wGYKGX97GX1nGVNDS0MbfwBfeM80TBwCEnIqGiZWDm4+ACUwUlxU8TzpeW1lVXVNbT0EcJNg02d-fzt7fU77Tx8EQ0iKCPtnfUsjGRtLGXtE5IxsPCIySmpacsk+QWFRHIluWQUkEBq1DS1TxqN7ChjzOxtXf0t7a37EcwsRibH-ZzRezA8wLEApZbpNZZTa5ba8AAiYAANmB9lsjlVTuc6ldQDdDOYKP5bm0os5TDJDJ8mlEzPpzIZHA4bO9umCIWlVpkNgcKnwAPKMYp8yTHaoqC71a6IEmBUz6BkWZzWDq2Uw0qzOIJAwz+PXWfSmeZJcFLLkZSi7ERkKCi7i8CCaShkABuqAA1pR8EIRGAALQYyonJSS3ENRDA2wUeyvd6dPVhGw0-RhGOp8IA8xGFkc80rS0Ua3qUh2oO8MDMVjMCiMZEiABmqGY6AoPr2AaD4uxYcuEYQoQpQWNNjsMnMgLGKbT3TC7TcOfsNjzqQL0KKJXQtvtXEdzoobs9lCEm87cMxIbOvel+MQqtMQRmS5ks31sZpAUsZkcIX+cQZJIrpC3KUBupTbuWlbVrW9ZcE2LYUCepRnocwYSrUfYyggbzvBQ+jMq49imLYwTUt4iCft+5i-u0-7UfoQEWtCSKoiWZbnruTqZIeXoUBAKJoihFTdqGGE3rod7UdqsQTI8hiGAqrIauRA7RvYeoqhO1jtAqjFrpkLFohBHEVlWzYwY2zatvxrFCWKWKiVKeISdh4yBJE-jGs4fhhA4zg0kRNgxhplhaW0nn4XpUKZEUuAQMZqF8FxLqkO6vG+hAgYcbAIlXmJzmNERdy0RYNiKgpthxDSEU6q8MSTJYjWGFFIEULF8WljuSX7jxx7CJlQY5ZYl44pht4IP61gyPc8njt0lIuH51UKrVVITEyMy2C1hbtQl-KmdBdaWQhGVZYluWjeJjSTf402shMEyuEyljPAFL0UNmMiuN86lWHMiSmvQ-HwKcnL6WA6FOf2k3mESMRDA4RpUm4U4qf6gSEt0QIvvqfjOCaiyrtF6zZPQXWQ+GWFlUEsbmNMf1TV9NLeXDcqRIySnNaaYPEzC5M9vl-b+IyFCjupryPF9jKWP5Kks-cbMWLERHRNt0LFntkgU2NLk4dqsz43YsTK++Kk2C+MbTOOcxzOMrhqzFxTgZ1Qba1dd6BUE1jGsLMxxK9KlDNqm3tMLUQvqYlgO5QhlsTubsFXesTTUuPTfHExshDS0RftRftGgEnTZtHbX9Zr+QJ-2S4Y3qnmTC+4tMyp1EfeOnmeQqdOhyXQrFOXXCV1hCkmLDOnBJYvRRDSsyRzGjiKj0lKdAkANAA */
|
||||
/** @xstate-layout N4IgpgJg5mDOIC5QAkD2BbMACdBDAxgBYCWAdmAMS6yzFSkDaADALqKgAOqtALsaqXYgAHogAsAJgA0IAJ6IJATgAcAOgBsAdgCsYzYoCMEsdu3LNAXwsy0mHARLlVyallKosHAE6oAVmHweWAoAYS8wXB5sbz8AnmY2JBAuXn5BJNEEAzF1MVVdXUV1JnVtCXUVGXkECW0DVTEAZgNlRvUDbINS5SsbDGw8IjIwZ1cY-0DggCUwUlw7cbiEoRTiPgEhTI6O-KZtJnMzOs0dKsRNVtU9xqbG5Wzc7V6QWwGHYdHYTx8JoNDwyLRH5LVgrbhrNKbRAPPIFMRFEplCrKM4IXL1MRdCq1C7KRSKRrPV72IZOFxfRaTCgAETAABswFFvrFAsskqt1ulQFscrCTPDiqVypU5OdlNpVBJlMUzLkVCcJET+iTHCNyczfsEAPIcWYakGJTjgzlQrK8-L8hFC5Go1oS7aFfSKJpKuyDVWqMIRPikKD6wIUCACEZkABuqAA1iN8ACogBaSnxUHs42QjLQ81wq1IkXVEzqVTSlQlJgGRoEgyut6kkZeyJkP2JihgLw+LyqDh0yIAM1QXnQqhj3rACeBrOTRtSG3TZtKheUTFLEnL2hXYlt9UaEiUBmdYjx6illesL2V7o+Mzm6Ab-p4geDqjDkZG4SvI8TbMnEOn3MQjR0hb4uoFSNGUkhMIoqKrnkByHioigQUoRRViqF6zPMN5Ni2bYdl2PC9v2qivvM75jkmhrJKmP4iH+AF4kUIFgRIEGomImKFkYyiSNo6h3EYigoeeTi0gyPqNmR95OE+UaqBA9KMqRLLkWCU5cjRs4SkwzH3Ioq56eYUHwqo2RboeoHOgYmjqIJ7zCfJYm3s2rZ9rhPZ9gOcmiYpvyfpRqmmg8BagauoHSroXHrqKaIElcRiHlxjRMCuTwnsSQkjDMuAQJhZHBEGUmkOGMkAhAo5KbAvkcmmv4IIozHGXUpYVGxByNKiFRMKoJz4icOSGGYYg2TWqiZdlvq3nlD7SS+ESlYmFUGBRVXUZkdWdQYjVdHurWos0ErLolJxcR05hWUNHqjTl5VOThnZuYRJVlZqlVUWpq31RtBhNdtrSotuEiqLpRR6IelklMeJ7uHJ8BJGltlgCp35vYgcbqKiqPnR86ruBNiMmjOnGaA0pilJomI6FKf11Pk+KtPiJTOkwg2pWe8OfLjKb+QTShqN1bHKOUOQC21UWaJZXV6V0G1MJoUopX0bps3WDmJnj1XqWDm73CYuiy0Ypiol9BZ4gN4raDomh8ZjTiXhh42q5zSOmpIUE3PkvH7KU7QmPo1sjCJjJXb8asrdCmilqofE63oEj69oUEVF1i6x7LBzStZLOK8Nl327lIfIwgZiKF1Nw3GWShJXsu31ZbiUErXuRnZn1YejqsxB3E+cBeHWvZKYMdxxu9TSvuYhNdKjR3FYVhAA */
|
||||
id: 'Home machine',
|
||||
|
||||
initial: 'Reading projects',
|
||||
|
||||
context: {
|
||||
context: ({ input }) => ({
|
||||
projects: [] as ProjectWithEntryPointMetadata[],
|
||||
defaultProjectName: '',
|
||||
defaultDirectory: '',
|
||||
},
|
||||
...input,
|
||||
}),
|
||||
|
||||
on: {
|
||||
assign: {
|
||||
actions: assign((_, event) => ({
|
||||
...event.data,
|
||||
})),
|
||||
target: '.Reading projects',
|
||||
},
|
||||
},
|
||||
states: {
|
||||
'Has no projects': {
|
||||
on: {
|
||||
@ -135,30 +161,9 @@ export const homeMachine = createMachine(
|
||||
entry: ['navigateToProject'],
|
||||
},
|
||||
},
|
||||
|
||||
schema: {
|
||||
events: {} as
|
||||
| { type: 'Open project'; data: HomeCommandSchema['Open project'] }
|
||||
| { type: 'Rename project'; data: HomeCommandSchema['Rename project'] }
|
||||
| { type: 'Create project'; data: HomeCommandSchema['Create project'] }
|
||||
| { type: 'Delete project'; data: HomeCommandSchema['Delete project'] }
|
||||
| { type: 'navigate'; data: { name: string } }
|
||||
| {
|
||||
type: 'done.invoke.read-projects'
|
||||
data: ProjectWithEntryPointMetadata[]
|
||||
}
|
||||
| { type: 'assign'; data: { [key: string]: any } },
|
||||
},
|
||||
|
||||
predictableActionArguments: true,
|
||||
preserveActionOrder: true,
|
||||
tsTypes: {} as import('./homeMachine.typegen').Typegen0,
|
||||
},
|
||||
{
|
||||
actions: {
|
||||
setProjects: assign((_, event) => {
|
||||
return { projects: event.data as ProjectWithEntryPointMetadata[] }
|
||||
}),
|
||||
},
|
||||
}
|
||||
)
|
||||
|
@ -39,12 +39,17 @@ import { isTauri } from 'lib/isTauri'
|
||||
import { kclManager } from 'lang/KclSingleton'
|
||||
import { useLspContext } from 'components/LspProvider'
|
||||
import { useValidateSettings } from 'hooks/useValidateSettings'
|
||||
import { AppMachineContext } from 'machines/appMachine'
|
||||
|
||||
// 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.
|
||||
const Home = () => {
|
||||
useValidateSettings()
|
||||
const { commandBarSend } = useCommandsContext()
|
||||
const home = AppMachineContext.useSelector(s => s.children.home)
|
||||
home?.on('done.invoke.read-projects', (e) => {
|
||||
// do some nonsense
|
||||
})
|
||||
const commands = AppMachineContext.useSelector(s => s.children.commands)
|
||||
const navigate = useNavigate()
|
||||
const { projects: loadedProjects } = useLoaderData() as HomeLoaderData
|
||||
const {
|
||||
|
23
yarn.lock
23
yarn.lock
@ -2868,10 +2868,10 @@
|
||||
"@babel/types" "^7.21.4"
|
||||
recast "^0.23.1"
|
||||
|
||||
"@xstate/react@^3.2.2":
|
||||
version "3.2.2"
|
||||
resolved "https://registry.yarnpkg.com/@xstate/react/-/react-3.2.2.tgz#ddf0f9d75e2c19375b1e1b7335e72cb99762aed8"
|
||||
integrity sha512-feghXWLedyq8JeL13yda3XnHPZKwYDN5HPBLykpLeuNpr9178tQd2/3d0NrH6gSd0sG5mLuLeuD+ck830fgzLQ==
|
||||
"@xstate/react@^4.1.0":
|
||||
version "4.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@xstate/react/-/react-4.1.0.tgz#369378951e1f7f9326700f65ed02847598aad704"
|
||||
integrity sha512-Fh89luCwuMXIVXIua67d8pNuVgdGpqke2jHfIIL+ZjkfNh6YFtPDSwNSZZDhdNUsOW1zZYSbtUzbC8MIUyTSHQ==
|
||||
dependencies:
|
||||
use-isomorphic-layout-effect "^1.1.2"
|
||||
use-sync-external-store "^1.0.0"
|
||||
@ -8372,10 +8372,10 @@ typed-array-length@^1.0.4:
|
||||
for-each "^0.3.3"
|
||||
is-typed-array "^1.1.9"
|
||||
|
||||
typescript@^5.2.2:
|
||||
version "5.3.2"
|
||||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.3.2.tgz#00d1c7c1c46928c5845c1ee8d0cc2791031d4c43"
|
||||
integrity sha512-6l+RyNy7oAHDfxC4FzSJcz9vnjTKxrLpDG5M2Vu4SHRVNg6xzqZp6LYSR9zjqQTu8DU/f5xwxUdADOkbrIX2gQ==
|
||||
typescript@^5.4.2:
|
||||
version "5.4.2"
|
||||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.4.2.tgz#0ae9cebcfae970718474fe0da2c090cad6577372"
|
||||
integrity sha512-+2/g0Fds1ERlP6JsakQQDXjZdZMM+rqpamFZJEKh4kwTIn3iDkgKtby0CeNd5ATNZ4Ry1ax15TMx0W2V+miizQ==
|
||||
|
||||
ua-parser-js@^1.0.35:
|
||||
version "1.0.35"
|
||||
@ -8912,11 +8912,16 @@ ws@^8.8.0:
|
||||
resolved "https://registry.yarnpkg.com/xstate/-/xstate-5.0.0-beta.54.tgz#d80f1a9e43ad883a65fc9b399161bd39633bd9bf"
|
||||
integrity sha512-BTnCPBQ2iTKe4uCnHEe1hNx6VTbXU+5mQGybSQHOjTLiBi4Ryi+tL9T6N1tmqagvM8rfl4XRfvndogfWCWcdpw==
|
||||
|
||||
xstate@^4.33.4, xstate@^4.38.2:
|
||||
xstate@^4.33.4:
|
||||
version "4.38.3"
|
||||
resolved "https://registry.yarnpkg.com/xstate/-/xstate-4.38.3.tgz#4e15e7ad3aa0ca1eea2010548a5379966d8f1075"
|
||||
integrity sha512-SH7nAaaPQx57dx6qvfcIgqKRXIh4L0A1iYEqim4s1u7c9VoCgzZc+63FY90AKU4ZzOC2cfJzTnpO4zK7fCUzzw==
|
||||
|
||||
xstate@^5.9.1:
|
||||
version "5.9.1"
|
||||
resolved "https://registry.yarnpkg.com/xstate/-/xstate-5.9.1.tgz#1f167fae423cadd362762e2ff697d84fd774979c"
|
||||
integrity sha512-85edx7iMqRJSRlEPevDwc98EWDYUlT5zEQ54AXuRVR+G76gFbcVTAUdtAeqOVxy8zYnUr9FBB5114iK6enljjw==
|
||||
|
||||
y18n@^5.0.5:
|
||||
version "5.0.8"
|
||||
resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55"
|
||||
|
Reference in New Issue
Block a user