[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>
This commit is contained in:
@ -73,7 +73,7 @@ export class ToolbarFixture {
|
||||
this.fileTreeBtn = page.locator('[id="files-button-holder"]')
|
||||
this.createFileBtn = page.getByTestId('create-file-button')
|
||||
this.treeInputField = page.getByTestId('tree-input-field')
|
||||
this.loadButton = page.getByTestId('load-external-model-pane-button')
|
||||
this.loadButton = page.getByTestId('add-file-to-project-pane-button')
|
||||
|
||||
this.filePane = page.locator('#files-pane')
|
||||
this.featureTreePane = page.locator('#feature-tree-pane')
|
||||
|
@ -550,7 +550,7 @@ test.describe(
|
||||
const expected = 'Open project'
|
||||
expect(actual).toBe(expected)
|
||||
})
|
||||
test('Modeling.File.Load external model', async ({
|
||||
test('Modeling.File.Add file to project', async ({
|
||||
tronApp,
|
||||
cmdBar,
|
||||
page,
|
||||
@ -571,10 +571,10 @@ test.describe(
|
||||
throw new Error('app or app.applicationMenu is missing')
|
||||
}
|
||||
const openProject = app.applicationMenu.getMenuItemById(
|
||||
'File.Load external model'
|
||||
'File.Add file to project'
|
||||
)
|
||||
if (!openProject) {
|
||||
throw new Error('File.Load external model')
|
||||
throw new Error('File.Add file to project')
|
||||
}
|
||||
openProject.click()
|
||||
})
|
||||
@ -584,7 +584,7 @@ test.describe(
|
||||
const actual = await cmdBar.cmdBarElement
|
||||
.getByTestId('command-name')
|
||||
.textContent()
|
||||
const expected = 'Load external model'
|
||||
const expected = 'Add file to project'
|
||||
expect(actual).toBe(expected)
|
||||
})
|
||||
test('Modeling.File.Export current part', async ({
|
||||
|
@ -23,7 +23,6 @@ test.describe('Testing loading external models', () => {
|
||||
'Web: should overwrite current code, cannot create new file',
|
||||
async ({ editor, context, page, homePage }) => {
|
||||
const u = await getUtils(page)
|
||||
|
||||
await test.step(`Test setup`, async () => {
|
||||
await context.addInitScript((code) => {
|
||||
window.localStorage.setItem('persistCode', code)
|
||||
@ -82,12 +81,13 @@ test.describe('Testing loading external models', () => {
|
||||
* "gear-rack": https://github.com/KittyCAD/kcl-samples/blob/main/gear-rack/main.kcl
|
||||
*/
|
||||
test(
|
||||
'Desktop: should create new file by default, optionally overwrite',
|
||||
'Desktop: should create new file by default, creates a second file with automatic unique name',
|
||||
{ tag: '@electron' },
|
||||
async ({ editor, context, page, scene, cmdBar, toolbar }) => {
|
||||
if (runningOnWindows()) {
|
||||
}
|
||||
const { dir } = await context.folderSetupFn(async (dir) => {
|
||||
|
||||
await context.folderSetupFn(async (dir) => {
|
||||
const bracketDir = join(dir, 'bracket')
|
||||
await fsp.mkdir(bracketDir, { recursive: true })
|
||||
await fsp.writeFile(join(bracketDir, 'main.kcl'), bracket, {
|
||||
@ -100,37 +100,28 @@ test.describe('Testing loading external models', () => {
|
||||
const sampleOne = {
|
||||
file: 'parametric-bearing-pillow-block' + FILE_EXT,
|
||||
title: 'Parametric Bearing Pillow Block',
|
||||
}
|
||||
const sampleTwo = {
|
||||
file: 'gear-rack' + FILE_EXT,
|
||||
title: '100mm Gear Rack',
|
||||
file1: 'parametric-bearing-pillow-block-1' + FILE_EXT,
|
||||
}
|
||||
const projectCard = page.getByRole('link', { name: 'bracket' })
|
||||
const commandMethodArgButton = page.getByRole('button', {
|
||||
name: 'Method',
|
||||
})
|
||||
const commandMethodOption = page.getByRole('option', {
|
||||
name: 'Overwrite',
|
||||
})
|
||||
const overwriteWarning = page.getByText(
|
||||
'Overwrite current file with sample?'
|
||||
)
|
||||
const confirmButton = page.getByRole('button', { name: 'Submit command' })
|
||||
const projectMenuButton = page.getByTestId('project-sidebar-toggle')
|
||||
const newlyCreatedFile = (name: string) =>
|
||||
page.getByRole('listitem').filter({
|
||||
has: page.getByRole('button', { name }),
|
||||
})
|
||||
const defaultLoadCmdBarState: CmdBarSerialised = {
|
||||
commandName: 'Load external model',
|
||||
currentArgKey: 'source',
|
||||
commandName: 'Add file to project',
|
||||
currentArgKey: 'sample',
|
||||
currentArgValue: '',
|
||||
headerArguments: {
|
||||
Method: 'newFile',
|
||||
Method: 'Existing project',
|
||||
Sample: '',
|
||||
Source: '',
|
||||
Source: 'kcl-samples',
|
||||
ProjectName: 'bracket',
|
||||
},
|
||||
highlightedHeaderArg: 'source',
|
||||
highlightedHeaderArg: 'sample',
|
||||
stage: 'arguments',
|
||||
}
|
||||
|
||||
@ -152,11 +143,10 @@ test.describe('Testing loading external models', () => {
|
||||
|
||||
await test.step(`Load a KCL sample with the command palette`, async () => {
|
||||
await toolbar.loadButton.click()
|
||||
await cmdBar.selectOption({ name: 'KCL Samples' }).click()
|
||||
await cmdBar.expectState(defaultLoadCmdBarState)
|
||||
await cmdBar.progressCmdBar()
|
||||
await cmdBar.selectOption({ name: sampleOne.title }).click()
|
||||
await expect(overwriteWarning).not.toBeVisible()
|
||||
await cmdBar.progressCmdBar()
|
||||
await page.waitForTimeout(1000)
|
||||
})
|
||||
|
||||
@ -166,33 +156,19 @@ test.describe('Testing loading external models', () => {
|
||||
await expect(projectMenuButton).toContainText(sampleOne.file)
|
||||
})
|
||||
|
||||
await test.step(`Now overwrite the current file`, async () => {
|
||||
await test.step(`Load a KCL sample with the command palette`, async () => {
|
||||
await toolbar.loadButton.click()
|
||||
await cmdBar.selectOption({ name: 'KCL Samples' }).click()
|
||||
await cmdBar.expectState(defaultLoadCmdBarState)
|
||||
await cmdBar.progressCmdBar()
|
||||
await cmdBar.selectOption({ name: sampleTwo.title }).click()
|
||||
await commandMethodArgButton.click()
|
||||
await commandMethodOption.click()
|
||||
await expect(commandMethodArgButton).toContainText('overwrite')
|
||||
await expect(overwriteWarning).toBeVisible()
|
||||
await confirmButton.click()
|
||||
await cmdBar.selectOption({ name: sampleOne.title }).click()
|
||||
await expect(overwriteWarning).not.toBeVisible()
|
||||
await page.waitForTimeout(1000)
|
||||
})
|
||||
|
||||
await test.step(`Ensure we overwrote the current file without navigating`, async () => {
|
||||
await editor.expectEditor.toContain('// ' + sampleTwo.title)
|
||||
await test.step(`Check actual file contents`, async () => {
|
||||
await expect
|
||||
.poll(async () => {
|
||||
return await fsp.readFile(
|
||||
join(dir, 'bracket', sampleOne.file),
|
||||
'utf-8'
|
||||
)
|
||||
})
|
||||
.toContain('// ' + sampleTwo.title)
|
||||
})
|
||||
await expect(newlyCreatedFile(sampleOne.file)).toBeVisible()
|
||||
await expect(newlyCreatedFile(sampleTwo.file)).not.toBeVisible()
|
||||
await expect(projectMenuButton).toContainText(sampleOne.file)
|
||||
await test.step(`Ensure we made and opened a new file with a unique name`, async () => {
|
||||
await editor.expectEditor.toContain('// ' + sampleOne.title)
|
||||
await expect(newlyCreatedFile(sampleOne.file1)).toBeVisible()
|
||||
await expect(projectMenuButton).toContainText(sampleOne.file1)
|
||||
})
|
||||
}
|
||||
)
|
||||
@ -226,19 +202,20 @@ test.describe('Testing loading external models', () => {
|
||||
|
||||
async function loadExternalFileThroughCommandBar(tronApp: ElectronZoo) {
|
||||
await toolbar.loadButton.click()
|
||||
await cmdBar.selectOption({ name: 'Local Drive' }).click()
|
||||
await cmdBar.expectState({
|
||||
commandName: 'Load external model',
|
||||
currentArgKey: 'source',
|
||||
commandName: 'Add file to project',
|
||||
currentArgKey: 'pathOpen file',
|
||||
currentArgValue: '',
|
||||
headerArguments: {
|
||||
Method: 'newFile',
|
||||
Sample: '',
|
||||
Source: '',
|
||||
Method: 'Existing project',
|
||||
Path: '',
|
||||
Source: 'local',
|
||||
ProjectName: 'testDefault',
|
||||
},
|
||||
highlightedHeaderArg: 'source',
|
||||
highlightedHeaderArg: 'path',
|
||||
stage: 'arguments',
|
||||
})
|
||||
await cmdBar.selectOption({ name: 'Local Drive' }).click()
|
||||
|
||||
// Mock the file picker selection
|
||||
const handleFile = tronApp.electron.evaluate(
|
||||
@ -251,14 +228,18 @@ test.describe('Testing loading external models', () => {
|
||||
await page.getByTestId('cmd-bar-arg-file-button').click()
|
||||
await handleFile
|
||||
|
||||
await cmdBar.progressCmdBar()
|
||||
await cmdBar.expectState({
|
||||
commandName: 'Load external model',
|
||||
commandName: 'Add file to project',
|
||||
currentArgKey: 'pathOpen file',
|
||||
currentArgValue: '',
|
||||
headerArguments: {
|
||||
Method: 'Existing project',
|
||||
Path: '',
|
||||
Source: 'local',
|
||||
Path: modelName,
|
||||
ProjectName: 'testDefault',
|
||||
},
|
||||
stage: 'review',
|
||||
highlightedHeaderArg: 'path',
|
||||
stage: 'arguments',
|
||||
})
|
||||
await cmdBar.progressCmdBar()
|
||||
}
|
||||
|
@ -144,7 +144,16 @@ export const CommandBar = () => {
|
||||
data-testid="command-bar"
|
||||
>
|
||||
{commandBarState.matches('Selecting command') ? (
|
||||
<CommandComboBox options={commands} />
|
||||
<CommandComboBox
|
||||
options={commands.filter((command) => {
|
||||
return (
|
||||
// By default everything is undefined
|
||||
// If marked explicitly as false hide
|
||||
command.hideFromSearch === undefined ||
|
||||
command.hideFromSearch === false
|
||||
)
|
||||
})}
|
||||
/>
|
||||
) : commandBarState.matches('Gathering arguments') ? (
|
||||
<CommandBarArgument stepBack={stepBack} />
|
||||
) : (
|
||||
|
@ -8,6 +8,7 @@ import { isArray, toSync } from '@src/lib/utils'
|
||||
import { commandBarActor, useCommandBarState } from '@src/lib/singletons'
|
||||
import { useSelector } from '@xstate/react'
|
||||
import type { AnyStateMachine, SnapshotFrom } from 'xstate'
|
||||
import type { OpenDialogOptions } from 'electron'
|
||||
|
||||
// TODO: remove the need for this selector once we decouple all actors from React
|
||||
const machineContextSelector = (snapshot?: SnapshotFrom<AnyStateMachine>) =>
|
||||
@ -54,10 +55,16 @@ function CommandBarPathInput({
|
||||
if (inputRef.current && inputRefVal && !isArray(inputRefVal)) {
|
||||
inputRef.current.value = inputRefVal
|
||||
} else if (inputRef.current) {
|
||||
const newPath = await window.electron.open({
|
||||
const configuration: OpenDialogOptions = {
|
||||
properties: ['openFile'],
|
||||
title: 'Pick a file to load into the current project',
|
||||
})
|
||||
}
|
||||
|
||||
if (arg.filters) {
|
||||
configuration.filters = arg.filters
|
||||
}
|
||||
|
||||
const newPath = await window.electron.open(configuration)
|
||||
if (newPath.canceled) return
|
||||
inputRef.current.value = newPath.filePaths[0]
|
||||
} else {
|
||||
|
@ -24,8 +24,6 @@ import {
|
||||
} from '@src/lib/constants'
|
||||
import { getProjectInfo } from '@src/lib/desktop'
|
||||
import { getNextDirName, getNextFileName } from '@src/lib/desktopFS'
|
||||
import type { KclSamplesManifestItem } from '@src/lib/getKclSamplesManifest'
|
||||
import { getKclSamplesManifest } from '@src/lib/getKclSamplesManifest'
|
||||
import { isDesktop } from '@src/lib/isDesktop'
|
||||
import { kclCommands } from '@src/lib/kclCommands'
|
||||
import { BROWSER_PATH, PATHS } from '@src/lib/paths'
|
||||
@ -59,9 +57,6 @@ export const FileMachineProvider = ({
|
||||
const settings = useSettings()
|
||||
const projectData = useRouteLoaderData(PATHS.FILE) as IndexLoaderData
|
||||
const { project, file } = projectData
|
||||
const [kclSamples, setKclSamples] = React.useState<KclSamplesManifestItem[]>(
|
||||
[]
|
||||
)
|
||||
|
||||
const filePath = useAbsoluteFilePath()
|
||||
// Only create the native file menus on desktop
|
||||
@ -102,12 +97,6 @@ export const FileMachineProvider = ({
|
||||
|
||||
useEffect(() => {
|
||||
markOnce('code/didLoadFile')
|
||||
async function fetchKclSamples() {
|
||||
const manifest = await getKclSamplesManifest()
|
||||
const filteredFiles = manifest.filter((file) => !file.multipleFiles)
|
||||
setKclSamples(filteredFiles)
|
||||
}
|
||||
fetchKclSamples().catch(reportError)
|
||||
}, [])
|
||||
|
||||
const [state, send] = useMachine(
|
||||
@ -468,28 +457,6 @@ export const FileMachineProvider = ({
|
||||
settings.modeling.defaultUnit.current ??
|
||||
DEFAULT_DEFAULT_LENGTH_UNIT,
|
||||
},
|
||||
specialPropsForLoadCommand: {
|
||||
onSubmit: async (data) => {
|
||||
if (data.method === 'overwrite' && data.content) {
|
||||
codeManager.updateCodeStateEditor(data.content)
|
||||
await kclManager.executeCode()
|
||||
await codeManager.writeToFile()
|
||||
} else if (data.method === 'newFile' && isDesktop()) {
|
||||
send({
|
||||
type: 'Create file',
|
||||
data: {
|
||||
...data,
|
||||
makeDir: false,
|
||||
shouldSetToRename: false,
|
||||
},
|
||||
})
|
||||
}
|
||||
},
|
||||
providedOptions: kclSamples.map((sample) => ({
|
||||
value: sample.pathFromProjectDirectoryToFirstFile,
|
||||
name: sample.title,
|
||||
})),
|
||||
},
|
||||
specialPropsForInsertCommand: {
|
||||
providedOptions: (isDesktop() && project?.children
|
||||
? project.children
|
||||
@ -510,10 +477,8 @@ export const FileMachineProvider = ({
|
||||
}
|
||||
}),
|
||||
},
|
||||
}).filter(
|
||||
(command) => kclSamples.length || command.name !== 'load-external-model'
|
||||
),
|
||||
[codeManager, kclManager, send, kclSamples, project, file]
|
||||
}),
|
||||
[codeManager, kclManager, send, project, file]
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
|
@ -1872,11 +1872,6 @@ export const ModelingMachineProvider = ({
|
||||
commandName: 'Shell',
|
||||
groupId: 'modeling',
|
||||
},
|
||||
{
|
||||
menuLabel: 'Design.Create with Zoo Text-To-CAD',
|
||||
commandName: 'Text-to-CAD',
|
||||
groupId: 'modeling',
|
||||
},
|
||||
{
|
||||
menuLabel: 'Design.Modify with Zoo Text-To-CAD',
|
||||
commandName: 'Prompt-to-edit',
|
||||
|
@ -9,7 +9,7 @@ import { useConvertToVariable } from '@src/hooks/useToolbarGuards'
|
||||
import { openExternalBrowserIfDesktop } from '@src/lib/openWindow'
|
||||
import { kclManager } from '@src/lib/singletons'
|
||||
import { reportRejection } from '@src/lib/trap'
|
||||
import { commandBarActor } from '@src/lib/singletons'
|
||||
import { commandBarActor, settingsActor } from '@src/lib/singletons'
|
||||
|
||||
import styles from './KclEditorMenu.module.css'
|
||||
|
||||
@ -86,17 +86,23 @@ export const KclEditorMenu = ({ children }: PropsWithChildren) => {
|
||||
<Menu.Item>
|
||||
<button
|
||||
onClick={() => {
|
||||
const currentProject =
|
||||
settingsActor.getSnapshot().context.currentProject
|
||||
commandBarActor.send({
|
||||
type: 'Find and select command',
|
||||
data: {
|
||||
groupId: 'code',
|
||||
name: 'load-external-model',
|
||||
name: 'add-kcl-file-to-project',
|
||||
groupId: 'application',
|
||||
argDefaultValues: {
|
||||
method: 'existingProject',
|
||||
projectName: currentProject?.name,
|
||||
},
|
||||
},
|
||||
})
|
||||
}}
|
||||
className={styles.button}
|
||||
>
|
||||
<span>Load external model</span>
|
||||
<span>Add file to project</span>
|
||||
</button>
|
||||
</Menu.Item>
|
||||
<Menu.Item>
|
||||
|
@ -29,6 +29,7 @@ import { reportRejection } from '@src/lib/trap'
|
||||
import { refreshPage } from '@src/lib/utils'
|
||||
import { hotkeyDisplay } from '@src/lib/hotkeyWrapper'
|
||||
import usePlatform from '@src/hooks/usePlatform'
|
||||
import { settingsActor } from '@src/lib/singletons'
|
||||
|
||||
interface ModelingSidebarProps {
|
||||
paneOpacity: '' | 'opacity-20' | 'opacity-40'
|
||||
@ -79,16 +80,27 @@ export function ModelingSidebar({ paneOpacity }: ModelingSidebarProps) {
|
||||
|
||||
const sidebarActions: SidebarAction[] = [
|
||||
{
|
||||
id: 'load-external-model',
|
||||
title: 'Load external model',
|
||||
sidebarName: 'Load external model',
|
||||
id: 'add-file-to-project',
|
||||
title: 'Add file to project',
|
||||
sidebarName: 'Add file to project',
|
||||
icon: 'importFile',
|
||||
keybinding: 'Mod + Alt + L',
|
||||
action: () =>
|
||||
action: () => {
|
||||
const currentProject =
|
||||
settingsActor.getSnapshot().context.currentProject
|
||||
commandBarActor.send({
|
||||
type: 'Find and select command',
|
||||
data: { name: 'load-external-model', groupId: 'code' },
|
||||
}),
|
||||
data: {
|
||||
name: 'add-kcl-file-to-project',
|
||||
groupId: 'application',
|
||||
argDefaultValues: {
|
||||
method: 'existingProject',
|
||||
projectName: currentProject?.name,
|
||||
...(!isDesktop() ? { source: 'kcl-samples' } : {}),
|
||||
},
|
||||
},
|
||||
})
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'export',
|
||||
|
@ -13,8 +13,9 @@ import { initializeWindowExceptionHandler } from '@src/lib/exceptions'
|
||||
import { isDesktop } from '@src/lib/isDesktop'
|
||||
import { markOnce } from '@src/lib/performance'
|
||||
import { reportRejection } from '@src/lib/trap'
|
||||
import { appActor } from '@src/lib/singletons'
|
||||
import { appActor, systemIOActor, commandBarActor } from '@src/lib/singletons'
|
||||
import reportWebVitals from '@src/reportWebVitals'
|
||||
import { createApplicationCommands } from '@src/lib/commandBarConfigs/applicationCommandConfig'
|
||||
|
||||
markOnce('code/willAuth')
|
||||
initializeWindowExceptionHandler()
|
||||
@ -32,6 +33,14 @@ initializeWindowExceptionHandler()
|
||||
initPromise
|
||||
.then(() => {
|
||||
appActor.start()
|
||||
// Application commands must be created after the initPromise because
|
||||
// it calls WASM functions to file extensions, this dependency is not available during initialization, it is an async dependency
|
||||
commandBarActor.send({
|
||||
type: 'Add commands',
|
||||
data: {
|
||||
commands: [...createApplicationCommands({ systemIOActor })],
|
||||
},
|
||||
})
|
||||
})
|
||||
.catch(reportRejection)
|
||||
|
||||
|
@ -3,6 +3,12 @@ import type { ActorRefFrom } from 'xstate'
|
||||
import type { Command, CommandArgumentOption } from '@src/lib/commandTypes'
|
||||
import { SystemIOMachineEvents } from '@src/machines/systemIO/utils'
|
||||
import { isDesktop } from '@src/lib/isDesktop'
|
||||
import { kclSamplesManifestWithNoMultipleFiles } from '@src/lib/kclSamples'
|
||||
import { getUniqueProjectName } from '@src/lib/desktopFS'
|
||||
import { FILE_EXT } from '@src/lib/constants'
|
||||
import toast from 'react-hot-toast'
|
||||
import { reportRejection } from '@src/lib/trap'
|
||||
import { relevantFileExtensions } from '@src/lang/wasmUtils'
|
||||
|
||||
export function createApplicationCommands({
|
||||
systemIOActor,
|
||||
@ -79,5 +85,196 @@ export function createApplicationCommands({
|
||||
},
|
||||
}
|
||||
|
||||
return isDesktop() ? [textToCADCommand] : [textToCADCommand]
|
||||
const addKCLFileToProject: Command = {
|
||||
name: 'add-kcl-file-to-project',
|
||||
displayName: 'Add file to project',
|
||||
description:
|
||||
'Add KCL file, Zoo sample, or 3D model to new or existing project.',
|
||||
needsReview: false,
|
||||
icon: 'importFile',
|
||||
groupId: 'application',
|
||||
onSubmit(data) {
|
||||
if (data) {
|
||||
/** TODO: Make a new machine for models. This is only a temporary location
|
||||
* to move it to the global application level. To reduce its footprint
|
||||
* and complexity the implementation lives here with systemIOMachine. Not
|
||||
* inside the systemIOMachine. We can have a fancy model machine that loads
|
||||
* KCL samples
|
||||
*/
|
||||
const folders = systemIOActor.getSnapshot().context.folders
|
||||
const isProjectNew = !!data.newProjectName
|
||||
const requestedProjectName = data.newProjectName || data.projectName
|
||||
const uniqueNameIfNeeded = isProjectNew
|
||||
? getUniqueProjectName(requestedProjectName, folders)
|
||||
: requestedProjectName
|
||||
|
||||
if (data.source === 'kcl-samples' && data.sample) {
|
||||
const pathParts = data.sample.split('/')
|
||||
const projectPathPart = pathParts[0]
|
||||
const primaryKclFile = pathParts[1]
|
||||
const folderNameBecomesKCLFileName = projectPathPart + FILE_EXT
|
||||
|
||||
const sampleCodeUrl =
|
||||
(isDesktop() ? '.' : '') +
|
||||
`/kcl-samples/${encodeURIComponent(
|
||||
projectPathPart
|
||||
)}/${encodeURIComponent(primaryKclFile)}`
|
||||
|
||||
fetch(sampleCodeUrl)
|
||||
.then(async (codeResponse) => {
|
||||
if (!codeResponse.ok) {
|
||||
console.error(
|
||||
'Failed to fetch sample code:',
|
||||
codeResponse.statusText
|
||||
)
|
||||
return Promise.reject(new Error('Failed to fetch sample code'))
|
||||
}
|
||||
const code = await codeResponse.text()
|
||||
systemIOActor.send({
|
||||
type: SystemIOMachineEvents.importFileFromURL,
|
||||
data: {
|
||||
requestedProjectName: uniqueNameIfNeeded,
|
||||
requestedFileName: folderNameBecomesKCLFileName,
|
||||
requestedCode: code,
|
||||
},
|
||||
})
|
||||
})
|
||||
.catch(reportError)
|
||||
} else if (data.source === 'local' && data.path) {
|
||||
const clonePath = data.path
|
||||
const fileWithExtension = clonePath.split('/').pop()
|
||||
const readFileContentsAndCreateNewFile = async () => {
|
||||
const text = await window.electron.readFile(clonePath, 'utf8')
|
||||
systemIOActor.send({
|
||||
type: SystemIOMachineEvents.importFileFromURL,
|
||||
data: {
|
||||
requestedProjectName: uniqueNameIfNeeded,
|
||||
requestedFileName: fileWithExtension,
|
||||
requestedCode: text,
|
||||
},
|
||||
})
|
||||
}
|
||||
readFileContentsAndCreateNewFile().catch(reportRejection)
|
||||
} else {
|
||||
toast.error("The command couldn't be submitted, check the arguments.")
|
||||
}
|
||||
}
|
||||
},
|
||||
args: {
|
||||
source: {
|
||||
inputType: 'options',
|
||||
required: true,
|
||||
skip: false,
|
||||
defaultValue: isDesktop() ? 'local' : 'kcl-samples',
|
||||
options() {
|
||||
return [
|
||||
{
|
||||
value: 'kcl-samples',
|
||||
name: 'KCL Samples',
|
||||
isCurrent: true,
|
||||
},
|
||||
...(isDesktop()
|
||||
? [
|
||||
{
|
||||
value: 'local',
|
||||
name: 'Local Drive',
|
||||
isCurrent: false,
|
||||
},
|
||||
]
|
||||
: []),
|
||||
]
|
||||
},
|
||||
},
|
||||
method: {
|
||||
inputType: 'options',
|
||||
required: true,
|
||||
skip: true,
|
||||
options: isDesktop()
|
||||
? [
|
||||
{ name: 'New project', value: 'newProject', isCurrent: true },
|
||||
{ name: 'Existing project', value: 'existingProject' },
|
||||
]
|
||||
: [{ name: 'Overwrite', value: 'existingProject' }],
|
||||
valueSummary(value) {
|
||||
return isDesktop()
|
||||
? value === 'newProject'
|
||||
? 'New project'
|
||||
: 'Existing project'
|
||||
: 'Overwrite'
|
||||
},
|
||||
},
|
||||
projectName: {
|
||||
inputType: 'options',
|
||||
required: (commandsContext) =>
|
||||
isDesktop() &&
|
||||
commandsContext.argumentsToSubmit.method === 'existingProject',
|
||||
skip: true,
|
||||
options: (_, context) => {
|
||||
const { folders } = systemIOActor.getSnapshot().context
|
||||
const options: CommandArgumentOption<string>[] = []
|
||||
folders.forEach((folder) => {
|
||||
options.push({
|
||||
name: folder.name,
|
||||
value: folder.name,
|
||||
isCurrent: false,
|
||||
})
|
||||
})
|
||||
return options
|
||||
},
|
||||
},
|
||||
newProjectName: {
|
||||
inputType: 'text',
|
||||
required: (commandsContext) =>
|
||||
isDesktop() &&
|
||||
commandsContext.argumentsToSubmit.method === 'newProject',
|
||||
skip: true,
|
||||
},
|
||||
sample: {
|
||||
inputType: 'options',
|
||||
required: (commandContext) =>
|
||||
!['local'].includes(
|
||||
commandContext.argumentsToSubmit.source as string
|
||||
),
|
||||
hidden: (commandContext) =>
|
||||
['local'].includes(commandContext.argumentsToSubmit.source as string),
|
||||
valueSummary(value) {
|
||||
const MAX_LENGTH = 12
|
||||
if (typeof value === 'string') {
|
||||
return value.length > MAX_LENGTH
|
||||
? value.substring(0, MAX_LENGTH) + '...'
|
||||
: value
|
||||
}
|
||||
return value
|
||||
},
|
||||
options: kclSamplesManifestWithNoMultipleFiles.map((sample) => {
|
||||
return {
|
||||
value: sample.pathFromProjectDirectoryToFirstFile,
|
||||
name: sample.title,
|
||||
}
|
||||
}),
|
||||
},
|
||||
path: {
|
||||
inputType: 'path',
|
||||
skip: true,
|
||||
hidden: !isDesktop(),
|
||||
defaultValue: '',
|
||||
valueSummary: (value) => {
|
||||
return isDesktop() ? window.electron.path.basename(value) : ''
|
||||
},
|
||||
required: (commandContext) =>
|
||||
isDesktop() &&
|
||||
['local'].includes(commandContext.argumentsToSubmit.source as string),
|
||||
filters: [
|
||||
{
|
||||
name: `Import ${relevantFileExtensions().map((f) => ` .${f}`)}`,
|
||||
extensions: relevantFileExtensions(),
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
return isDesktop()
|
||||
? [textToCADCommand, addKCLFileToProject]
|
||||
: [textToCADCommand, addKCLFileToProject]
|
||||
}
|
||||
|
@ -182,6 +182,7 @@ export function createProjectCommands({
|
||||
icon: 'file',
|
||||
description: 'Create a file',
|
||||
needsReview: true,
|
||||
hideFromSearch: true,
|
||||
onSubmit: (record) => {
|
||||
if (record) {
|
||||
systemIOActor.send({
|
||||
|
@ -41,6 +41,12 @@ export interface KclExpressionWithVariable extends KclExpression {
|
||||
export type KclCommandValue = KclExpression | KclExpressionWithVariable
|
||||
export type CommandInputType = INPUT_TYPE[number]
|
||||
|
||||
export type FileFilter = {
|
||||
name: string
|
||||
extensions: string[]
|
||||
}
|
||||
export type FiltersConfig = FileFilter[]
|
||||
|
||||
export type StateMachineCommandSetSchema<T extends AnyStateMachine> = Partial<{
|
||||
[EventType in EventFrom<T>['type']]: Record<string, any>
|
||||
}>
|
||||
@ -96,6 +102,7 @@ export type Command<
|
||||
description?: string
|
||||
icon?: Icon
|
||||
hide?: PLATFORM[number]
|
||||
hideFromSearch?: boolean
|
||||
}
|
||||
|
||||
export type CommandConfig<
|
||||
@ -373,6 +380,7 @@ export type CommandArgument<
|
||||
commandBarContext: ContextFrom<typeof commandBarMachine>,
|
||||
machineContext?: ContextFrom<T>
|
||||
) => OutputType)
|
||||
filters: FiltersConfig
|
||||
}
|
||||
| {
|
||||
inputType: 'text'
|
||||
|
@ -1,7 +1,6 @@
|
||||
import type { UnitLength_type } from '@kittycad/lib/dist/types/src/models'
|
||||
import toast from 'react-hot-toast'
|
||||
|
||||
import { CommandBarOverwriteWarning } from '@src/components/CommandBarOverwriteWarning'
|
||||
import { updateModelingState } from '@src/lang/modelingWorkflows'
|
||||
import { addImportAndInsert } from '@src/lang/modifyAst'
|
||||
import {
|
||||
@ -14,10 +13,8 @@ import {
|
||||
DEFAULT_DEFAULT_ANGLE_UNIT,
|
||||
DEFAULT_DEFAULT_LENGTH_UNIT,
|
||||
EXECUTION_TYPE_REAL,
|
||||
FILE_EXT,
|
||||
} from '@src/lib/constants'
|
||||
import { getPathFilenameInVariableCase } from '@src/lib/desktop'
|
||||
import { isDesktop } from '@src/lib/isDesktop'
|
||||
import { copyFileShareLink } from '@src/lib/links'
|
||||
import { baseUnitsUnion } from '@src/lib/settings/settingsTypes'
|
||||
import { codeManager, editorManager, kclManager } from '@src/lib/singletons'
|
||||
@ -25,21 +22,9 @@ import { err, reportRejection } from '@src/lib/trap'
|
||||
import type { IndexLoaderData } from '@src/lib/types'
|
||||
import type { CommandBarContext } from '@src/machines/commandBarMachine'
|
||||
|
||||
interface OnSubmitProps {
|
||||
name: string
|
||||
content?: string
|
||||
targetPathToClone?: string
|
||||
method: 'overwrite' | 'newFile'
|
||||
source: 'kcl-samples' | 'local'
|
||||
}
|
||||
|
||||
interface KclCommandConfig {
|
||||
// TODO: find a different approach that doesn't require
|
||||
// special props for a single command
|
||||
specialPropsForLoadCommand: {
|
||||
onSubmit: (p: OnSubmitProps) => Promise<void>
|
||||
providedOptions: CommandArgumentOption<string>[]
|
||||
}
|
||||
specialPropsForInsertCommand: {
|
||||
providedOptions: CommandArgumentOption<string>[]
|
||||
}
|
||||
@ -189,160 +174,6 @@ export function kclCommands(commandProps: KclCommandConfig): Command[] {
|
||||
kclManager.format().catch(reportRejection)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'load-external-model',
|
||||
displayName: 'Load external model',
|
||||
description:
|
||||
'Loads a model from an external source into the current project.',
|
||||
needsReview: true,
|
||||
icon: 'importFile',
|
||||
reviewMessage: ({ argumentsToSubmit }) =>
|
||||
argumentsToSubmit['method'] === 'overwrite'
|
||||
? CommandBarOverwriteWarning({
|
||||
heading: 'Overwrite current file with sample?',
|
||||
message:
|
||||
'This will erase your current file and load the sample part.',
|
||||
})
|
||||
: 'This will create a new file in the current project and open it.',
|
||||
groupId: 'code',
|
||||
onSubmit(data) {
|
||||
if (!data) {
|
||||
return new Error('No input data')
|
||||
}
|
||||
|
||||
const { method, source, sample, path } = data
|
||||
if (source === 'local' && path) {
|
||||
commandProps.specialPropsForLoadCommand
|
||||
.onSubmit({
|
||||
name: '',
|
||||
targetPathToClone: path,
|
||||
method,
|
||||
source,
|
||||
})
|
||||
.catch(reportError)
|
||||
} else if (source === 'kcl-samples' && sample) {
|
||||
const pathParts = sample.split('/')
|
||||
const projectPathPart = pathParts[0]
|
||||
const primaryKclFile = pathParts[1]
|
||||
// local only
|
||||
const sampleCodeUrl =
|
||||
(isDesktop() ? '.' : '') +
|
||||
`/kcl-samples/${encodeURIComponent(
|
||||
projectPathPart
|
||||
)}/${encodeURIComponent(primaryKclFile)}`
|
||||
|
||||
fetch(sampleCodeUrl)
|
||||
.then(async (codeResponse) => {
|
||||
if (!codeResponse.ok) {
|
||||
console.error(
|
||||
'Failed to fetch sample code:',
|
||||
codeResponse.statusText
|
||||
)
|
||||
return Promise.reject(new Error('Failed to fetch sample code'))
|
||||
}
|
||||
const code = await codeResponse.text()
|
||||
commandProps.specialPropsForLoadCommand
|
||||
.onSubmit({
|
||||
name: data.sample.split('/')[0] + FILE_EXT,
|
||||
content: code,
|
||||
source,
|
||||
method,
|
||||
})
|
||||
.catch(reportError)
|
||||
})
|
||||
.catch(reportError)
|
||||
} else {
|
||||
toast.error("The command couldn't be submitted, check the arguments.")
|
||||
}
|
||||
},
|
||||
args: {
|
||||
source: {
|
||||
inputType: 'options',
|
||||
required: true,
|
||||
skip: false,
|
||||
defaultValue: 'local',
|
||||
hidden: !isDesktop(),
|
||||
options() {
|
||||
return [
|
||||
{
|
||||
value: 'kcl-samples',
|
||||
name: 'KCL Samples',
|
||||
isCurrent: true,
|
||||
},
|
||||
...(isDesktop()
|
||||
? [
|
||||
{
|
||||
value: 'local',
|
||||
name: 'Local Drive',
|
||||
isCurrent: false,
|
||||
},
|
||||
]
|
||||
: []),
|
||||
]
|
||||
},
|
||||
},
|
||||
method: {
|
||||
inputType: 'options',
|
||||
skip: true,
|
||||
required: (commandContext) =>
|
||||
!['local'].includes(
|
||||
commandContext.argumentsToSubmit.source as string
|
||||
),
|
||||
hidden: (commandContext) =>
|
||||
['local'].includes(
|
||||
commandContext.argumentsToSubmit.source as string
|
||||
),
|
||||
defaultValue: isDesktop() ? 'newFile' : 'overwrite',
|
||||
options() {
|
||||
return [
|
||||
{
|
||||
value: 'overwrite',
|
||||
name: 'Overwrite current code',
|
||||
isCurrent: !isDesktop(),
|
||||
},
|
||||
...(isDesktop()
|
||||
? [
|
||||
{
|
||||
value: 'newFile',
|
||||
name: 'Create a new file',
|
||||
isCurrent: true,
|
||||
},
|
||||
]
|
||||
: []),
|
||||
]
|
||||
},
|
||||
},
|
||||
sample: {
|
||||
inputType: 'options',
|
||||
required: (commandContext) =>
|
||||
!['local'].includes(
|
||||
commandContext.argumentsToSubmit.source as string
|
||||
),
|
||||
hidden: (commandContext) =>
|
||||
['local'].includes(
|
||||
commandContext.argumentsToSubmit.source as string
|
||||
),
|
||||
valueSummary(value) {
|
||||
const MAX_LENGTH = 12
|
||||
if (typeof value === 'string') {
|
||||
return value.length > MAX_LENGTH
|
||||
? value.substring(0, MAX_LENGTH) + '...'
|
||||
: value
|
||||
}
|
||||
return value
|
||||
},
|
||||
options: commandProps.specialPropsForLoadCommand.providedOptions,
|
||||
},
|
||||
path: {
|
||||
inputType: 'path',
|
||||
valueSummary: (value) => window.electron.path.basename(value),
|
||||
required: (commandContext) =>
|
||||
['local'].includes(
|
||||
commandContext.argumentsToSubmit.source as string
|
||||
),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'share-file-link',
|
||||
displayName: 'Share part via Zoo link',
|
||||
|
7
src/lib/kclSamples.ts
Normal file
7
src/lib/kclSamples.ts
Normal file
@ -0,0 +1,7 @@
|
||||
import kclSamplesManifest from '@public/kcl-samples/manifest.json'
|
||||
|
||||
const kclSamplesManifestWithNoMultipleFiles = kclSamplesManifest.filter(
|
||||
(file) => !file.multipleFiles
|
||||
)
|
||||
|
||||
export { kclSamplesManifest, kclSamplesManifestWithNoMultipleFiles }
|
@ -28,7 +28,6 @@ import type { AppMachineContext } from '@src/lib/types'
|
||||
import { createAuthCommands } from '@src/lib/commandBarConfigs/authCommandConfig'
|
||||
import { commandBarMachine } from '@src/machines/commandBarMachine'
|
||||
import { createProjectCommands } from '@src/lib/commandBarConfigs/projectsCommandConfig'
|
||||
import { createApplicationCommands } from '@src/lib/commandBarConfigs/applicationCommandConfig'
|
||||
|
||||
export const codeManager = new CodeManager()
|
||||
export const engineCommandManager = new EngineCommandManager()
|
||||
@ -227,7 +226,6 @@ commandBarActor.send({
|
||||
commands: [
|
||||
...createAuthCommands({ authActor }),
|
||||
...createProjectCommands({ systemIOActor }),
|
||||
...createApplicationCommands({ systemIOActor }),
|
||||
],
|
||||
},
|
||||
})
|
||||
|
@ -469,10 +469,13 @@ export const systemIOMachine = setup({
|
||||
assign({
|
||||
requestedFileName: ({ context, event }) => {
|
||||
assertEvent(event, SystemIOMachineEvents.done_importFileFromURL)
|
||||
// Not the entire path
|
||||
// Gotcha: file could have an ending of .kcl...
|
||||
const file = event.output.fileName.endsWith('.kcl')
|
||||
? event.output.fileName
|
||||
: event.output.fileName + '.kcl'
|
||||
return {
|
||||
project: event.output.projectName,
|
||||
file: event.output.fileName + '.kcl',
|
||||
file,
|
||||
}
|
||||
},
|
||||
}),
|
||||
|
@ -204,7 +204,7 @@ export const systemIOMachineDesktop = systemIOMachine.provide({
|
||||
|
||||
return {
|
||||
message: 'File created successfully',
|
||||
fileName: input.requestedFileName,
|
||||
fileName: newFileName,
|
||||
projectName: newProjectName,
|
||||
}
|
||||
}
|
||||
|
@ -24,7 +24,7 @@ export type MenuLabels =
|
||||
| 'File.Sign out'
|
||||
| 'File.Create new file'
|
||||
| 'File.Create new folder'
|
||||
| 'File.Load external model'
|
||||
| 'File.Add file to project'
|
||||
| 'File.Export current part'
|
||||
| 'File.Share part via Zoo link'
|
||||
| 'File.Preferences.Project settings'
|
||||
|
@ -35,6 +35,25 @@ export const projectFileRole = (
|
||||
// TODO https://www.electronjs.org/docs/latest/tutorial/recent-documents
|
||||
// Appears to be only Windows and Mac OS specific. Linux does not have support
|
||||
{ type: 'separator' },
|
||||
{
|
||||
label: 'Add file to project',
|
||||
id: 'File.Add file to project',
|
||||
click: () => {
|
||||
typeSafeWebContentsSend(mainWindow, 'menu-action-clicked', {
|
||||
menuLabel: 'File.Add file to project',
|
||||
})
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'Create with Zoo Text-To-CAD',
|
||||
id: 'Design.Create with Zoo Text-To-CAD',
|
||||
click: () => {
|
||||
typeSafeWebContentsSend(mainWindow, 'menu-action-clicked', {
|
||||
menuLabel: 'Design.Create with Zoo Text-To-CAD',
|
||||
})
|
||||
},
|
||||
},
|
||||
{ type: 'separator' },
|
||||
{
|
||||
label: 'Preferences',
|
||||
submenu: [
|
||||
@ -148,11 +167,11 @@ export const modelingFileRole = (
|
||||
// Appears to be only Windows and Mac OS specific. Linux does not have support
|
||||
{ type: 'separator' },
|
||||
{
|
||||
label: 'Load external model',
|
||||
id: 'File.Load external model',
|
||||
label: 'Add file to project',
|
||||
id: 'File.Add file to project',
|
||||
click: () => {
|
||||
typeSafeWebContentsSend(mainWindow, 'menu-action-clicked', {
|
||||
menuLabel: 'File.Load external model',
|
||||
menuLabel: 'File.Add file to project',
|
||||
})
|
||||
},
|
||||
},
|
||||
|
@ -92,12 +92,17 @@ export function modelingMenuCallbackMostActions(
|
||||
}).catch(reportRejection)
|
||||
} else if (data.menuLabel === 'File.Preferences.User default units') {
|
||||
navigate(filePath + PATHS.SETTINGS_USER + '#defaultUnit')
|
||||
} else if (data.menuLabel === 'File.Load external model') {
|
||||
} else if (data.menuLabel === 'File.Add file to project') {
|
||||
const currentProject = settingsActor.getSnapshot().context.currentProject
|
||||
commandBarActor.send({
|
||||
type: 'Find and select command',
|
||||
data: {
|
||||
groupId: 'code',
|
||||
name: 'load-external-model',
|
||||
name: 'add-kcl-file-to-project',
|
||||
groupId: 'application',
|
||||
argDefaultValues: {
|
||||
method: 'existingProject',
|
||||
projectName: currentProject?.name,
|
||||
},
|
||||
},
|
||||
})
|
||||
} else if (data.menuLabel === 'File.Export current part') {
|
||||
@ -257,9 +262,17 @@ export function modelingMenuCallbackMostActions(
|
||||
},
|
||||
})
|
||||
} else if (data.menuLabel === 'Design.Create with Zoo Text-To-CAD') {
|
||||
const currentProject = settingsActor.getSnapshot().context.currentProject
|
||||
commandBarActor.send({
|
||||
type: 'Find and select command',
|
||||
data: { name: 'Text-to-CAD', groupId: 'modeling' },
|
||||
data: {
|
||||
name: 'Text-to-CAD',
|
||||
groupId: 'application',
|
||||
argDefaultValues: {
|
||||
method: 'existingProject',
|
||||
projectName: currentProject?.name,
|
||||
},
|
||||
},
|
||||
})
|
||||
} else if (data.menuLabel === 'Design.Modify with Zoo Text-To-CAD') {
|
||||
commandBarActor.send({
|
||||
|
@ -26,7 +26,7 @@ type FileRoleLabel =
|
||||
| 'Create new folder'
|
||||
| 'Share part via Zoo link'
|
||||
| 'Project settings'
|
||||
| 'Load external model'
|
||||
| 'Add file to project'
|
||||
| 'User default units'
|
||||
|
||||
type EditRoleLabel =
|
||||
|
@ -142,6 +142,26 @@ const Home = () => {
|
||||
})
|
||||
} else if (data.menuLabel === 'File.Preferences.Theme color') {
|
||||
navigate(`${PATHS.HOME}${PATHS.SETTINGS_USER}#themeColor`)
|
||||
} else if (data.menuLabel === 'File.Add file to project') {
|
||||
commandBarActor.send({
|
||||
type: 'Find and select command',
|
||||
data: {
|
||||
name: 'add-kcl-file-to-project',
|
||||
groupId: 'application',
|
||||
},
|
||||
})
|
||||
} else if (data.menuLabel === 'Design.Create with Zoo Text-To-CAD') {
|
||||
commandBarActor.send({
|
||||
type: 'Find and select command',
|
||||
data: {
|
||||
name: 'Text-to-CAD',
|
||||
groupId: 'application',
|
||||
argDefaultValues: {
|
||||
method: 'newProject',
|
||||
newProjectName: settings.projects.defaultProjectName.current,
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
useMenuListener(cb)
|
||||
@ -182,7 +202,7 @@ const Home = () => {
|
||||
readWriteProjectDir={readWriteProjectDir}
|
||||
className="col-start-2 -col-end-1"
|
||||
/>
|
||||
<aside className="row-start-2 -row-end-1 flex flex-col justify-between">
|
||||
<aside className="row-start-1 -row-end-1 flex flex-col justify-between">
|
||||
<ul className="flex flex-col">
|
||||
<li className="contents">
|
||||
<ActionButton
|
||||
@ -233,6 +253,34 @@ const Home = () => {
|
||||
Generate with Text-to-CAD
|
||||
</ActionButton>
|
||||
</li>
|
||||
<li className="contents">
|
||||
<ActionButton
|
||||
Element="button"
|
||||
onClick={() =>
|
||||
commandBarActor.send({
|
||||
type: 'Find and select command',
|
||||
data: {
|
||||
groupId: 'application',
|
||||
name: 'add-kcl-file-to-project',
|
||||
argDefaultValues: {
|
||||
source: 'kcl-samples',
|
||||
method: 'newProject',
|
||||
newProjectName:
|
||||
settings.projects.defaultProjectName.current,
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
className={sidebarButtonClasses}
|
||||
iconStart={{
|
||||
icon: 'importFile',
|
||||
bgClassName: '!bg-transparent rounded-sm',
|
||||
}}
|
||||
data-testid="home-create-from-sample"
|
||||
>
|
||||
Create from a sample
|
||||
</ActionButton>
|
||||
</li>
|
||||
</ul>
|
||||
<ul className="flex flex-col">
|
||||
<li className="contents">
|
||||
|
Reference in New Issue
Block a user