Compare commits
8 Commits
nested_dir
...
pierremtb/
Author | SHA1 | Date | |
---|---|---|---|
0573801381 | |||
140e8b56fb | |||
4d203b0c0c | |||
04d6fcf0c4 | |||
30b2a765fc | |||
8ddbb488d6 | |||
db82a54f27 | |||
077d1cfcef |
2
.github/ci-cd-scripts/upload-results.sh
vendored
2
.github/ci-cd-scripts/upload-results.sh
vendored
@ -6,7 +6,6 @@ if [ -z "${TAB_API_URL:-}" ] || [ -z "${TAB_API_KEY:-}" ]; then
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
project="https://github.com/KittyCAD/modeling-app"
|
project="https://github.com/KittyCAD/modeling-app"
|
||||||
suite="${CI_SUITE:-unit}"
|
|
||||||
branch="${GITHUB_HEAD_REF:-${GITHUB_REF_NAME:-}}"
|
branch="${GITHUB_HEAD_REF:-${GITHUB_REF_NAME:-}}"
|
||||||
commit="${CI_COMMIT_SHA:-${GITHUB_SHA:-}}"
|
commit="${CI_COMMIT_SHA:-${GITHUB_SHA:-}}"
|
||||||
|
|
||||||
@ -14,7 +13,6 @@ echo "Uploading batch results"
|
|||||||
curl --silent --request POST \
|
curl --silent --request POST \
|
||||||
--header "X-API-Key: ${TAB_API_KEY}" \
|
--header "X-API-Key: ${TAB_API_KEY}" \
|
||||||
--form "project=${project}" \
|
--form "project=${project}" \
|
||||||
--form "suite=${suite}" \
|
|
||||||
--form "branch=${branch}" \
|
--form "branch=${branch}" \
|
||||||
--form "commit=${commit}" \
|
--form "commit=${commit}" \
|
||||||
--form "tests=@test-results/junit.xml" \
|
--form "tests=@test-results/junit.xml" \
|
||||||
|
1
.github/workflows/cargo-test.yml
vendored
1
.github/workflows/cargo-test.yml
vendored
@ -193,7 +193,6 @@ jobs:
|
|||||||
TAB_API_KEY: ${{ secrets.TAB_API_KEY }}
|
TAB_API_KEY: ${{ secrets.TAB_API_KEY }}
|
||||||
CI_COMMIT_SHA: ${{ github.event.pull_request.head.sha }}
|
CI_COMMIT_SHA: ${{ github.event.pull_request.head.sha }}
|
||||||
CI_PR_NUMBER: ${{ github.event.pull_request.number }}
|
CI_PR_NUMBER: ${{ github.event.pull_request.number }}
|
||||||
CI_SUITE: unit:kcl
|
|
||||||
run-internal-kcl-samples:
|
run-internal-kcl-samples:
|
||||||
name: cargo test (internal-kcl-samples)
|
name: cargo test (internal-kcl-samples)
|
||||||
runs-on:
|
runs-on:
|
||||||
|
2
.github/workflows/e2e-tests.yml
vendored
2
.github/workflows/e2e-tests.yml
vendored
@ -156,7 +156,6 @@ jobs:
|
|||||||
TAB_API_KEY: ${{ secrets.TAB_API_KEY }}
|
TAB_API_KEY: ${{ secrets.TAB_API_KEY }}
|
||||||
CI_COMMIT_SHA: ${{ github.event.pull_request.head.sha }}
|
CI_COMMIT_SHA: ${{ github.event.pull_request.head.sha }}
|
||||||
CI_PR_NUMBER: ${{ github.event.pull_request.number }}
|
CI_PR_NUMBER: ${{ github.event.pull_request.number }}
|
||||||
CI_SUITE: snapshots
|
|
||||||
TARGET: web
|
TARGET: web
|
||||||
|
|
||||||
- name: Update snapshots
|
- name: Update snapshots
|
||||||
@ -168,7 +167,6 @@ jobs:
|
|||||||
TAB_API_KEY: ${{ secrets.TAB_API_KEY }}
|
TAB_API_KEY: ${{ secrets.TAB_API_KEY }}
|
||||||
CI_COMMIT_SHA: ${{ github.event.pull_request.head.sha }}
|
CI_COMMIT_SHA: ${{ github.event.pull_request.head.sha }}
|
||||||
CI_PR_NUMBER: ${{ github.event.pull_request.number }}
|
CI_PR_NUMBER: ${{ github.event.pull_request.number }}
|
||||||
CI_SUITE: snapshots
|
|
||||||
TARGET: web
|
TARGET: web
|
||||||
|
|
||||||
- uses: actions/upload-artifact@v4
|
- uses: actions/upload-artifact@v4
|
||||||
|
@ -105,19 +105,14 @@ export class CmdBarFixture {
|
|||||||
expectState = async (expected: CmdBarSerialised) => {
|
expectState = async (expected: CmdBarSerialised) => {
|
||||||
return expect.poll(() => this._serialiseCmdBar()).toEqual(expected)
|
return expect.poll(() => this._serialiseCmdBar()).toEqual(expected)
|
||||||
}
|
}
|
||||||
/**
|
/** The method will use buttons OR press enter randomly to progress the cmdbar,
|
||||||
* This method is used to progress the command bar to the next step, defaulting to clicking the next button.
|
* this could have unexpected results depending on what's focused
|
||||||
* Optionally, with the `shouldUseKeyboard` parameter, it will hit `Enter` to progress.
|
*
|
||||||
* * TODO: This method assumes the user has a valid input to the current stage,
|
* TODO: This method assumes the user has a valid input to the current stage,
|
||||||
* and assumes we are past the `pickCommand` step.
|
* and assumes we are past the `pickCommand` step.
|
||||||
*/
|
*/
|
||||||
progressCmdBar = async (shouldUseKeyboard = false) => {
|
progressCmdBar = async (shouldFuzzProgressMethod = true) => {
|
||||||
await this.page.waitForTimeout(2000)
|
await this.page.waitForTimeout(2000)
|
||||||
if (shouldUseKeyboard) {
|
|
||||||
await this.page.keyboard.press('Enter')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const arrowButton = this.page.getByRole('button', {
|
const arrowButton = this.page.getByRole('button', {
|
||||||
name: 'arrow right Continue',
|
name: 'arrow right Continue',
|
||||||
})
|
})
|
||||||
|
@ -61,7 +61,6 @@ class MyAPIReporter implements Reporter {
|
|||||||
const payload = {
|
const payload = {
|
||||||
// Required information
|
// Required information
|
||||||
project: 'https://github.com/KittyCAD/modeling-app',
|
project: 'https://github.com/KittyCAD/modeling-app',
|
||||||
suite: process.env.CI_SUITE || 'e2e',
|
|
||||||
branch: process.env.GITHUB_HEAD_REF || process.env.GITHUB_REF_NAME || '',
|
branch: process.env.GITHUB_HEAD_REF || process.env.GITHUB_REF_NAME || '',
|
||||||
commit: process.env.CI_COMMIT_SHA || process.env.GITHUB_SHA || '',
|
commit: process.env.CI_COMMIT_SHA || process.env.GITHUB_SHA || '',
|
||||||
test: test.titlePath().slice(2).join(' › '),
|
test: test.titlePath().slice(2).join(' › '),
|
||||||
|
@ -1855,11 +1855,7 @@ sketch002 = startSketchOn(XZ)
|
|||||||
},
|
},
|
||||||
stage: 'review',
|
stage: 'review',
|
||||||
})
|
})
|
||||||
// Confirm we can submit from the review step with just `Enter`
|
await cmdBar.progressCmdBar()
|
||||||
await cmdBar.progressCmdBar(true)
|
|
||||||
await cmdBar.expectState({
|
|
||||||
stage: 'commandBarClosed',
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
|
||||||
await test.step(`Confirm code is added to the editor, scene has changed`, async () => {
|
await test.step(`Confirm code is added to the editor, scene has changed`, async () => {
|
||||||
@ -1999,7 +1995,7 @@ profile001 = ${circleCode}`
|
|||||||
},
|
},
|
||||||
stage: 'review',
|
stage: 'review',
|
||||||
})
|
})
|
||||||
await cmdBar.progressCmdBar(true)
|
await cmdBar.progressCmdBar()
|
||||||
await editor.expectEditor.toContain(sweepDeclaration)
|
await editor.expectEditor.toContain(sweepDeclaration)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -281,14 +281,7 @@ impl ExecutorContext {
|
|||||||
|
|
||||||
// Track exports.
|
// Track exports.
|
||||||
if let ItemVisibility::Export = variable_declaration.visibility {
|
if let ItemVisibility::Export = variable_declaration.visibility {
|
||||||
if matches!(body_type, BodyType::Root) {
|
exec_state.mod_local.module_exports.push(var_name);
|
||||||
exec_state.mod_local.module_exports.push(var_name);
|
|
||||||
} else {
|
|
||||||
exec_state.err(CompilationError::err(
|
|
||||||
variable_declaration.as_source_range(),
|
|
||||||
"Exports are only supported at the top-level of a file. Remove `export` or move it to the top-level.",
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// Variable declaration can be the return value of a module.
|
// Variable declaration can be the return value of a module.
|
||||||
last_expr = matches!(body_type, BodyType::Root).then_some(value);
|
last_expr = matches!(body_type, BodyType::Root).then_some(value);
|
||||||
|
@ -996,27 +996,6 @@ mod import_cycle1 {
|
|||||||
super::execute(TEST_NAME, false).await
|
super::execute(TEST_NAME, false).await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
mod import_only_at_top_level {
|
|
||||||
const TEST_NAME: &str = "import_only_at_top_level";
|
|
||||||
|
|
||||||
/// 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, false).await
|
|
||||||
}
|
|
||||||
}
|
|
||||||
mod import_function_not_sketch {
|
mod import_function_not_sketch {
|
||||||
const TEST_NAME: &str = "import_function_not_sketch";
|
const TEST_NAME: &str = "import_function_not_sketch";
|
||||||
|
|
||||||
@ -1185,27 +1164,6 @@ mod import_foreign {
|
|||||||
super::execute(TEST_NAME, false).await
|
super::execute(TEST_NAME, false).await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
mod export_var_only_at_top_level {
|
|
||||||
const TEST_NAME: &str = "export_var_only_at_top_level";
|
|
||||||
|
|
||||||
/// 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, false).await
|
|
||||||
}
|
|
||||||
}
|
|
||||||
mod assembly_non_default_units {
|
mod assembly_non_default_units {
|
||||||
const TEST_NAME: &str = "assembly_non_default_units";
|
const TEST_NAME: &str = "assembly_non_default_units";
|
||||||
|
|
||||||
@ -3360,24 +3318,3 @@ mod nested_windows_main_kcl {
|
|||||||
super::execute(TEST_NAME, true).await
|
super::execute(TEST_NAME, true).await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
mod nested_assembly {
|
|
||||||
const TEST_NAME: &str = "nested_assembly";
|
|
||||||
|
|
||||||
/// 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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -1,5 +0,0 @@
|
|||||||
---
|
|
||||||
source: kcl-lib/src/simulation_tests.rs
|
|
||||||
description: Artifact commands export_only_at_top_level.kcl
|
|
||||||
---
|
|
||||||
[]
|
|
@ -1,6 +0,0 @@
|
|||||||
---
|
|
||||||
source: kcl-lib/src/simulation_tests.rs
|
|
||||||
description: Artifact graph flowchart export_only_at_top_level.kcl
|
|
||||||
extension: md
|
|
||||||
snapshot_kind: binary
|
|
||||||
---
|
|
@ -1,3 +0,0 @@
|
|||||||
```mermaid
|
|
||||||
flowchart LR
|
|
||||||
```
|
|
@ -1,148 +0,0 @@
|
|||||||
---
|
|
||||||
source: kcl-lib/src/simulation_tests.rs
|
|
||||||
description: Result of parsing export_only_at_top_level.kcl
|
|
||||||
---
|
|
||||||
{
|
|
||||||
"Ok": {
|
|
||||||
"body": [
|
|
||||||
{
|
|
||||||
"commentStart": 0,
|
|
||||||
"declaration": {
|
|
||||||
"commentStart": 0,
|
|
||||||
"end": 0,
|
|
||||||
"id": {
|
|
||||||
"commentStart": 0,
|
|
||||||
"end": 0,
|
|
||||||
"name": "main",
|
|
||||||
"start": 0,
|
|
||||||
"type": "Identifier"
|
|
||||||
},
|
|
||||||
"init": {
|
|
||||||
"body": {
|
|
||||||
"body": [
|
|
||||||
{
|
|
||||||
"commentStart": 0,
|
|
||||||
"declaration": {
|
|
||||||
"commentStart": 0,
|
|
||||||
"end": 0,
|
|
||||||
"id": {
|
|
||||||
"commentStart": 0,
|
|
||||||
"end": 0,
|
|
||||||
"name": "x",
|
|
||||||
"start": 0,
|
|
||||||
"type": "Identifier"
|
|
||||||
},
|
|
||||||
"init": {
|
|
||||||
"commentStart": 0,
|
|
||||||
"end": 0,
|
|
||||||
"raw": "2",
|
|
||||||
"start": 0,
|
|
||||||
"type": "Literal",
|
|
||||||
"type": "Literal",
|
|
||||||
"value": {
|
|
||||||
"value": 2.0,
|
|
||||||
"suffix": "None"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"start": 0,
|
|
||||||
"type": "VariableDeclarator"
|
|
||||||
},
|
|
||||||
"end": 0,
|
|
||||||
"kind": "const",
|
|
||||||
"start": 0,
|
|
||||||
"type": "VariableDeclaration",
|
|
||||||
"type": "VariableDeclaration",
|
|
||||||
"visibility": "export"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"argument": {
|
|
||||||
"commentStart": 0,
|
|
||||||
"end": 0,
|
|
||||||
"raw": "0",
|
|
||||||
"start": 0,
|
|
||||||
"type": "Literal",
|
|
||||||
"type": "Literal",
|
|
||||||
"value": {
|
|
||||||
"value": 0.0,
|
|
||||||
"suffix": "None"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"commentStart": 0,
|
|
||||||
"end": 0,
|
|
||||||
"start": 0,
|
|
||||||
"type": "ReturnStatement",
|
|
||||||
"type": "ReturnStatement"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"commentStart": 0,
|
|
||||||
"end": 0,
|
|
||||||
"start": 0
|
|
||||||
},
|
|
||||||
"commentStart": 0,
|
|
||||||
"end": 0,
|
|
||||||
"params": [],
|
|
||||||
"start": 0,
|
|
||||||
"type": "FunctionExpression",
|
|
||||||
"type": "FunctionExpression"
|
|
||||||
},
|
|
||||||
"start": 0,
|
|
||||||
"type": "VariableDeclarator"
|
|
||||||
},
|
|
||||||
"end": 0,
|
|
||||||
"kind": "fn",
|
|
||||||
"start": 0,
|
|
||||||
"type": "VariableDeclaration",
|
|
||||||
"type": "VariableDeclaration"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"commentStart": 0,
|
|
||||||
"end": 0,
|
|
||||||
"expression": {
|
|
||||||
"callee": {
|
|
||||||
"abs_path": false,
|
|
||||||
"commentStart": 0,
|
|
||||||
"end": 0,
|
|
||||||
"name": {
|
|
||||||
"commentStart": 0,
|
|
||||||
"end": 0,
|
|
||||||
"name": "main",
|
|
||||||
"start": 0,
|
|
||||||
"type": "Identifier"
|
|
||||||
},
|
|
||||||
"path": [],
|
|
||||||
"start": 0,
|
|
||||||
"type": "Name"
|
|
||||||
},
|
|
||||||
"commentStart": 0,
|
|
||||||
"end": 0,
|
|
||||||
"start": 0,
|
|
||||||
"type": "CallExpressionKw",
|
|
||||||
"type": "CallExpressionKw",
|
|
||||||
"unlabeled": null
|
|
||||||
},
|
|
||||||
"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
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,15 +0,0 @@
|
|||||||
---
|
|
||||||
source: kcl-lib/src/simulation_tests.rs
|
|
||||||
description: Error from executing export_only_at_top_level.kcl
|
|
||||||
---
|
|
||||||
KCL Semantic error
|
|
||||||
|
|
||||||
× semantic: Exports are only supported at the top-level of a file. Remove
|
|
||||||
│ `export` or move it to the top-level.
|
|
||||||
╭─[2:3]
|
|
||||||
1 │ fn main() {
|
|
||||||
2 │ export x = 2
|
|
||||||
· ──────┬─────
|
|
||||||
· ╰── main
|
|
||||||
3 │ return 0
|
|
||||||
╰────
|
|
@ -1,6 +0,0 @@
|
|||||||
fn main() {
|
|
||||||
export x = 2
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
main()
|
|
@ -1,5 +0,0 @@
|
|||||||
---
|
|
||||||
source: kcl-lib/src/simulation_tests.rs
|
|
||||||
description: Operations executed export_only_at_top_level.kcl
|
|
||||||
---
|
|
||||||
[]
|
|
@ -1,10 +0,0 @@
|
|||||||
---
|
|
||||||
source: kcl-lib/src/simulation_tests.rs
|
|
||||||
description: Result of unparsing export_only_at_top_level.kcl
|
|
||||||
---
|
|
||||||
fn main() {
|
|
||||||
export x = 2
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
main()
|
|
@ -1,32 +0,0 @@
|
|||||||
---
|
|
||||||
source: kcl-lib/src/simulation_tests.rs
|
|
||||||
description: Artifact commands import_only_at_top_level.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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
@ -1,6 +0,0 @@
|
|||||||
---
|
|
||||||
source: kcl-lib/src/simulation_tests.rs
|
|
||||||
description: Artifact graph flowchart import_only_at_top_level.kcl
|
|
||||||
extension: md
|
|
||||||
snapshot_kind: binary
|
|
||||||
---
|
|
@ -1,3 +0,0 @@
|
|||||||
```mermaid
|
|
||||||
flowchart LR
|
|
||||||
```
|
|
@ -1,129 +0,0 @@
|
|||||||
---
|
|
||||||
source: kcl-lib/src/simulation_tests.rs
|
|
||||||
description: Result of parsing import_only_at_top_level.kcl
|
|
||||||
---
|
|
||||||
{
|
|
||||||
"Ok": {
|
|
||||||
"body": [
|
|
||||||
{
|
|
||||||
"commentStart": 0,
|
|
||||||
"declaration": {
|
|
||||||
"commentStart": 0,
|
|
||||||
"end": 0,
|
|
||||||
"id": {
|
|
||||||
"commentStart": 0,
|
|
||||||
"end": 0,
|
|
||||||
"name": "main",
|
|
||||||
"start": 0,
|
|
||||||
"type": "Identifier"
|
|
||||||
},
|
|
||||||
"init": {
|
|
||||||
"body": {
|
|
||||||
"body": [
|
|
||||||
{
|
|
||||||
"commentStart": 0,
|
|
||||||
"end": 0,
|
|
||||||
"path": {
|
|
||||||
"type": "Kcl",
|
|
||||||
"filename": "empty.kcl"
|
|
||||||
},
|
|
||||||
"selector": {
|
|
||||||
"type": "None",
|
|
||||||
"alias": null
|
|
||||||
},
|
|
||||||
"start": 0,
|
|
||||||
"type": "ImportStatement",
|
|
||||||
"type": "ImportStatement"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"argument": {
|
|
||||||
"commentStart": 0,
|
|
||||||
"end": 0,
|
|
||||||
"raw": "0",
|
|
||||||
"start": 0,
|
|
||||||
"type": "Literal",
|
|
||||||
"type": "Literal",
|
|
||||||
"value": {
|
|
||||||
"value": 0.0,
|
|
||||||
"suffix": "None"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"commentStart": 0,
|
|
||||||
"end": 0,
|
|
||||||
"start": 0,
|
|
||||||
"type": "ReturnStatement",
|
|
||||||
"type": "ReturnStatement"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"commentStart": 0,
|
|
||||||
"end": 0,
|
|
||||||
"start": 0
|
|
||||||
},
|
|
||||||
"commentStart": 0,
|
|
||||||
"end": 0,
|
|
||||||
"params": [],
|
|
||||||
"start": 0,
|
|
||||||
"type": "FunctionExpression",
|
|
||||||
"type": "FunctionExpression"
|
|
||||||
},
|
|
||||||
"start": 0,
|
|
||||||
"type": "VariableDeclarator"
|
|
||||||
},
|
|
||||||
"end": 0,
|
|
||||||
"kind": "fn",
|
|
||||||
"start": 0,
|
|
||||||
"type": "VariableDeclaration",
|
|
||||||
"type": "VariableDeclaration"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"commentStart": 0,
|
|
||||||
"end": 0,
|
|
||||||
"expression": {
|
|
||||||
"callee": {
|
|
||||||
"abs_path": false,
|
|
||||||
"commentStart": 0,
|
|
||||||
"end": 0,
|
|
||||||
"name": {
|
|
||||||
"commentStart": 0,
|
|
||||||
"end": 0,
|
|
||||||
"name": "main",
|
|
||||||
"start": 0,
|
|
||||||
"type": "Identifier"
|
|
||||||
},
|
|
||||||
"path": [],
|
|
||||||
"start": 0,
|
|
||||||
"type": "Name"
|
|
||||||
},
|
|
||||||
"commentStart": 0,
|
|
||||||
"end": 0,
|
|
||||||
"start": 0,
|
|
||||||
"type": "CallExpressionKw",
|
|
||||||
"type": "CallExpressionKw",
|
|
||||||
"unlabeled": null
|
|
||||||
},
|
|
||||||
"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
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,30 +0,0 @@
|
|||||||
---
|
|
||||||
source: kcl-lib/src/simulation_tests.rs
|
|
||||||
description: Error from executing import_only_at_top_level.kcl
|
|
||||||
---
|
|
||||||
KCL Semantic error
|
|
||||||
|
|
||||||
× semantic: Imports are only supported at the top-level of a file.
|
|
||||||
╭─[2:3]
|
|
||||||
1 │ fn main() {
|
|
||||||
2 │ import "empty.kcl"
|
|
||||||
· ─────────┬────────
|
|
||||||
· ╰── tests/import_only_at_top_level/input.kcl
|
|
||||||
3 │ return 0
|
|
||||||
╰────
|
|
||||||
╭─[6:1]
|
|
||||||
5 │
|
|
||||||
6 │ main()
|
|
||||||
· ───┬──
|
|
||||||
· ╰── tests/import_only_at_top_level/input.kcl
|
|
||||||
╰────
|
|
||||||
╰─▶ KCL Semantic error
|
|
||||||
|
|
||||||
× semantic: Imports are only supported at the top-level of a file.
|
|
||||||
╭─[2:3]
|
|
||||||
1 │ fn main() {
|
|
||||||
2 │ import "empty.kcl"
|
|
||||||
· ─────────┬────────
|
|
||||||
· ╰── tests/import_only_at_top_level/input.kcl
|
|
||||||
3 │ return 0
|
|
||||||
╰────
|
|
@ -1,6 +0,0 @@
|
|||||||
fn main() {
|
|
||||||
import "empty.kcl"
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
main()
|
|
@ -1,32 +0,0 @@
|
|||||||
---
|
|
||||||
source: kcl-lib/src/simulation_tests.rs
|
|
||||||
description: Operations executed import_only_at_top_level.kcl
|
|
||||||
---
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"type": "GroupBegin",
|
|
||||||
"group": {
|
|
||||||
"type": "ModuleInstance",
|
|
||||||
"name": "empty.kcl",
|
|
||||||
"moduleId": 0
|
|
||||||
},
|
|
||||||
"sourceRange": []
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "GroupBegin",
|
|
||||||
"group": {
|
|
||||||
"type": "FunctionCall",
|
|
||||||
"name": "main",
|
|
||||||
"functionSourceRange": [],
|
|
||||||
"unlabeledArg": null,
|
|
||||||
"labeledArgs": {}
|
|
||||||
},
|
|
||||||
"sourceRange": []
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "GroupEnd"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "GroupEnd"
|
|
||||||
}
|
|
||||||
]
|
|
@ -1,10 +0,0 @@
|
|||||||
---
|
|
||||||
source: kcl-lib/src/simulation_tests.rs
|
|
||||||
description: Result of unparsing import_only_at_top_level.kcl
|
|
||||||
---
|
|
||||||
fn main() {
|
|
||||||
import "empty.kcl"
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
main()
|
|
@ -1,5 +0,0 @@
|
|||||||
---
|
|
||||||
source: kcl-lib/src/simulation_tests.rs
|
|
||||||
description: Result of unparsing tests/import_only_at_top_level/empty.kcl
|
|
||||||
---
|
|
||||||
|
|
@ -1,184 +0,0 @@
|
|||||||
---
|
|
||||||
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]"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
@ -1,6 +0,0 @@
|
|||||||
---
|
|
||||||
source: kcl-lib/src/simulation_tests.rs
|
|
||||||
description: Artifact graph flowchart nested_main_kcl.kcl
|
|
||||||
extension: md
|
|
||||||
snapshot_kind: binary
|
|
||||||
---
|
|
@ -1,23 +0,0 @@
|
|||||||
```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
|
|
||||||
```
|
|
@ -1,73 +0,0 @@
|
|||||||
---
|
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,3 +0,0 @@
|
|||||||
import "nested/foo/bar/main.kcl" as bar
|
|
||||||
|
|
||||||
bar
|
|
@ -1,7 +0,0 @@
|
|||||||
// A donut shape.
|
|
||||||
startSketchOn(XY)
|
|
||||||
|> circle( center = [15, 0], radius = 5 )
|
|
||||||
|> revolve(
|
|
||||||
angle = 360,
|
|
||||||
axis = Y,
|
|
||||||
)
|
|
@ -1,3 +0,0 @@
|
|||||||
import "imported.kcl" as imported
|
|
||||||
|
|
||||||
imported
|
|
@ -1,18 +0,0 @@
|
|||||||
---
|
|
||||||
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"
|
|
||||||
}
|
|
||||||
]
|
|
@ -1,10 +0,0 @@
|
|||||||
---
|
|
||||||
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.
Before Width: | Height: | Size: 150 KiB |
@ -1,7 +0,0 @@
|
|||||||
---
|
|
||||||
source: kcl-lib/src/simulation_tests.rs
|
|
||||||
description: Result of unparsing nested_main_kcl.kcl
|
|
||||||
---
|
|
||||||
import "nested/foo/bar/main.kcl" as bar
|
|
||||||
|
|
||||||
bar
|
|
@ -1,8 +0,0 @@
|
|||||||
---
|
|
||||||
source: kcl-lib/src/simulation_tests.rs
|
|
||||||
description: Result of unparsing tests/nested_assembly/nested/foo/bar/imported.kcl
|
|
||||||
---
|
|
||||||
// A donut shape.
|
|
||||||
startSketchOn(XY)
|
|
||||||
|> circle(center = [15, 0], radius = 5)
|
|
||||||
|> revolve(angle = 360, axis = Y)
|
|
@ -1,7 +0,0 @@
|
|||||||
---
|
|
||||||
source: kcl-lib/src/simulation_tests.rs
|
|
||||||
description: Result of unparsing tests/nested_assembly/nested/foo/bar/main.kcl
|
|
||||||
---
|
|
||||||
import "imported.kcl" as imported
|
|
||||||
|
|
||||||
imported
|
|
@ -1,5 +1,4 @@
|
|||||||
import type React from 'react'
|
import React, { useMemo, useState } from 'react'
|
||||||
import { useMemo, useEffect, useRef, useState } from 'react'
|
|
||||||
import { useHotkeys } from 'react-hotkeys-hook'
|
import { useHotkeys } from 'react-hotkeys-hook'
|
||||||
|
|
||||||
import { ActionButton } from '@src/components/ActionButton'
|
import { ActionButton } from '@src/components/ActionButton'
|
||||||
@ -122,7 +121,6 @@ function CommandBarHeader({ children }: React.PropsWithChildren<object>) {
|
|||||||
data-is-current-arg={
|
data-is-current-arg={
|
||||||
argName === currentArgument?.name ? 'true' : 'false'
|
argName === currentArgument?.name ? 'true' : 'false'
|
||||||
}
|
}
|
||||||
type="button"
|
|
||||||
disabled={!isReviewing && currentArgument?.name === argName}
|
disabled={!isReviewing && currentArgument?.name === argName}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
commandBarActor.send({
|
commandBarActor.send({
|
||||||
@ -246,20 +244,13 @@ function CommandBarHeader({ children }: React.PropsWithChildren<object>) {
|
|||||||
|
|
||||||
type ButtonProps = { bgClassName?: string; iconClassName?: string }
|
type ButtonProps = { bgClassName?: string; iconClassName?: string }
|
||||||
function ReviewingButton({ bgClassName, iconClassName }: ButtonProps) {
|
function ReviewingButton({ bgClassName, iconClassName }: ButtonProps) {
|
||||||
const buttonRef = useRef<HTMLButtonElement>(null)
|
|
||||||
useEffect(() => {
|
|
||||||
if (buttonRef.current) {
|
|
||||||
buttonRef.current.focus()
|
|
||||||
}
|
|
||||||
}, [])
|
|
||||||
return (
|
return (
|
||||||
<ActionButton
|
<ActionButton
|
||||||
Element="button"
|
Element="button"
|
||||||
ref={buttonRef}
|
autoFocus
|
||||||
type="submit"
|
type="submit"
|
||||||
form="review-form"
|
form="review-form"
|
||||||
className="w-fit !p-0 rounded-sm hover:shadow focus:outline-current"
|
className="w-fit !p-0 rounded-sm hover:shadow"
|
||||||
tabIndex={0}
|
|
||||||
data-testid="command-bar-submit"
|
data-testid="command-bar-submit"
|
||||||
iconStart={{
|
iconStart={{
|
||||||
icon: 'checkmark',
|
icon: 'checkmark',
|
||||||
@ -278,8 +269,7 @@ function GatheringArgsButton({ bgClassName, iconClassName }: ButtonProps) {
|
|||||||
Element="button"
|
Element="button"
|
||||||
type="submit"
|
type="submit"
|
||||||
form="arg-form"
|
form="arg-form"
|
||||||
className="w-fit !p-0 rounded-sm hover:shadow focus:outline-current"
|
className="w-fit !p-0 rounded-sm hover:shadow"
|
||||||
tabIndex={0}
|
|
||||||
data-testid="command-bar-continue"
|
data-testid="command-bar-continue"
|
||||||
iconStart={{
|
iconStart={{
|
||||||
icon: 'arrowRight',
|
icon: 'arrowRight',
|
||||||
|
@ -12,8 +12,12 @@ import { useLoaderData } from 'react-router-dom'
|
|||||||
import type { Actor, ContextFrom, Prop, SnapshotFrom, StateFrom } from 'xstate'
|
import type { Actor, ContextFrom, Prop, SnapshotFrom, StateFrom } from 'xstate'
|
||||||
import { assign, fromPromise } from 'xstate'
|
import { assign, fromPromise } from 'xstate'
|
||||||
|
|
||||||
import type { OutputFormat3d } from '@rust/kcl-lib/bindings/ModelingCmd'
|
import type {
|
||||||
|
OutputFormat3d,
|
||||||
|
Point3d,
|
||||||
|
} from '@rust/kcl-lib/bindings/ModelingCmd'
|
||||||
import type { Node } from '@rust/kcl-lib/bindings/Node'
|
import type { Node } from '@rust/kcl-lib/bindings/Node'
|
||||||
|
import type { Plane } from '@rust/kcl-lib/bindings/Plane'
|
||||||
|
|
||||||
import { useAppState } from '@src/AppState'
|
import { useAppState } from '@src/AppState'
|
||||||
import { letEngineAnimateAndSyncCamAfter } from '@src/clientSideScene/CameraControls'
|
import { letEngineAnimateAndSyncCamAfter } from '@src/clientSideScene/CameraControls'
|
||||||
@ -34,16 +38,26 @@ import useModelingMachineCommands from '@src/hooks/useStateMachineCommands'
|
|||||||
import { useKclContext } from '@src/lang/KclProvider'
|
import { useKclContext } from '@src/lang/KclProvider'
|
||||||
import { updateModelingState } from '@src/lang/modelingWorkflows'
|
import { updateModelingState } from '@src/lang/modelingWorkflows'
|
||||||
import {
|
import {
|
||||||
|
insertNamedConstant,
|
||||||
|
replaceValueAtNodePath,
|
||||||
sketchOnExtrudedFace,
|
sketchOnExtrudedFace,
|
||||||
sketchOnOffsetPlane,
|
sketchOnOffsetPlane,
|
||||||
splitPipedProfile,
|
splitPipedProfile,
|
||||||
startSketchOnDefault,
|
startSketchOnDefault,
|
||||||
} from '@src/lang/modifyAst'
|
} from '@src/lang/modifyAst'
|
||||||
import {
|
import {
|
||||||
|
artifactIsPlaneWithPaths,
|
||||||
doesSketchPipeNeedSplitting,
|
doesSketchPipeNeedSplitting,
|
||||||
getNodeFromPath,
|
getNodeFromPath,
|
||||||
|
isCursorInFunctionDefinition,
|
||||||
traverse,
|
traverse,
|
||||||
} from '@src/lang/queryAst'
|
} from '@src/lang/queryAst'
|
||||||
|
import { getNodePathFromSourceRange } from '@src/lang/queryAstNodePathUtils'
|
||||||
|
import {
|
||||||
|
getFaceCodeRef,
|
||||||
|
getPathsFromArtifact,
|
||||||
|
getPlaneFromArtifact,
|
||||||
|
} from '@src/lang/std/artifactGraph'
|
||||||
import {
|
import {
|
||||||
EngineConnectionStateType,
|
EngineConnectionStateType,
|
||||||
EngineConnectionEvents,
|
EngineConnectionEvents,
|
||||||
@ -52,6 +66,7 @@ import { err, reportRejection, trap, reject } from '@src/lib/trap'
|
|||||||
import { isNonNullable, platform, uuidv4 } from '@src/lib/utils'
|
import { isNonNullable, platform, uuidv4 } from '@src/lib/utils'
|
||||||
import { promptToEditFlow } from '@src/lib/promptToEdit'
|
import { promptToEditFlow } from '@src/lib/promptToEdit'
|
||||||
import type { FileMeta } from '@src/lib/types'
|
import type { FileMeta } from '@src/lib/types'
|
||||||
|
import { kclEditorActor } from '@src/machines/kclEditorMachine'
|
||||||
import { commandBarActor } from '@src/lib/singletons'
|
import { commandBarActor } from '@src/lib/singletons'
|
||||||
import { useToken, useSettings } from '@src/lib/singletons'
|
import { useToken, useSettings } from '@src/lib/singletons'
|
||||||
import type { IndexLoaderData } from '@src/lib/types'
|
import type { IndexLoaderData } from '@src/lib/types'
|
||||||
@ -83,13 +98,23 @@ import {
|
|||||||
} from '@src/lib/singletons'
|
} from '@src/lib/singletons'
|
||||||
import type { MachineManager } from '@src/components/MachineManagerProvider'
|
import type { MachineManager } from '@src/components/MachineManagerProvider'
|
||||||
import { MachineManagerContext } from '@src/components/MachineManagerProvider'
|
import { MachineManagerContext } from '@src/components/MachineManagerProvider'
|
||||||
import { updateSelections } from '@src/lib/selections'
|
import {
|
||||||
import { updateSketchDetailsNodePaths } from '@src/lang/util'
|
handleSelectionBatch,
|
||||||
|
updateSelections,
|
||||||
|
type Selections,
|
||||||
|
} from '@src/lib/selections'
|
||||||
|
import {
|
||||||
|
crossProduct,
|
||||||
|
isCursorInSketchCommandRange,
|
||||||
|
updateSketchDetailsNodePaths,
|
||||||
|
} from '@src/lang/util'
|
||||||
import {
|
import {
|
||||||
modelingMachineCommandConfig,
|
modelingMachineCommandConfig,
|
||||||
type ModelingCommandSchema,
|
type ModelingCommandSchema,
|
||||||
} from '@src/lib/commandBarConfigs/modelingCommandConfig'
|
} from '@src/lib/commandBarConfigs/modelingCommandConfig'
|
||||||
import type {
|
import type {
|
||||||
|
KclValue,
|
||||||
|
PathToNode,
|
||||||
PipeExpression,
|
PipeExpression,
|
||||||
Program,
|
Program,
|
||||||
VariableDeclaration,
|
VariableDeclaration,
|
||||||
@ -295,6 +320,229 @@ export const ModelingMachineProvider = ({
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
|
'Set selection': assign(
|
||||||
|
({ context: { selectionRanges, sketchDetails }, event }) => {
|
||||||
|
// this was needed for ts after adding 'Set selection' action to on done modal events
|
||||||
|
const setSelections =
|
||||||
|
('data' in event &&
|
||||||
|
event.data &&
|
||||||
|
'selectionType' in event.data &&
|
||||||
|
event.data) ||
|
||||||
|
('output' in event &&
|
||||||
|
event.output &&
|
||||||
|
'selectionType' in event.output &&
|
||||||
|
event.output) ||
|
||||||
|
null
|
||||||
|
if (!setSelections) return {}
|
||||||
|
|
||||||
|
let selections: Selections = {
|
||||||
|
graphSelections: [],
|
||||||
|
otherSelections: [],
|
||||||
|
}
|
||||||
|
if (setSelections.selectionType === 'singleCodeCursor') {
|
||||||
|
if (!setSelections.selection && editorManager.isShiftDown) {
|
||||||
|
// if the user is holding shift, but they didn't select anything
|
||||||
|
// don't nuke their other selections (frustrating to have one bad click ruin your
|
||||||
|
// whole selection)
|
||||||
|
selections = {
|
||||||
|
graphSelections: selectionRanges.graphSelections,
|
||||||
|
otherSelections: selectionRanges.otherSelections,
|
||||||
|
}
|
||||||
|
} else if (
|
||||||
|
!setSelections.selection &&
|
||||||
|
!editorManager.isShiftDown
|
||||||
|
) {
|
||||||
|
selections = {
|
||||||
|
graphSelections: [],
|
||||||
|
otherSelections: [],
|
||||||
|
}
|
||||||
|
} else if (
|
||||||
|
setSelections.selection &&
|
||||||
|
!editorManager.isShiftDown
|
||||||
|
) {
|
||||||
|
selections = {
|
||||||
|
graphSelections: [setSelections.selection],
|
||||||
|
otherSelections: [],
|
||||||
|
}
|
||||||
|
} else if (setSelections.selection && editorManager.isShiftDown) {
|
||||||
|
// selecting and deselecting multiple objects
|
||||||
|
|
||||||
|
/**
|
||||||
|
* There are two scenarios:
|
||||||
|
* 1. General case:
|
||||||
|
* When selecting and deselecting edges,
|
||||||
|
* faces or segment (during sketch edit)
|
||||||
|
* we use its artifact ID to identify the selection
|
||||||
|
* 2. Initial sketch setup:
|
||||||
|
* The artifact is not yet created
|
||||||
|
* so we use the codeRef.range
|
||||||
|
*/
|
||||||
|
|
||||||
|
let updatedSelections: typeof selectionRanges.graphSelections
|
||||||
|
|
||||||
|
// 1. General case: Artifact exists, use its ID
|
||||||
|
if (setSelections.selection.artifact?.id) {
|
||||||
|
// check if already selected
|
||||||
|
const alreadySelected = selectionRanges.graphSelections.some(
|
||||||
|
(selection) =>
|
||||||
|
selection.artifact?.id ===
|
||||||
|
setSelections.selection?.artifact?.id
|
||||||
|
)
|
||||||
|
if (
|
||||||
|
alreadySelected &&
|
||||||
|
setSelections.selection?.artifact?.id
|
||||||
|
) {
|
||||||
|
// remove it
|
||||||
|
updatedSelections = selectionRanges.graphSelections.filter(
|
||||||
|
(selection) =>
|
||||||
|
selection.artifact?.id !==
|
||||||
|
setSelections.selection?.artifact?.id
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
// add it
|
||||||
|
updatedSelections = [
|
||||||
|
...selectionRanges.graphSelections,
|
||||||
|
setSelections.selection,
|
||||||
|
]
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 2. Initial sketch setup: Artifact not yet created – use codeRef.range
|
||||||
|
const selectionRange = JSON.stringify(
|
||||||
|
setSelections.selection?.codeRef?.range
|
||||||
|
)
|
||||||
|
|
||||||
|
// check if already selected
|
||||||
|
const alreadySelected = selectionRanges.graphSelections.some(
|
||||||
|
(selection) => {
|
||||||
|
const existingRange = JSON.stringify(
|
||||||
|
selection.codeRef?.range
|
||||||
|
)
|
||||||
|
return existingRange === selectionRange
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
if (
|
||||||
|
alreadySelected &&
|
||||||
|
setSelections.selection?.codeRef?.range
|
||||||
|
) {
|
||||||
|
// remove it
|
||||||
|
updatedSelections = selectionRanges.graphSelections.filter(
|
||||||
|
(selection) =>
|
||||||
|
JSON.stringify(selection.codeRef?.range) !==
|
||||||
|
selectionRange
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
// add it
|
||||||
|
updatedSelections = [
|
||||||
|
...selectionRanges.graphSelections,
|
||||||
|
setSelections.selection,
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
selections = {
|
||||||
|
graphSelections: updatedSelections,
|
||||||
|
otherSelections: selectionRanges.otherSelections,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const {
|
||||||
|
engineEvents,
|
||||||
|
codeMirrorSelection,
|
||||||
|
updateSceneObjectColors,
|
||||||
|
} = handleSelectionBatch({
|
||||||
|
selections,
|
||||||
|
})
|
||||||
|
if (codeMirrorSelection) {
|
||||||
|
kclEditorActor.send({
|
||||||
|
type: 'setLastSelectionEvent',
|
||||||
|
data: {
|
||||||
|
codeMirrorSelection,
|
||||||
|
scrollIntoView: setSelections.scrollIntoView ?? false,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// If there are engine commands that need sent off, send them
|
||||||
|
// TODO: This should be handled outside of an action as its own
|
||||||
|
// actor, so that the system state is more controlled.
|
||||||
|
engineEvents &&
|
||||||
|
engineEvents.forEach((event) => {
|
||||||
|
engineCommandManager
|
||||||
|
.sendSceneCommand(event)
|
||||||
|
.catch(reportRejection)
|
||||||
|
})
|
||||||
|
updateSceneObjectColors()
|
||||||
|
|
||||||
|
return {
|
||||||
|
selectionRanges: selections,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (setSelections.selectionType === 'mirrorCodeMirrorSelections') {
|
||||||
|
return {
|
||||||
|
selectionRanges: setSelections.selection,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
setSelections.selectionType === 'axisSelection' ||
|
||||||
|
setSelections.selectionType === 'defaultPlaneSelection'
|
||||||
|
) {
|
||||||
|
if (editorManager.isShiftDown) {
|
||||||
|
selections = {
|
||||||
|
graphSelections: selectionRanges.graphSelections,
|
||||||
|
otherSelections: [setSelections.selection],
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
selections = {
|
||||||
|
graphSelections: [],
|
||||||
|
otherSelections: [setSelections.selection],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
selectionRanges: selections,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (setSelections.selectionType === 'completeSelection') {
|
||||||
|
const codeMirrorSelection = editorManager.createEditorSelection(
|
||||||
|
setSelections.selection
|
||||||
|
)
|
||||||
|
kclEditorActor.send({
|
||||||
|
type: 'setLastSelectionEvent',
|
||||||
|
data: {
|
||||||
|
codeMirrorSelection,
|
||||||
|
scrollIntoView: false,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if (!sketchDetails)
|
||||||
|
return {
|
||||||
|
selectionRanges: setSelections.selection,
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
selectionRanges: setSelections.selection,
|
||||||
|
sketchDetails: {
|
||||||
|
...sketchDetails,
|
||||||
|
sketchEntryNodePath:
|
||||||
|
setSelections.updatedSketchEntryNodePath ||
|
||||||
|
sketchDetails?.sketchEntryNodePath ||
|
||||||
|
[],
|
||||||
|
sketchNodePaths:
|
||||||
|
setSelections.updatedSketchNodePaths ||
|
||||||
|
sketchDetails?.sketchNodePaths ||
|
||||||
|
[],
|
||||||
|
planeNodePath:
|
||||||
|
setSelections.updatedPlaneNodePath ||
|
||||||
|
sketchDetails?.planeNodePath ||
|
||||||
|
[],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {}
|
||||||
|
}
|
||||||
|
),
|
||||||
},
|
},
|
||||||
guards: {
|
guards: {
|
||||||
'has valid selection for deletion': ({
|
'has valid selection for deletion': ({
|
||||||
@ -304,6 +552,35 @@ export const ModelingMachineProvider = ({
|
|||||||
if (selectionRanges.graphSelections.length <= 0) return false
|
if (selectionRanges.graphSelections.length <= 0) return false
|
||||||
return true
|
return true
|
||||||
},
|
},
|
||||||
|
'is-error-free': () => {
|
||||||
|
return kclManager.errors.length === 0 && !kclManager.hasErrors()
|
||||||
|
},
|
||||||
|
'Selection is on face': ({ context: { selectionRanges }, event }) => {
|
||||||
|
if (event.type !== 'Enter sketch') return false
|
||||||
|
if (event.data?.forceNewSketch) return false
|
||||||
|
if (artifactIsPlaneWithPaths(selectionRanges)) {
|
||||||
|
return true
|
||||||
|
} else if (selectionRanges.graphSelections[0]?.artifact) {
|
||||||
|
// See if the selection is "close enough" to be coerced to the plane later
|
||||||
|
const maybePlane = getPlaneFromArtifact(
|
||||||
|
selectionRanges.graphSelections[0].artifact,
|
||||||
|
kclManager.artifactGraph
|
||||||
|
)
|
||||||
|
return !err(maybePlane)
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
isCursorInFunctionDefinition(
|
||||||
|
kclManager.ast,
|
||||||
|
selectionRanges.graphSelections[0]
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return !!isCursorInSketchCommandRange(
|
||||||
|
kclManager.artifactGraph,
|
||||||
|
selectionRanges
|
||||||
|
)
|
||||||
|
},
|
||||||
'Has exportable geometry': () =>
|
'Has exportable geometry': () =>
|
||||||
!kclManager.hasErrors() && kclManager.ast.body.length > 0,
|
!kclManager.hasErrors() && kclManager.ast.body.length > 0,
|
||||||
},
|
},
|
||||||
@ -573,6 +850,123 @@ export const ModelingMachineProvider = ({
|
|||||||
animateTargetId: input.planeId,
|
animateTargetId: input.planeId,
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
|
'animate-to-sketch': fromPromise(
|
||||||
|
async ({ input: { selectionRanges } }) => {
|
||||||
|
const artifact = selectionRanges.graphSelections[0].artifact
|
||||||
|
const plane = getPlaneFromArtifact(
|
||||||
|
artifact,
|
||||||
|
kclManager.artifactGraph
|
||||||
|
)
|
||||||
|
if (err(plane)) return Promise.reject(plane)
|
||||||
|
// if the user selected a segment, make sure we enter the right sketch as there can be multiple on a plane
|
||||||
|
// but still works if the user selected a plane/face by defaulting to the first path
|
||||||
|
const mainPath =
|
||||||
|
artifact?.type === 'segment' || artifact?.type === 'solid2d'
|
||||||
|
? artifact?.pathId
|
||||||
|
: plane?.pathIds[0]
|
||||||
|
let sketch: KclValue | null = null
|
||||||
|
let planeVar: Plane | null = null
|
||||||
|
|
||||||
|
for (const variable of Object.values(
|
||||||
|
kclManager.execState.variables
|
||||||
|
)) {
|
||||||
|
// find programMemory that matches path artifact
|
||||||
|
if (
|
||||||
|
variable?.type === 'Sketch' &&
|
||||||
|
variable.value.artifactId === mainPath
|
||||||
|
) {
|
||||||
|
sketch = variable
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
// if the variable is an sweep, check if the underlying sketch matches the artifact
|
||||||
|
variable?.type === 'Solid' &&
|
||||||
|
variable.value.sketch.on.type === 'plane' &&
|
||||||
|
variable.value.sketch.artifactId === mainPath
|
||||||
|
) {
|
||||||
|
sketch = {
|
||||||
|
type: 'Sketch',
|
||||||
|
value: variable.value.sketch,
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
variable?.type === 'Plane' &&
|
||||||
|
plane.id === variable.value.id
|
||||||
|
) {
|
||||||
|
planeVar = variable.value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!sketch || sketch.type !== 'Sketch') {
|
||||||
|
if (artifact?.type !== 'plane')
|
||||||
|
return Promise.reject(new Error('No sketch'))
|
||||||
|
const planeCodeRef = getFaceCodeRef(artifact)
|
||||||
|
if (planeVar && planeCodeRef) {
|
||||||
|
const toTuple = (point: Point3d): [number, number, number] => [
|
||||||
|
point.x,
|
||||||
|
point.y,
|
||||||
|
point.z,
|
||||||
|
]
|
||||||
|
const planPath = getNodePathFromSourceRange(
|
||||||
|
kclManager.ast,
|
||||||
|
planeCodeRef.range
|
||||||
|
)
|
||||||
|
await letEngineAnimateAndSyncCamAfter(
|
||||||
|
engineCommandManager,
|
||||||
|
artifact.id
|
||||||
|
)
|
||||||
|
const normal = crossProduct(planeVar.xAxis, planeVar.yAxis)
|
||||||
|
return {
|
||||||
|
sketchEntryNodePath: [],
|
||||||
|
planeNodePath: planPath,
|
||||||
|
sketchNodePaths: [],
|
||||||
|
zAxis: toTuple(normal),
|
||||||
|
yAxis: toTuple(planeVar.yAxis),
|
||||||
|
origin: toTuple(planeVar.origin),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Promise.reject(new Error('No sketch'))
|
||||||
|
}
|
||||||
|
const info = await sceneEntitiesManager.getSketchOrientationDetails(
|
||||||
|
sketch.value
|
||||||
|
)
|
||||||
|
await letEngineAnimateAndSyncCamAfter(
|
||||||
|
engineCommandManager,
|
||||||
|
info?.sketchDetails?.faceId || ''
|
||||||
|
)
|
||||||
|
|
||||||
|
const sketchArtifact = kclManager.artifactGraph.get(mainPath)
|
||||||
|
if (sketchArtifact?.type !== 'path')
|
||||||
|
return Promise.reject(new Error('No sketch artifact'))
|
||||||
|
const sketchPaths = getPathsFromArtifact({
|
||||||
|
artifact: kclManager.artifactGraph.get(plane.id),
|
||||||
|
sketchPathToNode: sketchArtifact?.codeRef?.pathToNode,
|
||||||
|
artifactGraph: kclManager.artifactGraph,
|
||||||
|
ast: kclManager.ast,
|
||||||
|
})
|
||||||
|
if (err(sketchPaths)) return Promise.reject(sketchPaths)
|
||||||
|
let codeRef = getFaceCodeRef(plane)
|
||||||
|
if (!codeRef) return Promise.reject(new Error('No plane codeRef'))
|
||||||
|
// codeRef.pathToNode is not always populated correctly
|
||||||
|
const planeNodePath = getNodePathFromSourceRange(
|
||||||
|
kclManager.ast,
|
||||||
|
codeRef.range
|
||||||
|
)
|
||||||
|
return {
|
||||||
|
sketchEntryNodePath: sketchArtifact.codeRef.pathToNode || [],
|
||||||
|
sketchNodePaths: sketchPaths,
|
||||||
|
planeNodePath,
|
||||||
|
zAxis: info.sketchDetails.zAxis || null,
|
||||||
|
yAxis: info.sketchDetails.yAxis || null,
|
||||||
|
origin: info.sketchDetails.origin.map(
|
||||||
|
(a) => a / sceneInfra._baseUnitMultiplier
|
||||||
|
) as [number, number, number],
|
||||||
|
animateTargetId: info?.sketchDetails?.faceId || '',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
),
|
||||||
|
|
||||||
'Get horizontal info': fromPromise(
|
'Get horizontal info': fromPromise(
|
||||||
async ({ input: { selectionRanges, sketchDetails } }) => {
|
async ({ input: { selectionRanges, sketchDetails } }) => {
|
||||||
const { modifiedAst, pathToNodeMap, exprInsertIndex } =
|
const { modifiedAst, pathToNodeMap, exprInsertIndex } =
|
||||||
@ -977,6 +1371,130 @@ export const ModelingMachineProvider = ({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
|
'Apply named value constraint': fromPromise(
|
||||||
|
async ({ input: { selectionRanges, sketchDetails, data } }) => {
|
||||||
|
if (!sketchDetails) {
|
||||||
|
return Promise.reject(new Error('No sketch details'))
|
||||||
|
}
|
||||||
|
if (!data) {
|
||||||
|
return Promise.reject(new Error('No data from command flow'))
|
||||||
|
}
|
||||||
|
let pResult = parse(recast(kclManager.ast))
|
||||||
|
if (trap(pResult) || !resultIsOk(pResult))
|
||||||
|
return Promise.reject(new Error('Unexpected compilation error'))
|
||||||
|
let parsed = pResult.program
|
||||||
|
|
||||||
|
let result: {
|
||||||
|
modifiedAst: Node<Program>
|
||||||
|
pathToReplaced: PathToNode | null
|
||||||
|
exprInsertIndex: number
|
||||||
|
} = {
|
||||||
|
modifiedAst: parsed,
|
||||||
|
pathToReplaced: null,
|
||||||
|
exprInsertIndex: -1,
|
||||||
|
}
|
||||||
|
// If the user provided a constant name,
|
||||||
|
// we need to insert the named constant
|
||||||
|
// and then replace the node with the constant's name.
|
||||||
|
if ('variableName' in data.namedValue) {
|
||||||
|
const astAfterReplacement = replaceValueAtNodePath({
|
||||||
|
ast: parsed,
|
||||||
|
pathToNode: data.currentValue.pathToNode,
|
||||||
|
newExpressionString: data.namedValue.variableName,
|
||||||
|
})
|
||||||
|
if (trap(astAfterReplacement)) {
|
||||||
|
return Promise.reject(astAfterReplacement)
|
||||||
|
}
|
||||||
|
const parseResultAfterInsertion = parse(
|
||||||
|
recast(
|
||||||
|
insertNamedConstant({
|
||||||
|
node: astAfterReplacement.modifiedAst,
|
||||||
|
newExpression: data.namedValue,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
)
|
||||||
|
result.exprInsertIndex = data.namedValue.insertIndex
|
||||||
|
|
||||||
|
if (
|
||||||
|
trap(parseResultAfterInsertion) ||
|
||||||
|
!resultIsOk(parseResultAfterInsertion)
|
||||||
|
)
|
||||||
|
return Promise.reject(parseResultAfterInsertion)
|
||||||
|
result = {
|
||||||
|
modifiedAst: parseResultAfterInsertion.program,
|
||||||
|
pathToReplaced: astAfterReplacement.pathToReplaced,
|
||||||
|
exprInsertIndex: result.exprInsertIndex,
|
||||||
|
}
|
||||||
|
} else if ('valueText' in data.namedValue) {
|
||||||
|
// If they didn't provide a constant name,
|
||||||
|
// just replace the node with the value.
|
||||||
|
const astAfterReplacement = replaceValueAtNodePath({
|
||||||
|
ast: parsed,
|
||||||
|
pathToNode: data.currentValue.pathToNode,
|
||||||
|
newExpressionString: data.namedValue.valueText,
|
||||||
|
})
|
||||||
|
if (trap(astAfterReplacement)) {
|
||||||
|
return Promise.reject(astAfterReplacement)
|
||||||
|
}
|
||||||
|
// The `replacer` function returns a pathToNode that assumes
|
||||||
|
// an identifier is also being inserted into the AST, creating an off-by-one error.
|
||||||
|
// This corrects that error, but TODO we should fix this upstream
|
||||||
|
// to avoid this kind of error in the future.
|
||||||
|
astAfterReplacement.pathToReplaced[1][0] =
|
||||||
|
(astAfterReplacement.pathToReplaced[1][0] as number) - 1
|
||||||
|
result = astAfterReplacement
|
||||||
|
}
|
||||||
|
|
||||||
|
pResult = parse(recast(result.modifiedAst))
|
||||||
|
if (trap(pResult) || !resultIsOk(pResult))
|
||||||
|
return Promise.reject(new Error('Unexpected compilation error'))
|
||||||
|
parsed = pResult.program
|
||||||
|
|
||||||
|
if (trap(parsed)) return Promise.reject(parsed)
|
||||||
|
if (!result.pathToReplaced)
|
||||||
|
return Promise.reject(new Error('No path to replaced node'))
|
||||||
|
const {
|
||||||
|
updatedSketchEntryNodePath,
|
||||||
|
updatedSketchNodePaths,
|
||||||
|
updatedPlaneNodePath,
|
||||||
|
} = updateSketchDetailsNodePaths({
|
||||||
|
sketchEntryNodePath: sketchDetails.sketchEntryNodePath,
|
||||||
|
sketchNodePaths: sketchDetails.sketchNodePaths,
|
||||||
|
planeNodePath: sketchDetails.planeNodePath,
|
||||||
|
exprInsertIndex: result.exprInsertIndex,
|
||||||
|
})
|
||||||
|
|
||||||
|
const updatedAst =
|
||||||
|
await sceneEntitiesManager.updateAstAndRejigSketch(
|
||||||
|
updatedSketchEntryNodePath,
|
||||||
|
updatedSketchNodePaths,
|
||||||
|
updatedPlaneNodePath,
|
||||||
|
parsed,
|
||||||
|
sketchDetails.zAxis,
|
||||||
|
sketchDetails.yAxis,
|
||||||
|
sketchDetails.origin
|
||||||
|
)
|
||||||
|
if (err(updatedAst)) return Promise.reject(updatedAst)
|
||||||
|
|
||||||
|
await codeManager.updateEditorWithAstAndWriteToFile(
|
||||||
|
updatedAst.newAst
|
||||||
|
)
|
||||||
|
|
||||||
|
const selection = updateSelections(
|
||||||
|
{ 0: result.pathToReplaced },
|
||||||
|
selectionRanges,
|
||||||
|
updatedAst.newAst
|
||||||
|
)
|
||||||
|
if (err(selection)) return Promise.reject(selection)
|
||||||
|
return {
|
||||||
|
selectionType: 'completeSelection',
|
||||||
|
selection,
|
||||||
|
updatedSketchEntryNodePath,
|
||||||
|
updatedSketchNodePaths,
|
||||||
|
updatedPlaneNodePath,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
),
|
||||||
'set-up-draft-circle': fromPromise(
|
'set-up-draft-circle': fromPromise(
|
||||||
async ({ input: { sketchDetails, data } }) => {
|
async ({ input: { sketchDetails, data } }) => {
|
||||||
if (!sketchDetails || !data)
|
if (!sketchDetails || !data)
|
||||||
@ -1090,6 +1608,38 @@ export const ModelingMachineProvider = ({
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
|
'setup-client-side-sketch-segments': fromPromise(
|
||||||
|
async ({ input: { sketchDetails, selectionRanges } }) => {
|
||||||
|
if (!sketchDetails) return
|
||||||
|
if (!sketchDetails.sketchEntryNodePath?.length) return
|
||||||
|
sceneInfra.resetMouseListeners()
|
||||||
|
await sceneEntitiesManager.setupSketch({
|
||||||
|
sketchEntryNodePath: sketchDetails?.sketchEntryNodePath || [],
|
||||||
|
sketchNodePaths: sketchDetails.sketchNodePaths,
|
||||||
|
forward: sketchDetails.zAxis,
|
||||||
|
up: sketchDetails.yAxis,
|
||||||
|
position: sketchDetails.origin,
|
||||||
|
maybeModdedAst: kclManager.ast,
|
||||||
|
selectionRanges,
|
||||||
|
})
|
||||||
|
sceneInfra.resetMouseListeners()
|
||||||
|
|
||||||
|
sceneEntitiesManager.setupSketchIdleCallbacks({
|
||||||
|
sketchEntryNodePath: sketchDetails?.sketchEntryNodePath || [],
|
||||||
|
forward: sketchDetails.zAxis,
|
||||||
|
up: sketchDetails.yAxis,
|
||||||
|
position: sketchDetails.origin,
|
||||||
|
sketchNodePaths: sketchDetails.sketchNodePaths,
|
||||||
|
planeNodePath: sketchDetails.planeNodePath,
|
||||||
|
// We will want to pass sketchTools here
|
||||||
|
// to add their interactions
|
||||||
|
})
|
||||||
|
|
||||||
|
// We will want to update the context with sketchTools.
|
||||||
|
// They'll be used for their .destroy() in tearDownSketch
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
),
|
||||||
'split-sketch-pipe-if-needed': fromPromise(
|
'split-sketch-pipe-if-needed': fromPromise(
|
||||||
async ({ input: { sketchDetails } }) => {
|
async ({ input: { sketchDetails } }) => {
|
||||||
if (!sketchDetails) return reject('No sketch details')
|
if (!sketchDetails) return reject('No sketch details')
|
||||||
|
@ -14,8 +14,13 @@ import { IS_ML_EXPERIMENTAL, PROJECT_ENTRYPOINT } from '@src/lib/constants'
|
|||||||
import toast from 'react-hot-toast'
|
import toast from 'react-hot-toast'
|
||||||
import { reportRejection } from '@src/lib/trap'
|
import { reportRejection } from '@src/lib/trap'
|
||||||
import { relevantFileExtensions } from '@src/lang/wasmUtils'
|
import { relevantFileExtensions } from '@src/lang/wasmUtils'
|
||||||
import { getStringAfterLastSeparator, webSafePathSplit } from '@src/lib/paths'
|
import {
|
||||||
|
getStringAfterLastSeparator,
|
||||||
|
joinOSPaths,
|
||||||
|
webSafePathSplit,
|
||||||
|
} from '@src/lib/paths'
|
||||||
import { FILE_EXT } from '@src/lib/constants'
|
import { FILE_EXT } from '@src/lib/constants'
|
||||||
|
import { getAllSubDirectoriesAtProjectRoot } from '@src/machines/systemIO/snapshotContext'
|
||||||
|
|
||||||
function onSubmitKCLSampleCreation({
|
function onSubmitKCLSampleCreation({
|
||||||
sample,
|
sample,
|
||||||
@ -87,6 +92,26 @@ function onSubmitKCLSampleCreation({
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
|
/**
|
||||||
|
* When adding assemblies to an existing project create the assembly into a unique sub directory
|
||||||
|
*/
|
||||||
|
if (!isProjectNew) {
|
||||||
|
requestedFiles.forEach((requestedFile) => {
|
||||||
|
const subDirectoryName = projectPathPart
|
||||||
|
const firstLevelDirectories = getAllSubDirectoriesAtProjectRoot({
|
||||||
|
projectFolderName: requestedFile.requestedProjectName,
|
||||||
|
})
|
||||||
|
const uniqueSubDirectoryName = getUniqueProjectName(
|
||||||
|
subDirectoryName,
|
||||||
|
firstLevelDirectories
|
||||||
|
)
|
||||||
|
requestedFile.requestedProjectName = joinOSPaths(
|
||||||
|
requestedFile.requestedProjectName,
|
||||||
|
uniqueSubDirectoryName
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Bulk create the assembly and navigate to the project
|
* Bulk create the assembly and navigate to the project
|
||||||
*/
|
*/
|
||||||
@ -278,10 +303,9 @@ export function createApplicationCommands({
|
|||||||
return value
|
return value
|
||||||
},
|
},
|
||||||
options: ({ argumentsToSubmit }) => {
|
options: ({ argumentsToSubmit }) => {
|
||||||
const samples =
|
const samples = isDesktop()
|
||||||
isDesktop() && argumentsToSubmit.method !== 'existingProject'
|
? everyKclSample
|
||||||
? everyKclSample
|
: kclSamplesManifestWithNoMultipleFiles
|
||||||
: kclSamplesManifestWithNoMultipleFiles
|
|
||||||
return samples.map((sample) => {
|
return samples.map((sample) => {
|
||||||
return {
|
return {
|
||||||
value: sample.pathFromProjectDirectoryToFirstFile,
|
value: sample.pathFromProjectDirectoryToFirstFile,
|
||||||
@ -296,17 +320,10 @@ export function createApplicationCommands({
|
|||||||
skip: true,
|
skip: true,
|
||||||
options: ({ argumentsToSubmit }, _) => {
|
options: ({ argumentsToSubmit }, _) => {
|
||||||
if (isDesktop() && typeof argumentsToSubmit.sample === 'string') {
|
if (isDesktop() && typeof argumentsToSubmit.sample === 'string') {
|
||||||
const kclSample = findKclSample(argumentsToSubmit.sample)
|
return [
|
||||||
if (kclSample && kclSample.files.length > 1) {
|
{ name: 'New project', value: 'newProject', isCurrent: true },
|
||||||
return [
|
{ name: 'Existing project', value: 'existingProject' },
|
||||||
{ name: 'New project', value: 'newProject', isCurrent: true },
|
]
|
||||||
]
|
|
||||||
} else {
|
|
||||||
return [
|
|
||||||
{ name: 'New project', value: 'newProject', isCurrent: true },
|
|
||||||
{ name: 'Existing project', value: 'existingProject' },
|
|
||||||
]
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
return [{ name: 'Overwrite', value: 'existingProject' }]
|
return [{ name: 'Overwrite', value: 'existingProject' }]
|
||||||
}
|
}
|
||||||
|
@ -28,6 +28,7 @@ import type { Selections } from '@src/lib/selections'
|
|||||||
import { codeManager, kclManager } from '@src/lib/singletons'
|
import { codeManager, kclManager } from '@src/lib/singletons'
|
||||||
import { err } from '@src/lib/trap'
|
import { err } from '@src/lib/trap'
|
||||||
import type { SketchTool, modelingMachine } from '@src/machines/modelingMachine'
|
import type { SketchTool, modelingMachine } from '@src/machines/modelingMachine'
|
||||||
|
import { isDesktop } from '../isDesktop'
|
||||||
|
|
||||||
type OutputFormat = Models['OutputFormat3d_type']
|
type OutputFormat = Models['OutputFormat3d_type']
|
||||||
type OutputTypeKey = OutputFormat['type']
|
type OutputTypeKey = OutputFormat['type']
|
||||||
@ -159,6 +160,10 @@ export type ModelingCommandSchema = {
|
|||||||
nodeToEdit?: PathToNode
|
nodeToEdit?: PathToNode
|
||||||
color: string
|
color: string
|
||||||
}
|
}
|
||||||
|
Insert: {
|
||||||
|
path: string
|
||||||
|
localName: string
|
||||||
|
}
|
||||||
Translate: {
|
Translate: {
|
||||||
nodeToEdit?: PathToNode
|
nodeToEdit?: PathToNode
|
||||||
selection: Selections
|
selection: Selections
|
||||||
@ -1011,6 +1016,74 @@ export const modelingMachineCommandConfig: StateMachineCommandSetConfig<
|
|||||||
// Add more fields
|
// Add more fields
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
Insert: {
|
||||||
|
description: 'Insert from a file in the current project directory',
|
||||||
|
icon: 'import',
|
||||||
|
hide: 'web',
|
||||||
|
needsReview: true,
|
||||||
|
args: {
|
||||||
|
path: {
|
||||||
|
inputType: 'options',
|
||||||
|
required: true,
|
||||||
|
// options
|
||||||
|
validation: async ({ data }) => {
|
||||||
|
const importExists = kclManager.ast.body.find(
|
||||||
|
(n) =>
|
||||||
|
n.type === 'ImportStatement' &&
|
||||||
|
((n.path.type === 'Kcl' && n.path.filename === data.path) ||
|
||||||
|
(n.path.type === 'Foreign' && n.path.path === data.path))
|
||||||
|
)
|
||||||
|
if (importExists) {
|
||||||
|
return 'This file is already imported, use the Clone command instead.'
|
||||||
|
// TODO: see if we can transition to the clone command, see #6515
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
},
|
||||||
|
},
|
||||||
|
localName: {
|
||||||
|
inputType: 'string',
|
||||||
|
required: true,
|
||||||
|
defaultValue: (context: CommandBarContext) => {
|
||||||
|
if (!context.argumentsToSubmit['path']) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const path = context.argumentsToSubmit['path'] as string
|
||||||
|
return getPathFilenameInVariableCase(path)
|
||||||
|
},
|
||||||
|
validation: async ({ data }) => {
|
||||||
|
const variableExists = kclManager.variables[data.localName]
|
||||||
|
if (variableExists) {
|
||||||
|
return 'This variable name is already in use.'
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
// onSubmit: (data) => {
|
||||||
|
// if (!data) {
|
||||||
|
// return new Error('No input provided')
|
||||||
|
// }
|
||||||
|
|
||||||
|
// const ast = kclManager.ast
|
||||||
|
// const { path, localName } = data
|
||||||
|
// const { modifiedAst, pathToNode } = addModuleImport({
|
||||||
|
// ast,
|
||||||
|
// path,
|
||||||
|
// localName,
|
||||||
|
// })
|
||||||
|
// updateModelingState(
|
||||||
|
// modifiedAst,
|
||||||
|
// EXECUTION_TYPE_REAL,
|
||||||
|
// { kclManager, editorManager, codeManager },
|
||||||
|
// {
|
||||||
|
// focusPath: [pathToNode],
|
||||||
|
// }
|
||||||
|
// ).catch(reportRejection)
|
||||||
|
// },
|
||||||
|
},
|
||||||
Translate: {
|
Translate: {
|
||||||
description: 'Set translation on solid or sketch.',
|
description: 'Set translation on solid or sketch.',
|
||||||
icon: 'move',
|
icon: 'move',
|
||||||
|
@ -89,76 +89,76 @@ export function kclCommands(commandProps: KclCommandConfig): Command[] {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
// {
|
||||||
name: 'Insert',
|
// name: 'Insert',
|
||||||
description: 'Insert from a file in the current project directory',
|
// description: 'Insert from a file in the current project directory',
|
||||||
icon: 'import',
|
// icon: 'import',
|
||||||
groupId: 'code',
|
// groupId: 'code',
|
||||||
hide: 'web',
|
// hide: 'web',
|
||||||
needsReview: true,
|
// needsReview: true,
|
||||||
args: {
|
// args: {
|
||||||
path: {
|
// path: {
|
||||||
inputType: 'options',
|
// inputType: 'options',
|
||||||
required: true,
|
// required: true,
|
||||||
options: commandProps.specialPropsForInsertCommand.providedOptions,
|
// options: commandProps.specialPropsForInsertCommand.providedOptions,
|
||||||
validation: async ({ data }) => {
|
// validation: async ({ data }) => {
|
||||||
const importExists = kclManager.ast.body.find(
|
// const importExists = kclManager.ast.body.find(
|
||||||
(n) =>
|
// (n) =>
|
||||||
n.type === 'ImportStatement' &&
|
// n.type === 'ImportStatement' &&
|
||||||
((n.path.type === 'Kcl' && n.path.filename === data.path) ||
|
// ((n.path.type === 'Kcl' && n.path.filename === data.path) ||
|
||||||
(n.path.type === 'Foreign' && n.path.path === data.path))
|
// (n.path.type === 'Foreign' && n.path.path === data.path))
|
||||||
)
|
// )
|
||||||
if (importExists) {
|
// if (importExists) {
|
||||||
return 'This file is already imported, use the Clone command instead.'
|
// return 'This file is already imported, use the Clone command instead.'
|
||||||
// TODO: see if we can transition to the clone command, see #6515
|
// // TODO: see if we can transition to the clone command, see #6515
|
||||||
}
|
// }
|
||||||
|
|
||||||
return true
|
// return true
|
||||||
},
|
// },
|
||||||
},
|
// },
|
||||||
localName: {
|
// localName: {
|
||||||
inputType: 'string',
|
// inputType: 'string',
|
||||||
required: true,
|
// required: true,
|
||||||
defaultValue: (context: CommandBarContext) => {
|
// defaultValue: (context: CommandBarContext) => {
|
||||||
if (!context.argumentsToSubmit['path']) {
|
// if (!context.argumentsToSubmit['path']) {
|
||||||
return
|
// return
|
||||||
}
|
// }
|
||||||
|
|
||||||
const path = context.argumentsToSubmit['path'] as string
|
// const path = context.argumentsToSubmit['path'] as string
|
||||||
return getPathFilenameInVariableCase(path)
|
// return getPathFilenameInVariableCase(path)
|
||||||
},
|
// },
|
||||||
validation: async ({ data }) => {
|
// validation: async ({ data }) => {
|
||||||
const variableExists = kclManager.variables[data.localName]
|
// const variableExists = kclManager.variables[data.localName]
|
||||||
if (variableExists) {
|
// if (variableExists) {
|
||||||
return 'This variable name is already in use.'
|
// return 'This variable name is already in use.'
|
||||||
}
|
// }
|
||||||
|
|
||||||
return true
|
// return true
|
||||||
},
|
// },
|
||||||
},
|
// },
|
||||||
},
|
// },
|
||||||
onSubmit: (data) => {
|
// onSubmit: (data) => {
|
||||||
if (!data) {
|
// if (!data) {
|
||||||
return new Error('No input provided')
|
// return new Error('No input provided')
|
||||||
}
|
// }
|
||||||
|
|
||||||
const ast = kclManager.ast
|
// const ast = kclManager.ast
|
||||||
const { path, localName } = data
|
// const { path, localName } = data
|
||||||
const { modifiedAst, pathToNode } = addModuleImport({
|
// const { modifiedAst, pathToNode } = addModuleImport({
|
||||||
ast,
|
// ast,
|
||||||
path,
|
// path,
|
||||||
localName,
|
// localName,
|
||||||
})
|
// })
|
||||||
updateModelingState(
|
// updateModelingState(
|
||||||
modifiedAst,
|
// modifiedAst,
|
||||||
EXECUTION_TYPE_REAL,
|
// EXECUTION_TYPE_REAL,
|
||||||
{ kclManager, editorManager, codeManager },
|
// { kclManager, editorManager, codeManager },
|
||||||
{
|
// {
|
||||||
focusPath: [pathToNode],
|
// focusPath: [pathToNode],
|
||||||
}
|
// }
|
||||||
).catch(reportRejection)
|
// ).catch(reportRejection)
|
||||||
},
|
// },
|
||||||
},
|
// },
|
||||||
{
|
{
|
||||||
name: 'format-code',
|
name: 'format-code',
|
||||||
displayName: 'Format Code',
|
displayName: 'Format Code',
|
||||||
|
@ -1,11 +1,6 @@
|
|||||||
import { DEFAULT_DEFAULT_LENGTH_UNIT } from '@src/lib/constants'
|
import { DEFAULT_DEFAULT_LENGTH_UNIT } from '@src/lib/constants'
|
||||||
import { isPlaywright } from '@src/lib/isPlaywright'
|
import { isPlaywright } from '@src/lib/isPlaywright'
|
||||||
import {
|
import { engineCommandManager, kclManager } from '@src/lib/singletons'
|
||||||
engineCommandManager,
|
|
||||||
kclManager,
|
|
||||||
sceneInfra,
|
|
||||||
settingsActor,
|
|
||||||
} from '@src/lib/singletons'
|
|
||||||
import {
|
import {
|
||||||
engineStreamZoomToFit,
|
engineStreamZoomToFit,
|
||||||
engineViewIsometricWithoutGeometryPresent,
|
engineViewIsometricWithoutGeometryPresent,
|
||||||
@ -27,15 +22,6 @@ export async function resetCameraPosition() {
|
|||||||
if (isPlaywright()) {
|
if (isPlaywright()) {
|
||||||
await engineStreamZoomToFit({ engineCommandManager, padding })
|
await engineStreamZoomToFit({ engineCommandManager, padding })
|
||||||
} else {
|
} else {
|
||||||
// Get user camera projection
|
|
||||||
const cameraProjection =
|
|
||||||
settingsActor.getSnapshot().context.modeling.cameraProjection.current
|
|
||||||
|
|
||||||
// We need to keep the users projection setting when resetting their camera
|
|
||||||
if (cameraProjection === 'perspective') {
|
|
||||||
await sceneInfra.camControls.usePerspectiveCamera()
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the scene is empty you cannot use view_isometric, it will not move the camera
|
// If the scene is empty you cannot use view_isometric, it will not move the camera
|
||||||
if (kclManager.isAstBodyEmpty(kclManager.ast)) {
|
if (kclManager.isAstBodyEmpty(kclManager.ast)) {
|
||||||
await engineViewIsometricWithoutGeometryPresent({
|
await engineViewIsometricWithoutGeometryPresent({
|
||||||
@ -43,7 +29,6 @@ export async function resetCameraPosition() {
|
|||||||
unit:
|
unit:
|
||||||
kclManager.fileSettings.defaultLengthUnit ||
|
kclManager.fileSettings.defaultLengthUnit ||
|
||||||
DEFAULT_DEFAULT_LENGTH_UNIT,
|
DEFAULT_DEFAULT_LENGTH_UNIT,
|
||||||
cameraProjection,
|
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
await engineViewIsometricWithGeometryPresent({
|
await engineViewIsometricWithGeometryPresent({
|
||||||
|
@ -12,7 +12,6 @@ import type {
|
|||||||
CameraViewState_type,
|
CameraViewState_type,
|
||||||
UnitLength_type,
|
UnitLength_type,
|
||||||
} from '@kittycad/lib/dist/types/src/models'
|
} from '@kittycad/lib/dist/types/src/models'
|
||||||
import type { CameraProjectionType } from '@rust/kcl-lib/bindings/CameraProjectionType'
|
|
||||||
|
|
||||||
export const uuidv4 = v4
|
export const uuidv4 = v4
|
||||||
|
|
||||||
@ -623,11 +622,9 @@ export async function engineViewIsometricWithGeometryPresent({
|
|||||||
export async function engineViewIsometricWithoutGeometryPresent({
|
export async function engineViewIsometricWithoutGeometryPresent({
|
||||||
engineCommandManager,
|
engineCommandManager,
|
||||||
unit,
|
unit,
|
||||||
cameraProjection,
|
|
||||||
}: {
|
}: {
|
||||||
engineCommandManager: EngineCommandManager
|
engineCommandManager: EngineCommandManager
|
||||||
unit?: UnitLength_type
|
unit?: UnitLength_type
|
||||||
cameraProjection: CameraProjectionType
|
|
||||||
}) {
|
}) {
|
||||||
// If you load an empty scene with any file unit it will have an eye offset of this
|
// If you load an empty scene with any file unit it will have an eye offset of this
|
||||||
const MAGIC_ENGINE_EYE_OFFSET = 1378.0057
|
const MAGIC_ENGINE_EYE_OFFSET = 1378.0057
|
||||||
@ -647,8 +644,8 @@ export async function engineViewIsometricWithoutGeometryPresent({
|
|||||||
eye_offset: MAGIC_ENGINE_EYE_OFFSET,
|
eye_offset: MAGIC_ENGINE_EYE_OFFSET,
|
||||||
fov_y: 45,
|
fov_y: 45,
|
||||||
ortho_scale_factor: 1.4063792,
|
ortho_scale_factor: 1.4063792,
|
||||||
is_ortho: cameraProjection !== 'perspective',
|
is_ortho: true,
|
||||||
ortho_scale_enabled: cameraProjection !== 'perspective',
|
ortho_scale_enabled: true,
|
||||||
world_coord_system: 'right_handed_up_z',
|
world_coord_system: 'right_handed_up_z',
|
||||||
}
|
}
|
||||||
await engineCommandManager.sendSceneCommand({
|
await engineCommandManager.sendSceneCommand({
|
||||||
|
@ -1,245 +0,0 @@
|
|||||||
import {
|
|
||||||
modelingMachine,
|
|
||||||
modelingMachineDefaultContext,
|
|
||||||
} from '@src/machines/modelingMachine'
|
|
||||||
import { createActor } from 'xstate'
|
|
||||||
import { vi } from 'vitest'
|
|
||||||
import { assertParse, type CallExpressionKw } from '@src/lang/wasm'
|
|
||||||
import { initPromise } from '@src/lang/wasmUtils'
|
|
||||||
import {
|
|
||||||
codeManager,
|
|
||||||
engineCommandManager,
|
|
||||||
kclManager,
|
|
||||||
} from '@src/lib/singletons'
|
|
||||||
import { VITE_KC_DEV_TOKEN } from '@src/env'
|
|
||||||
import { line } from '@src/lang/std/sketch'
|
|
||||||
import { getNodeFromPath } from '@src/lang/queryAst'
|
|
||||||
import type { Node } from '@rust/kcl-lib/bindings/Node'
|
|
||||||
import { err } from '@src/lib/trap'
|
|
||||||
import {
|
|
||||||
createIdentifier,
|
|
||||||
createLiteral,
|
|
||||||
createVariableDeclaration,
|
|
||||||
} from '@src/lang/create'
|
|
||||||
|
|
||||||
// Store original method to restore in afterAll
|
|
||||||
|
|
||||||
beforeAll(async () => {
|
|
||||||
await initPromise
|
|
||||||
|
|
||||||
// THESE TEST WILL FAIL without VITE_KC_DEV_TOKEN set in .env.development.local
|
|
||||||
await new Promise((resolve) => {
|
|
||||||
engineCommandManager.start({
|
|
||||||
token: VITE_KC_DEV_TOKEN,
|
|
||||||
width: 256,
|
|
||||||
height: 256,
|
|
||||||
setMediaStream: () => {},
|
|
||||||
setIsStreamReady: () => {},
|
|
||||||
callbackOnEngineLiteConnect: () => {
|
|
||||||
resolve(true)
|
|
||||||
},
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}, 30_000)
|
|
||||||
|
|
||||||
afterAll(() => {
|
|
||||||
// Restore the original method
|
|
||||||
|
|
||||||
engineCommandManager.tearDown()
|
|
||||||
})
|
|
||||||
|
|
||||||
// Define mock implementations that will be referenced in vi.mock calls
|
|
||||||
vi.mock('@src/components/SetHorVertDistanceModal', () => ({
|
|
||||||
createInfoModal: vi.fn(() => ({
|
|
||||||
open: vi.fn().mockResolvedValue({
|
|
||||||
value: '10',
|
|
||||||
segName: 'test',
|
|
||||||
valueNode: {},
|
|
||||||
newVariableInsertIndex: 0,
|
|
||||||
sign: 1,
|
|
||||||
}),
|
|
||||||
})),
|
|
||||||
GetInfoModal: vi.fn(),
|
|
||||||
}))
|
|
||||||
|
|
||||||
vi.mock('@src/components/SetAngleLengthModal', () => ({
|
|
||||||
createSetAngleLengthModal: vi.fn(() => ({
|
|
||||||
open: vi.fn().mockResolvedValue({
|
|
||||||
value: '45',
|
|
||||||
segName: 'test',
|
|
||||||
valueNode: {},
|
|
||||||
newVariableInsertIndex: 0,
|
|
||||||
sign: 1,
|
|
||||||
}),
|
|
||||||
})),
|
|
||||||
SetAngleLengthModal: vi.fn(),
|
|
||||||
}))
|
|
||||||
|
|
||||||
// Add this function before the test cases
|
|
||||||
// Utility function to wait for a condition to be met
|
|
||||||
const waitForCondition = async (
|
|
||||||
condition: () => boolean,
|
|
||||||
timeout = 5000,
|
|
||||||
interval = 100
|
|
||||||
) => {
|
|
||||||
const startTime = Date.now()
|
|
||||||
|
|
||||||
while (Date.now() - startTime < timeout) {
|
|
||||||
try {
|
|
||||||
if (condition()) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
} catch {
|
|
||||||
// Ignore errors, keep polling
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wait for the next interval
|
|
||||||
await new Promise((resolve) => setTimeout(resolve, interval))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Last attempt before failing
|
|
||||||
return condition()
|
|
||||||
}
|
|
||||||
|
|
||||||
describe('modelingMachine - XState', () => {
|
|
||||||
describe('when initialized', () => {
|
|
||||||
it('should start in the idle state', () => {
|
|
||||||
const actor = createActor(modelingMachine, {
|
|
||||||
input: modelingMachineDefaultContext,
|
|
||||||
}).start()
|
|
||||||
const state = actor.getSnapshot().value
|
|
||||||
|
|
||||||
// The machine should start in the idle state
|
|
||||||
expect(state).toEqual({ idle: 'hidePlanes' })
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe('when in sketch mode', () => {
|
|
||||||
it('should transition to sketch state when entering sketch mode', async () => {
|
|
||||||
const code = `sketch001 = startSketchOn(XZ)
|
|
||||||
profile001 = startProfile(sketch001, at = [2263.04, -2721.2])
|
|
||||||
|> line(end = [16.27, 73.81])
|
|
||||||
|> line(end = [75.72, 18.41])
|
|
||||||
`
|
|
||||||
|
|
||||||
const ast = assertParse(code)
|
|
||||||
|
|
||||||
await kclManager.executeAst({ ast })
|
|
||||||
|
|
||||||
expect(kclManager.errors).toEqual([])
|
|
||||||
|
|
||||||
const indexOfInterest = code.indexOf('[16.27, 73.81]')
|
|
||||||
|
|
||||||
// segment artifact with that source range
|
|
||||||
const artifact = [...kclManager.artifactGraph].find(
|
|
||||||
([_, artifact]) =>
|
|
||||||
artifact?.type === 'segment' &&
|
|
||||||
artifact.codeRef.range[0] <= indexOfInterest &&
|
|
||||||
indexOfInterest <= artifact.codeRef.range[1]
|
|
||||||
)?.[1]
|
|
||||||
if (!artifact || !('codeRef' in artifact)) {
|
|
||||||
throw new Error('Artifact not found or invalid artifact structure')
|
|
||||||
}
|
|
||||||
|
|
||||||
const actor = createActor(modelingMachine, {
|
|
||||||
input: modelingMachineDefaultContext,
|
|
||||||
}).start()
|
|
||||||
|
|
||||||
// Send event to transition to sketch mode
|
|
||||||
actor.send({
|
|
||||||
type: 'Set selection',
|
|
||||||
data: {
|
|
||||||
selectionType: 'mirrorCodeMirrorSelections',
|
|
||||||
selection: {
|
|
||||||
graphSelections: [
|
|
||||||
{
|
|
||||||
artifact: artifact,
|
|
||||||
codeRef: artifact.codeRef,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
otherSelections: [],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
actor.send({ type: 'Enter sketch' })
|
|
||||||
|
|
||||||
// Check that we're in the sketch state
|
|
||||||
let state = actor.getSnapshot()
|
|
||||||
expect(state.value).toBe('animating to existing sketch')
|
|
||||||
|
|
||||||
// wait for it to transition
|
|
||||||
await waitForCondition(() => {
|
|
||||||
const snapshot = actor.getSnapshot()
|
|
||||||
return snapshot.value !== 'animating to existing sketch'
|
|
||||||
}, 5000)
|
|
||||||
|
|
||||||
// After the condition is met, do the actual assertion
|
|
||||||
expect(actor.getSnapshot().value).toEqual({
|
|
||||||
Sketch: { SketchIdle: 'scene drawn' },
|
|
||||||
})
|
|
||||||
|
|
||||||
const getConstraintInfo = line.getConstraintInfo
|
|
||||||
const callExp = getNodeFromPath<Node<CallExpressionKw>>(
|
|
||||||
kclManager.ast,
|
|
||||||
artifact.codeRef.pathToNode,
|
|
||||||
'CallExpressionKw'
|
|
||||||
)
|
|
||||||
if (err(callExp)) {
|
|
||||||
throw new Error('Failed to get CallExpressionKw node')
|
|
||||||
}
|
|
||||||
const constraintInfo = getConstraintInfo(
|
|
||||||
callExp.node,
|
|
||||||
codeManager.code,
|
|
||||||
artifact.codeRef.pathToNode
|
|
||||||
)
|
|
||||||
const first = constraintInfo[0]
|
|
||||||
|
|
||||||
// Now that we're in sketchIdle state, test the "Constrain with named value" event
|
|
||||||
actor.send({
|
|
||||||
type: 'Constrain with named value',
|
|
||||||
data: {
|
|
||||||
currentValue: {
|
|
||||||
valueText: first.value,
|
|
||||||
pathToNode: first.pathToNode,
|
|
||||||
variableName: 'test_variable',
|
|
||||||
},
|
|
||||||
// Use type assertion to mock the complex type
|
|
||||||
namedValue: {
|
|
||||||
valueText: '20',
|
|
||||||
variableName: 'test_variable',
|
|
||||||
insertIndex: 0,
|
|
||||||
valueCalculated: '20',
|
|
||||||
variableDeclarationAst: createVariableDeclaration(
|
|
||||||
'test_variable',
|
|
||||||
createLiteral('20')
|
|
||||||
),
|
|
||||||
variableIdentifierAst: createIdentifier('test_variable') as any,
|
|
||||||
valueAst: createLiteral('20'),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
// Wait for the state to change in response to the constraint
|
|
||||||
await waitForCondition(() => {
|
|
||||||
const snapshot = actor.getSnapshot()
|
|
||||||
// Check if we've transitioned to a different state
|
|
||||||
return (
|
|
||||||
JSON.stringify(snapshot.value) !==
|
|
||||||
JSON.stringify({
|
|
||||||
Sketch: { SketchIdle: 'set up segments' },
|
|
||||||
})
|
|
||||||
)
|
|
||||||
}, 5000)
|
|
||||||
|
|
||||||
await waitForCondition(() => {
|
|
||||||
const snapshot = actor.getSnapshot()
|
|
||||||
// Check if we've transitioned to a different state
|
|
||||||
return (
|
|
||||||
JSON.stringify(snapshot.value) !==
|
|
||||||
JSON.stringify({ Sketch: 'Converting to named value' })
|
|
||||||
)
|
|
||||||
}, 5000)
|
|
||||||
expect(codeManager.code).toContain('line(end = [test_variable,')
|
|
||||||
}, 10_000)
|
|
||||||
})
|
|
||||||
})
|
|
File diff suppressed because one or more lines are too long
@ -1,4 +1,6 @@
|
|||||||
|
import type { FileEntry } from '@src/lib/project'
|
||||||
import { systemIOActor } from '@src/lib/singletons'
|
import { systemIOActor } from '@src/lib/singletons'
|
||||||
|
import { isArray } from '@src/lib/utils'
|
||||||
|
|
||||||
export const folderSnapshot = () => {
|
export const folderSnapshot = () => {
|
||||||
const { folders } = systemIOActor.getSnapshot().context
|
const { folders } = systemIOActor.getSnapshot().context
|
||||||
@ -9,3 +11,48 @@ export const defaultProjectFolderNameSnapshot = () => {
|
|||||||
const { defaultProjectFolderName } = systemIOActor.getSnapshot().context
|
const { defaultProjectFolderName } = systemIOActor.getSnapshot().context
|
||||||
return defaultProjectFolderName
|
return defaultProjectFolderName
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* From the application project directory go down to a project folder and list all the folders at that directory level
|
||||||
|
* application project directory: /home/documents/zoo-modeling-app-projects/
|
||||||
|
*
|
||||||
|
* /home/documents/zoo-modeling-app-projects/car-door/
|
||||||
|
* ├── handle
|
||||||
|
* ├── main.kcl
|
||||||
|
* └── window
|
||||||
|
*
|
||||||
|
* The two folders are handle and window
|
||||||
|
*
|
||||||
|
* @param {Object} params
|
||||||
|
* @param {string} params.projectFolderName - The name with no path information.
|
||||||
|
* @returns {FileEntry[]} An array of subdirectory names found at the root level of the specified project folder.
|
||||||
|
*/
|
||||||
|
export const getAllSubDirectoriesAtProjectRoot = ({
|
||||||
|
projectFolderName,
|
||||||
|
}: { projectFolderName: string }): FileEntry[] => {
|
||||||
|
const subDirectories: FileEntry[] = []
|
||||||
|
const { folders } = systemIOActor.getSnapshot().context
|
||||||
|
|
||||||
|
const projectFolder = folders.find((folder) => {
|
||||||
|
return folder.name === projectFolderName
|
||||||
|
})
|
||||||
|
|
||||||
|
// Find the subdirectories
|
||||||
|
if (projectFolder) {
|
||||||
|
// 1st level
|
||||||
|
const children = projectFolder.children
|
||||||
|
if (children) {
|
||||||
|
children.forEach((childFileOrDirectory) => {
|
||||||
|
// 2nd level
|
||||||
|
const secondLevelChild = childFileOrDirectory.children
|
||||||
|
// if secondLevelChild is null then it is a file
|
||||||
|
if (secondLevelChild && isArray(secondLevelChild)) {
|
||||||
|
// this is a directory!
|
||||||
|
subDirectories.push(childFileOrDirectory)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return subDirectories
|
||||||
|
}
|
||||||
|
Reference in New Issue
Block a user