Compare commits
8 Commits
kurt-web-a
...
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 toast from 'react-hot-toast'
|
||||||
import { reportRejection } from '@src/lib/trap'
|
import { reportRejection } from '@src/lib/trap'
|
||||||
import { relevantFileExtensions } from '@src/lang/wasmUtils'
|
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 { FILE_EXT } from '@src/lib/constants'
|
||||||
|
import { getAllSubDirectoriesAtProjectRoot } from '@src/machines/systemIO/snapshotContext'
|
||||||
|
|
||||||
function onSubmitKCLSampleCreation({
|
function onSubmitKCLSampleCreation({
|
||||||
sample,
|
sample,
|
||||||
@ -87,6 +92,26 @@ function onSubmitKCLSampleCreation({
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
} else {
|
} 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
|
* Bulk create the assembly and navigate to the project
|
||||||
*/
|
*/
|
||||||
@ -278,10 +303,9 @@ export function createApplicationCommands({
|
|||||||
return value
|
return value
|
||||||
},
|
},
|
||||||
options: ({ argumentsToSubmit }) => {
|
options: ({ argumentsToSubmit }) => {
|
||||||
const samples =
|
const samples = isDesktop()
|
||||||
isDesktop() && argumentsToSubmit.method !== 'existingProject'
|
? everyKclSample
|
||||||
? everyKclSample
|
: kclSamplesManifestWithNoMultipleFiles
|
||||||
: kclSamplesManifestWithNoMultipleFiles
|
|
||||||
return samples.map((sample) => {
|
return samples.map((sample) => {
|
||||||
return {
|
return {
|
||||||
value: sample.pathFromProjectDirectoryToFirstFile,
|
value: sample.pathFromProjectDirectoryToFirstFile,
|
||||||
@ -296,17 +320,10 @@ export function createApplicationCommands({
|
|||||||
skip: true,
|
skip: true,
|
||||||
options: ({ argumentsToSubmit }, _) => {
|
options: ({ argumentsToSubmit }, _) => {
|
||||||
if (isDesktop() && typeof argumentsToSubmit.sample === 'string') {
|
if (isDesktop() && typeof argumentsToSubmit.sample === 'string') {
|
||||||
const kclSample = findKclSample(argumentsToSubmit.sample)
|
return [
|
||||||
if (kclSample && kclSample.files.length > 1) {
|
{ name: 'New project', value: 'newProject', isCurrent: true },
|
||||||
return [
|
{ name: 'Existing project', value: 'existingProject' },
|
||||||
{ name: 'New project', value: 'newProject', isCurrent: true },
|
]
|
||||||
]
|
|
||||||
} else {
|
|
||||||
return [
|
|
||||||
{ name: 'New project', value: 'newProject', isCurrent: true },
|
|
||||||
{ name: 'Existing project', value: 'existingProject' },
|
|
||||||
]
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
return [{ name: 'Overwrite', value: 'existingProject' }]
|
return [{ name: 'Overwrite', value: 'existingProject' }]
|
||||||
}
|
}
|
||||||
|
@ -28,6 +28,7 @@ import type { Selections } from '@src/lib/selections'
|
|||||||
import { codeManager, kclManager } from '@src/lib/singletons'
|
import { codeManager, kclManager } from '@src/lib/singletons'
|
||||||
import { err } from '@src/lib/trap'
|
import { err } from '@src/lib/trap'
|
||||||
import type { SketchTool, modelingMachine } from '@src/machines/modelingMachine'
|
import type { SketchTool, modelingMachine } from '@src/machines/modelingMachine'
|
||||||
|
import { isDesktop } from '../isDesktop'
|
||||||
|
|
||||||
type OutputFormat = Models['OutputFormat3d_type']
|
type OutputFormat = Models['OutputFormat3d_type']
|
||||||
type OutputTypeKey = OutputFormat['type']
|
type OutputTypeKey = OutputFormat['type']
|
||||||
@ -159,6 +160,10 @@ export type ModelingCommandSchema = {
|
|||||||
nodeToEdit?: PathToNode
|
nodeToEdit?: PathToNode
|
||||||
color: string
|
color: string
|
||||||
}
|
}
|
||||||
|
Insert: {
|
||||||
|
path: string
|
||||||
|
localName: string
|
||||||
|
}
|
||||||
Translate: {
|
Translate: {
|
||||||
nodeToEdit?: PathToNode
|
nodeToEdit?: PathToNode
|
||||||
selection: Selections
|
selection: Selections
|
||||||
@ -1011,6 +1016,74 @@ export const modelingMachineCommandConfig: StateMachineCommandSetConfig<
|
|||||||
// Add more fields
|
// 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: {
|
Translate: {
|
||||||
description: 'Set translation on solid or sketch.',
|
description: 'Set translation on solid or sketch.',
|
||||||
icon: 'move',
|
icon: 'move',
|
||||||
|
@ -89,76 +89,76 @@ export function kclCommands(commandProps: KclCommandConfig): Command[] {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
// {
|
||||||
name: 'Insert',
|
// name: 'Insert',
|
||||||
description: 'Insert from a file in the current project directory',
|
// description: 'Insert from a file in the current project directory',
|
||||||
icon: 'import',
|
// icon: 'import',
|
||||||
groupId: 'code',
|
// groupId: 'code',
|
||||||
hide: 'web',
|
// hide: 'web',
|
||||||
needsReview: true,
|
// needsReview: true,
|
||||||
args: {
|
// args: {
|
||||||
path: {
|
// path: {
|
||||||
inputType: 'options',
|
// inputType: 'options',
|
||||||
required: true,
|
// required: true,
|
||||||
options: commandProps.specialPropsForInsertCommand.providedOptions,
|
// options: commandProps.specialPropsForInsertCommand.providedOptions,
|
||||||
validation: async ({ data }) => {
|
// validation: async ({ data }) => {
|
||||||
const importExists = kclManager.ast.body.find(
|
// const importExists = kclManager.ast.body.find(
|
||||||
(n) =>
|
// (n) =>
|
||||||
n.type === 'ImportStatement' &&
|
// n.type === 'ImportStatement' &&
|
||||||
((n.path.type === 'Kcl' && n.path.filename === data.path) ||
|
// ((n.path.type === 'Kcl' && n.path.filename === data.path) ||
|
||||||
(n.path.type === 'Foreign' && n.path.path === data.path))
|
// (n.path.type === 'Foreign' && n.path.path === data.path))
|
||||||
)
|
// )
|
||||||
if (importExists) {
|
// if (importExists) {
|
||||||
return 'This file is already imported, use the Clone command instead.'
|
// return 'This file is already imported, use the Clone command instead.'
|
||||||
// TODO: see if we can transition to the clone command, see #6515
|
// // TODO: see if we can transition to the clone command, see #6515
|
||||||
}
|
// }
|
||||||
|
|
||||||
return true
|
// return true
|
||||||
},
|
// },
|
||||||
},
|
// },
|
||||||
localName: {
|
// localName: {
|
||||||
inputType: 'string',
|
// inputType: 'string',
|
||||||
required: true,
|
// required: true,
|
||||||
defaultValue: (context: CommandBarContext) => {
|
// defaultValue: (context: CommandBarContext) => {
|
||||||
if (!context.argumentsToSubmit['path']) {
|
// if (!context.argumentsToSubmit['path']) {
|
||||||
return
|
// return
|
||||||
}
|
// }
|
||||||
|
|
||||||
const path = context.argumentsToSubmit['path'] as string
|
// const path = context.argumentsToSubmit['path'] as string
|
||||||
return getPathFilenameInVariableCase(path)
|
// return getPathFilenameInVariableCase(path)
|
||||||
},
|
// },
|
||||||
validation: async ({ data }) => {
|
// validation: async ({ data }) => {
|
||||||
const variableExists = kclManager.variables[data.localName]
|
// const variableExists = kclManager.variables[data.localName]
|
||||||
if (variableExists) {
|
// if (variableExists) {
|
||||||
return 'This variable name is already in use.'
|
// return 'This variable name is already in use.'
|
||||||
}
|
// }
|
||||||
|
|
||||||
return true
|
// return true
|
||||||
},
|
// },
|
||||||
},
|
// },
|
||||||
},
|
// },
|
||||||
onSubmit: (data) => {
|
// onSubmit: (data) => {
|
||||||
if (!data) {
|
// if (!data) {
|
||||||
return new Error('No input provided')
|
// return new Error('No input provided')
|
||||||
}
|
// }
|
||||||
|
|
||||||
const ast = kclManager.ast
|
// const ast = kclManager.ast
|
||||||
const { path, localName } = data
|
// const { path, localName } = data
|
||||||
const { modifiedAst, pathToNode } = addModuleImport({
|
// const { modifiedAst, pathToNode } = addModuleImport({
|
||||||
ast,
|
// ast,
|
||||||
path,
|
// path,
|
||||||
localName,
|
// localName,
|
||||||
})
|
// })
|
||||||
updateModelingState(
|
// updateModelingState(
|
||||||
modifiedAst,
|
// modifiedAst,
|
||||||
EXECUTION_TYPE_REAL,
|
// EXECUTION_TYPE_REAL,
|
||||||
{ kclManager, editorManager, codeManager },
|
// { kclManager, editorManager, codeManager },
|
||||||
{
|
// {
|
||||||
focusPath: [pathToNode],
|
// focusPath: [pathToNode],
|
||||||
}
|
// }
|
||||||
).catch(reportRejection)
|
// ).catch(reportRejection)
|
||||||
},
|
// },
|
||||||
},
|
// },
|
||||||
{
|
{
|
||||||
name: 'format-code',
|
name: 'format-code',
|
||||||
displayName: 'Format Code',
|
displayName: 'Format Code',
|
||||||
|
@ -47,6 +47,7 @@ import { updateModelingState } from '@src/lang/modelingWorkflows'
|
|||||||
import {
|
import {
|
||||||
addClone,
|
addClone,
|
||||||
addHelix,
|
addHelix,
|
||||||
|
addModuleImport,
|
||||||
addOffsetPlane,
|
addOffsetPlane,
|
||||||
addShell,
|
addShell,
|
||||||
insertNamedConstant,
|
insertNamedConstant,
|
||||||
@ -381,6 +382,7 @@ export type ModelingMachineEvent =
|
|||||||
data: ModelingCommandSchema['Delete selection']
|
data: ModelingCommandSchema['Delete selection']
|
||||||
}
|
}
|
||||||
| { type: 'Appearance'; data: ModelingCommandSchema['Appearance'] }
|
| { type: 'Appearance'; data: ModelingCommandSchema['Appearance'] }
|
||||||
|
| { type: 'Insert'; data: ModelingCommandSchema['Insert'] }
|
||||||
| { type: 'Translate'; data: ModelingCommandSchema['Translate'] }
|
| { type: 'Translate'; data: ModelingCommandSchema['Translate'] }
|
||||||
| { type: 'Rotate'; data: ModelingCommandSchema['Rotate'] }
|
| { type: 'Rotate'; data: ModelingCommandSchema['Rotate'] }
|
||||||
| { type: 'Clone'; data: ModelingCommandSchema['Clone'] }
|
| { 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(
|
translateAstMod: fromPromise(
|
||||||
async ({
|
async ({
|
||||||
input,
|
input,
|
||||||
@ -3269,6 +3299,12 @@ export const modelingMachine = setup({
|
|||||||
guard: 'no kcl errors',
|
guard: 'no kcl errors',
|
||||||
},
|
},
|
||||||
|
|
||||||
|
Insert: {
|
||||||
|
target: 'Applying insert',
|
||||||
|
reenter: true,
|
||||||
|
guard: 'no kcl errors',
|
||||||
|
},
|
||||||
|
|
||||||
Translate: {
|
Translate: {
|
||||||
target: 'Applying translate',
|
target: 'Applying translate',
|
||||||
reenter: true,
|
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': {
|
'Applying translate': {
|
||||||
invoke: {
|
invoke: {
|
||||||
src: 'translateAstMod',
|
src: 'translateAstMod',
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
|
import type { FileEntry } from '@src/lib/project'
|
||||||
import { systemIOActor } from '@src/lib/singletons'
|
import { systemIOActor } from '@src/lib/singletons'
|
||||||
|
import { isArray } from '@src/lib/utils'
|
||||||
|
|
||||||
export const folderSnapshot = () => {
|
export const folderSnapshot = () => {
|
||||||
const { folders } = systemIOActor.getSnapshot().context
|
const { folders } = systemIOActor.getSnapshot().context
|
||||||
@ -9,3 +11,48 @@ export const defaultProjectFolderNameSnapshot = () => {
|
|||||||
const { defaultProjectFolderName } = systemIOActor.getSnapshot().context
|
const { defaultProjectFolderName } = systemIOActor.getSnapshot().context
|
||||||
return defaultProjectFolderName
|
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