Compare commits

...

22 Commits

Author SHA1 Message Date
0573801381 pierremtb/wip-moving-insert-to-modeling 2025-05-20 11:27:46 -04:00
140e8b56fb fix: cleaning up old comment 2025-05-20 09:59:51 -05:00
4d203b0c0c Merge branch 'main' into nadro/adhoc/kcl-samples-subdirs 2025-05-20 09:58:44 -05:00
223a4ad45d Style the experimental badge to match the website (#7100)
* Style the experimental badge to match the website

* Match the styling of the rest of the app

We don't use all apps monospace fonts anywhere.

* Bring back all caps
2025-05-20 14:57:53 +00:00
edf31ec1d3 Make the textarea command bar input run its resize initially (#7103)
We have a hook to auto-grow the textarea input but it wasn't running
once initially. This is noticable on the onboarding, where we show the
user a long Text-to-CAD Edit prompt that overflows.
2025-05-20 10:56:03 -04:00
04d6fcf0c4 Merge branch 'main' into nadro/adhoc/kcl-samples-subdirs 2025-05-20 09:54:13 -05:00
30b2a765fc fix: removing testing console logs 2025-05-20 09:53:30 -05:00
8ddbb488d6 chore: implemented kcl sample assembly unique sub dir creation 2025-05-20 09:50:01 -05:00
1539557005 Always update snapshots if needed (#7105) 2025-05-20 14:34:26 +00:00
1d3ba4e3ac [Fix]: Remove console logs (#7102)
* fix: these got in when they should not have

* fix: spacemacs found wrong eslint again biome thing..
2025-05-20 14:12:32 +00:00
4110aa00db Only update snapshots for tests that repeatedly fail (#7101)
* Only update snapshots for tests that repeatedly fail

* Let TAB silence snapshot capture failures as well
2025-05-20 14:00:33 +00:00
7eb52cda36 Fix: creating a dir ending with .kcl panics the app (#7099)
* Fix: creating a dir ending with .kcl panics the app
Fixes #7082

* Update snapshots

* Update snapshots

* Add test

* tag: ['@electron', '@macos', '@windows']

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2025-05-20 13:53:26 +00:00
db82a54f27 fix: saving off progress 2025-05-20 08:52:51 -05:00
077d1cfcef fix: saving off code 2025-05-20 08:52:31 -05:00
7872fb9cbd Update snapshots on CI (#7069) 2025-05-20 06:00:31 -04:00
651181e62c Restrict subdirectory imports to main.kcl (#7094)
Signed-off-by: Nick Cameron <nrc@ncameron.org>
2025-05-20 18:13:17 +12:00
max
38a245f2fc fix typos in the kcl samples (#7078)
typos
2025-05-20 05:47:33 +00:00
1b4289f93f allow nested files imported (#7090)
* allow nested files

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* fix test

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* disallow bad things

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* add playwright test on windows

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* add playwright test on windows

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* fixes

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* updates

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* updates

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* fix test

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* Update rust/kcl-lib/tests/nested_windows_main_kcl/unparsed@main.kcl.snap

Co-authored-by: graphite-app[bot] <96075541+graphite-app[bot]@users.noreply.github.com>

* updates

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* updates

Signed-off-by: Jess Frazelle <github@jessfraz.com>

---------

Signed-off-by: Jess Frazelle <github@jessfraz.com>
Co-authored-by: graphite-app[bot] <96075541+graphite-app[bot]@users.noreply.github.com>
2025-05-19 22:42:25 -04:00
d0697c24fd Change Sketch to use the units of the module (#7076)
* Change Sketch to use the units of the module

* Update output
2025-05-19 20:20:47 -04:00
8c24e29081 [Fix]: P2E base path is always the project directory, P2E when completed stays in your current file (#7091)
* fix: fixes for p2e

* fix: yep tsc fixes

* fix: fixing reject workflow and navigate
2025-05-19 20:05:38 -04:00
2b9d26e2ff Make Text-to-CAD Edit the default workflow in the toolbar (#7092)
We want users to make edits first and foremost within projects, so we're
going to surface it as the default workflow button in the toolbar. WIP
until I verify that tests are okay with this.
2025-05-19 19:58:18 -04:00
ab148a7654 Fix: Esc key doesn't work in Text-to-CAD prompt (#7089)
* Fix: Esc key doesn't work in Text-to-CAD prompt
Fixes #7086

* Add e2e test because why not
2025-05-19 23:31:33 +00:00
65 changed files with 3438 additions and 2131 deletions

View File

@ -143,7 +143,7 @@ jobs:
- name: Install browsers
run: npm run playwright install --with-deps
- name: Capture snapshots
- name: Test snapshots
uses: nick-fields/retry@v3.0.2
with:
shell: bash
@ -158,6 +158,17 @@ jobs:
CI_PR_NUMBER: ${{ github.event.pull_request.number }}
TARGET: web
- name: Update snapshots
if: always()
run: npm run test:snapshots -- --last-failed --update-snapshots
env:
token: ${{ secrets.KITTYCAD_API_TOKEN_DEV }}
TAB_API_URL: ${{ secrets.TAB_API_URL }}
TAB_API_KEY: ${{ secrets.TAB_API_KEY }}
CI_COMMIT_SHA: ${{ github.event.pull_request.head.sha }}
CI_PR_NUMBER: ${{ github.event.pull_request.number }}
TARGET: web
- uses: actions/upload-artifact@v4
if: ${{ !cancelled() && (success() || failure()) }}
with:
@ -173,7 +184,7 @@ jobs:
id: git-check
run: |
git add e2e/playwright/snapshot-tests.spec.ts-snapshots e2e/playwright/snapshots
if git status | grep -q "Changes to be committed"
if git status | grep --quiet "Changes to be committed"
then echo "modified=true" >> $GITHUB_OUTPUT
else echo "modified=false" >> $GITHUB_OUTPUT
fi

View File

@ -27,9 +27,6 @@ import increment from "util.kcl"
answer = increment(41)
```
Imported files _must_ be in the same project so that units are uniform across
modules. This means that it must be in the same directory.
Import statements must be at the top-level of a file. It is not allowed to have
an `import` statement inside a function or in the body of an ifelse.
@ -58,6 +55,9 @@ Imported symbols can be renamed for convenience or to avoid name collisions.
import increment as inc, decrement as dec from "util.kcl"
```
You can import files from the current directory or from subdirectories, but if importing from a
subdirectory you can only import `main.kcl`.
---
## Functions vs `clone`
@ -229,6 +229,19 @@ The final statement is what's important because it's the return value of the
entire module. The module is expected to return a single object that can be used
as a variable by the file that imports it.
The name of the file or subdirectory is used as the name of the variable within the importing program.
If you want to use a different name, you can do so by using the `as` keyword:
```kcl,norun
import "cube.kcl" // Introduces a new variable called `cube`.
import "cube.kcl" as block // Introduces a new variable called `block`.
import "cube/main.kcl" // Introduces a new variable called `cube`.
import "cube/main.kcl" as block // Introduces a new variable called `block`.
```
If the filename includes hyphens (`-`) or starts with an underscore (`_`), then you must specify a
variable name.
---
## Multiple instances of the same import

View File

@ -684,4 +684,33 @@ c = 3 + a`
highlightedHeaderArg: 'value',
})
})
test('Text-to-CAD command can be closed with escape while in prompt', async ({
page,
homePage,
cmdBar,
}) => {
await homePage.expectState({
projectCards: [],
sortBy: 'last-modified-desc',
})
await homePage.textToCadBtn.click()
await cmdBar.expectState({
stage: 'arguments',
commandName: 'Text-to-CAD Create',
currentArgKey: 'prompt',
currentArgValue: '',
headerArguments: {
Method: 'New project',
NewProjectName: 'untitled',
Prompt: '',
},
highlightedHeaderArg: 'prompt',
})
await page.keyboard.press('Escape')
await cmdBar.toBeClosed()
await cmdBar.expectState({
stage: 'commandBarClosed',
})
})
})

View File

@ -238,6 +238,26 @@ test.describe('when using the file tree to', () => {
}
)
test(
`create new folders and that doesn't trigger a navigation`,
{ tag: ['@electron', '@macos', '@windows'] },
async ({ page, homePage, scene, toolbar, cmdBar }) => {
await homePage.goToModelingScene()
await scene.settled(cmdBar)
await toolbar.openPane('files')
const { createNewFolder } = await getUtils(page, test)
await createNewFolder('folder')
await createNewFolder('folder.kcl')
await test.step(`Postcondition: folders are created and we didn't navigate`, async () => {
await toolbar.expectFileTreeState(['folder', 'folder.kcl', 'main.kcl'])
await expect(toolbar.fileName).toHaveText('main.kcl')
})
}
)
test(
'deleting all files recreates a default main.kcl with no code',
{ tag: '@electron' },

View File

@ -308,6 +308,11 @@ export class CmdBarFixture {
await expect(this.cmdBarElement).toBeVisible({ timeout: 10_000 })
}
async toBeClosed() {
// Check that the command bar is closed
await expect(this.cmdBarElement).not.toBeVisible({ timeout: 10_000 })
}
async expectArgValue(value: string) {
// Check the placeholder project name exists
const actualArgument = await this.cmdBarElement

View File

@ -26,6 +26,7 @@ export class HomePageFixture {
sortByNameBtn!: Locator
appHeader!: Locator
tutorialBtn!: Locator
textToCadBtn!: Locator
constructor(page: Page) {
this.page = page
@ -47,6 +48,7 @@ export class HomePageFixture {
this.sortByNameBtn = this.page.getByTestId('home-sort-by-name')
this.appHeader = this.page.getByTestId('app-header')
this.tutorialBtn = this.page.getByTestId('home-tutorial-button')
this.textToCadBtn = this.page.getByTestId('home-text-to-cad')
}
private _serialiseSortBy = async (): Promise<

View File

@ -2064,3 +2064,59 @@ test(
})
}
)
test(
'nested dir import works on windows',
{ tag: ['@electron', '@windows'] },
async ({ scene, cmdBar, context, page }, testInfo) => {
// Skip if on non-windows
if (process.platform !== 'win32') {
test.skip()
}
await context.folderSetupFn(async (dir) => {
const bracketDir = path.join(dir, 'bracket')
await fsp.mkdir(bracketDir, { recursive: true })
const nestedDir = path.join(bracketDir, 'nested')
await fsp.mkdir(nestedDir, { recursive: true })
await fsp.copyFile(
executorInputPath('cylinder-inches.kcl'),
path.join(nestedDir, 'main.kcl')
)
await fsp.writeFile(
path.join(bracketDir, 'main.kcl'),
`import 'nested\\main.kcl' as thing
thing`
)
})
await page.setBodyDimensions({ width: 1200, height: 500 })
const u = await getUtils(page)
const pointOnModel = { x: 630, y: 280 }
await test.step('Opening the bracket project should load the stream', async () => {
// expect to see the text bracket
await expect(page.getByText('bracket')).toBeVisible()
await page.getByText('bracket').click()
await scene.settled(cmdBar)
await expect(
page.getByRole('button', { name: 'Start Sketch' })
).toBeEnabled({
timeout: 20_000,
})
// gray at this pixel means the stream has loaded in the most
// user way we can verify it (pixel color)
await expect
.poll(() => u.getGreatestPixDiff(pointOnModel, [125, 125, 125]), {
timeout: 10_000,
})
.toBeLessThan(15)
})
}
)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 133 KiB

After

Width:  |  Height:  |  Size: 133 KiB

View File

@ -557,6 +557,14 @@ export async function getUtils(page: Page, test_?: typeof test) {
})
},
createNewFolder: async (name: string) => {
return test?.step(`Create a folder named ${name}`, async () => {
await page.getByTestId('create-folder-button').click()
await page.getByTestId('tree-input-field').fill(name)
await page.keyboard.press('Enter')
})
},
cloneFile: async (name: string) => {
return test?.step(`Cloning file '${name}'`, async () => {
await page

View File

@ -72,25 +72,25 @@ leftSpacerShape = boxModuleFn(width = leftSpacerWidth)
// Module for power switch including front plate and red rocker button
switchPosition = leftSpacerPosition + leftSpacerWidth / 2 + moduleWidth / 2
swtichWidth = moduleWidth
switchWidth = moduleWidth
// Switch Body
switchBody = boxModuleFn(width = moduleWidth)
// Switch Plate
swtichPlateWidth = 20
switchPlateWidth = 20
switchPlateHeight = 30
switchPlateThickness = 3
switchPlateShape = startSketchOn(switchBody, face = END)
|> startProfile(
%,
at = [
-swtichPlateWidth / 2,
-switchPlateWidth / 2,
-switchPlateHeight / 2
],
)
|> yLine(length = switchPlateHeight)
|> xLine(length = swtichPlateWidth)
|> xLine(length = switchPlateWidth)
|> yLine(length = -switchPlateHeight)
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|> close()
@ -104,8 +104,8 @@ switchPlateBody = extrude(switchPlateShape, length = switchPlateThickness)
// Switch Button
switchButtonHeight = 26
swtichButtonWidth = 15
switchButtonShape = startSketchOn(offsetPlane(-YZ, offset = -swtichButtonWidth / 2))
switchButtonWidth = 15
switchButtonShape = startSketchOn(offsetPlane(-YZ, offset = -switchButtonWidth / 2))
|> startProfile(
%,
at = [
@ -121,7 +121,7 @@ switchButtonShape = startSketchOn(offsetPlane(-YZ, offset = -swtichButtonWidth /
])
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|> close()
switchButtonBody = extrude(switchButtonShape, length = swtichButtonWidth)
switchButtonBody = extrude(switchButtonShape, length = switchButtonWidth)
|> translate(
%,
x = switchPosition,
@ -132,7 +132,7 @@ switchButtonBody = extrude(switchButtonShape, length = swtichButtonWidth)
// Spacer between switch and plug modules for layout alignment
secondSpacerWidth = moduleWidth / 2
secondSpacerPosition = switchPosition + swtichWidth / 2 + secondSpacerWidth / 2
secondSpacerPosition = switchPosition + switchWidth / 2 + secondSpacerWidth / 2
secondSpacerBody = boxModuleFn(width = secondSpacerWidth)
|> translate(
%,

View File

@ -53,8 +53,8 @@ baseSlab = boxFn(plane = XY, width = slabWidth, height = -baseThickness)
|> appearance(%, color = "#dbd7d2")
// Create ground platform beneath the base
goundSize = 50
groundBody = boxFn(plane = offsetPlane(XY, offset = -baseThickness), width = goundSize, height = -5)
groundSize = 50
groundBody = boxFn(plane = offsetPlane(XY, offset = -baseThickness), width = groundSize, height = -5)
|> appearance(%, color = "#3a3631")
// Create a single slab with handrail height to be reused with pattern

View File

@ -35,31 +35,28 @@ impl Default for TypedPath {
impl From<&String> for TypedPath {
fn from(path: &String) -> Self {
#[cfg(target_arch = "wasm32")]
{
TypedPath(typed_path::TypedPath::derive(path).to_path_buf())
}
#[cfg(not(target_arch = "wasm32"))]
{
TypedPath(std::path::PathBuf::from(path))
}
TypedPath::new(path)
}
}
impl From<&str> for TypedPath {
fn from(path: &str) -> Self {
TypedPath::new(path)
}
}
impl TypedPath {
pub fn new(path: &str) -> Self {
#[cfg(target_arch = "wasm32")]
{
TypedPath(typed_path::TypedPath::derive(path).to_path_buf())
}
#[cfg(not(target_arch = "wasm32"))]
{
TypedPath(std::path::PathBuf::from(path))
TypedPath(normalise_import(path))
}
}
}
impl TypedPath {
pub fn extension(&self) -> Option<&str> {
#[cfg(target_arch = "wasm32")]
{
@ -85,6 +82,17 @@ impl TypedPath {
}
}
pub fn join_typed(&self, path: &TypedPath) -> Self {
#[cfg(target_arch = "wasm32")]
{
TypedPath(self.0.join(path.0.to_path()))
}
#[cfg(not(target_arch = "wasm32"))]
{
TypedPath(self.0.join(&path.0))
}
}
pub fn parent(&self) -> Option<Self> {
#[cfg(target_arch = "wasm32")]
{
@ -206,3 +214,19 @@ impl schemars::JsonSchema for TypedPath {
gen.subschema_for::<std::path::PathBuf>()
}
}
/// Turn `nested\foo\bar\main.kcl` or `nested/foo/bar/main.kcl`
/// into a PathBuf that works on the host OS.
///
/// * Does **not** touch `..` or symlinks call `canonicalize()` if you need that.
/// * Returns an owned `PathBuf` only when normalisation was required.
fn normalise_import<S: AsRef<str>>(raw: S) -> std::path::PathBuf {
let s = raw.as_ref();
// On Unix we need to swap `\` → `/`. On Windows we leave it alone.
// (Windows happily consumes `/`)
if cfg!(unix) && s.contains('\\') {
std::path::PathBuf::from(s.replace('\\', "/"))
} else {
std::path::Path::new(s).to_path_buf()
}
}

View File

@ -185,9 +185,9 @@ impl ModulePath {
match path {
ImportPath::Kcl { filename: path } | ImportPath::Foreign { path } => {
let resolved_path = if let Some(project_dir) = project_directory {
project_dir.join(path)
project_dir.join_typed(path)
} else {
TypedPath::from(path)
path.clone()
};
ModulePath::Local { value: resolved_path }
}

View File

@ -34,7 +34,7 @@ use crate::{
},
parsing::{ast::digest::Digest, token::NumericSuffix, PIPE_OPERATOR},
source_range::SourceRange,
ModuleId,
ModuleId, TypedPath,
};
mod condition;
@ -1741,8 +1741,8 @@ impl ImportSelector {
#[ts(export)]
#[serde(tag = "type")]
pub enum ImportPath {
Kcl { filename: String },
Foreign { path: String },
Kcl { filename: TypedPath },
Foreign { path: TypedPath },
Std { path: Vec<String> },
}
@ -1811,16 +1811,25 @@ impl ImportStatement {
match &self.path {
ImportPath::Kcl { filename: s } | ImportPath::Foreign { path: s } => {
let mut parts = s.split('.');
let path = parts.next()?;
let _ext = parts.next()?;
let rest = parts.next();
let name = s.to_string_lossy();
if name.ends_with("/main.kcl") || name.ends_with("\\main.kcl") {
let name = &name[..name.len() - 9];
let start = name.rfind(['/', '\\']).map(|s| s + 1).unwrap_or(0);
return Some(name[start..].to_owned());
}
if rest.is_some() {
let name = s.file_name().map(|f| f.to_string())?;
if name.contains('\\') || name.contains('/') {
return None;
}
path.rsplit(&['/', '\\']).next().map(str::to_owned)
// Remove the extension if it exists.
let extension = s.extension();
Some(if let Some(extension) = extension {
name.trim_end_matches(extension).trim_end_matches('.').to_string()
} else {
name
})
}
ImportPath::Std { path } => path.last().cloned(),
}
@ -4332,4 +4341,20 @@ startSketchOn(XY)
"#
);
}
#[test]
fn module_name() {
#[track_caller]
fn assert_mod_name(stmt: &str, name: &str) {
let tokens = crate::parsing::token::lex(stmt, ModuleId::default()).unwrap();
let stmt = crate::parsing::parser::import_stmt(&mut tokens.as_slice()).unwrap();
assert_eq!(stmt.module_name().unwrap(), name);
}
assert_mod_name("import 'foo.kcl'", "foo");
assert_mod_name("import 'foo.kcl' as bar", "bar");
assert_mod_name("import 'main.kcl'", "main");
assert_mod_name("import 'foo/main.kcl'", "foo");
assert_mod_name("import 'foo\\bar\\main.kcl'", "bar");
}
}

View File

@ -35,7 +35,7 @@ use crate::{
token::{Token, TokenSlice, TokenType},
PIPE_OPERATOR, PIPE_SUBSTITUTION_OPERATOR,
},
SourceRange, IMPORT_FILE_EXTENSIONS,
SourceRange, TypedPath, IMPORT_FILE_EXTENSIONS,
};
thread_local! {
@ -1729,7 +1729,7 @@ fn glob(i: &mut TokenSlice) -> PResult<Token> {
.parse_next(i)
}
fn import_stmt(i: &mut TokenSlice) -> PResult<BoxNode<ImportStatement>> {
pub(super) fn import_stmt(i: &mut TokenSlice) -> PResult<BoxNode<ImportStatement>> {
let (visibility, visibility_token) = opt(terminated(item_visibility, whitespace))
.parse_next(i)?
.map_or((ItemVisibility::Default, None), |pair| (pair.0, Some(pair.1)));
@ -1862,18 +1862,50 @@ fn validate_path_string(path_string: String, var_name: bool, path_range: SourceR
let path = if path_string.ends_with(".kcl") {
if path_string
.chars()
.any(|c| !c.is_ascii_alphanumeric() && c != '_' && c != '-' && c != '.')
.any(|c| !c.is_ascii_alphanumeric() && c != '_' && c != '-' && c != '.' && c != '/' && c != '\\')
{
return Err(ErrMode::Cut(
CompilationError::fatal(
path_range,
"import path may only contain alphanumeric characters, underscore, hyphen, and period. KCL files in other directories are not yet supported.",
"import path may only contain alphanumeric characters, `_`, `-`, `.`, `/`, and `\\`.",
)
.into(),
));
}
ImportPath::Kcl { filename: path_string }
if path_string.starts_with("..") {
return Err(ErrMode::Cut(
CompilationError::fatal(
path_range,
"import path may not start with '..'. Cannot traverse to something outside the bounds of your project. If this path is inside your project please find a better way to reference it.",
)
.into(),
));
}
// Make sure they are not using an absolute path.
if path_string.starts_with('/') || path_string.starts_with('\\') {
return Err(ErrMode::Cut(
CompilationError::fatal(
path_range,
"import path may not start with '/' or '\\'. Cannot traverse to something outside the bounds of your project. If this path is inside your project please find a better way to reference it.",
)
.into(),
));
}
if (path_string.contains('/') || path_string.contains('\\'))
&& !(path_string.ends_with("/main.kcl") || path_string.ends_with("\\main.kcl"))
{
return Err(ErrMode::Cut(
CompilationError::fatal(path_range, "import path to a subdirectory must only refer to main.kcl.")
.into(),
));
}
ImportPath::Kcl {
filename: TypedPath::new(&path_string),
}
} else if path_string.starts_with("std::") {
ParseContext::warn(CompilationError::err(
path_range,
@ -1910,7 +1942,9 @@ fn validate_path_string(path_string: String, var_name: bool, path_range: SourceR
format!("unsupported import path format. KCL files can be imported from the current project, CAD files with the following formats are supported: {}", IMPORT_FILE_EXTENSIONS.join(", ")),
))
}
ImportPath::Foreign { path: path_string }
ImportPath::Foreign {
path: TypedPath::new(&path_string),
}
} else {
return Err(ErrMode::Cut(
CompilationError::fatal(
@ -4534,9 +4568,24 @@ e
fn bad_imports() {
assert_err(
r#"import cube from "../cube.kcl""#,
"import path may only contain alphanumeric characters, underscore, hyphen, and period. KCL files in other directories are not yet supported.",
"import path may not start with '..'. Cannot traverse to something outside the bounds of your project. If this path is inside your project please find a better way to reference it.",
[17, 30],
);
assert_err(
r#"import cube from "/cube.kcl""#,
"import path may not start with '/' or '\\'. Cannot traverse to something outside the bounds of your project. If this path is inside your project please find a better way to reference it.",
[17, 28],
);
assert_err(
r#"import cube from "C:\cube.kcl""#,
"import path may only contain alphanumeric characters, `_`, `-`, `.`, `/`, and `\\`.",
[17, 30],
);
assert_err(
r#"import cube from "cube/cube.kcl""#,
"import path to a subdirectory must only refer to main.kcl.",
[17, 32],
);
assert_err(
r#"import * as foo from "dsfs""#,
"as is not the 'from' keyword",

View File

@ -3276,3 +3276,45 @@ mod subtract_regression10 {
super::execute(TEST_NAME, true).await
}
}
mod nested_main_kcl {
const TEST_NAME: &str = "nested_main_kcl";
/// Test parsing KCL.
#[test]
fn parse() {
super::parse(TEST_NAME)
}
/// Test that parsing and unparsing KCL produces the original KCL input.
#[tokio::test(flavor = "multi_thread")]
async fn unparse() {
super::unparse(TEST_NAME).await
}
/// Test that KCL is executed correctly.
#[tokio::test(flavor = "multi_thread")]
async fn kcl_test_execute() {
super::execute(TEST_NAME, true).await
}
}
mod nested_windows_main_kcl {
const TEST_NAME: &str = "nested_windows_main_kcl";
/// Test parsing KCL.
#[test]
fn parse() {
super::parse(TEST_NAME)
}
/// Test that parsing and unparsing KCL produces the original KCL input.
#[tokio::test(flavor = "multi_thread")]
async fn unparse() {
super::unparse(TEST_NAME).await
}
/// Test that KCL is executed correctly.
#[tokio::test(flavor = "multi_thread")]
async fn kcl_test_execute() {
super::execute(TEST_NAME, true).await
}
}

View File

@ -26,7 +26,7 @@ use crate::{
args::{Args, TyF64},
utils::{
arc_center_and_end, get_tangential_arc_to_info, get_x_component, get_y_component,
intersection_with_parallel_line, point_to_len_unit, point_to_mm, untype_point, untyped_point_to_mm,
intersection_with_parallel_line, point_to_len_unit, point_to_mm, untyped_point_to_mm,
TangentialArcInfoInput,
},
},
@ -1396,12 +1396,14 @@ pub(crate) async fn inner_start_profile(
])
.await?;
let (to, ty) = untype_point(at);
// Convert to the units of the module. This is what the frontend expects.
let units = exec_state.length_unit();
let to = point_to_len_unit(at, units);
let current_path = BasePath {
from: to,
to,
tag: tag.clone(),
units: ty.expect_length(),
units,
geo_meta: GeoMeta {
id: move_pen_id,
metadata: args.source_range.into(),
@ -1414,7 +1416,7 @@ pub(crate) async fn inner_start_profile(
artifact_id: path_id.into(),
on: sketch_surface.clone(),
paths: vec![],
units: ty.expect_length(),
units,
mirror: Default::default(),
meta: vec![args.source_range.into()],
tags: if let Some(tag) = &tag {

View File

@ -203,7 +203,7 @@ description: Artifact commands helium-tank.kcl
"path": "[uuid]",
"segment": {
"type": "tangential_arc",
"radius": 95.88500000000016,
"radius": 95.8850000000001,
"offset": {
"unit": "degrees",
"value": -90.0

View File

@ -3271,7 +3271,7 @@ description: Result of parsing pdu-faceplate.kcl
"id": {
"commentStart": 0,
"end": 0,
"name": "swtichWidth",
"name": "switchWidth",
"start": 0,
"type": "Identifier"
},
@ -3385,7 +3385,7 @@ description: Result of parsing pdu-faceplate.kcl
"id": {
"commentStart": 0,
"end": 0,
"name": "swtichPlateWidth",
"name": "switchPlateWidth",
"start": 0,
"type": "Identifier"
},
@ -3586,7 +3586,7 @@ description: Result of parsing pdu-faceplate.kcl
"name": {
"commentStart": 0,
"end": 0,
"name": "swtichPlateWidth",
"name": "switchPlateWidth",
"start": 0,
"type": "Identifier"
},
@ -3768,7 +3768,7 @@ description: Result of parsing pdu-faceplate.kcl
"name": {
"commentStart": 0,
"end": 0,
"name": "swtichPlateWidth",
"name": "switchPlateWidth",
"start": 0,
"type": "Identifier"
},
@ -4257,7 +4257,7 @@ description: Result of parsing pdu-faceplate.kcl
"id": {
"commentStart": 0,
"end": 0,
"name": "swtichButtonWidth",
"name": "switchButtonWidth",
"start": 0,
"type": "Identifier"
},
@ -4339,7 +4339,7 @@ description: Result of parsing pdu-faceplate.kcl
"name": {
"commentStart": 0,
"end": 0,
"name": "swtichButtonWidth",
"name": "switchButtonWidth",
"start": 0,
"type": "Identifier"
},
@ -4994,7 +4994,7 @@ description: Result of parsing pdu-faceplate.kcl
"name": {
"commentStart": 0,
"end": 0,
"name": "swtichButtonWidth",
"name": "switchButtonWidth",
"start": 0,
"type": "Identifier"
},
@ -5329,7 +5329,7 @@ description: Result of parsing pdu-faceplate.kcl
"name": {
"commentStart": 0,
"end": 0,
"name": "swtichWidth",
"name": "switchWidth",
"start": 0,
"type": "Identifier"
},

View File

@ -17406,6 +17406,19 @@ description: Variables in memory after executing pdu-faceplate.kcl
}
}
},
"switchButtonWidth": {
"type": "Number",
"value": 15.0,
"ty": {
"type": "Default",
"len": {
"type": "Mm"
},
"angle": {
"type": "Degrees"
}
}
},
"switchPlateBody": {
"type": "Solid",
"value": {
@ -18170,6 +18183,19 @@ description: Variables in memory after executing pdu-faceplate.kcl
}
}
},
"switchPlateWidth": {
"type": "Number",
"value": 20.0,
"ty": {
"type": "Default",
"len": {
"type": "Mm"
},
"angle": {
"type": "Degrees"
}
}
},
"switchPosition": {
"type": "Number",
"value": -158.4,
@ -18183,33 +18209,7 @@ description: Variables in memory after executing pdu-faceplate.kcl
}
}
},
"swtichButtonWidth": {
"type": "Number",
"value": 15.0,
"ty": {
"type": "Default",
"len": {
"type": "Mm"
},
"angle": {
"type": "Degrees"
}
}
},
"swtichPlateWidth": {
"type": "Number",
"value": 20.0,
"ty": {
"type": "Default",
"len": {
"type": "Mm"
},
"angle": {
"type": "Degrees"
}
}
},
"swtichWidth": {
"switchWidth": {
"type": "Number",
"value": 40.45,
"ty": {

View File

@ -148,7 +148,7 @@ description: Artifact commands router-template-cross-bar.kcl
"segment": {
"type": "line",
"end": {
"x": 22.6312,
"x": 22.6313,
"y": -10.0,
"z": 0.0
},
@ -182,7 +182,7 @@ description: Artifact commands router-template-cross-bar.kcl
"segment": {
"type": "line",
"end": {
"x": 32.6312,
"x": 32.6313,
"y": 10.9406,
"z": 0.0
},
@ -199,7 +199,7 @@ description: Artifact commands router-template-cross-bar.kcl
"segment": {
"type": "line",
"end": {
"x": 102.6312,
"x": 102.6313,
"y": 10.9406,
"z": 0.0
},
@ -233,7 +233,7 @@ description: Artifact commands router-template-cross-bar.kcl
"segment": {
"type": "line",
"end": {
"x": 32.6312,
"x": 32.6313,
"y": 30.9406,
"z": 0.0
},
@ -250,7 +250,7 @@ description: Artifact commands router-template-cross-bar.kcl
"segment": {
"type": "line",
"end": {
"x": 32.6312,
"x": 32.6313,
"y": 41.8813,
"z": 0.0
},
@ -284,7 +284,7 @@ description: Artifact commands router-template-cross-bar.kcl
"segment": {
"type": "line",
"end": {
"x": -32.6312,
"x": -32.6313,
"y": 0.0,
"z": 0.0
},

View File

@ -61,32 +61,32 @@ flowchart LR
50[Solid2d]
end
subgraph path13 [Path]
13["Path<br>[2227, 2279, 0]"]
13["Path<br>[2229, 2281, 0]"]
%% [ProgramBodyItem { index: 25 }, VariableDeclarationDeclaration, VariableDeclarationInit, PipeBodyItem { index: 1 }]
35["Segment<br>[2285, 2318, 0]"]
35["Segment<br>[2287, 2320, 0]"]
%% [ProgramBodyItem { index: 25 }, VariableDeclarationDeclaration, VariableDeclarationInit, PipeBodyItem { index: 2 }]
36["Segment<br>[2324, 2357, 0]"]
36["Segment<br>[2326, 2359, 0]"]
%% [ProgramBodyItem { index: 25 }, VariableDeclarationDeclaration, VariableDeclarationInit, PipeBodyItem { index: 3 }]
37["Segment<br>[2363, 2397, 0]"]
37["Segment<br>[2365, 2399, 0]"]
%% [ProgramBodyItem { index: 25 }, VariableDeclarationDeclaration, VariableDeclarationInit, PipeBodyItem { index: 4 }]
38["Segment<br>[2403, 2459, 0]"]
38["Segment<br>[2405, 2461, 0]"]
%% [ProgramBodyItem { index: 25 }, VariableDeclarationDeclaration, VariableDeclarationInit, PipeBodyItem { index: 5 }]
39["Segment<br>[2465, 2473, 0]"]
39["Segment<br>[2467, 2475, 0]"]
%% [ProgramBodyItem { index: 25 }, VariableDeclarationDeclaration, VariableDeclarationInit, PipeBodyItem { index: 6 }]
49[Solid2d]
end
subgraph path14 [Path]
14["Path<br>[2803, 2858, 0]"]
14["Path<br>[2805, 2860, 0]"]
%% [ProgramBodyItem { index: 29 }, VariableDeclarationDeclaration, VariableDeclarationInit, PipeBodyItem { index: 1 }]
40["Segment<br>[2864, 2893, 0]"]
40["Segment<br>[2866, 2895, 0]"]
%% [ProgramBodyItem { index: 29 }, VariableDeclarationDeclaration, VariableDeclarationInit, PipeBodyItem { index: 2 }]
41["Segment<br>[2899, 2929, 0]"]
41["Segment<br>[2901, 2931, 0]"]
%% [ProgramBodyItem { index: 29 }, VariableDeclarationDeclaration, VariableDeclarationInit, PipeBodyItem { index: 3 }]
42["Segment<br>[2935, 2969, 0]"]
42["Segment<br>[2937, 2971, 0]"]
%% [ProgramBodyItem { index: 29 }, VariableDeclarationDeclaration, VariableDeclarationInit, PipeBodyItem { index: 4 }]
43["Segment<br>[2975, 3031, 0]"]
43["Segment<br>[2977, 3033, 0]"]
%% [ProgramBodyItem { index: 29 }, VariableDeclarationDeclaration, VariableDeclarationInit, PipeBodyItem { index: 5 }]
44["Segment<br>[3037, 3045, 0]"]
44["Segment<br>[3039, 3047, 0]"]
%% [ProgramBodyItem { index: 29 }, VariableDeclarationDeclaration, VariableDeclarationInit, PipeBodyItem { index: 6 }]
45[Solid2d]
end
@ -94,17 +94,17 @@ flowchart LR
%% [ProgramBodyItem { index: 16 }, VariableDeclarationDeclaration, VariableDeclarationInit, FunctionExpressionBody, FunctionExpressionBodyItem { index: 0 }, VariableDeclarationDeclaration, VariableDeclarationInit, PipeBodyItem { index: 0 }]
2["Plane<br>[945, 965, 0]"]
%% [ProgramBodyItem { index: 16 }, VariableDeclarationDeclaration, VariableDeclarationInit, FunctionExpressionBody, FunctionExpressionBodyItem { index: 0 }, VariableDeclarationDeclaration, VariableDeclarationInit, PipeBodyItem { index: 0 }]
3["Plane<br>[1713, 1753, 0]"]
3["Plane<br>[1714, 1754, 0]"]
%% [ProgramBodyItem { index: 21 }, VariableDeclarationDeclaration, VariableDeclarationInit, PipeBodyItem { index: 0 }, CallKwArg { index: 0 }]
4["Plane<br>[1937, 1990, 0]"]
4["Plane<br>[1939, 1992, 0]"]
%% [ProgramBodyItem { index: 22 }, VariableDeclarationDeclaration, VariableDeclarationInit, CallKwArg { index: 0 }]
5["Plane<br>[2780, 2797, 0]"]
5["Plane<br>[2782, 2799, 0]"]
%% [ProgramBodyItem { index: 29 }, VariableDeclarationDeclaration, VariableDeclarationInit, PipeBodyItem { index: 0 }]
6["StartSketchOnPlane<br>[945, 965, 0]"]
%% [ProgramBodyItem { index: 16 }, VariableDeclarationDeclaration, VariableDeclarationInit, FunctionExpressionBody, FunctionExpressionBodyItem { index: 0 }, VariableDeclarationDeclaration, VariableDeclarationInit, PipeBodyItem { index: 0 }]
7["StartSketchOnPlane<br>[945, 965, 0]"]
%% [ProgramBodyItem { index: 16 }, VariableDeclarationDeclaration, VariableDeclarationInit, FunctionExpressionBody, FunctionExpressionBodyItem { index: 0 }, VariableDeclarationDeclaration, VariableDeclarationInit, PipeBodyItem { index: 0 }]
8["StartSketchOnFace<br>[2171, 2221, 0]"]
8["StartSketchOnFace<br>[2173, 2223, 0]"]
%% [ProgramBodyItem { index: 25 }, VariableDeclarationDeclaration, VariableDeclarationInit, PipeBodyItem { index: 0 }]
51["Sweep Extrusion<br>[1209, 1240, 0]"]
%% [ProgramBodyItem { index: 16 }, VariableDeclarationDeclaration, VariableDeclarationInit, FunctionExpressionBody, FunctionExpressionBodyItem { index: 1 }, VariableDeclarationDeclaration, VariableDeclarationInit]
@ -114,9 +114,9 @@ flowchart LR
%% [ProgramBodyItem { index: 16 }, VariableDeclarationDeclaration, VariableDeclarationInit, FunctionExpressionBody, FunctionExpressionBodyItem { index: 1 }, VariableDeclarationDeclaration, VariableDeclarationInit]
54["Sweep Extrusion<br>[1209, 1240, 0]"]
%% [ProgramBodyItem { index: 16 }, VariableDeclarationDeclaration, VariableDeclarationInit, FunctionExpressionBody, FunctionExpressionBodyItem { index: 1 }, VariableDeclarationDeclaration, VariableDeclarationInit]
55["Sweep Extrusion<br>[2545, 2593, 0]"]
55["Sweep Extrusion<br>[2547, 2595, 0]"]
%% [ProgramBodyItem { index: 26 }, VariableDeclarationDeclaration, VariableDeclarationInit, PipeBodyItem { index: 0 }]
56["Sweep Extrusion<br>[3051, 3082, 0]"]
56["Sweep Extrusion<br>[3053, 3084, 0]"]
%% [ProgramBodyItem { index: 29 }, VariableDeclarationDeclaration, VariableDeclarationInit, PipeBodyItem { index: 7 }]
57[Wall]
%% face_code_ref=Missing NodePath

View File

@ -1997,7 +1997,7 @@ description: Result of parsing spinning-highrise-tower.kcl
"id": {
"commentStart": 0,
"end": 0,
"name": "goundSize",
"name": "groundSize",
"start": 0,
"type": "Identifier"
},
@ -2138,7 +2138,7 @@ description: Result of parsing spinning-highrise-tower.kcl
"name": {
"commentStart": 0,
"end": 0,
"name": "goundSize",
"name": "groundSize",
"start": 0,
"type": "Identifier"
},

View File

@ -3936,19 +3936,6 @@ description: Variables in memory after executing spinning-highrise-tower.kcl
}
}
},
"goundSize": {
"type": "Number",
"value": 50.0,
"ty": {
"type": "Default",
"len": {
"type": "M"
},
"angle": {
"type": "Degrees"
}
}
},
"groundBody": {
"type": "Solid",
"value": {
@ -4148,6 +4135,19 @@ description: Variables in memory after executing spinning-highrise-tower.kcl
"sectional": false
}
},
"groundSize": {
"type": "Number",
"value": 50.0,
"ty": {
"type": "Default",
"len": {
"type": "M"
},
"angle": {
"type": "Degrees"
}
}
},
"handrailHeight": {
"type": "Number",
"value": 1.2,

View File

@ -0,0 +1,184 @@
---
source: kcl-lib/src/simulation_tests.rs
description: Artifact commands nested_main_kcl.kcl
---
[
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "edge_lines_visible",
"hidden": false
}
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "object_visible",
"object_id": "[uuid]",
"hidden": true
}
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "object_visible",
"object_id": "[uuid]",
"hidden": true
}
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "make_plane",
"origin": {
"x": 0.0,
"y": 0.0,
"z": 0.0
},
"x_axis": {
"x": 1.0,
"y": 0.0,
"z": 0.0
},
"y_axis": {
"x": 0.0,
"y": 1.0,
"z": 0.0
},
"size": 60.0,
"clobber": false,
"hide": true
}
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "close_path",
"path_id": "[uuid]"
}
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "enable_sketch_mode",
"entity_id": "[uuid]",
"ortho": false,
"animated": false,
"adjust_camera": false,
"planar_normal": {
"x": 0.0,
"y": 0.0,
"z": 1.0
}
}
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "extend_path",
"path": "[uuid]",
"segment": {
"type": "arc",
"center": {
"x": 15.0,
"y": 0.0
},
"radius": 5.0,
"start": {
"unit": "degrees",
"value": 0.0
},
"end": {
"unit": "degrees",
"value": 360.0
},
"relative": false
}
}
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "move_path_pen",
"path": "[uuid]",
"to": {
"x": 20.0,
"y": 0.0,
"z": 0.0
}
}
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "sketch_mode_disable"
}
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "start_path"
}
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "object_bring_to_front",
"object_id": "[uuid]"
}
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "revolve",
"target": "[uuid]",
"origin": {
"x": 0.0,
"y": 0.0,
"z": 0.0
},
"axis": {
"x": 0.0,
"y": 1.0,
"z": 0.0
},
"axis_is_2d": true,
"angle": {
"unit": "degrees",
"value": 360.0
},
"tolerance": 0.0000001,
"opposite": "None"
}
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "solid3d_get_adjacency_info",
"object_id": "[uuid]",
"edge_id": "[uuid]"
}
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "solid3d_get_extrusion_face_info",
"object_id": "[uuid]",
"edge_id": "[uuid]"
}
}
]

View File

@ -0,0 +1,6 @@
---
source: kcl-lib/src/simulation_tests.rs
description: Artifact graph flowchart nested_main_kcl.kcl
extension: md
snapshot_kind: binary
---

View File

@ -0,0 +1,23 @@
```mermaid
flowchart LR
subgraph path2 [Path]
2["Path<br>[43, 81, 1]"]
3["Segment<br>[43, 81, 1]"]
4[Solid2d]
end
1["Plane<br>[18, 35, 1]"]
5["Sweep Revolve<br>[89, 142, 1]"]
6[Wall]
%% face_code_ref=Missing NodePath
7["SweepEdge Adjacent"]
1 --- 2
2 --- 3
2 --- 4
2 ---- 5
5 <--x 3
3 --- 6
3 --- 7
5 --- 6
5 --- 7
6 --- 7
```

View File

@ -0,0 +1,73 @@
---
source: kcl-lib/src/simulation_tests.rs
description: Result of parsing nested_main_kcl.kcl
---
{
"Ok": {
"body": [
{
"commentStart": 0,
"end": 0,
"path": {
"type": "Kcl",
"filename": "nested/foo/bar/main.kcl"
},
"selector": {
"type": "None",
"alias": {
"commentStart": 0,
"end": 0,
"name": "bar",
"start": 0,
"type": "Identifier"
}
},
"start": 0,
"type": "ImportStatement",
"type": "ImportStatement"
},
{
"commentStart": 0,
"end": 0,
"expression": {
"abs_path": false,
"commentStart": 0,
"end": 0,
"name": {
"commentStart": 0,
"end": 0,
"name": "bar",
"start": 0,
"type": "Identifier"
},
"path": [],
"start": 0,
"type": "Name",
"type": "Name"
},
"start": 0,
"type": "ExpressionStatement",
"type": "ExpressionStatement"
}
],
"commentStart": 0,
"end": 0,
"nonCodeMeta": {
"nonCodeNodes": {
"0": [
{
"commentStart": 0,
"end": 0,
"start": 0,
"type": "NonCodeNode",
"value": {
"type": "newLine"
}
}
]
},
"startNodes": []
},
"start": 0
}
}

View File

@ -0,0 +1,3 @@
import "nested/foo/bar/main.kcl" as bar
bar

View File

@ -0,0 +1,7 @@
// A donut shape.
startSketchOn(XY)
|> circle( center = [15, 0], radius = 5 )
|> revolve(
angle = 360,
axis = Y,
)

View File

@ -0,0 +1,18 @@
---
source: kcl-lib/src/simulation_tests.rs
description: Operations executed nested_main_kcl.kcl
---
[
{
"type": "GroupBegin",
"group": {
"type": "ModuleInstance",
"name": "main.kcl",
"moduleId": 0
},
"sourceRange": []
},
{
"type": "GroupEnd"
}
]

View File

@ -0,0 +1,10 @@
---
source: kcl-lib/src/simulation_tests.rs
description: Variables in memory after executing nested_main_kcl.kcl
---
{
"bar": {
"type": "Module",
"value": 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 150 KiB

View File

@ -0,0 +1,7 @@
---
source: kcl-lib/src/simulation_tests.rs
description: Result of unparsing nested_main_kcl.kcl
---
import "nested/foo/bar/main.kcl" as bar
bar

View File

@ -0,0 +1,8 @@
---
source: kcl-lib/src/simulation_tests.rs
description: Result of unparsing tests/nested_main_kcl/nested/foo/bar/main.kcl
---
// A donut shape.
startSketchOn(XY)
|> circle(center = [15, 0], radius = 5)
|> revolve(angle = 360, axis = Y)

View File

@ -0,0 +1,184 @@
---
source: kcl-lib/src/simulation_tests.rs
description: Artifact commands nested_main_kcl.kcl
---
[
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "edge_lines_visible",
"hidden": false
}
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "object_visible",
"object_id": "[uuid]",
"hidden": true
}
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "object_visible",
"object_id": "[uuid]",
"hidden": true
}
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "make_plane",
"origin": {
"x": 0.0,
"y": 0.0,
"z": 0.0
},
"x_axis": {
"x": 1.0,
"y": 0.0,
"z": 0.0
},
"y_axis": {
"x": 0.0,
"y": 1.0,
"z": 0.0
},
"size": 60.0,
"clobber": false,
"hide": true
}
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "close_path",
"path_id": "[uuid]"
}
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "enable_sketch_mode",
"entity_id": "[uuid]",
"ortho": false,
"animated": false,
"adjust_camera": false,
"planar_normal": {
"x": 0.0,
"y": 0.0,
"z": 1.0
}
}
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "extend_path",
"path": "[uuid]",
"segment": {
"type": "arc",
"center": {
"x": 15.0,
"y": 0.0
},
"radius": 5.0,
"start": {
"unit": "degrees",
"value": 0.0
},
"end": {
"unit": "degrees",
"value": 360.0
},
"relative": false
}
}
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "move_path_pen",
"path": "[uuid]",
"to": {
"x": 20.0,
"y": 0.0,
"z": 0.0
}
}
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "sketch_mode_disable"
}
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "start_path"
}
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "object_bring_to_front",
"object_id": "[uuid]"
}
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "revolve",
"target": "[uuid]",
"origin": {
"x": 0.0,
"y": 0.0,
"z": 0.0
},
"axis": {
"x": 0.0,
"y": 1.0,
"z": 0.0
},
"axis_is_2d": true,
"angle": {
"unit": "degrees",
"value": 360.0
},
"tolerance": 0.0000001,
"opposite": "None"
}
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "solid3d_get_adjacency_info",
"object_id": "[uuid]",
"edge_id": "[uuid]"
}
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "solid3d_get_extrusion_face_info",
"object_id": "[uuid]",
"edge_id": "[uuid]"
}
}
]

View File

@ -0,0 +1,6 @@
---
source: kcl-lib/src/simulation_tests.rs
description: Artifact graph flowchart nested_main_kcl.kcl
extension: md
snapshot_kind: binary
---

View File

@ -0,0 +1,23 @@
```mermaid
flowchart LR
subgraph path2 [Path]
2["Path<br>[43, 81, 1]"]
3["Segment<br>[43, 81, 1]"]
4[Solid2d]
end
1["Plane<br>[18, 35, 1]"]
5["Sweep Revolve<br>[89, 142, 1]"]
6[Wall]
%% face_code_ref=Missing NodePath
7["SweepEdge Adjacent"]
1 --- 2
2 --- 3
2 --- 4
2 ---- 5
5 <--x 3
3 --- 6
3 --- 7
5 --- 6
5 --- 7
6 --- 7
```

View File

@ -0,0 +1,67 @@
---
source: kcl-lib/src/simulation_tests.rs
description: Result of parsing nested_windows_main_kcl.kcl
---
{
"Ok": {
"body": [
{
"commentStart": 0,
"end": 0,
"path": {
"type": "Kcl",
"filename": "nested/foo/bar/main.kcl"
},
"selector": {
"type": "None",
"alias": null
},
"start": 0,
"type": "ImportStatement",
"type": "ImportStatement"
},
{
"commentStart": 0,
"end": 0,
"expression": {
"abs_path": false,
"commentStart": 0,
"end": 0,
"name": {
"commentStart": 0,
"end": 0,
"name": "bar",
"start": 0,
"type": "Identifier"
},
"path": [],
"start": 0,
"type": "Name",
"type": "Name"
},
"start": 0,
"type": "ExpressionStatement",
"type": "ExpressionStatement"
}
],
"commentStart": 0,
"end": 0,
"nonCodeMeta": {
"nonCodeNodes": {
"0": [
{
"commentStart": 0,
"end": 0,
"start": 0,
"type": "NonCodeNode",
"value": {
"type": "newLine"
}
}
]
},
"startNodes": []
},
"start": 0
}
}

View File

@ -0,0 +1,3 @@
import "nested\foo\bar\main.kcl"
bar

View File

@ -0,0 +1,7 @@
// A donut shape.
startSketchOn(XY)
|> circle( center = [15, 0], radius = 5 )
|> revolve(
angle = 360,
axis = Y,
)

View File

@ -0,0 +1,18 @@
---
source: kcl-lib/src/simulation_tests.rs
description: Operations executed nested_main_kcl.kcl
---
[
{
"type": "GroupBegin",
"group": {
"type": "ModuleInstance",
"name": "main.kcl",
"moduleId": 0
},
"sourceRange": []
},
{
"type": "GroupEnd"
}
]

View File

@ -0,0 +1,10 @@
---
source: kcl-lib/src/simulation_tests.rs
description: Variables in memory after executing nested_main_kcl.kcl
---
{
"bar": {
"type": "Module",
"value": 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 150 KiB

View File

@ -0,0 +1,7 @@
---
source: kcl-lib/src/simulation_tests.rs
description: Result of unparsing nested_windows_main_kcl.kcl
---
import "nested/foo/bar/main.kcl"
bar

View File

@ -0,0 +1,8 @@
---
source: kcl-lib/src/simulation_tests.rs
description: Result of unparsing tests/nested_windows_main_kcl/nested/foo/bar/main.kcl
---
// A donut shape.
startSketchOn(XY)
|> circle(center = [15, 0], radius = 5)
|> revolve(angle = 360, axis = Y)

View File

@ -86,11 +86,11 @@ flowchart LR
8 --- 18
8 ---- 20
8 --- 21
12 --- 22
12 <--x 23
12 <--x 22
12 --- 23
12 <--x 24
16 --- 25
16 <--x 26
16 <--x 25
16 --- 26
16 <--x 27
19 --- 22
19 --- 23

View File

@ -94,7 +94,7 @@ function CommandBarHeader({ children }: React.PropsWithChildren<object>) {
{selectedCommand.displayName || selectedCommand.name}
</span>
{selectedCommand.status === 'experimental' ? (
<span className="text-ml-black text-xs bg-ml-green rounded-full ml-2 px-2 py-1">
<span className="uppercase text-xs rounded-full ml-2 px-2 py-1 border border-ml-green dark:text-ml-green">
experimental
</span>
) : (

View File

@ -86,6 +86,8 @@ function CommandBarTextareaInput({
formRef.current?.dispatchEvent(
new Event('submit', { bubbles: true })
)
} else if (event.key === 'Escape') {
commandBarActor.send({ type: 'Close' })
}
}}
autoFocus
@ -109,6 +111,8 @@ const useTextareaAutoGrow = (ref: RefObject<HTMLTextAreaElement>) => {
}
if (ref.current === null) return
// Run initially to set all this stuff at the start
listener()
ref.current.addEventListener('input', listener)
return () => {

View File

@ -654,7 +654,12 @@ export const useFileTreeOperations = () => {
send({
type: 'Create file',
data: { name: args.name, makeDir: true, shouldSetToRename: false },
data: {
name: args.name,
makeDir: true,
silent: true,
shouldSetToRename: false,
},
})
}

View File

@ -75,6 +75,7 @@ import {
MAKE_TOAST_MESSAGES,
EXECUTION_TYPE_MOCK,
FILE_EXT,
PROJECT_ENTRYPOINT,
} from '@src/lib/constants'
import { exportMake } from '@src/lib/exportMake'
import { exportSave } from '@src/lib/exportSave'
@ -1759,7 +1760,8 @@ export const ModelingMachineProvider = ({
)
let basePath = ''
if (isDesktop() && context?.project?.children) {
basePath = context?.selectedDirectory?.path
// Use the entire project directory as the basePath for prompt to edit, do not use relative subdir paths
basePath = context?.project?.path
const filePromises: Promise<FileMeta | null>[] = []
let uploadSize = 0
const recursivelyPushFilePromises = (files: FileEntry[]) => {
@ -1826,6 +1828,13 @@ export const ModelingMachineProvider = ({
)
}
}
let filePath = file?.path
// When prompt to edit finishes, try to route to the file they were in otherwise go to main.kcl
if (filePath) {
filePath = window.electron.path.relative(basePath, filePath)
} else {
filePath = PROJECT_ENTRYPOINT
}
return await promptToEditFlow({
projectFiles,
prompt: input.prompt,
@ -1833,6 +1842,7 @@ export const ModelingMachineProvider = ({
token,
artifactGraph: kclManager.artifactGraph,
projectName: context.project.name,
filePath,
})
}),
},

View File

@ -35,6 +35,7 @@ import {
} from '@src/machines/systemIO/utils'
import {
useProjectDirectoryPath,
useRequestedFileName,
useRequestedProjectName,
} from '@src/machines/systemIO/hooks'
import { commandBarActor } from '@src/lib/singletons'
@ -498,7 +499,14 @@ export function ToastPromptToEditCadSuccess({
token?: string
}) {
const modelId = data.id
const requestedProjectName = useRequestedProjectName()
const possibleRequestedProjectName = useRequestedProjectName()
const possibleRequestedFileName = useRequestedFileName()
// Depends on navigation method
const requestedProjectName = {
name:
possibleRequestedProjectName.name || possibleRequestedFileName.project,
}
return (
<div className="flex gap-4 min-w-80">
@ -548,6 +556,7 @@ export function ToastPromptToEditCadSuccess({
await writeOverFilesAndExecute({
requestedFiles: requestedFiles,
projectName: requestedProjectName.name,
filePath: possibleRequestedFileName.file,
})
} else {
codeManager.updateCodeEditor(oldCode)
@ -588,18 +597,32 @@ export function ToastPromptToEditCadSuccess({
export const writeOverFilesAndExecute = async ({
requestedFiles,
projectName,
filePath,
}: {
requestedFiles: RequestedKCLFile[]
projectName: string
filePath?: string | undefined
}) => {
systemIOActor.send({
type: SystemIOMachineEvents.bulkCreateKCLFilesAndNavigateToProject,
data: {
files: requestedFiles,
requestedProjectName: projectName,
override: true,
},
})
if (filePath) {
systemIOActor.send({
type: SystemIOMachineEvents.bulkCreateKCLFilesAndNavigateToFile,
data: {
files: requestedFiles,
requestedProjectName: projectName,
requestedFileNameWithExtension: filePath,
override: true,
},
})
} else {
systemIOActor.send({
type: SystemIOMachineEvents.bulkCreateKCLFilesAndNavigateToProject,
data: {
files: requestedFiles,
requestedProjectName: projectName,
override: true,
},
})
}
// to await the result of the send event above
await waitForIdleState({ systemIOActor })

View File

@ -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,10 +303,9 @@ export function createApplicationCommands({
return value
},
options: ({ argumentsToSubmit }) => {
const samples =
isDesktop() && argumentsToSubmit.method !== 'existingProject'
? everyKclSample
: kclSamplesManifestWithNoMultipleFiles
const samples = isDesktop()
? everyKclSample
: kclSamplesManifestWithNoMultipleFiles
return samples.map((sample) => {
return {
value: sample.pathFromProjectDirectoryToFirstFile,
@ -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' },
]
}
return [
{ name: 'New project', value: 'newProject', isCurrent: true },
{ name: 'Existing project', value: 'existingProject' },
]
} else {
return [{ name: 'Overwrite', value: 'existingProject' }]
}

View File

@ -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',

View File

@ -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',

View File

@ -415,6 +415,7 @@ export async function promptToEditFlow({
token,
artifactGraph,
projectName,
filePath,
}: {
prompt: string
selections: Selections
@ -422,6 +423,7 @@ export async function promptToEditFlow({
token?: string
artifactGraph: ArtifactGraph
projectName: string
filePath: string | undefined
}) {
const result = await doPromptEdit({
prompt,
@ -498,6 +500,7 @@ export async function promptToEditFlow({
await writeOverFilesAndExecute({
requestedFiles,
projectName,
filePath,
})
} else {
const newCode = result.outputs['main.kcl']

View File

@ -427,6 +427,21 @@ export const toolbarConfig: Record<ToolbarModeName, ToolbarMode> = {
{
id: 'ai',
array: [
{
id: 'prompt-to-edit',
onClick: () =>
commandBarActor.send({
type: 'Find and select command',
data: { name: 'Prompt-to-edit', groupId: 'modeling' },
}),
icon: 'sparkles',
iconColor: '#29FFA4',
alwaysDark: true,
status: IS_ML_EXPERIMENTAL ? 'experimental' : 'available',
title: 'Modify with Zoo Text-to-CAD',
description: 'Edit geometry with AI / ML.',
links: [],
},
{
id: 'text-to-cad',
onClick: () => {
@ -457,21 +472,6 @@ export const toolbarConfig: Record<ToolbarModeName, ToolbarMode> = {
},
],
},
{
id: 'prompt-to-edit',
onClick: () =>
commandBarActor.send({
type: 'Find and select command',
data: { name: 'Prompt-to-edit', groupId: 'modeling' },
}),
icon: 'sparkles',
iconColor: '#29FFA4',
alwaysDark: true,
status: IS_ML_EXPERIMENTAL ? 'experimental' : 'available',
title: 'Modify with Zoo Text-to-CAD',
description: 'Edit geometry with AI / ML.',
links: [],
},
],
},
],

View File

@ -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',

View File

@ -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
}

View File

@ -102,6 +102,16 @@ export const systemIOMachine = setup({
requestedSubRoute?: string
}
}
| {
type: SystemIOMachineEvents.bulkCreateKCLFilesAndNavigateToFile
data: {
files: RequestedKCLFile[]
requestedProjectName: string
requestedFileNameWithExtension: string
override?: boolean
requestedSubRoute?: string
}
}
| {
type: SystemIOMachineEvents.importFileFromURL
data: {
@ -339,6 +349,27 @@ export const systemIOMachine = setup({
return { message: '', fileName: '', projectName: '', subRoute: '' }
}
),
[SystemIOMachineActors.bulkCreateKCLFilesAndNavigateToFile]: fromPromise(
async ({
input,
}: {
input: {
context: SystemIOContext
files: RequestedKCLFile[]
rootContext: AppMachineContext
requestedProjectName: string
requestedFileNameWithExtension: string
requestedSubRoute?: string
}
}): Promise<{
message: string
fileName: string
projectName: string
subRoute: string
}> => {
return { message: '', fileName: '', projectName: '', subRoute: '' }
}
),
},
}).createMachine({
initial: SystemIOMachineStates.idle,
@ -414,6 +445,9 @@ export const systemIOMachine = setup({
target:
SystemIOMachineStates.bulkCreatingKCLFilesAndNavigateToProject,
},
[SystemIOMachineEvents.bulkCreateKCLFilesAndNavigateToFile]: {
target: SystemIOMachineStates.bulkCreatingKCLFilesAndNavigateToFile,
},
},
},
[SystemIOMachineStates.readingFolders]: {
@ -683,5 +717,53 @@ export const systemIOMachine = setup({
},
},
},
[SystemIOMachineStates.bulkCreatingKCLFilesAndNavigateToFile]: {
invoke: {
id: SystemIOMachineActors.bulkCreateKCLFilesAndNavigateToFile,
src: SystemIOMachineActors.bulkCreateKCLFilesAndNavigateToFile,
input: ({ context, event, self }) => {
assertEvent(
event,
SystemIOMachineEvents.bulkCreateKCLFilesAndNavigateToFile
)
return {
context,
files: event.data.files,
rootContext: self.system.get('root').getSnapshot().context,
requestedProjectName: event.data.requestedProjectName,
override: event.data.override,
requestedFileNameWithExtension:
event.data.requestedFileNameWithExtension,
requestedSubRoute: event.data.requestedSubRoute,
}
},
onDone: {
target: SystemIOMachineStates.readingFolders,
actions: [
assign({
requestedFileName: ({ event }) => {
assertEvent(
event,
SystemIOMachineEvents.done_bulkCreateKCLFilesAndNavigateToFile
)
// 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,
}
},
}),
SystemIOMachineActions.toastSuccess,
],
},
onError: {
target: SystemIOMachineStates.idle,
actions: [SystemIOMachineActions.toastError],
},
},
},
},
})

View File

@ -370,5 +370,33 @@ export const systemIOMachineDesktop = systemIOMachine.provide({
}
}
),
[SystemIOMachineActors.bulkCreateKCLFilesAndNavigateToFile]: fromPromise(
async ({
input,
}: {
input: {
context: SystemIOContext
files: RequestedKCLFile[]
rootContext: AppMachineContext
requestedProjectName: string
override?: boolean
requestedFileNameWithExtension: string
requestedSubRoute?: string
}
}) => {
const message = await sharedBulkCreateWorkflow({
input: {
...input,
override: input.override,
},
})
return {
...message,
projectName: input.requestedProjectName,
fileName: input.requestedFileNameWithExtension || '',
subRoute: input.requestedSubRoute || '',
}
}
),
},
})

View File

@ -15,6 +15,7 @@ export enum SystemIOMachineActors {
deleteKCLFile = 'delete kcl delete',
bulkCreateKCLFiles = 'bulk create kcl files',
bulkCreateKCLFilesAndNavigateToProject = 'bulk create kcl files and navigate to project',
bulkCreateKCLFilesAndNavigateToFile = 'bulk create kcl files and navigate to file',
}
export enum SystemIOMachineStates {
@ -31,6 +32,7 @@ export enum SystemIOMachineStates {
deletingKCLFile = 'deletingKCLFile',
bulkCreatingKCLFiles = 'bulkCreatingKCLFiles',
bulkCreatingKCLFilesAndNavigateToProject = 'bulkCreatingKCLFilesAndNavigateToProject',
bulkCreatingKCLFilesAndNavigateToFile = 'bulkCreatingKCLFilesAndNavigateToFile',
}
const donePrefix = 'xstate.done.actor.'
@ -56,6 +58,9 @@ export enum SystemIOMachineEvents {
deleteKCLFile = 'delete kcl file',
bulkCreateKCLFiles = 'bulk create kcl files',
bulkCreateKCLFilesAndNavigateToProject = 'bulk create kcl files and navigate to project',
bulkCreateKCLFilesAndNavigateToFile = 'bulk create kcl files and navigate to file',
done_bulkCreateKCLFilesAndNavigateToFile = donePrefix +
'bulk create kcl files and navigate to file',
}
export enum SystemIOMachineActions {