chore: saving off skeleton

This commit is contained in:
Kevin Nadro
2025-04-09 09:46:30 -05:00
parent 5d0530257c
commit 0236926cc8
10 changed files with 282 additions and 178 deletions

View File

@ -20,6 +20,7 @@ import { MachineManagerProvider } from '@src/components/MachineManagerProvider'
import ModelingMachineProvider from '@src/components/ModelingMachineProvider'
import { OpenInDesktopAppHandler } from '@src/components/OpenInDesktopAppHandler'
import { ProjectsContextProvider } from '@src/components/ProjectsContextProvider'
import { SystemIOMachineLogicListener } from '@src/components/Providers/SystemIOProviderDesktop'
import { RouteProvider } from '@src/components/RouteProvider'
import { WasmErrBanner } from '@src/components/WasmErrBanner'
import { NetworkContext } from '@src/hooks/useNetworkContext'
@ -143,6 +144,7 @@ const router = createRouter([
path: PATHS.HOME,
element: (
<Auth>
<SystemIOMachineLogicListener />
<Outlet />
<Home />
<CommandBar />

View File

@ -229,6 +229,7 @@ const ProjectsContextDesktop = ({
const newPathName = `${PATHS.FILE}/${encodeURIComponent(
projectPath
)}`
console.log(newPathName,'asdf')
navigate(newPathName)
}
},

View File

@ -0,0 +1,22 @@
import { PATHS } from '@src/lib/paths'
import { systemIOActor } from '@src/machines/appMachine'
import { useSelector } from '@xstate/react'
import { useEffect } from 'react'
import { useNavigate } from 'react-router-dom'
import {
NO_PROJECT_DIRECTORY,
} from '@src/machines/systemIO/utils'
export const useAuthState = () => useSelector(systemIOActor, (state) => state)
export function SystemIOMachineLogicListener() {
const state = useAuthState()
useEffect(() => {
/* const requestedPath = `${PATHS.FILE}/${encodeURIComponent(
* requestedProjectName
)}`
* navigate(requestedPath) */
console.log(state)
}, [state])
return null
}

View File

@ -5,16 +5,14 @@ import { createSettings } from '@src/lib/settings/initialSettings'
import { authMachine } from '@src/machines/authMachine'
import { ACTOR_IDS } from '@src/machines/machineConstants'
import { settingsMachine } from '@src/machines/settingsMachine'
import { SystemIOMachineEvents } from "@src/machines/systemIO/utils"
import { systemIOMachineWeb} from "@src/machines/systemIO/systemIOMachineWeb"
import { systemIOMachineDesktop} from "@src/machines/systemIO/systemIOMachineDesktop"
import { systemIOMachine} from "@src/machines/systemIO/systemIOMachine"
import { systemIOMachineDesktop } from '@src/machines/systemIO/systemIOMachineDesktop'
import { SystemIOMachineEvents } from '@src/machines/systemIO/utils'
const { AUTH, SETTINGS, SYSTEM_IO } = ACTOR_IDS
const appMachineActors = {
[AUTH]: authMachine,
[SETTINGS]: settingsMachine,
[SYSTEM_IO]: systemIOMachineDesktop
[SYSTEM_IO]: systemIOMachineDesktop,
} as const
const appMachine = setup({
@ -35,7 +33,7 @@ const appMachine = setup({
systemId: SETTINGS,
input: createSettings(),
}),
spawnChild(SYSTEM_IO, {id: SYSTEM_IO, systemId: SYSTEM_IO})
spawnChild(SYSTEM_IO, { id: SYSTEM_IO, systemId: SYSTEM_IO }),
],
})
@ -71,10 +69,16 @@ export const useSettings = () =>
})
// TODO: Debugging
const systemIOActor = appActor.getSnapshot().children.systemIO!
export const systemIOActor = appActor.getSnapshot().children.systemIO!
// systemIOActor.send({type:SystemIOMachineEvents.readFoldersFromProjectDirectory, data: {}})
systemIOActor.send({type:SystemIOMachineEvents.setProjectDirectoryPath, data: {requestedProjectDirectoryPath:'/home/kevin-nadro/Documents/zoo-modeling-app-projects'}})
systemIOActor.send({
type: SystemIOMachineEvents.setProjectDirectoryPath,
data: {
requestedProjectDirectoryPath:
'/home/kevin-nadro/Documents/zoo-modeling-app-projects',
},
})
window.systemIOActor = systemIOActor

