Compare commits
8 Commits
wait-for-r
...
pierremtb/
Author | SHA1 | Date | |
---|---|---|---|
0573801381 | |||
140e8b56fb | |||
4d203b0c0c | |||
04d6fcf0c4 | |||
30b2a765fc | |||
8ddbb488d6 | |||
db82a54f27 | |||
077d1cfcef |
@ -14,8 +14,13 @@ import { IS_ML_EXPERIMENTAL, PROJECT_ENTRYPOINT } from '@src/lib/constants'
|
||||
import toast from 'react-hot-toast'
|
||||
import { reportRejection } from '@src/lib/trap'
|
||||
import { relevantFileExtensions } from '@src/lang/wasmUtils'
|
||||
import { getStringAfterLastSeparator, webSafePathSplit } from '@src/lib/paths'
|
||||
import {
|
||||
getStringAfterLastSeparator,
|
||||
joinOSPaths,
|
||||
webSafePathSplit,
|
||||
} from '@src/lib/paths'
|
||||
import { FILE_EXT } from '@src/lib/constants'
|
||||
import { getAllSubDirectoriesAtProjectRoot } from '@src/machines/systemIO/snapshotContext'
|
||||
|
||||
function onSubmitKCLSampleCreation({
|
||||
sample,
|
||||
@ -87,6 +92,26 @@ function onSubmitKCLSampleCreation({
|
||||
},
|
||||
})
|
||||
} else {
|
||||
/**
|
||||
* When adding assemblies to an existing project create the assembly into a unique sub directory
|
||||
*/
|
||||
if (!isProjectNew) {
|
||||
requestedFiles.forEach((requestedFile) => {
|
||||
const subDirectoryName = projectPathPart
|
||||
const firstLevelDirectories = getAllSubDirectoriesAtProjectRoot({
|
||||
projectFolderName: requestedFile.requestedProjectName,
|
||||
})
|
||||
const uniqueSubDirectoryName = getUniqueProjectName(
|
||||
subDirectoryName,
|
||||
firstLevelDirectories
|
||||
)
|
||||
requestedFile.requestedProjectName = joinOSPaths(
|
||||
requestedFile.requestedProjectName,
|
||||
uniqueSubDirectoryName
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Bulk create the assembly and navigate to the project
|
||||
*/
|
||||
@ -278,8 +303,7 @@ export function createApplicationCommands({
|
||||
return value
|
||||
},
|
||||
options: ({ argumentsToSubmit }) => {
|
||||
const samples =
|
||||
isDesktop() && argumentsToSubmit.method !== 'existingProject'
|
||||
const samples = isDesktop()
|
||||
? everyKclSample
|
||||
: kclSamplesManifestWithNoMultipleFiles
|
||||
return samples.map((sample) => {
|
||||
@ -296,17 +320,10 @@ export function createApplicationCommands({
|
||||
skip: true,
|
||||
options: ({ argumentsToSubmit }, _) => {
|
||||
if (isDesktop() && typeof argumentsToSubmit.sample === 'string') {
|
||||
const kclSample = findKclSample(argumentsToSubmit.sample)
|
||||
if (kclSample && kclSample.files.length > 1) {
|
||||
return [
|
||||
{ name: 'New project', value: 'newProject', isCurrent: true },
|
||||
]
|
||||
} else {
|
||||
return [
|
||||
{ name: 'New project', value: 'newProject', isCurrent: true },
|
||||
{ name: 'Existing project', value: 'existingProject' },
|
||||
]
|
||||
}
|
||||
} else {
|
||||
return [{ name: 'Overwrite', value: 'existingProject' }]
|
||||
}
|
||||
|
@ -28,6 +28,7 @@ import type { Selections } from '@src/lib/selections'
|
||||
import { codeManager, kclManager } from '@src/lib/singletons'
|
||||
import { err } from '@src/lib/trap'
|
||||
import type { SketchTool, modelingMachine } from '@src/machines/modelingMachine'
|
||||
import { isDesktop } from '../isDesktop'
|
||||
|
||||
type OutputFormat = Models['OutputFormat3d_type']
|
||||
type OutputTypeKey = OutputFormat['type']
|
||||
@ -159,6 +160,10 @@ export type ModelingCommandSchema = {
|
||||
nodeToEdit?: PathToNode
|
||||
color: string
|
||||
}
|
||||
Insert: {
|
||||
path: string
|
||||
localName: string
|
||||
}
|
||||
Translate: {
|
||||
nodeToEdit?: PathToNode
|
||||
selection: Selections
|
||||
@ -1011,6 +1016,74 @@ export const modelingMachineCommandConfig: StateMachineCommandSetConfig<
|
||||
// Add more fields
|
||||
},
|
||||
},
|
||||
Insert: {
|
||||
description: 'Insert from a file in the current project directory',
|
||||
icon: 'import',
|
||||
hide: 'web',
|
||||
needsReview: true,
|
||||
args: {
|
||||
path: {
|
||||
inputType: 'options',
|
||||
required: true,
|
||||
// options
|
||||
validation: async ({ data }) => {
|
||||
const importExists = kclManager.ast.body.find(
|
||||
(n) =>
|
||||
n.type === 'ImportStatement' &&
|
||||
((n.path.type === 'Kcl' && n.path.filename === data.path) ||
|
||||
(n.path.type === 'Foreign' && n.path.path === data.path))
|
||||
)
|
||||
if (importExists) {
|
||||
return 'This file is already imported, use the Clone command instead.'
|
||||
// TODO: see if we can transition to the clone command, see #6515
|
||||
}
|
||||
|
||||
return true
|
||||
},
|
||||
},
|
||||
localName: {
|
||||
inputType: 'string',
|
||||
required: true,
|
||||
defaultValue: (context: CommandBarContext) => {
|
||||
if (!context.argumentsToSubmit['path']) {
|
||||
return
|
||||
}
|
||||
|
||||
const path = context.argumentsToSubmit['path'] as string
|
||||
return getPathFilenameInVariableCase(path)
|
||||
},
|
||||
validation: async ({ data }) => {
|
||||
const variableExists = kclManager.variables[data.localName]
|
||||
if (variableExists) {
|
||||
return 'This variable name is already in use.'
|
||||
}
|
||||
|
||||
return true
|
||||
},
|
||||
},
|
||||
},
|
||||
// onSubmit: (data) => {
|
||||
// if (!data) {
|
||||
// return new Error('No input provided')
|
||||
// }
|
||||
|
||||
// const ast = kclManager.ast
|
||||
// const { path, localName } = data
|
||||
// const { modifiedAst, pathToNode } = addModuleImport({
|
||||
// ast,
|
||||
// path,
|
||||
// localName,
|
||||
// })
|
||||
// updateModelingState(
|
||||
// modifiedAst,
|
||||
// EXECUTION_TYPE_REAL,
|
||||
// { kclManager, editorManager, codeManager },
|
||||
// {
|
||||
// focusPath: [pathToNode],
|
||||
// }
|
||||
// ).catch(reportRejection)
|
||||
// },
|
||||
},
|
||||
Translate: {
|
||||
description: 'Set translation on solid or sketch.',
|
||||
icon: 'move',
|
||||
|
@ -89,76 +89,76 @@ export function kclCommands(commandProps: KclCommandConfig): Command[] {
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'Insert',
|
||||
description: 'Insert from a file in the current project directory',
|
||||
icon: 'import',
|
||||
groupId: 'code',
|
||||
hide: 'web',
|
||||
needsReview: true,
|
||||
args: {
|
||||
path: {
|
||||
inputType: 'options',
|
||||
required: true,
|
||||
options: commandProps.specialPropsForInsertCommand.providedOptions,
|
||||
validation: async ({ data }) => {
|
||||
const importExists = kclManager.ast.body.find(
|
||||
(n) =>
|
||||
n.type === 'ImportStatement' &&
|
||||
((n.path.type === 'Kcl' && n.path.filename === data.path) ||
|
||||
(n.path.type === 'Foreign' && n.path.path === data.path))
|
||||
)
|
||||
if (importExists) {
|
||||
return 'This file is already imported, use the Clone command instead.'
|
||||
// TODO: see if we can transition to the clone command, see #6515
|
||||
}
|
||||
// {
|
||||
// name: 'Insert',
|
||||
// description: 'Insert from a file in the current project directory',
|
||||
// icon: 'import',
|
||||
// groupId: 'code',
|
||||
// hide: 'web',
|
||||
// needsReview: true,
|
||||
// args: {
|
||||
// path: {
|
||||
// inputType: 'options',
|
||||
// required: true,
|
||||
// options: commandProps.specialPropsForInsertCommand.providedOptions,
|
||||
// validation: async ({ data }) => {
|
||||
// const importExists = kclManager.ast.body.find(
|
||||
// (n) =>
|
||||
// n.type === 'ImportStatement' &&
|
||||
// ((n.path.type === 'Kcl' && n.path.filename === data.path) ||
|
||||
// (n.path.type === 'Foreign' && n.path.path === data.path))
|
||||
// )
|
||||
// if (importExists) {
|
||||
// return 'This file is already imported, use the Clone command instead.'
|
||||
// // TODO: see if we can transition to the clone command, see #6515
|
||||
// }
|
||||
|
||||
return true
|
||||
},
|
||||
},
|
||||
localName: {
|
||||
inputType: 'string',
|
||||
required: true,
|
||||
defaultValue: (context: CommandBarContext) => {
|
||||
if (!context.argumentsToSubmit['path']) {
|
||||
return
|
||||
}
|
||||
// return true
|
||||
// },
|
||||
// },
|
||||
// localName: {
|
||||
// inputType: 'string',
|
||||
// required: true,
|
||||
// defaultValue: (context: CommandBarContext) => {
|
||||
// if (!context.argumentsToSubmit['path']) {
|
||||
// return
|
||||
// }
|
||||
|
||||
const path = context.argumentsToSubmit['path'] as string
|
||||
return getPathFilenameInVariableCase(path)
|
||||
},
|
||||
validation: async ({ data }) => {
|
||||
const variableExists = kclManager.variables[data.localName]
|
||||
if (variableExists) {
|
||||
return 'This variable name is already in use.'
|
||||
}
|
||||
// const path = context.argumentsToSubmit['path'] as string
|
||||
// return getPathFilenameInVariableCase(path)
|
||||
// },
|
||||
// validation: async ({ data }) => {
|
||||
// const variableExists = kclManager.variables[data.localName]
|
||||
// if (variableExists) {
|
||||
// return 'This variable name is already in use.'
|
||||
// }
|
||||
|
||||
return true
|
||||
},
|
||||
},
|
||||
},
|
||||
onSubmit: (data) => {
|
||||
if (!data) {
|
||||
return new Error('No input provided')
|
||||
}
|
||||
// return true
|
||||
// },
|
||||
// },
|
||||
// },
|
||||
// onSubmit: (data) => {
|
||||
// if (!data) {
|
||||
// return new Error('No input provided')
|
||||
// }
|
||||
|
||||
const ast = kclManager.ast
|
||||
const { path, localName } = data
|
||||
const { modifiedAst, pathToNode } = addModuleImport({
|
||||
ast,
|
||||
path,
|
||||
localName,
|
||||
})
|
||||
updateModelingState(
|
||||
modifiedAst,
|
||||
EXECUTION_TYPE_REAL,
|
||||
{ kclManager, editorManager, codeManager },
|
||||
{
|
||||
focusPath: [pathToNode],
|
||||
}
|
||||
).catch(reportRejection)
|
||||
},
|
||||
},
|
||||
// const ast = kclManager.ast
|
||||
// const { path, localName } = data
|
||||
// const { modifiedAst, pathToNode } = addModuleImport({
|
||||
// ast,
|
||||
// path,
|
||||
// localName,
|
||||
// })
|
||||
// updateModelingState(
|
||||
// modifiedAst,
|
||||
// EXECUTION_TYPE_REAL,
|
||||
// { kclManager, editorManager, codeManager },
|
||||
// {
|
||||
// focusPath: [pathToNode],
|
||||
// }
|
||||
// ).catch(reportRejection)
|
||||
// },
|
||||
// },
|
||||
{
|
||||
name: 'format-code',
|
||||
displayName: 'Format Code',
|
||||
|
@ -47,6 +47,7 @@ import { updateModelingState } from '@src/lang/modelingWorkflows'
|
||||
import {
|
||||
addClone,
|
||||
addHelix,
|
||||
addModuleImport,
|
||||
addOffsetPlane,
|
||||
addShell,
|
||||
insertNamedConstant,
|
||||
@ -381,6 +382,7 @@ export type ModelingMachineEvent =
|
||||
data: ModelingCommandSchema['Delete selection']
|
||||
}
|
||||
| { type: 'Appearance'; data: ModelingCommandSchema['Appearance'] }
|
||||
| { type: 'Insert'; data: ModelingCommandSchema['Insert'] }
|
||||
| { type: 'Translate'; data: ModelingCommandSchema['Translate'] }
|
||||
| { type: 'Rotate'; data: ModelingCommandSchema['Rotate'] }
|
||||
| { type: 'Clone'; data: ModelingCommandSchema['Clone'] }
|
||||
@ -2736,6 +2738,34 @@ export const modelingMachine = setup({
|
||||
)
|
||||
}
|
||||
),
|
||||
insertAstMod: fromPromise(
|
||||
async ({
|
||||
input,
|
||||
}: {
|
||||
input: ModelingCommandSchema['Insert'] | undefined
|
||||
}) => {
|
||||
if (!input) {
|
||||
return Promise.reject(new Error(NO_INPUT_PROVIDED_MESSAGE))
|
||||
}
|
||||
|
||||
const ast = kclManager.ast
|
||||
const { path, localName } = input
|
||||
const { modifiedAst, pathToNode } = addModuleImport({
|
||||
ast,
|
||||
path,
|
||||
localName,
|
||||
})
|
||||
|
||||
await updateModelingState(
|
||||
modifiedAst,
|
||||
EXECUTION_TYPE_REAL,
|
||||
{ kclManager, editorManager, codeManager },
|
||||
{
|
||||
focusPath: [pathToNode],
|
||||
}
|
||||
)
|
||||
}
|
||||
),
|
||||
translateAstMod: fromPromise(
|
||||
async ({
|
||||
input,
|
||||
@ -3269,6 +3299,12 @@ export const modelingMachine = setup({
|
||||
guard: 'no kcl errors',
|
||||
},
|
||||
|
||||
Insert: {
|
||||
target: 'Applying insert',
|
||||
reenter: true,
|
||||
guard: 'no kcl errors',
|
||||
},
|
||||
|
||||
Translate: {
|
||||
target: 'Applying translate',
|
||||
reenter: true,
|
||||
@ -4738,6 +4774,22 @@ export const modelingMachine = setup({
|
||||
},
|
||||
},
|
||||
|
||||
'Applying insert': {
|
||||
invoke: {
|
||||
src: 'insertAstMod',
|
||||
id: 'insertAstMod',
|
||||
input: ({ event }) => {
|
||||
if (event.type !== 'Insert') return undefined
|
||||
return event.data
|
||||
},
|
||||
onDone: ['idle'],
|
||||
onError: {
|
||||
target: 'idle',
|
||||
actions: 'toastError',
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
'Applying translate': {
|
||||
invoke: {
|
||||
src: 'translateAstMod',
|
||||
|
@ -1,4 +1,6 @@
|
||||
import type { FileEntry } from '@src/lib/project'
|
||||
import { systemIOActor } from '@src/lib/singletons'
|
||||
import { isArray } from '@src/lib/utils'
|
||||
|
||||
export const folderSnapshot = () => {
|
||||
const { folders } = systemIOActor.getSnapshot().context
|
||||
@ -9,3 +11,48 @@ export const defaultProjectFolderNameSnapshot = () => {
|
||||
const { defaultProjectFolderName } = systemIOActor.getSnapshot().context
|
||||
return defaultProjectFolderName
|
||||
}
|
||||
|
||||
/**
|
||||
* From the application project directory go down to a project folder and list all the folders at that directory level
|
||||
* application project directory: /home/documents/zoo-modeling-app-projects/
|
||||
*
|
||||
* /home/documents/zoo-modeling-app-projects/car-door/
|
||||
* ├── handle
|
||||
* ├── main.kcl
|
||||
* └── window
|
||||
*
|
||||
* The two folders are handle and window
|
||||
*
|
||||
* @param {Object} params
|
||||
* @param {string} params.projectFolderName - The name with no path information.
|
||||
* @returns {FileEntry[]} An array of subdirectory names found at the root level of the specified project folder.
|
||||
*/
|
||||
export const getAllSubDirectoriesAtProjectRoot = ({
|
||||
projectFolderName,
|
||||
}: { projectFolderName: string }): FileEntry[] => {
|
||||
const subDirectories: FileEntry[] = []
|
||||
const { folders } = systemIOActor.getSnapshot().context
|
||||
|
||||
const projectFolder = folders.find((folder) => {
|
||||
return folder.name === projectFolderName
|
||||
})
|
||||
|
||||
// Find the subdirectories
|
||||
if (projectFolder) {
|
||||
// 1st level
|
||||
const children = projectFolder.children
|
||||
if (children) {
|
||||
children.forEach((childFileOrDirectory) => {
|
||||
// 2nd level
|
||||
const secondLevelChild = childFileOrDirectory.children
|
||||
// if secondLevelChild is null then it is a file
|
||||
if (secondLevelChild && isArray(secondLevelChild)) {
|
||||
// this is a directory!
|
||||
subDirectories.push(childFileOrDirectory)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return subDirectories
|
||||
}
|
||||
|
Reference in New Issue
Block a user