2025-04-24 13:32:49 -05:00
|
|
|
import {
|
|
|
|
createNewProjectDirectory,
|
|
|
|
getProjectInfo,
|
|
|
|
mkdirOrNOOP,
|
|
|
|
readAppSettingsFile,
|
|
|
|
renameProjectDirectory,
|
|
|
|
} from '@src/lib/desktop'
|
|
|
|
import {
|
|
|
|
doesProjectNameNeedInterpolated,
|
|
|
|
getNextFileName,
|
|
|
|
getNextProjectIndex,
|
|
|
|
getUniqueProjectName,
|
|
|
|
interpolateProjectNameWithIndex,
|
|
|
|
} from '@src/lib/desktopFS'
|
|
|
|
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'
|
|
|
|
import type { AppMachineContext } from '@src/lib/types'
|
|
|
|
|
|
|
|
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) {
|
|
|
|
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
|
|
|
|
)
|
|
|
|
|
|
|
|
// 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)
|
|
|
|
}
|
|
|
|
return projects
|
|
|
|
}
|
|
|
|
),
|
|
|
|
[SystemIOMachineActors.createProject]: fromPromise(
|
|
|
|
async ({
|
|
|
|
input,
|
|
|
|
}: {
|
|
|
|
input: { context: SystemIOContext; requestedProjectName: string }
|
|
|
|
}) => {
|
|
|
|
const folders = input.context.folders
|
|
|
|
const requestedProjectName = input.requestedProjectName
|
|
|
|
const uniqueName = getUniqueProjectName(requestedProjectName, folders)
|
|
|
|
await createNewProjectDirectory(uniqueName)
|
|
|
|
return {
|
|
|
|
message: `Successfully created "${uniqueName}"`,
|
|
|
|
name: uniqueName,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
),
|
|
|
|
[SystemIOMachineActors.renameProject]: fromPromise(
|
|
|
|
async ({
|
|
|
|
input,
|
|
|
|
}: {
|
|
|
|
input: {
|
|
|
|
context: SystemIOContext
|
|
|
|
requestedProjectName: string
|
|
|
|
projectName: string
|
|
|
|
}
|
|
|
|
}) => {
|
|
|
|
const folders = input.context.folders
|
|
|
|
const requestedProjectName = input.requestedProjectName
|
|
|
|
const projectName = input.projectName
|
|
|
|
let newProjectName: string = requestedProjectName
|
|
|
|
if (doesProjectNameNeedInterpolated(requestedProjectName)) {
|
|
|
|
const nextIndex = getNextProjectIndex(requestedProjectName, folders)
|
|
|
|
newProjectName = interpolateProjectNameWithIndex(
|
|
|
|
requestedProjectName,
|
|
|
|
nextIndex
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Toast an error if the project name is taken
|
|
|
|
if (folders.find((p) => p.name === newProjectName)) {
|
|
|
|
return Promise.reject(
|
|
|
|
new Error(`Project with name "${newProjectName}" already exists`)
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
await renameProjectDirectory(
|
|
|
|
window.electron.path.join(
|
|
|
|
input.context.projectDirectoryPath,
|
|
|
|
projectName
|
|
|
|
),
|
|
|
|
newProjectName
|
|
|
|
)
|
|
|
|
|
|
|
|
return {
|
|
|
|
message: `Successfully renamed "${projectName}" to "${newProjectName}"`,
|
|
|
|
oldName: projectName,
|
|
|
|
newName: newProjectName,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
),
|
|
|
|
[SystemIOMachineActors.deleteProject]: fromPromise(
|
|
|
|
async ({
|
|
|
|
input,
|
|
|
|
}: {
|
|
|
|
input: { context: SystemIOContext; requestedProjectName: string }
|
|
|
|
}) => {
|
|
|
|
await window.electron.rm(
|
|
|
|
window.electron.path.join(
|
|
|
|
input.context.projectDirectoryPath,
|
|
|
|
input.requestedProjectName
|
|
|
|
),
|
|
|
|
{
|
|
|
|
recursive: true,
|
|
|
|
}
|
|
|
|
)
|
|
|
|
|
|
|
|
return {
|
|
|
|
message: `Successfully deleted "${input.requestedProjectName}"`,
|
|
|
|
name: input.requestedProjectName,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
),
|
|
|
|
[SystemIOMachineActors.createKCLFile]: fromPromise(
|
|
|
|
async ({
|
|
|
|
input,
|
|
|
|
}: {
|
|
|
|
input: {
|
|
|
|
context: SystemIOContext
|
|
|
|
requestedProjectName: string
|
|
|
|
requestedFileName: string
|
|
|
|
requestedCode: string
|
|
|
|
rootContext: AppMachineContext
|
2025-05-02 15:54:49 -04:00
|
|
|
requestedSubRoute?: string
|
2025-04-24 13:32:49 -05:00
|
|
|
}
|
|
|
|
}) => {
|
|
|
|
const requestedProjectName = input.requestedProjectName
|
|
|
|
const requestedFileName = input.requestedFileName
|
|
|
|
const requestedCode = input.requestedCode
|
|
|
|
const folders = input.context.folders
|
|
|
|
|
|
|
|
let newProjectName = requestedProjectName
|
|
|
|
|
|
|
|
if (!newProjectName) {
|
|
|
|
newProjectName = getUniqueProjectName(
|
|
|
|
input.context.defaultProjectFolderName,
|
|
|
|
input.context.folders
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
const needsInterpolated =
|
|
|
|
doesProjectNameNeedInterpolated(newProjectName)
|
|
|
|
if (needsInterpolated) {
|
|
|
|
const nextIndex = getNextProjectIndex(newProjectName, folders)
|
|
|
|
newProjectName = interpolateProjectNameWithIndex(
|
|
|
|
newProjectName,
|
|
|
|
nextIndex
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
const baseDir = window.electron.join(
|
|
|
|
input.context.projectDirectoryPath,
|
|
|
|
newProjectName
|
|
|
|
)
|
|
|
|
const { name: newFileName } = getNextFileName({
|
|
|
|
entryName: requestedFileName,
|
|
|
|
baseDir,
|
|
|
|
})
|
|
|
|
const configuration = await readAppSettingsFile()
|
|
|
|
|
|
|
|
// Create the project around the file if newProject
|
|
|
|
await createNewProjectDirectory(
|
|
|
|
newProjectName,
|
|
|
|
requestedCode,
|
|
|
|
configuration,
|
|
|
|
newFileName
|
|
|
|
)
|
|
|
|
|
|
|
|
return {
|
|
|
|
message: 'File created successfully',
|
[Feature]: Load external model becomes Add file to project, global application add file to project with home page update. (#6506)
* chore: saving off skeleton
* fix: saving skeleton
* chore: skeleton for loading projects from project directory path
* chore: cleaning up useless state transition to be an on event direct to action state
* fix: new structure for web vs desktop vs react machine provider code
* chore: saving off skeleton
* fix: skeleton logic for react? going to move it from a string to obj.string
* fix: trying to prevent error element unmount on global react components. This is bricking JS state
* fix: we are so back
* chore: implemented navigating to specfic KCL file
* chore: implementing renaming project
* chore: deleting project
* fix: auto fixes
* fix: old debug/testing file oops
* chore: generic create new file
* chore: skeleton for web create file provide
* chore: basic machine vitest... need to figure out how to get window.electron implemented in vitest?
* chore: save off progress before deleting other project implementation, a few missing features still
* chore: trying a different init skeleton? most likely will migrate
* chore: first attempt of purging projects context provider
* chore: enabling toast for some machine state
* chore: enabling more toast success and error
* chore: writing read write state to the system io based on the project path
* fix: tsc fixes
* fix: use file system watcher, navigate to project after creation via the requestProjectName
* chore: open project command, hooks vs snapshot context helpers
* chore: implemented open and create project for e2e testing. They are hard coded in poor spot for now.
* fix: codespell fixes
* chore: implementing more project commands
* chore: PR improvements for root.tsx
* chore: leaving comment about new Router.tsx layout
* fix: removing debugging code
* fix: rewriting component for readability
* fix: improving web initialization
* chore: implementing import file from url which is not actually that?
* fix: clearing search params on import file from url
* fix: fixed two e2e tests, forgot needsReview when making new command
* fix: fixing some import from url business logic to pass e2e tests
* chore: script for diffing circular deps +/-
* fix: formatting
* fix: massive fix for circular depsga!
* fix: trying to fix some errors and auto fmt
* fix: updating deps
* fix: removing debugging code
* fix: big clean up
* fix: more deletion
* fix: tsc cleanup
* fix: TSC TSC TSC TSC!
* fix: typo fix
* fix: clear query params on web only, desktop not required
* fix: removing unused code
* fmt
* Bring back `trap` removed in merge
* Use explicit types instead of `any`s on arg configs
* Add project commands directly to command palette
* fix: deleting debugging code, from PR review
* fix: this got added back(?)
* fix: using referred type
* fix: more PR clean up
* fix: big block comment for xstate architecture decision
* fix: more pr comment fixes
* fix: saving off logic, need a big cleanup because I hacked it together to get a POC
* fix: extra business?
* fix: merge conflict just added them back why dude
* fix: more PR comments
* fix: big ciruclar deps fix, commandBarActor in appActor
* chore: writing e2e test, still need to fix 3 bugs
* chore: adding more scenarios
* fix: formatting
* fix: fixing tsc errors
* chore: deleting the old text to cad and using the new application level one, almost there
* fix: prompt to edit works
* fix: large push to get 1 text to cad command... the usage is a little buggy with delete and navigate within /file
* fix: settings for highlight edges now works
* chore: adding another e2e test
* fix: cleaning up e2e tests and writing more of them
* fix: tsc type
* chore: more e2e improvements, unique project name in text to cad
* chore: e2e tests should be good to go
* fix: gotcha comment
* fix: enabled web t2c, codespell fixes
* fix: fixing merge conflcits??
* feat: implemented load external for kcl samples
* feat: load external model from disk
* fix: trying to delete old stuff
* fix: all command trigger locations now have defaults for current project
* fix: gotcha comment for the future
* chore: hiding import file from url command, two separate commands for 3d and kcl file adding
* chore: commands are now add file to project, 3rd iteration
* fix: t2c in file menu fixed
* chore: updating file menu for new global actions
* fix: auto fixes
* fix: the command bar arg flow for web add kcl file seems backwards?
* chore: updated home layout, added create from kcl sample button
* chore: remapping some menu actions
* fix: fixing open dialog copy
* fix: an e2e test
* fix: fixed e2e tests
* fix: fixed e2e tests
* fix: auto fixes
* fix: pr clean up
* fix: removing console log
* fix: PR updates
* fix: the reviewed stage boolean required the expected state to change. Also I progressed the command bar too soon
* fix: no idea how this passed locally yesterday? I removed the {dir} unused but I need the function's logic but not the return value...
* fix: should be good to go?
---------
Co-authored-by: Frank Noirot <frankjohnson1993@gmail.com>
2025-04-29 13:04:45 -05:00
|
|
|
fileName: newFileName,
|
2025-04-24 13:32:49 -05:00
|
|
|
projectName: newProjectName,
|
2025-05-02 15:54:49 -04:00
|
|
|
subRoute: input.requestedSubRoute || '',
|
2025-04-24 13:32:49 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
),
|
|
|
|
[SystemIOMachineActors.checkReadWrite]: fromPromise(
|
|
|
|
async ({
|
|
|
|
input,
|
|
|
|
}: {
|
|
|
|
input: {
|
|
|
|
context: SystemIOContext
|
|
|
|
requestedProjectDirectoryPath: string
|
|
|
|
}
|
|
|
|
}) => {
|
|
|
|
const requestProjectDirectoryPath = input.requestedProjectDirectoryPath
|
|
|
|
if (!requestProjectDirectoryPath) {
|
|
|
|
return { value: true, error: undefined }
|
|
|
|
}
|
|
|
|
const result = await window.electron.canReadWriteDirectory(
|
|
|
|
requestProjectDirectoryPath
|
|
|
|
)
|
|
|
|
return result
|
|
|
|
}
|
|
|
|
),
|
[Feature]: Enable Text-to-CAD at the application level (#6501)
* chore: saving off skeleton
* fix: saving skeleton
* chore: skeleton for loading projects from project directory path
* chore: cleaning up useless state transition to be an on event direct to action state
* fix: new structure for web vs desktop vs react machine provider code
* chore: saving off skeleton
* fix: skeleton logic for react? going to move it from a string to obj.string
* fix: trying to prevent error element unmount on global react components. This is bricking JS state
* fix: we are so back
* chore: implemented navigating to specfic KCL file
* chore: implementing renaming project
* chore: deleting project
* fix: auto fixes
* fix: old debug/testing file oops
* chore: generic create new file
* chore: skeleton for web create file provide
* chore: basic machine vitest... need to figure out how to get window.electron implemented in vitest?
* chore: save off progress before deleting other project implementation, a few missing features still
* chore: trying a different init skeleton? most likely will migrate
* chore: first attempt of purging projects context provider
* chore: enabling toast for some machine state
* chore: enabling more toast success and error
* chore: writing read write state to the system io based on the project path
* fix: tsc fixes
* fix: use file system watcher, navigate to project after creation via the requestProjectName
* chore: open project command, hooks vs snapshot context helpers
* chore: implemented open and create project for e2e testing. They are hard coded in poor spot for now.
* fix: codespell fixes
* chore: implementing more project commands
* chore: PR improvements for root.tsx
* chore: leaving comment about new Router.tsx layout
* fix: removing debugging code
* fix: rewriting component for readability
* fix: improving web initialization
* chore: implementing import file from url which is not actually that?
* fix: clearing search params on import file from url
* fix: fixed two e2e tests, forgot needsReview when making new command
* fix: fixing some import from url business logic to pass e2e tests
* chore: script for diffing circular deps +/-
* fix: formatting
* fix: massive fix for circular depsga!
* fix: trying to fix some errors and auto fmt
* fix: updating deps
* fix: removing debugging code
* fix: big clean up
* fix: more deletion
* fix: tsc cleanup
* fix: TSC TSC TSC TSC!
* fix: typo fix
* fix: clear query params on web only, desktop not required
* fix: removing unused code
* fmt
* Bring back `trap` removed in merge
* Use explicit types instead of `any`s on arg configs
* Add project commands directly to command palette
* fix: deleting debugging code, from PR review
* fix: this got added back(?)
* fix: using referred type
* fix: more PR clean up
* fix: big block comment for xstate architecture decision
* fix: more pr comment fixes
* fix: saving off logic, need a big cleanup because I hacked it together to get a POC
* fix: extra business?
* fix: merge conflict just added them back why dude
* fix: more PR comments
* fix: big ciruclar deps fix, commandBarActor in appActor
* chore: writing e2e test, still need to fix 3 bugs
* chore: adding more scenarios
* fix: formatting
* fix: fixing tsc errors
* chore: deleting the old text to cad and using the new application level one, almost there
* fix: prompt to edit works
* fix: large push to get 1 text to cad command... the usage is a little buggy with delete and navigate within /file
* fix: settings for highlight edges now works
* chore: adding another e2e test
* fix: cleaning up e2e tests and writing more of them
* fix: tsc type
* chore: more e2e improvements, unique project name in text to cad
* chore: e2e tests should be good to go
* fix: gotcha comment
* fix: enabled web t2c, codespell fixes
* fix: fixing merge conflcits??
* fix: t2c is back
* Remove spaces in command bar test
* fmt
---------
Co-authored-by: Frank Noirot <frankjohnson1993@gmail.com>
Co-authored-by: lee-at-zoo-corp <lee@zoo.dev>
2025-04-25 18:04:47 -05:00
|
|
|
[SystemIOMachineActors.deleteKCLFile]: fromPromise(
|
|
|
|
async ({
|
|
|
|
input,
|
|
|
|
}: {
|
|
|
|
input: {
|
|
|
|
context: SystemIOContext
|
|
|
|
requestedProjectName: string
|
|
|
|
requestedFileName: string
|
|
|
|
}
|
|
|
|
}) => {
|
|
|
|
const path = window.electron.path.join(
|
|
|
|
input.context.projectDirectoryPath,
|
|
|
|
input.requestedProjectName,
|
|
|
|
input.requestedFileName
|
|
|
|
)
|
|
|
|
await window.electron.rm(path)
|
|
|
|
return {
|
|
|
|
message: 'File deleted successfully',
|
|
|
|
projectName: input.requestedProjectName,
|
|
|
|
fileName: input.requestedFileName,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
),
|
2025-04-24 13:32:49 -05:00
|
|
|
},
|
|
|
|
})
|