View File

@ -1,5 +1,5 @@
export const ACTOR_IDS = {
AUTH: 'auth',
SETTINGS: 'settings',
SYSTEM_IO: 'systemIO'
SYSTEM_IO: 'systemIO',
} as const

View File

@ -1,7 +1,15 @@
import { setup, fromPromise, assign, assertEvent} from 'xstate'
import { DEFAULT_PROJECT_NAME } from "@src/lib/constants"
import { DEFAULT_PROJECT_NAME } from '@src/lib/constants'
import type { Project } from '@src/lib/project'
import {SystemIOMachineEvents, SystemIOMachineActions, SystemIOMachineActors, SystemIOMachineStates, NO_PROJECT_DIRECTORY, SystemIOContext} from "@src/machines/systemIO/utils"
import type {
SystemIOContext} from '@src/machines/systemIO/utils'
import {
NO_PROJECT_DIRECTORY,
SystemIOMachineActions,
SystemIOMachineActors,
SystemIOMachineEvents,
SystemIOMachineStates,
} from '@src/machines/systemIO/utils'
import { assertEvent, assign, fromPromise, setup } from 'xstate'
/**
* Handles any system level I/O for folders and files
@ -13,31 +21,57 @@ export const systemIOMachine = setup({
types: {
context: {} as SystemIOContext,
events: {} as
| { type: SystemIOMachineEvents.readFoldersFromProjectDirectory; data: {} }
| { type: SystemIOMachineEvents.done_readFoldersFromProjectDirectory; data: {}, output: Project[] }
| { type: SystemIOMachineEvents.setProjectDirectoryPath; data: {requestedProjectDirectoryPath: string}}
| {
type: SystemIOMachineEvents.readFoldersFromProjectDirectory
data: {}
}
| {
type: SystemIOMachineEvents.done_readFoldersFromProjectDirectory
data: {}
output: Project[]
}
| {
type: SystemIOMachineEvents.setProjectDirectoryPath
data: { requestedProjectDirectoryPath: string }
}
| {
type: SystemIOMachineEvents.openProject
data: { requestedProjectName: string }
},
},
actions: {
[SystemIOMachineActions.setFolders]: assign({
folders:({event})=>{
assertEvent(event, SystemIOMachineEvents.done_readFoldersFromProjectDirectory)
folders: ({ event }) => {
assertEvent(
event,
SystemIOMachineEvents.done_readFoldersFromProjectDirectory
)
return event.output
}
},
}),
[SystemIOMachineActions.setProjectDirectoryPath]: assign({
projectDirectoryPath:({event})=>{
projectDirectoryPath: ({ event }) => {
assertEvent(event, SystemIOMachineEvents.setProjectDirectoryPath)
return event.data.requestedProjectDirectoryPath
}
})
},
}),
[SystemIOMachineActions.setRequestedProjectName]: assign({
requestedProjectName: ({ event }) => {
assertEvent(event, SystemIOMachineEvents.openProject)
console.log('event', event.data.requestedProjectName)
return event.data.requestedProjectName
},
}),
},
actors: {
[SystemIOMachineActors.readFoldersFromProjectDirectory]: fromPromise(async ({input:context}:{input:SystemIOContext}) => {
return []
}),
}
[SystemIOMachineActors.readFoldersFromProjectDirectory]: fromPromise(
async ({ input: context }: { input: SystemIOContext }) => {
return []
}
),
},
}).createMachine({
initial:SystemIOMachineStates.idle,
initial: SystemIOMachineStates.idle,
// Remember, this machine and change its projectDirectory at any point
// '' will be no project directory, aka clear this machine out!
// To be the aboslute root of someones computer we should take the string of path.resolve() in node.js which is different for each OS
@ -45,43 +79,46 @@ export const systemIOMachine = setup({
folders: [],
defaultProjectFolderName: DEFAULT_PROJECT_NAME,
projectDirectoryPath: NO_PROJECT_DIRECTORY,
hasListedProjects: false
hasListedProjects: false,
requestedProjectName: NO_PROJECT_DIRECTORY,
}),
states: {
[SystemIOMachineStates.idle]: {
on: {
// on can be an action
[SystemIOMachineEvents.readFoldersFromProjectDirectory]:SystemIOMachineStates.readingFolders,
[SystemIOMachineEvents.setProjectDirectoryPath]:{
[SystemIOMachineEvents.readFoldersFromProjectDirectory]:
SystemIOMachineStates.readingFolders,
[SystemIOMachineEvents.setProjectDirectoryPath]: {
target: SystemIOMachineStates.readingFolders,
actions: [
SystemIOMachineActions.setProjectDirectoryPath
]
}
}
actions: [SystemIOMachineActions.setProjectDirectoryPath],
},
[SystemIOMachineEvents.openProject]: {
target: SystemIOMachineStates.openingProject,
actions: [SystemIOMachineActions.setRequestedProjectName],
},
},
},
[SystemIOMachineStates.readingFolders]: {
invoke: {
id: SystemIOMachineActors.readFoldersFromProjectDirectory,
src: SystemIOMachineActors.readFoldersFromProjectDirectory,
input: ({context}) => {
input: ({ context }) => {
return context
},
onDone: {
target: SystemIOMachineStates.idle,
actions: [
SystemIOMachineActions.setFolders
]
actions: [SystemIOMachineActions.setFolders],
},
onError: {
target: SystemIOMachineStates.idle,
}
}
},
},
},
}
[SystemIOMachineStates.openingProject] : {
}
},
})
// Watcher handler
// look at projectDirectory useEffect then send this event if it changes or if we need to do this?
// The handler needs to live somewhere... aka the provider?

View File

@ -1,47 +1,58 @@
import { SystemIOMachineEvents, SystemIOMachineActors, SystemIOContext, NO_PROJECT_DIRECTORY} from "@src/machines/systemIO/utils"
import { systemIOMachine} from "@src/machines/systemIO/systemIOMachine"
import { setup, fromPromise, assign, assertEvent} from 'xstate'
import { mkdirOrNOOP, getProjectInfo } from "@src/lib/desktop"
import { getProjectInfo, mkdirOrNOOP } from '@src/lib/desktop'
import type { Project } from '@src/lib/project'
import { systemIOMachine } from '@src/machines/systemIO/systemIOMachine'
import type {
SystemIOContext} from '@src/machines/systemIO/utils'
import {
NO_PROJECT_DIRECTORY,
SystemIOMachineActors,
} from '@src/machines/systemIO/utils'
import { fromPromise } from 'xstate'
export const systemIOMachineDesktop = systemIOMachine.provide({
actors: {
[SystemIOMachineActors.readFoldersFromProjectDirectory]: fromPromise(async ({input:context}:{input:SystemIOContext}) => {
const projects = []
const projectDirectoryPath = context.projectDirectoryPath
if (projectDirectoryPath === NO_PROJECT_DIRECTORY) {
// TODO
return []
}
await mkdirOrNOOP(projectDirectoryPath)
// Gotcha: readdir will list all folders at this project directory even if you do not have readwrite access on the directory path
const entries = await window.electron.readdir(projectDirectoryPath)
const { value: canReadWriteProjectDirectory } = await window.electron.canReadWriteDirectory(projectDirectoryPath)
[SystemIOMachineActors.readFoldersFromProjectDirectory]: fromPromise(
async ({ input: context }: { input: SystemIOContext }) => {
const projects = []
const projectDirectoryPath = context.projectDirectoryPath
if (projectDirectoryPath === NO_PROJECT_DIRECTORY) {
// TODO
return []
}
await mkdirOrNOOP(projectDirectoryPath)
// Gotcha: readdir will list all folders at this project directory even if you do not have readwrite access on the directory path
const entries = await window.electron.readdir(projectDirectoryPath)
const { value: canReadWriteProjectDirectory } =
await window.electron.canReadWriteDirectory(projectDirectoryPath)
for (let entry of entries) {
// Skip directories that start with a dot
if (entry.startsWith('.')) {
continue
}
const projectPath = window.electron.path.join(projectDirectoryPath, entry)
for (let entry of entries) {
// Skip directories that start with a dot
if (entry.startsWith('.')) {
continue
}
const projectPath = window.electron.path.join(
projectDirectoryPath,
entry
)
// if it's not a directory ignore.
// Gotcha: statIsDirectory will work even if you do not have read write permissions on the project path
const isDirectory = await window.electron.statIsDirectory(projectPath)
if (!isDirectory) {
continue
// if it's not a directory ignore.
// Gotcha: statIsDirectory will work even if you do not have read write permissions on the project path
const isDirectory = await window.electron.statIsDirectory(projectPath)
if (!isDirectory) {
continue
}
const project: Project = await getProjectInfo(projectPath)
if (
project.kcl_file_count === 0 &&
project.readWriteAccess &&
canReadWriteProjectDirectory
) {
continue
}
projects.push(project)
}
const project : Project = await getProjectInfo(projectPath)
if (
project.kcl_file_count === 0 &&
project.readWriteAccess &&
canReadWriteProjectDirectory
) {
continue
}
projects.push(project)
return projects
}
return projects
})
}
),
},
})

View File

@ -1,14 +1,20 @@
import { SystemIOMachineEvents, SystemIOMachineActors, SystemIOContext} from "@src/machines/systemIO/utils"
import { systemIOMachine} from "@src/machines/systemIO/systemIOMachine"
import { setup, fromPromise, assign, assertEvent} from 'xstate'
import type { Project } from '@src/lib/project'
import { systemIOMachine } from '@src/machines/systemIO/systemIOMachine'
import type {
SystemIOContext} from '@src/machines/systemIO/utils'
import {
SystemIOMachineActors,
} from '@src/machines/systemIO/utils'
import { fromPromise } from 'xstate'
export const systemIOMachineWeb = systemIOMachine.provide({
actors: {
[SystemIOMachineActors.readFoldersFromProjectDirectory]: fromPromise(async ({input:context}:{input:SystemIOContext}) => {
const projects: Project[] = []
console.log('nothing!')
return projects
})
}
[SystemIOMachineActors.readFoldersFromProjectDirectory]: fromPromise(
async ({ input: context }: { input: SystemIOContext }) => {
const projects: Project[] = []
console.log('nothing!')
return projects
}
),
},
})

View File

@ -1,40 +1,45 @@
import type { Project } from '@src/lib/project'
export enum SystemIOMachineActors {
readFoldersFromProjectDirectory = "read folders from project directory",
setProjectDirectoryPath = "set project directory path"
readFoldersFromProjectDirectory = 'read folders from project directory',
setProjectDirectoryPath = 'set project directory path',
}
export enum SystemIOMachineStates {
idle = "idle",
readingFolders = "readingFolders",
settingProjectDirectoryPath = "settingProjectDirectoryPath"
idle = 'idle',
readingFolders = 'readingFolders',
settingProjectDirectoryPath = 'settingProjectDirectoryPath',
openingProject = 'openingProject'
}
const donePrefix = 'xstate.done.actor.'
export enum SystemIOMachineEvents {
readFoldersFromProjectDirectory = "read folders from project directory",
done_readFoldersFromProjectDirectory = donePrefix + "read folders from project directory",
setProjectDirectoryPath = "set project directory path",
readFoldersFromProjectDirectory = 'read folders from project directory',
done_readFoldersFromProjectDirectory = donePrefix +
'read folders from project directory',
setProjectDirectoryPath = 'set project directory path',
openProject = 'open project',
}
export enum SystemIOMachineActions {
setFolders = "set folders",
setProjectDirectoryPath = "set project directory path"
setFolders = 'set folders',
setProjectDirectoryPath = 'set project directory path',
setRequestedProjectName = 'set requested project name',
}
export const NO_PROJECT_DIRECTORY = ''
export type SystemIOContext = {
// Only store folders under the projectDirectory, do not maintain folders outside this directory
folders: Project[],
folders: Project[]
// For this machines runtime, this is the default string when creating a project
// A project is defined by creating a folder at the one level below the working project directory
defaultProjectFolderName:string,
defaultProjectFolderName: string
// working project directory that stores all the project folders
projectDirectoryPath: string,
projectDirectoryPath: string
// has the application gone through the initialiation of systemIOMachine at least once.
// this is required to prevent chokidar from spamming invalid events during initialization.
hasListedProjects: boolean
requestedProjectName: string
}

View File

@ -1,42 +1,43 @@
import { setup, fromPromise, assign, assertEvent} from 'xstate'
import { DEFAULT_PROJECT_NAME } from "@src/lib/constants"
import { mkdirOrNOOP, getProjectInfo } from "@src/lib/desktop"
import { DEFAULT_PROJECT_NAME } from '@src/lib/constants'
import { getProjectInfo, mkdirOrNOOP } from '@src/lib/desktop'
import type { Project } from '@src/lib/project'
import { assertEvent, assign, fromPromise, setup } from 'xstate'
export enum SystemIOMachineActors {
readFoldersFromProjectDirectory = "read folders from project directory",
setProjectDirectoryPath = "set project directory path"
readFoldersFromProjectDirectory = 'read folders from project directory',
setProjectDirectoryPath = 'set project directory path',
}
export enum SystemIOMachineStates {
idle = "idle",
readingFolders = "readingFolders",
settingProjectDirectoryPath = "settingProjectDirectoryPath"
idle = 'idle',
readingFolders = 'readingFolders',
settingProjectDirectoryPath = 'settingProjectDirectoryPath',
}
const donePrefix = 'xstate.done.actor.'
export enum SystemIOMachineEvents {
readFoldersFromProjectDirectory = "read folders from project directory",
done_readFoldersFromProjectDirectory = donePrefix + "read folders from project directory",
setProjectDirectoryPath = "set project directory path",
readFoldersFromProjectDirectory = 'read folders from project directory',
done_readFoldersFromProjectDirectory = donePrefix +
'read folders from project directory',
setProjectDirectoryPath = 'set project directory path',
}
export enum SystemIOMachineActions {
setFolders = "set folders",
setProjectDirectoryPath = "set project directory path"
setFolders = 'set folders',
setProjectDirectoryPath = 'set project directory path',
}
const NO_PROJECT_DIRECTORY = ''
export type SystemIOContext = {
// Only store folders under the projectDirectory, do not maintain folders outside this directory
folders: Project[],
folders: Project[]
// For this machines runtime, this is the default string when creating a project
// A project is defined by creating a folder at the one level below the working project directory
defaultProjectFolderName:string,
defaultProjectFolderName: string
// working project directory that stores all the project folders
projectDirectoryPath: string,
projectDirectoryPath: string
// has the application gone through the initialiation of systemIOMachine at least once.
// this is required to prevent chokidar from spamming invalid events during initialization.
hasListedProjects: boolean
@ -52,65 +53,84 @@ export const systemIOMachine = setup({
types: {
context: {} as SystemIOContext,
events: {} as
| { type: SystemIOMachineEvents.readFoldersFromProjectDirectory; data: {} }
| { type: SystemIOMachineEvents.done_readFoldersFromProjectDirectory; data: {}, output: Project[] }
| { type: SystemIOMachineEvents.setProjectDirectoryPath; data: {requestedProjectDirectoryPath: string}}
| {
type: SystemIOMachineEvents.readFoldersFromProjectDirectory
data: {}
}
| {
type: SystemIOMachineEvents.done_readFoldersFromProjectDirectory
data: {}
output: Project[]
}
| {
type: SystemIOMachineEvents.setProjectDirectoryPath
data: { requestedProjectDirectoryPath: string }
},
},
actions: {
[SystemIOMachineActions.setFolders]: assign({
folders:({event})=>{
assertEvent(event, SystemIOMachineEvents.done_readFoldersFromProjectDirectory)
folders: ({ event }) => {
assertEvent(
event,
SystemIOMachineEvents.done_readFoldersFromProjectDirectory
)
return event.output
}
},
}),
[SystemIOMachineActions.setProjectDirectoryPath]: assign({
projectDirectoryPath:({event})=>{
projectDirectoryPath: ({ event }) => {
assertEvent(event, SystemIOMachineEvents.setProjectDirectoryPath)
return event.data.requestedProjectDirectoryPath
}
})
},
}),
},
actors: {
[SystemIOMachineActors.readFoldersFromProjectDirectory]: fromPromise(async ({input:context}:{input:SystemIOContext}) => {
const projects = []
const projectDirectoryPath = context.projectDirectoryPath
if (projectDirectoryPath === NO_PROJECT_DIRECTORY) {
// TODO
return []
}
await mkdirOrNOOP(projectDirectoryPath)
// Gotcha: readdir will list all folders at this project directory even if you do not have readwrite access on the directory path
const entries = await window.electron.readdir(projectDirectoryPath)
const { value: canReadWriteProjectDirectory } = await window.electron.canReadWriteDirectory(projectDirectoryPath)
[SystemIOMachineActors.readFoldersFromProjectDirectory]: fromPromise(
async ({ input: context }: { input: SystemIOContext }) => {
const projects = []
const projectDirectoryPath = context.projectDirectoryPath
if (projectDirectoryPath === NO_PROJECT_DIRECTORY) {
// TODO
return []
}
await mkdirOrNOOP(projectDirectoryPath)
// Gotcha: readdir will list all folders at this project directory even if you do not have readwrite access on the directory path
const entries = await window.electron.readdir(projectDirectoryPath)
const { value: canReadWriteProjectDirectory } =
await window.electron.canReadWriteDirectory(projectDirectoryPath)
for (let entry of entries) {
// Skip directories that start with a dot
if (entry.startsWith('.')) {
continue
}
const projectPath = window.electron.path.join(projectDirectoryPath, entry)
for (let entry of entries) {
// Skip directories that start with a dot
if (entry.startsWith('.')) {
continue
}
const projectPath = window.electron.path.join(
projectDirectoryPath,
entry
)
// if it's not a directory ignore.
// Gotcha: statIsDirectory will work even if you do not have read write permissions on the project path
const isDirectory = await window.electron.statIsDirectory(projectPath)
if (!isDirectory) {
continue
// if it's not a directory ignore.
// Gotcha: statIsDirectory will work even if you do not have read write permissions on the project path
const isDirectory = await window.electron.statIsDirectory(projectPath)
if (!isDirectory) {
continue
}
const project: Project = await getProjectInfo(projectPath)
if (
project.kcl_file_count === 0 &&
project.readWriteAccess &&
canReadWriteProjectDirectory
) {
continue
}
projects.push(project)
}
const project : Project = await getProjectInfo(projectPath)
if (
project.kcl_file_count === 0 &&
project.readWriteAccess &&
canReadWriteProjectDirectory
) {
continue
}
projects.push(project)
return projects
}
return projects
}),
}
),
},
}).createMachine({
initial:SystemIOMachineStates.idle,
initial: SystemIOMachineStates.idle,
// Remember, this machine and change its projectDirectory at any point
// '' will be no project directory, aka clear this machine out!
// To be the aboslute root of someones computer we should take the string of path.resolve() in node.js which is different for each OS
@ -118,43 +138,39 @@ export const systemIOMachine = setup({
folders: [],
defaultProjectFolderName: DEFAULT_PROJECT_NAME,
projectDirectoryPath: NO_PROJECT_DIRECTORY,
hasListedProjects: false
hasListedProjects: false,
}),
states: {
[SystemIOMachineStates.idle]: {
on: {
// on can be an action
[SystemIOMachineEvents.readFoldersFromProjectDirectory]:SystemIOMachineStates.readingFolders,
[SystemIOMachineEvents.setProjectDirectoryPath]:{
[SystemIOMachineEvents.readFoldersFromProjectDirectory]:
SystemIOMachineStates.readingFolders,
[SystemIOMachineEvents.setProjectDirectoryPath]: {
target: SystemIOMachineStates.readingFolders,
actions: [
SystemIOMachineActions.setProjectDirectoryPath
]
}
}
actions: [SystemIOMachineActions.setProjectDirectoryPath],
},
},
},
[SystemIOMachineStates.readingFolders]: {
invoke: {
id: SystemIOMachineActors.readFoldersFromProjectDirectory,
src: SystemIOMachineActors.readFoldersFromProjectDirectory,
input: ({context}) => {
input: ({ context }) => {
return context
},
onDone: {
target: SystemIOMachineStates.idle,
actions: [
SystemIOMachineActions.setFolders
]
actions: [SystemIOMachineActions.setFolders],
},
onError: {
target: SystemIOMachineStates.idle,
}
}
},
},
},
}
},
})
// Watcher handler
// look at projectDirectory useEffect then send this event if it changes or if we need to do this?
// The handler needs to live somewhere... aka the provider?