Compare commits
26 Commits
v0.26.2
...
franknoiro
Author | SHA1 | Date | |
---|---|---|---|
57e030a0ad | |||
0adecf4f58 | |||
26e995dc3f | |||
08e411e1a3 | |||
a8b816a3e2 | |||
43bec115c0 | |||
0c6c646fe7 | |||
0d52851da2 | |||
6b105897f7 | |||
9ff51de301 | |||
c161f578fd | |||
4804eedf3e | |||
99db31a6a4 | |||
90b57ec202 | |||
3f86f99f5e | |||
83e2b093a6 | |||
58f7e0086d | |||
c147b3bfa2 | |||
7103ded32a | |||
81279aa4e8 | |||
550c8ae165 | |||
05610bb0f3 | |||
4a62862ca0 | |||
a4783d4951 | |||
30cfac06b8 | |||
c5509dabb1 |
42
.github/workflows/build-test-publish-apps.yml
vendored
@ -109,17 +109,8 @@ jobs:
|
|||||||
platform: linux
|
platform: linux
|
||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
env:
|
env:
|
||||||
APPLE_ID: ${{ secrets.APPLE_ID }}
|
|
||||||
APPLE_PASSWORD: ${{ secrets.APPLE_PASSWORD }}
|
|
||||||
APPLE_APP_SPECIFIC_PASSWORD: ${{ secrets.APPLE_PASSWORD }}
|
|
||||||
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
|
|
||||||
CSC_LINK: ${{ secrets.APPLE_CERTIFICATE }}
|
|
||||||
CSC_KEY_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
|
|
||||||
CSC_KEYCHAIN: ${{ secrets.APPLE_SIGNING_IDENTITY }}
|
|
||||||
CSC_FOR_PULL_REQUEST: true
|
|
||||||
VERSION: ${{ github.event_name == 'schedule' && needs.prepare-files.outputs.version || format('v{0}', needs.prepare-files.outputs.version) }}
|
VERSION: ${{ github.event_name == 'schedule' && needs.prepare-files.outputs.version || format('v{0}', needs.prepare-files.outputs.version) }}
|
||||||
VERSION_NO_V: ${{ needs.prepare-files.outputs.version }}
|
VERSION_NO_V: ${{ needs.prepare-files.outputs.version }}
|
||||||
WINDOWS_CERTIFICATE_THUMBPRINT: F4C9A52FF7BC26EE5E054946F6B11DEEA94C748D
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
@ -181,8 +172,25 @@ jobs:
|
|||||||
smksp_cert_sync.exe
|
smksp_cert_sync.exe
|
||||||
shell: cmd
|
shell: cmd
|
||||||
|
|
||||||
- name: Build the app
|
- name: Build the app (debug)
|
||||||
run: yarn electron-builder --config ${{ env.BUILD_RELEASE && '--publish always' || '' }}
|
if: ${{ env.BUILD_RELEASE == 'false' }}
|
||||||
|
# electron-builder doesn't have a concept of release vs debug,
|
||||||
|
# this is just not doing any codesign or release yml generation
|
||||||
|
run: yarn electron-builder --config
|
||||||
|
|
||||||
|
- name: Build the app (release)
|
||||||
|
if: ${{ env.BUILD_RELEASE == 'true' }}
|
||||||
|
env:
|
||||||
|
APPLE_ID: ${{ secrets.APPLE_ID }}
|
||||||
|
APPLE_PASSWORD: ${{ secrets.APPLE_PASSWORD }}
|
||||||
|
APPLE_APP_SPECIFIC_PASSWORD: ${{ secrets.APPLE_PASSWORD }}
|
||||||
|
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
|
||||||
|
CSC_LINK: ${{ secrets.APPLE_CERTIFICATE }}
|
||||||
|
CSC_KEY_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
|
||||||
|
CSC_KEYCHAIN: ${{ secrets.APPLE_SIGNING_IDENTITY }}
|
||||||
|
CSC_FOR_PULL_REQUEST: true
|
||||||
|
WINDOWS_CERTIFICATE_THUMBPRINT: ${{ secrets.WINDOWS_CERTIFICATE_THUMBPRINT }}
|
||||||
|
run: yarn electron-builder --config --publish always
|
||||||
|
|
||||||
- name: List artifacts in out/
|
- name: List artifacts in out/
|
||||||
run: ls -R out
|
run: ls -R out
|
||||||
@ -226,7 +234,17 @@ jobs:
|
|||||||
|
|
||||||
- name: Build the app (updater-test)
|
- name: Build the app (updater-test)
|
||||||
if: ${{ env.CUT_RELEASE_PR == 'true' }}
|
if: ${{ env.CUT_RELEASE_PR == 'true' }}
|
||||||
run: yarn electron-builder --config ${{ env.BUILD_RELEASE && '--publish always' || '' }}
|
env:
|
||||||
|
APPLE_ID: ${{ secrets.APPLE_ID }}
|
||||||
|
APPLE_PASSWORD: ${{ secrets.APPLE_PASSWORD }}
|
||||||
|
APPLE_APP_SPECIFIC_PASSWORD: ${{ secrets.APPLE_PASSWORD }}
|
||||||
|
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
|
||||||
|
CSC_LINK: ${{ secrets.APPLE_CERTIFICATE }}
|
||||||
|
CSC_KEY_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
|
||||||
|
CSC_KEYCHAIN: ${{ secrets.APPLE_SIGNING_IDENTITY }}
|
||||||
|
CSC_FOR_PULL_REQUEST: true
|
||||||
|
WINDOWS_CERTIFICATE_THUMBPRINT: ${{ secrets.WINDOWS_CERTIFICATE_THUMBPRINT }}
|
||||||
|
run: yarn electron-builder --config --publish always
|
||||||
|
|
||||||
- uses: actions/upload-artifact@v3
|
- uses: actions/upload-artifact@v3
|
||||||
if: ${{ env.CUT_RELEASE_PR == 'true' }}
|
if: ${{ env.CUT_RELEASE_PR == 'true' }}
|
||||||
|
2
.github/workflows/cargo-check.yml
vendored
@ -37,4 +37,4 @@ jobs:
|
|||||||
# We specifically want to test the disable-println feature
|
# We specifically want to test the disable-println feature
|
||||||
# Since it is not enabled by default, we need to specify it
|
# Since it is not enabled by default, we need to specify it
|
||||||
# This is used in kcl-lsp
|
# This is used in kcl-lsp
|
||||||
cargo check --all --features disable-println --features pyo3 --features cli
|
cargo check --workspace --features disable-println --features pyo3 --features cli
|
||||||
|
@ -74,10 +74,12 @@ layout: manual
|
|||||||
* [`patternTransform`](kcl/patternTransform)
|
* [`patternTransform`](kcl/patternTransform)
|
||||||
* [`pi`](kcl/pi)
|
* [`pi`](kcl/pi)
|
||||||
* [`polar`](kcl/polar)
|
* [`polar`](kcl/polar)
|
||||||
|
* [`polygon`](kcl/polygon)
|
||||||
* [`pow`](kcl/pow)
|
* [`pow`](kcl/pow)
|
||||||
* [`profileStart`](kcl/profileStart)
|
* [`profileStart`](kcl/profileStart)
|
||||||
* [`profileStartX`](kcl/profileStartX)
|
* [`profileStartX`](kcl/profileStartX)
|
||||||
* [`profileStartY`](kcl/profileStartY)
|
* [`profileStartY`](kcl/profileStartY)
|
||||||
|
* [`push`](kcl/push)
|
||||||
* [`reduce`](kcl/reduce)
|
* [`reduce`](kcl/reduce)
|
||||||
* [`rem`](kcl/rem)
|
* [`rem`](kcl/rem)
|
||||||
* [`revolve`](kcl/revolve)
|
* [`revolve`](kcl/revolve)
|
||||||
|
60
docs/kcl/polygon.md
Normal file
38
docs/kcl/push.md
Normal file
27320
docs/kcl/std.json
@ -23,11 +23,11 @@ layout: manual
|
|||||||
| Property | Type | Description | Required |
|
| Property | Type | Description | Required |
|
||||||
|----------|------|-------------|----------|
|
|----------|------|-------------|----------|
|
||||||
| `type` |enum: `Literal`| | No |
|
| `type` |enum: `Literal`| | No |
|
||||||
| `start` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
|
|
||||||
| `end` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
|
|
||||||
| `value` |[`LiteralValue`](/docs/kcl/types/LiteralValue)| | No |
|
| `value` |[`LiteralValue`](/docs/kcl/types/LiteralValue)| | No |
|
||||||
| `raw` |`string`| | No |
|
| `raw` |`string`| | No |
|
||||||
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||||
|
| `start` |`integer`| | No |
|
||||||
|
| `end` |`integer`| | No |
|
||||||
|
|
||||||
|
|
||||||
----
|
----
|
||||||
@ -43,10 +43,10 @@ layout: manual
|
|||||||
| Property | Type | Description | Required |
|
| Property | Type | Description | Required |
|
||||||
|----------|------|-------------|----------|
|
|----------|------|-------------|----------|
|
||||||
| `type` |enum: [`Identifier`](/docs/kcl/types/Identifier)| | No |
|
| `type` |enum: [`Identifier`](/docs/kcl/types/Identifier)| | No |
|
||||||
| `start` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
|
|
||||||
| `end` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
|
|
||||||
| `name` |`string`| | No |
|
| `name` |`string`| | No |
|
||||||
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||||
|
| `start` |`integer`| | No |
|
||||||
|
| `end` |`integer`| | No |
|
||||||
|
|
||||||
|
|
||||||
----
|
----
|
||||||
@ -62,12 +62,12 @@ layout: manual
|
|||||||
| Property | Type | Description | Required |
|
| Property | Type | Description | Required |
|
||||||
|----------|------|-------------|----------|
|
|----------|------|-------------|----------|
|
||||||
| `type` |enum: `BinaryExpression`| | No |
|
| `type` |enum: `BinaryExpression`| | No |
|
||||||
| `start` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
|
|
||||||
| `end` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
|
|
||||||
| `operator` |[`BinaryOperator`](/docs/kcl/types/BinaryOperator)| | No |
|
| `operator` |[`BinaryOperator`](/docs/kcl/types/BinaryOperator)| | No |
|
||||||
| `left` |[`BinaryPart`](/docs/kcl/types/BinaryPart)| | No |
|
| `left` |[`BinaryPart`](/docs/kcl/types/BinaryPart)| | No |
|
||||||
| `right` |[`BinaryPart`](/docs/kcl/types/BinaryPart)| | No |
|
| `right` |[`BinaryPart`](/docs/kcl/types/BinaryPart)| | No |
|
||||||
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||||
|
| `start` |`integer`| | No |
|
||||||
|
| `end` |`integer`| | No |
|
||||||
|
|
||||||
|
|
||||||
----
|
----
|
||||||
@ -83,12 +83,12 @@ layout: manual
|
|||||||
| Property | Type | Description | Required |
|
| Property | Type | Description | Required |
|
||||||
|----------|------|-------------|----------|
|
|----------|------|-------------|----------|
|
||||||
| `type` |enum: `CallExpression`| | No |
|
| `type` |enum: `CallExpression`| | No |
|
||||||
| `start` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
|
|
||||||
| `end` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
|
|
||||||
| `callee` |[`Identifier`](/docs/kcl/types/Identifier)| | No |
|
| `callee` |[`Identifier`](/docs/kcl/types/Identifier)| | No |
|
||||||
| `arguments` |`[` [`Expr`](/docs/kcl/types/Expr) `]`| | No |
|
| `arguments` |`[` [`Expr`](/docs/kcl/types/Expr) `]`| | No |
|
||||||
| `optional` |`boolean`| | No |
|
| `optional` |`boolean`| | No |
|
||||||
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||||
|
| `start` |`integer`| | No |
|
||||||
|
| `end` |`integer`| | No |
|
||||||
|
|
||||||
|
|
||||||
----
|
----
|
||||||
@ -104,11 +104,11 @@ layout: manual
|
|||||||
| Property | Type | Description | Required |
|
| Property | Type | Description | Required |
|
||||||
|----------|------|-------------|----------|
|
|----------|------|-------------|----------|
|
||||||
| `type` |enum: `UnaryExpression`| | No |
|
| `type` |enum: `UnaryExpression`| | No |
|
||||||
| `start` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
|
|
||||||
| `end` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
|
|
||||||
| `operator` |[`UnaryOperator`](/docs/kcl/types/UnaryOperator)| | No |
|
| `operator` |[`UnaryOperator`](/docs/kcl/types/UnaryOperator)| | No |
|
||||||
| `argument` |[`BinaryPart`](/docs/kcl/types/BinaryPart)| | No |
|
| `argument` |[`BinaryPart`](/docs/kcl/types/BinaryPart)| | No |
|
||||||
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||||
|
| `start` |`integer`| | No |
|
||||||
|
| `end` |`integer`| | No |
|
||||||
|
|
||||||
|
|
||||||
----
|
----
|
||||||
@ -124,12 +124,12 @@ layout: manual
|
|||||||
| Property | Type | Description | Required |
|
| Property | Type | Description | Required |
|
||||||
|----------|------|-------------|----------|
|
|----------|------|-------------|----------|
|
||||||
| `type` |enum: `MemberExpression`| | No |
|
| `type` |enum: `MemberExpression`| | No |
|
||||||
| `start` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
|
|
||||||
| `end` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
|
|
||||||
| `object` |[`MemberObject`](/docs/kcl/types/MemberObject)| | No |
|
| `object` |[`MemberObject`](/docs/kcl/types/MemberObject)| | No |
|
||||||
| `property` |[`LiteralIdentifier`](/docs/kcl/types/LiteralIdentifier)| | No |
|
| `property` |[`LiteralIdentifier`](/docs/kcl/types/LiteralIdentifier)| | No |
|
||||||
| `computed` |`boolean`| | No |
|
| `computed` |`boolean`| | No |
|
||||||
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||||
|
| `start` |`integer`| | No |
|
||||||
|
| `end` |`integer`| | No |
|
||||||
|
|
||||||
|
|
||||||
----
|
----
|
||||||
@ -145,13 +145,13 @@ layout: manual
|
|||||||
| Property | Type | Description | Required |
|
| Property | Type | Description | Required |
|
||||||
|----------|------|-------------|----------|
|
|----------|------|-------------|----------|
|
||||||
| `type` |enum: `IfExpression`| | No |
|
| `type` |enum: `IfExpression`| | No |
|
||||||
| `start` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
|
|
||||||
| `end` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
|
|
||||||
| `cond` |[`Expr`](/docs/kcl/types/Expr)| | No |
|
| `cond` |[`Expr`](/docs/kcl/types/Expr)| | No |
|
||||||
| `then_val` |[`Program`](/docs/kcl/types/Program)| | No |
|
| `then_val` |[`Program`](/docs/kcl/types/Program)| | No |
|
||||||
| `else_ifs` |`[` [`ElseIf`](/docs/kcl/types/ElseIf) `]`| | No |
|
| `else_ifs` |`[` [`ElseIf`](/docs/kcl/types/ElseIf) `]`| | No |
|
||||||
| `final_else` |[`Program`](/docs/kcl/types/Program)| | No |
|
| `final_else` |[`Program`](/docs/kcl/types/Program)| | No |
|
||||||
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||||
|
| `start` |`integer`| | No |
|
||||||
|
| `end` |`integer`| | No |
|
||||||
|
|
||||||
|
|
||||||
----
|
----
|
||||||
|
@ -23,12 +23,12 @@ layout: manual
|
|||||||
| Property | Type | Description | Required |
|
| Property | Type | Description | Required |
|
||||||
|----------|------|-------------|----------|
|
|----------|------|-------------|----------|
|
||||||
| `type` |enum: `ImportStatement`| | No |
|
| `type` |enum: `ImportStatement`| | No |
|
||||||
| `start` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
|
|
||||||
| `end` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
|
|
||||||
| `items` |`[` [`ImportItem`](/docs/kcl/types/ImportItem) `]`| | No |
|
| `items` |`[` [`ImportItem`](/docs/kcl/types/ImportItem) `]`| | No |
|
||||||
| `path` |`string`| | No |
|
| `path` |`string`| | No |
|
||||||
| `raw_path` |`string`| | No |
|
| `raw_path` |`string`| | No |
|
||||||
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||||
|
| `start` |`integer`| | No |
|
||||||
|
| `end` |`integer`| | No |
|
||||||
|
|
||||||
|
|
||||||
----
|
----
|
||||||
@ -44,10 +44,10 @@ layout: manual
|
|||||||
| Property | Type | Description | Required |
|
| Property | Type | Description | Required |
|
||||||
|----------|------|-------------|----------|
|
|----------|------|-------------|----------|
|
||||||
| `type` |enum: `ExpressionStatement`| | No |
|
| `type` |enum: `ExpressionStatement`| | No |
|
||||||
| `start` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
|
|
||||||
| `end` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
|
|
||||||
| `expression` |[`Expr`](/docs/kcl/types/Expr)| | No |
|
| `expression` |[`Expr`](/docs/kcl/types/Expr)| | No |
|
||||||
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||||
|
| `start` |`integer`| | No |
|
||||||
|
| `end` |`integer`| | No |
|
||||||
|
|
||||||
|
|
||||||
----
|
----
|
||||||
@ -63,12 +63,12 @@ layout: manual
|
|||||||
| Property | Type | Description | Required |
|
| Property | Type | Description | Required |
|
||||||
|----------|------|-------------|----------|
|
|----------|------|-------------|----------|
|
||||||
| `type` |enum: `VariableDeclaration`| | No |
|
| `type` |enum: `VariableDeclaration`| | No |
|
||||||
| `start` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
|
|
||||||
| `end` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
|
|
||||||
| `declarations` |`[` [`VariableDeclarator`](/docs/kcl/types/VariableDeclarator) `]`| | No |
|
| `declarations` |`[` [`VariableDeclarator`](/docs/kcl/types/VariableDeclarator) `]`| | No |
|
||||||
| `visibility` |[`ItemVisibility`](/docs/kcl/types/ItemVisibility)| | No |
|
| `visibility` |[`ItemVisibility`](/docs/kcl/types/ItemVisibility)| | No |
|
||||||
| `kind` |[`VariableKind`](/docs/kcl/types/VariableKind)| | No |
|
| `kind` |[`VariableKind`](/docs/kcl/types/VariableKind)| | No |
|
||||||
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||||
|
| `start` |`integer`| | No |
|
||||||
|
| `end` |`integer`| | No |
|
||||||
|
|
||||||
|
|
||||||
----
|
----
|
||||||
@ -84,10 +84,10 @@ layout: manual
|
|||||||
| Property | Type | Description | Required |
|
| Property | Type | Description | Required |
|
||||||
|----------|------|-------------|----------|
|
|----------|------|-------------|----------|
|
||||||
| `type` |enum: `ReturnStatement`| | No |
|
| `type` |enum: `ReturnStatement`| | No |
|
||||||
| `start` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
|
|
||||||
| `end` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
|
|
||||||
| `argument` |[`Expr`](/docs/kcl/types/Expr)| | No |
|
| `argument` |[`Expr`](/docs/kcl/types/Expr)| | No |
|
||||||
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||||
|
| `start` |`integer`| | No |
|
||||||
|
| `end` |`integer`| | No |
|
||||||
|
|
||||||
|
|
||||||
----
|
----
|
||||||
|
@ -15,10 +15,10 @@ layout: manual
|
|||||||
|
|
||||||
| Property | Type | Description | Required |
|
| Property | Type | Description | Required |
|
||||||
|----------|------|-------------|----------|
|
|----------|------|-------------|----------|
|
||||||
| `start` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
|
|
||||||
| `end` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
|
|
||||||
| `cond` |[`Expr`](/docs/kcl/types/Expr)| | No |
|
| `cond` |[`Expr`](/docs/kcl/types/Expr)| | No |
|
||||||
| `then_val` |[`Program`](/docs/kcl/types/Program)| | No |
|
| `then_val` |[`Program`](/docs/kcl/types/Program)| | No |
|
||||||
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||||
|
| `start` |`integer`| | No |
|
||||||
|
| `end` |`integer`| | No |
|
||||||
|
|
||||||
|
|
||||||
|
@ -16,6 +16,6 @@ layout: manual
|
|||||||
| Property | Type | Description | Required |
|
| Property | Type | Description | Required |
|
||||||
|----------|------|-------------|----------|
|
|----------|------|-------------|----------|
|
||||||
| `bindings` |`object`| | No |
|
| `bindings` |`object`| | No |
|
||||||
| `parent` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
|
| `parent` |`integer`| | No |
|
||||||
|
|
||||||
|
|
||||||
|
@ -24,11 +24,11 @@ An expression can be evaluated to yield a single KCL value.
|
|||||||
| Property | Type | Description | Required |
|
| Property | Type | Description | Required |
|
||||||
|----------|------|-------------|----------|
|
|----------|------|-------------|----------|
|
||||||
| `type` |enum: `Literal`| | No |
|
| `type` |enum: `Literal`| | No |
|
||||||
| `start` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
|
|
||||||
| `end` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
|
|
||||||
| `value` |[`LiteralValue`](/docs/kcl/types/LiteralValue)| An expression can be evaluated to yield a single KCL value. | No |
|
| `value` |[`LiteralValue`](/docs/kcl/types/LiteralValue)| An expression can be evaluated to yield a single KCL value. | No |
|
||||||
| `raw` |`string`| | No |
|
| `raw` |`string`| | No |
|
||||||
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||||
|
| `start` |`integer`| | No |
|
||||||
|
| `end` |`integer`| | No |
|
||||||
|
|
||||||
|
|
||||||
----
|
----
|
||||||
@ -44,10 +44,10 @@ An expression can be evaluated to yield a single KCL value.
|
|||||||
| Property | Type | Description | Required |
|
| Property | Type | Description | Required |
|
||||||
|----------|------|-------------|----------|
|
|----------|------|-------------|----------|
|
||||||
| `type` |enum: [`Identifier`](/docs/kcl/types/Identifier)| | No |
|
| `type` |enum: [`Identifier`](/docs/kcl/types/Identifier)| | No |
|
||||||
| `start` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
|
|
||||||
| `end` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
|
|
||||||
| `name` |`string`| | No |
|
| `name` |`string`| | No |
|
||||||
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||||
|
| `start` |`integer`| | No |
|
||||||
|
| `end` |`integer`| | No |
|
||||||
|
|
||||||
|
|
||||||
----
|
----
|
||||||
@ -63,10 +63,10 @@ An expression can be evaluated to yield a single KCL value.
|
|||||||
| Property | Type | Description | Required |
|
| Property | Type | Description | Required |
|
||||||
|----------|------|-------------|----------|
|
|----------|------|-------------|----------|
|
||||||
| `type` |enum: [`TagDeclarator`](/docs/kcl/types#tag-declaration)| | No |
|
| `type` |enum: [`TagDeclarator`](/docs/kcl/types#tag-declaration)| | No |
|
||||||
| `start` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
|
|
||||||
| `end` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
|
|
||||||
| `value` |`string`| | No |
|
| `value` |`string`| | No |
|
||||||
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||||
|
| `start` |`integer`| | No |
|
||||||
|
| `end` |`integer`| | No |
|
||||||
|
|
||||||
|
|
||||||
----
|
----
|
||||||
@ -82,12 +82,12 @@ An expression can be evaluated to yield a single KCL value.
|
|||||||
| Property | Type | Description | Required |
|
| Property | Type | Description | Required |
|
||||||
|----------|------|-------------|----------|
|
|----------|------|-------------|----------|
|
||||||
| `type` |enum: `BinaryExpression`| | No |
|
| `type` |enum: `BinaryExpression`| | No |
|
||||||
| `start` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
|
|
||||||
| `end` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
|
|
||||||
| `operator` |[`BinaryOperator`](/docs/kcl/types/BinaryOperator)| An expression can be evaluated to yield a single KCL value. | No |
|
| `operator` |[`BinaryOperator`](/docs/kcl/types/BinaryOperator)| An expression can be evaluated to yield a single KCL value. | No |
|
||||||
| `left` |[`BinaryPart`](/docs/kcl/types/BinaryPart)| An expression can be evaluated to yield a single KCL value. | No |
|
| `left` |[`BinaryPart`](/docs/kcl/types/BinaryPart)| An expression can be evaluated to yield a single KCL value. | No |
|
||||||
| `right` |[`BinaryPart`](/docs/kcl/types/BinaryPart)| An expression can be evaluated to yield a single KCL value. | No |
|
| `right` |[`BinaryPart`](/docs/kcl/types/BinaryPart)| An expression can be evaluated to yield a single KCL value. | No |
|
||||||
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||||
|
| `start` |`integer`| | No |
|
||||||
|
| `end` |`integer`| | No |
|
||||||
|
|
||||||
|
|
||||||
----
|
----
|
||||||
@ -103,11 +103,11 @@ An expression can be evaluated to yield a single KCL value.
|
|||||||
| Property | Type | Description | Required |
|
| Property | Type | Description | Required |
|
||||||
|----------|------|-------------|----------|
|
|----------|------|-------------|----------|
|
||||||
| `type` |enum: [`FunctionExpression`](/docs/kcl/types/FunctionExpression)| | No |
|
| `type` |enum: [`FunctionExpression`](/docs/kcl/types/FunctionExpression)| | No |
|
||||||
| `start` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
|
|
||||||
| `end` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
|
|
||||||
| `params` |`[` [`Parameter`](/docs/kcl/types/Parameter) `]`| | No |
|
| `params` |`[` [`Parameter`](/docs/kcl/types/Parameter) `]`| | No |
|
||||||
| `body` |[`Program`](/docs/kcl/types/Program)| An expression can be evaluated to yield a single KCL value. | No |
|
| `body` |[`Program`](/docs/kcl/types/Program)| An expression can be evaluated to yield a single KCL value. | No |
|
||||||
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||||
|
| `start` |`integer`| | No |
|
||||||
|
| `end` |`integer`| | No |
|
||||||
|
|
||||||
|
|
||||||
----
|
----
|
||||||
@ -123,12 +123,12 @@ An expression can be evaluated to yield a single KCL value.
|
|||||||
| Property | Type | Description | Required |
|
| Property | Type | Description | Required |
|
||||||
|----------|------|-------------|----------|
|
|----------|------|-------------|----------|
|
||||||
| `type` |enum: `CallExpression`| | No |
|
| `type` |enum: `CallExpression`| | No |
|
||||||
| `start` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
|
|
||||||
| `end` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
|
|
||||||
| `callee` |[`Identifier`](/docs/kcl/types/Identifier)| An expression can be evaluated to yield a single KCL value. | No |
|
| `callee` |[`Identifier`](/docs/kcl/types/Identifier)| An expression can be evaluated to yield a single KCL value. | No |
|
||||||
| `arguments` |`[` [`Expr`](/docs/kcl/types/Expr) `]`| | No |
|
| `arguments` |`[` [`Expr`](/docs/kcl/types/Expr) `]`| | No |
|
||||||
| `optional` |`boolean`| | No |
|
| `optional` |`boolean`| | No |
|
||||||
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||||
|
| `start` |`integer`| | No |
|
||||||
|
| `end` |`integer`| | No |
|
||||||
|
|
||||||
|
|
||||||
----
|
----
|
||||||
@ -144,11 +144,11 @@ An expression can be evaluated to yield a single KCL value.
|
|||||||
| Property | Type | Description | Required |
|
| Property | Type | Description | Required |
|
||||||
|----------|------|-------------|----------|
|
|----------|------|-------------|----------|
|
||||||
| `type` |enum: `PipeExpression`| | No |
|
| `type` |enum: `PipeExpression`| | No |
|
||||||
| `start` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
|
|
||||||
| `end` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
|
|
||||||
| `body` |`[` [`Expr`](/docs/kcl/types/Expr) `]`| | No |
|
| `body` |`[` [`Expr`](/docs/kcl/types/Expr) `]`| | No |
|
||||||
| `nonCodeMeta` |[`NonCodeMeta`](/docs/kcl/types/NonCodeMeta)| An expression can be evaluated to yield a single KCL value. | No |
|
| `nonCodeMeta` |[`NonCodeMeta`](/docs/kcl/types/NonCodeMeta)| An expression can be evaluated to yield a single KCL value. | No |
|
||||||
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||||
|
| `start` |`integer`| | No |
|
||||||
|
| `end` |`integer`| | No |
|
||||||
|
|
||||||
|
|
||||||
----
|
----
|
||||||
@ -164,9 +164,9 @@ An expression can be evaluated to yield a single KCL value.
|
|||||||
| Property | Type | Description | Required |
|
| Property | Type | Description | Required |
|
||||||
|----------|------|-------------|----------|
|
|----------|------|-------------|----------|
|
||||||
| `type` |enum: `PipeSubstitution`| | No |
|
| `type` |enum: `PipeSubstitution`| | No |
|
||||||
| `start` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
|
|
||||||
| `end` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
|
|
||||||
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||||
|
| `start` |`integer`| | No |
|
||||||
|
| `end` |`integer`| | No |
|
||||||
|
|
||||||
|
|
||||||
----
|
----
|
||||||
@ -182,11 +182,11 @@ An expression can be evaluated to yield a single KCL value.
|
|||||||
| Property | Type | Description | Required |
|
| Property | Type | Description | Required |
|
||||||
|----------|------|-------------|----------|
|
|----------|------|-------------|----------|
|
||||||
| `type` |enum: `ArrayExpression`| | No |
|
| `type` |enum: `ArrayExpression`| | No |
|
||||||
| `start` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
|
|
||||||
| `end` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
|
|
||||||
| `elements` |`[` [`Expr`](/docs/kcl/types/Expr) `]`| | No |
|
| `elements` |`[` [`Expr`](/docs/kcl/types/Expr) `]`| | No |
|
||||||
| `nonCodeMeta` |[`NonCodeMeta`](/docs/kcl/types/NonCodeMeta)| An expression can be evaluated to yield a single KCL value. | No |
|
| `nonCodeMeta` |[`NonCodeMeta`](/docs/kcl/types/NonCodeMeta)| An expression can be evaluated to yield a single KCL value. | No |
|
||||||
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||||
|
| `start` |`integer`| | No |
|
||||||
|
| `end` |`integer`| | No |
|
||||||
|
|
||||||
|
|
||||||
----
|
----
|
||||||
@ -202,12 +202,12 @@ An expression can be evaluated to yield a single KCL value.
|
|||||||
| Property | Type | Description | Required |
|
| Property | Type | Description | Required |
|
||||||
|----------|------|-------------|----------|
|
|----------|------|-------------|----------|
|
||||||
| `type` |enum: `ArrayRangeExpression`| | No |
|
| `type` |enum: `ArrayRangeExpression`| | No |
|
||||||
| `start` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
|
|
||||||
| `end` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
|
|
||||||
| `startElement` |[`Expr`](/docs/kcl/types/Expr)| An expression can be evaluated to yield a single KCL value. | No |
|
| `startElement` |[`Expr`](/docs/kcl/types/Expr)| An expression can be evaluated to yield a single KCL value. | No |
|
||||||
| `endElement` |[`Expr`](/docs/kcl/types/Expr)| An expression can be evaluated to yield a single KCL value. | No |
|
| `endElement` |[`Expr`](/docs/kcl/types/Expr)| An expression can be evaluated to yield a single KCL value. | No |
|
||||||
| `endInclusive` |`boolean`| Is the `end_element` included in the range? | No |
|
| `endInclusive` |`boolean`| Is the `end_element` included in the range? | No |
|
||||||
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||||
|
| `start` |`integer`| | No |
|
||||||
|
| `end` |`integer`| | No |
|
||||||
|
|
||||||
|
|
||||||
----
|
----
|
||||||
@ -223,11 +223,11 @@ An expression can be evaluated to yield a single KCL value.
|
|||||||
| Property | Type | Description | Required |
|
| Property | Type | Description | Required |
|
||||||
|----------|------|-------------|----------|
|
|----------|------|-------------|----------|
|
||||||
| `type` |enum: `ObjectExpression`| | No |
|
| `type` |enum: `ObjectExpression`| | No |
|
||||||
| `start` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
|
|
||||||
| `end` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
|
|
||||||
| `properties` |`[` [`ObjectProperty`](/docs/kcl/types/ObjectProperty) `]`| | No |
|
| `properties` |`[` [`ObjectProperty`](/docs/kcl/types/ObjectProperty) `]`| | No |
|
||||||
| `nonCodeMeta` |[`NonCodeMeta`](/docs/kcl/types/NonCodeMeta)| An expression can be evaluated to yield a single KCL value. | No |
|
| `nonCodeMeta` |[`NonCodeMeta`](/docs/kcl/types/NonCodeMeta)| An expression can be evaluated to yield a single KCL value. | No |
|
||||||
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||||
|
| `start` |`integer`| | No |
|
||||||
|
| `end` |`integer`| | No |
|
||||||
|
|
||||||
|
|
||||||
----
|
----
|
||||||
@ -243,12 +243,12 @@ An expression can be evaluated to yield a single KCL value.
|
|||||||
| Property | Type | Description | Required |
|
| Property | Type | Description | Required |
|
||||||
|----------|------|-------------|----------|
|
|----------|------|-------------|----------|
|
||||||
| `type` |enum: `MemberExpression`| | No |
|
| `type` |enum: `MemberExpression`| | No |
|
||||||
| `start` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
|
|
||||||
| `end` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
|
|
||||||
| `object` |[`MemberObject`](/docs/kcl/types/MemberObject)| An expression can be evaluated to yield a single KCL value. | No |
|
| `object` |[`MemberObject`](/docs/kcl/types/MemberObject)| An expression can be evaluated to yield a single KCL value. | No |
|
||||||
| `property` |[`LiteralIdentifier`](/docs/kcl/types/LiteralIdentifier)| An expression can be evaluated to yield a single KCL value. | No |
|
| `property` |[`LiteralIdentifier`](/docs/kcl/types/LiteralIdentifier)| An expression can be evaluated to yield a single KCL value. | No |
|
||||||
| `computed` |`boolean`| | No |
|
| `computed` |`boolean`| | No |
|
||||||
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||||
|
| `start` |`integer`| | No |
|
||||||
|
| `end` |`integer`| | No |
|
||||||
|
|
||||||
|
|
||||||
----
|
----
|
||||||
@ -264,11 +264,11 @@ An expression can be evaluated to yield a single KCL value.
|
|||||||
| Property | Type | Description | Required |
|
| Property | Type | Description | Required |
|
||||||
|----------|------|-------------|----------|
|
|----------|------|-------------|----------|
|
||||||
| `type` |enum: `UnaryExpression`| | No |
|
| `type` |enum: `UnaryExpression`| | No |
|
||||||
| `start` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
|
|
||||||
| `end` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
|
|
||||||
| `operator` |[`UnaryOperator`](/docs/kcl/types/UnaryOperator)| An expression can be evaluated to yield a single KCL value. | No |
|
| `operator` |[`UnaryOperator`](/docs/kcl/types/UnaryOperator)| An expression can be evaluated to yield a single KCL value. | No |
|
||||||
| `argument` |[`BinaryPart`](/docs/kcl/types/BinaryPart)| An expression can be evaluated to yield a single KCL value. | No |
|
| `argument` |[`BinaryPart`](/docs/kcl/types/BinaryPart)| An expression can be evaluated to yield a single KCL value. | No |
|
||||||
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||||
|
| `start` |`integer`| | No |
|
||||||
|
| `end` |`integer`| | No |
|
||||||
|
|
||||||
|
|
||||||
----
|
----
|
||||||
@ -284,13 +284,13 @@ An expression can be evaluated to yield a single KCL value.
|
|||||||
| Property | Type | Description | Required |
|
| Property | Type | Description | Required |
|
||||||
|----------|------|-------------|----------|
|
|----------|------|-------------|----------|
|
||||||
| `type` |enum: `IfExpression`| | No |
|
| `type` |enum: `IfExpression`| | No |
|
||||||
| `start` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
|
|
||||||
| `end` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
|
|
||||||
| `cond` |[`Expr`](/docs/kcl/types/Expr)| An expression can be evaluated to yield a single KCL value. | No |
|
| `cond` |[`Expr`](/docs/kcl/types/Expr)| An expression can be evaluated to yield a single KCL value. | No |
|
||||||
| `then_val` |[`Program`](/docs/kcl/types/Program)| An expression can be evaluated to yield a single KCL value. | No |
|
| `then_val` |[`Program`](/docs/kcl/types/Program)| An expression can be evaluated to yield a single KCL value. | No |
|
||||||
| `else_ifs` |`[` [`ElseIf`](/docs/kcl/types/ElseIf) `]`| | No |
|
| `else_ifs` |`[` [`ElseIf`](/docs/kcl/types/ElseIf) `]`| | No |
|
||||||
| `final_else` |[`Program`](/docs/kcl/types/Program)| An expression can be evaluated to yield a single KCL value. | No |
|
| `final_else` |[`Program`](/docs/kcl/types/Program)| An expression can be evaluated to yield a single KCL value. | No |
|
||||||
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||||
|
| `start` |`integer`| | No |
|
||||||
|
| `end` |`integer`| | No |
|
||||||
|
|
||||||
|
|
||||||
----
|
----
|
||||||
@ -307,8 +307,8 @@ KCL value for an optional parameter which was not given an argument. (remember,
|
|||||||
| Property | Type | Description | Required |
|
| Property | Type | Description | Required |
|
||||||
|----------|------|-------------|----------|
|
|----------|------|-------------|----------|
|
||||||
| `type` |enum: `None`| | No |
|
| `type` |enum: `None`| | No |
|
||||||
| `start` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
|
| `start` |`integer`| | No |
|
||||||
| `end` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
|
| `end` |`integer`| | No |
|
||||||
|
|
||||||
|
|
||||||
----
|
----
|
||||||
|
@ -15,10 +15,10 @@ layout: manual
|
|||||||
|
|
||||||
| Property | Type | Description | Required |
|
| Property | Type | Description | Required |
|
||||||
|----------|------|-------------|----------|
|
|----------|------|-------------|----------|
|
||||||
| `start` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
|
|
||||||
| `end` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
|
|
||||||
| `params` |`[` [`Parameter`](/docs/kcl/types/Parameter) `]`| | No |
|
| `params` |`[` [`Parameter`](/docs/kcl/types/Parameter) `]`| | No |
|
||||||
| `body` |[`Program`](/docs/kcl/types/Program)| | No |
|
| `body` |[`Program`](/docs/kcl/types/Program)| | No |
|
||||||
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||||
|
| `start` |`integer`| | No |
|
||||||
|
| `end` |`integer`| | No |
|
||||||
|
|
||||||
|
|
||||||
|
@ -15,9 +15,9 @@ layout: manual
|
|||||||
|
|
||||||
| Property | Type | Description | Required |
|
| Property | Type | Description | Required |
|
||||||
|----------|------|-------------|----------|
|
|----------|------|-------------|----------|
|
||||||
| `start` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
|
|
||||||
| `end` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
|
|
||||||
| `name` |`string`| | No |
|
| `name` |`string`| | No |
|
||||||
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||||
|
| `start` |`integer`| | No |
|
||||||
|
| `end` |`integer`| | No |
|
||||||
|
|
||||||
|
|
||||||
|
@ -17,8 +17,8 @@ layout: manual
|
|||||||
|----------|------|-------------|----------|
|
|----------|------|-------------|----------|
|
||||||
| `name` |[`Identifier`](/docs/kcl/types/Identifier)| Name of the item to import. | No |
|
| `name` |[`Identifier`](/docs/kcl/types/Identifier)| Name of the item to import. | No |
|
||||||
| `alias` |[`Identifier`](/docs/kcl/types/Identifier)| Rename the item using an identifier after "as". | No |
|
| `alias` |[`Identifier`](/docs/kcl/types/Identifier)| Rename the item using an identifier after "as". | No |
|
||||||
| `start` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
|
|
||||||
| `end` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
|
|
||||||
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||||
|
| `start` |`integer`| | No |
|
||||||
|
| `end` |`integer`| | No |
|
||||||
|
|
||||||
|
|
||||||
|
@ -59,10 +59,10 @@ Any KCL value.
|
|||||||
| Property | Type | Description | Required |
|
| Property | Type | Description | Required |
|
||||||
|----------|------|-------------|----------|
|
|----------|------|-------------|----------|
|
||||||
| `type` |enum: [`TagDeclarator`](/docs/kcl/types#tag-declaration)| | No |
|
| `type` |enum: [`TagDeclarator`](/docs/kcl/types#tag-declaration)| | No |
|
||||||
| `start` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
|
|
||||||
| `end` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
|
|
||||||
| `value` |`string`| | No |
|
| `value` |`string`| | No |
|
||||||
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||||
|
| `start` |`integer`| | No |
|
||||||
|
| `end` |`integer`| | No |
|
||||||
|
|
||||||
|
|
||||||
----
|
----
|
||||||
|
@ -23,10 +23,10 @@ layout: manual
|
|||||||
| Property | Type | Description | Required |
|
| Property | Type | Description | Required |
|
||||||
|----------|------|-------------|----------|
|
|----------|------|-------------|----------|
|
||||||
| `type` |enum: [`Identifier`](/docs/kcl/types/Identifier)| | No |
|
| `type` |enum: [`Identifier`](/docs/kcl/types/Identifier)| | No |
|
||||||
| `start` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
|
|
||||||
| `end` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
|
|
||||||
| `name` |`string`| | No |
|
| `name` |`string`| | No |
|
||||||
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||||
|
| `start` |`integer`| | No |
|
||||||
|
| `end` |`integer`| | No |
|
||||||
|
|
||||||
|
|
||||||
----
|
----
|
||||||
@ -42,11 +42,11 @@ layout: manual
|
|||||||
| Property | Type | Description | Required |
|
| Property | Type | Description | Required |
|
||||||
|----------|------|-------------|----------|
|
|----------|------|-------------|----------|
|
||||||
| `type` |enum: `Literal`| | No |
|
| `type` |enum: `Literal`| | No |
|
||||||
| `start` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
|
|
||||||
| `end` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
|
|
||||||
| `value` |[`LiteralValue`](/docs/kcl/types/LiteralValue)| | No |
|
| `value` |[`LiteralValue`](/docs/kcl/types/LiteralValue)| | No |
|
||||||
| `raw` |`string`| | No |
|
| `raw` |`string`| | No |
|
||||||
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||||
|
| `start` |`integer`| | No |
|
||||||
|
| `end` |`integer`| | No |
|
||||||
|
|
||||||
|
|
||||||
----
|
----
|
||||||
|
@ -23,12 +23,12 @@ layout: manual
|
|||||||
| Property | Type | Description | Required |
|
| Property | Type | Description | Required |
|
||||||
|----------|------|-------------|----------|
|
|----------|------|-------------|----------|
|
||||||
| `type` |enum: `MemberExpression`| | No |
|
| `type` |enum: `MemberExpression`| | No |
|
||||||
| `start` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
|
|
||||||
| `end` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
|
|
||||||
| `object` |[`MemberObject`](/docs/kcl/types/MemberObject)| | No |
|
| `object` |[`MemberObject`](/docs/kcl/types/MemberObject)| | No |
|
||||||
| `property` |[`LiteralIdentifier`](/docs/kcl/types/LiteralIdentifier)| | No |
|
| `property` |[`LiteralIdentifier`](/docs/kcl/types/LiteralIdentifier)| | No |
|
||||||
| `computed` |`boolean`| | No |
|
| `computed` |`boolean`| | No |
|
||||||
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||||
|
| `start` |`integer`| | No |
|
||||||
|
| `end` |`integer`| | No |
|
||||||
|
|
||||||
|
|
||||||
----
|
----
|
||||||
@ -44,10 +44,10 @@ layout: manual
|
|||||||
| Property | Type | Description | Required |
|
| Property | Type | Description | Required |
|
||||||
|----------|------|-------------|----------|
|
|----------|------|-------------|----------|
|
||||||
| `type` |enum: [`Identifier`](/docs/kcl/types/Identifier)| | No |
|
| `type` |enum: [`Identifier`](/docs/kcl/types/Identifier)| | No |
|
||||||
| `start` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
|
|
||||||
| `end` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
|
|
||||||
| `name` |`string`| | No |
|
| `name` |`string`| | No |
|
||||||
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||||
|
| `start` |`integer`| | No |
|
||||||
|
| `end` |`integer`| | No |
|
||||||
|
|
||||||
|
|
||||||
----
|
----
|
||||||
|
@ -16,7 +16,7 @@ layout: manual
|
|||||||
| Property | Type | Description | Required |
|
| Property | Type | Description | Required |
|
||||||
|----------|------|-------------|----------|
|
|----------|------|-------------|----------|
|
||||||
| `nonCodeNodes` |`object`| | No |
|
| `nonCodeNodes` |`object`| | No |
|
||||||
| `start` |`[` [`NonCodeNode`](/docs/kcl/types/NonCodeNode) `]`| | No |
|
| `startNodes` |`[` [`NonCodeNode`](/docs/kcl/types/NonCodeNode) `]`| | No |
|
||||||
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||||
|
|
||||||
|
|
||||||
|
@ -15,9 +15,9 @@ layout: manual
|
|||||||
|
|
||||||
| Property | Type | Description | Required |
|
| Property | Type | Description | Required |
|
||||||
|----------|------|-------------|----------|
|
|----------|------|-------------|----------|
|
||||||
| `start` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
|
|
||||||
| `end` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
|
|
||||||
| `value` |[`NonCodeValue`](/docs/kcl/types/NonCodeValue)| | No |
|
| `value` |[`NonCodeValue`](/docs/kcl/types/NonCodeValue)| | No |
|
||||||
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||||
|
| `start` |`integer`| | No |
|
||||||
|
| `end` |`integer`| | No |
|
||||||
|
|
||||||
|
|
||||||
|
@ -15,10 +15,10 @@ layout: manual
|
|||||||
|
|
||||||
| Property | Type | Description | Required |
|
| Property | Type | Description | Required |
|
||||||
|----------|------|-------------|----------|
|
|----------|------|-------------|----------|
|
||||||
| `start` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
|
|
||||||
| `end` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
|
|
||||||
| `key` |[`Identifier`](/docs/kcl/types/Identifier)| | No |
|
| `key` |[`Identifier`](/docs/kcl/types/Identifier)| | No |
|
||||||
| `value` |[`Expr`](/docs/kcl/types/Expr)| | No |
|
| `value` |[`Expr`](/docs/kcl/types/Expr)| | No |
|
||||||
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||||
|
| `start` |`integer`| | No |
|
||||||
|
| `end` |`integer`| | No |
|
||||||
|
|
||||||
|
|
||||||
|
24
docs/kcl/types/PolygonData.md
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
---
|
||||||
|
title: "PolygonData"
|
||||||
|
excerpt: "Data for drawing a polygon"
|
||||||
|
layout: manual
|
||||||
|
---
|
||||||
|
|
||||||
|
Data for drawing a polygon
|
||||||
|
|
||||||
|
**Type:** `object`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
| Property | Type | Description | Required |
|
||||||
|
|----------|------|-------------|----------|
|
||||||
|
| `radius` |`number`| The radius of the polygon | No |
|
||||||
|
| `numSides` |`integer`| The number of sides in the polygon | No |
|
||||||
|
| `center` |`[number, number]`| The center point of the polygon | No |
|
||||||
|
| `inscribed` |`boolean`| Whether the polygon is inscribed (true) or circumscribed (false) about a circle with the specified radius | No |
|
||||||
|
|
||||||
|
|
@ -16,10 +16,10 @@ A KCL program top level, or function body.
|
|||||||
|
|
||||||
| Property | Type | Description | Required |
|
| Property | Type | Description | Required |
|
||||||
|----------|------|-------------|----------|
|
|----------|------|-------------|----------|
|
||||||
| `start` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
|
|
||||||
| `end` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
|
|
||||||
| `body` |`[` [`BodyItem`](/docs/kcl/types/BodyItem) `]`| | No |
|
| `body` |`[` [`BodyItem`](/docs/kcl/types/BodyItem) `]`| | No |
|
||||||
| `nonCodeMeta` |[`NonCodeMeta`](/docs/kcl/types/NonCodeMeta)| A KCL program top level, or function body. | No |
|
| `nonCodeMeta` |[`NonCodeMeta`](/docs/kcl/types/NonCodeMeta)| A KCL program top level, or function body. | No |
|
||||||
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||||
|
| `start` |`integer`| | No |
|
||||||
|
| `end` |`integer`| | No |
|
||||||
|
|
||||||
|
|
||||||
|
@ -16,7 +16,7 @@ layout: manual
|
|||||||
| Property | Type | Description | Required |
|
| Property | Type | Description | Required |
|
||||||
|----------|------|-------------|----------|
|
|----------|------|-------------|----------|
|
||||||
| `environments` |`[` [`Environment`](/docs/kcl/types/Environment) `]`| | No |
|
| `environments` |`[` [`Environment`](/docs/kcl/types/Environment) `]`| | No |
|
||||||
| `currentEnv` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
|
| `currentEnv` |`integer`| | No |
|
||||||
| `return` |[`KclValue`](/docs/kcl/types/KclValue)| | No |
|
| `return` |[`KclValue`](/docs/kcl/types/KclValue)| | No |
|
||||||
|
|
||||||
|
|
||||||
|
@ -15,10 +15,10 @@ layout: manual
|
|||||||
|
|
||||||
| Property | Type | Description | Required |
|
| Property | Type | Description | Required |
|
||||||
|----------|------|-------------|----------|
|
|----------|------|-------------|----------|
|
||||||
| `start` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
|
|
||||||
| `end` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
|
|
||||||
| `id` |[`Identifier`](/docs/kcl/types/Identifier)| The identifier of the variable. | No |
|
| `id` |[`Identifier`](/docs/kcl/types/Identifier)| The identifier of the variable. | No |
|
||||||
| `init` |[`Expr`](/docs/kcl/types/Expr)| The value of the variable. | No |
|
| `init` |[`Expr`](/docs/kcl/types/Expr)| The value of the variable. | No |
|
||||||
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||||
|
| `start` |`integer`| | No |
|
||||||
|
| `end` |`integer`| | No |
|
||||||
|
|
||||||
|
|
||||||
|
@ -86,7 +86,7 @@ async function doBasicSketch(page: Page, openPanes: string[]) {
|
|||||||
|> startProfileAt(${commonPoints.startAt}, %)
|
|> startProfileAt(${commonPoints.startAt}, %)
|
||||||
|> line([${commonPoints.num1}, 0], %)
|
|> line([${commonPoints.num1}, 0], %)
|
||||||
|> line([0, ${commonPoints.num1 + 0.01}], %)
|
|> line([0, ${commonPoints.num1 + 0.01}], %)
|
||||||
|> line([-${commonPoints.num2}, 0], %)`)
|
|> lineTo([0, ${commonPoints.num3}], %)`)
|
||||||
}
|
}
|
||||||
|
|
||||||
// deselect line tool
|
// deselect line tool
|
||||||
|
@ -3,6 +3,7 @@ import { test, expect } from './fixtures/fixtureSetup'
|
|||||||
import * as fsp from 'fs/promises'
|
import * as fsp from 'fs/promises'
|
||||||
import * as fs from 'fs'
|
import * as fs from 'fs'
|
||||||
import {
|
import {
|
||||||
|
createProject,
|
||||||
executorInputPath,
|
executorInputPath,
|
||||||
getUtils,
|
getUtils,
|
||||||
setup,
|
setup,
|
||||||
@ -114,20 +115,15 @@ test.describe('when using the file tree to', () => {
|
|||||||
async ({ browser: _, tronApp }, testInfo) => {
|
async ({ browser: _, tronApp }, testInfo) => {
|
||||||
await tronApp.initialise()
|
await tronApp.initialise()
|
||||||
|
|
||||||
const {
|
const { panesOpen, pasteCodeInEditor, renameFile, editorTextMatches } =
|
||||||
panesOpen,
|
await getUtils(tronApp.page, test)
|
||||||
createAndSelectProject,
|
|
||||||
pasteCodeInEditor,
|
|
||||||
renameFile,
|
|
||||||
editorTextMatches,
|
|
||||||
} = await getUtils(tronApp.page, test)
|
|
||||||
|
|
||||||
await tronApp.page.setViewportSize({ width: 1200, height: 500 })
|
await tronApp.page.setViewportSize({ width: 1200, height: 500 })
|
||||||
tronApp.page.on('console', console.log)
|
tronApp.page.on('console', console.log)
|
||||||
|
|
||||||
await panesOpen(['files', 'code'])
|
await panesOpen(['files', 'code'])
|
||||||
|
|
||||||
await createAndSelectProject('project-000')
|
await createProject({ name: 'project-000', page: tronApp.page })
|
||||||
|
|
||||||
// File the main.kcl with contents
|
// File the main.kcl with contents
|
||||||
const kclCube = await fsp.readFile(
|
const kclCube = await fsp.readFile(
|
||||||
@ -167,15 +163,14 @@ test.describe('when using the file tree to', () => {
|
|||||||
async ({ browser: _, tronApp }, testInfo) => {
|
async ({ browser: _, tronApp }, testInfo) => {
|
||||||
await tronApp.initialise()
|
await tronApp.initialise()
|
||||||
|
|
||||||
const { panesOpen, createAndSelectProject, createNewFile } =
|
const { panesOpen, createNewFile } = await getUtils(tronApp.page, test)
|
||||||
await getUtils(tronApp.page, test)
|
|
||||||
|
|
||||||
await tronApp.page.setViewportSize({ width: 1200, height: 500 })
|
await tronApp.page.setViewportSize({ width: 1200, height: 500 })
|
||||||
tronApp.page.on('console', console.log)
|
tronApp.page.on('console', console.log)
|
||||||
|
|
||||||
await panesOpen(['files'])
|
await panesOpen(['files'])
|
||||||
|
|
||||||
await createAndSelectProject('project-000')
|
await createProject({ name: 'project-000', page: tronApp.page })
|
||||||
|
|
||||||
await createNewFile('')
|
await createNewFile('')
|
||||||
await createNewFile('')
|
await createNewFile('')
|
||||||
@ -198,62 +193,74 @@ test.describe('when using the file tree to', () => {
|
|||||||
test(
|
test(
|
||||||
'create a new file with the same name as an existing file cancels the operation',
|
'create a new file with the same name as an existing file cancels the operation',
|
||||||
{ tag: '@electron' },
|
{ tag: '@electron' },
|
||||||
async ({ browser: _, tronApp }, testInfo) => {
|
async (
|
||||||
await tronApp.initialise()
|
{ browser: _, tronApp, homePage, scene, editor, toolbar },
|
||||||
|
testInfo
|
||||||
|
) => {
|
||||||
|
const projectName = 'cube'
|
||||||
|
const mainFile = 'main.kcl'
|
||||||
|
const secondFile = 'cylinder.kcl'
|
||||||
|
const kclCube = await fsp.readFile(executorInputPath('cube.kcl'), 'utf-8')
|
||||||
|
const kclCylinder = await fsp.readFile(
|
||||||
|
executorInputPath('cylinder.kcl'),
|
||||||
|
'utf-8'
|
||||||
|
)
|
||||||
|
await tronApp.initialise({
|
||||||
|
fixtures: { homePage, scene, editor, toolbar },
|
||||||
|
folderSetupFn: async (dir) => {
|
||||||
|
const cubeDir = join(dir, projectName)
|
||||||
|
await fsp.mkdir(cubeDir, { recursive: true })
|
||||||
|
await fsp.copyFile(
|
||||||
|
executorInputPath('cube.kcl'),
|
||||||
|
join(cubeDir, mainFile)
|
||||||
|
)
|
||||||
|
await fsp.copyFile(
|
||||||
|
executorInputPath('cylinder.kcl'),
|
||||||
|
join(cubeDir, secondFile)
|
||||||
|
)
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
const {
|
const {
|
||||||
openKclCodePanel,
|
|
||||||
openFilePanel,
|
openFilePanel,
|
||||||
createAndSelectProject,
|
|
||||||
pasteCodeInEditor,
|
|
||||||
createNewFileAndSelect,
|
|
||||||
renameFile,
|
renameFile,
|
||||||
selectFile,
|
selectFile,
|
||||||
editorTextMatches,
|
editorTextMatches,
|
||||||
|
waitForPageLoad,
|
||||||
} = await getUtils(tronApp.page, _test)
|
} = await getUtils(tronApp.page, _test)
|
||||||
|
|
||||||
await tronApp.page.setViewportSize({ width: 1200, height: 500 })
|
await test.step(`Setup: Open project and navigate to ${secondFile}`, async () => {
|
||||||
tronApp.page.on('console', console.log)
|
await homePage.expectState({
|
||||||
|
projectCards: [
|
||||||
await createAndSelectProject('project-000')
|
{
|
||||||
await openKclCodePanel()
|
title: projectName,
|
||||||
|
fileCount: 2,
|
||||||
|
folderCount: 2, // TODO: This is a pre-existing bug, there are no folders within the project
|
||||||
|
},
|
||||||
|
],
|
||||||
|
sortBy: 'last-modified-desc',
|
||||||
|
})
|
||||||
|
await homePage.openProject(projectName)
|
||||||
|
await waitForPageLoad()
|
||||||
await openFilePanel()
|
await openFilePanel()
|
||||||
// File the main.kcl with contents
|
await selectFile(secondFile)
|
||||||
const kclCube = await fsp.readFile(
|
})
|
||||||
'src/wasm-lib/tests/executor/inputs/cube.kcl',
|
|
||||||
'utf-8'
|
|
||||||
)
|
|
||||||
await pasteCodeInEditor(kclCube)
|
|
||||||
|
|
||||||
// TODO: We have a timeout of 1s between edits to write to disk. If you reload the page too quickly it won't write to disk.
|
await test.step(`Attempt to rename ${secondFile} to ${mainFile}`, async () => {
|
||||||
await tronApp.page.waitForTimeout(2000)
|
await renameFile(secondFile, mainFile)
|
||||||
|
})
|
||||||
|
|
||||||
const kcl1 = 'main.kcl'
|
await test.step(`Postcondition: ${mainFile} still has the original content`, async () => {
|
||||||
const kcl2 = '2.kcl'
|
await selectFile(mainFile)
|
||||||
await createNewFileAndSelect(kcl2)
|
|
||||||
const kclCylinder = await fsp.readFile(
|
|
||||||
'src/wasm-lib/tests/executor/inputs/cylinder.kcl',
|
|
||||||
'utf-8'
|
|
||||||
)
|
|
||||||
await pasteCodeInEditor(kclCylinder)
|
|
||||||
|
|
||||||
// TODO: We have a timeout of 1s between edits to write to disk. If you reload the page too quickly it won't write to disk.
|
|
||||||
await tronApp.page.waitForTimeout(2000)
|
|
||||||
|
|
||||||
await renameFile(kcl2, kcl1)
|
|
||||||
|
|
||||||
await test.step(`Postcondition: ${kcl1} still has the original content`, async () => {
|
|
||||||
await selectFile(kcl1)
|
|
||||||
await editorTextMatches(kclCube)
|
await editorTextMatches(kclCube)
|
||||||
})
|
})
|
||||||
await tronApp.page.waitForTimeout(500)
|
|
||||||
|
|
||||||
await test.step(`Postcondition: ${kcl2} still exists with the original content`, async () => {
|
await test.step(`Postcondition: ${secondFile} still exists with the original content`, async () => {
|
||||||
await selectFile(kcl2)
|
await selectFile(secondFile)
|
||||||
await editorTextMatches(kclCylinder)
|
await editorTextMatches(kclCylinder)
|
||||||
})
|
})
|
||||||
|
|
||||||
await tronApp?.close?.()
|
await tronApp.close()
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -263,20 +270,15 @@ test.describe('when using the file tree to', () => {
|
|||||||
async ({ browser: _, tronApp }, testInfo) => {
|
async ({ browser: _, tronApp }, testInfo) => {
|
||||||
await tronApp.initialise()
|
await tronApp.initialise()
|
||||||
|
|
||||||
const {
|
const { panesOpen, pasteCodeInEditor, deleteFile, editorTextMatches } =
|
||||||
panesOpen,
|
await getUtils(tronApp.page, _test)
|
||||||
createAndSelectProject,
|
|
||||||
pasteCodeInEditor,
|
|
||||||
deleteFile,
|
|
||||||
editorTextMatches,
|
|
||||||
} = await getUtils(tronApp.page, _test)
|
|
||||||
|
|
||||||
await tronApp.page.setViewportSize({ width: 1200, height: 500 })
|
await tronApp.page.setViewportSize({ width: 1200, height: 500 })
|
||||||
tronApp.page.on('console', console.log)
|
tronApp.page.on('console', console.log)
|
||||||
|
|
||||||
await panesOpen(['files', 'code'])
|
await panesOpen(['files', 'code'])
|
||||||
|
|
||||||
await createAndSelectProject('project-000')
|
await createProject({ name: 'project-000', page: tronApp.page })
|
||||||
// File the main.kcl with contents
|
// File the main.kcl with contents
|
||||||
const kclCube = await fsp.readFile(
|
const kclCube = await fsp.readFile(
|
||||||
'src/wasm-lib/tests/executor/inputs/cube.kcl',
|
'src/wasm-lib/tests/executor/inputs/cube.kcl',
|
||||||
@ -284,11 +286,11 @@ test.describe('when using the file tree to', () => {
|
|||||||
)
|
)
|
||||||
await pasteCodeInEditor(kclCube)
|
await pasteCodeInEditor(kclCube)
|
||||||
|
|
||||||
const kcl1 = 'main.kcl'
|
const mainFile = 'main.kcl'
|
||||||
|
|
||||||
await deleteFile(kcl1)
|
await deleteFile(mainFile)
|
||||||
|
|
||||||
await test.step(`Postcondition: ${kcl1} is recreated but has no content`, async () => {
|
await test.step(`Postcondition: ${mainFile} is recreated but has no content`, async () => {
|
||||||
await editorTextMatches('')
|
await editorTextMatches('')
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -306,7 +308,6 @@ test.describe('when using the file tree to', () => {
|
|||||||
|
|
||||||
const {
|
const {
|
||||||
panesOpen,
|
panesOpen,
|
||||||
createAndSelectProject,
|
|
||||||
pasteCodeInEditor,
|
pasteCodeInEditor,
|
||||||
createNewFile,
|
createNewFile,
|
||||||
openDebugPanel,
|
openDebugPanel,
|
||||||
@ -318,7 +319,7 @@ test.describe('when using the file tree to', () => {
|
|||||||
tronApp.page.on('console', console.log)
|
tronApp.page.on('console', console.log)
|
||||||
|
|
||||||
await panesOpen(['files', 'code'])
|
await panesOpen(['files', 'code'])
|
||||||
await createAndSelectProject('project-000')
|
await createProject({ name: 'project-000', page: tronApp.page })
|
||||||
|
|
||||||
// Create a small file
|
// Create a small file
|
||||||
const kclCube = await fsp.readFile(
|
const kclCube = await fsp.readFile(
|
||||||
@ -722,7 +723,7 @@ _test.describe('Renaming in the file tree', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
await _test.step('Rename the folder', async () => {
|
await _test.step('Rename the folder', async () => {
|
||||||
await page.waitForTimeout(60000)
|
await page.waitForTimeout(1000)
|
||||||
await folderToRename.click({ button: 'right' })
|
await folderToRename.click({ button: 'right' })
|
||||||
await _expect(renameMenuItem).toBeVisible()
|
await _expect(renameMenuItem).toBeVisible()
|
||||||
await renameMenuItem.click()
|
await renameMenuItem.click()
|
||||||
|
@ -1,6 +1,11 @@
|
|||||||
import type { Page, Locator } from '@playwright/test'
|
import type { Page, Locator } from '@playwright/test'
|
||||||
import { expect } from '@playwright/test'
|
import { expect } from '@playwright/test'
|
||||||
import { sansWhitespace } from '../test-utils'
|
import {
|
||||||
|
closePane,
|
||||||
|
checkIfPaneIsOpen,
|
||||||
|
openPane,
|
||||||
|
sansWhitespace,
|
||||||
|
} from '../test-utils'
|
||||||
|
|
||||||
interface EditorState {
|
interface EditorState {
|
||||||
activeLines: Array<string>
|
activeLines: Array<string>
|
||||||
@ -11,6 +16,7 @@ interface EditorState {
|
|||||||
export class EditorFixture {
|
export class EditorFixture {
|
||||||
public page: Page
|
public page: Page
|
||||||
|
|
||||||
|
private paneButtonTestId = 'code-pane-button'
|
||||||
private diagnosticsTooltip!: Locator
|
private diagnosticsTooltip!: Locator
|
||||||
private diagnosticsGutterIcon!: Locator
|
private diagnosticsGutterIcon!: Locator
|
||||||
private codeContent!: Locator
|
private codeContent!: Locator
|
||||||
@ -31,19 +37,32 @@ export class EditorFixture {
|
|||||||
|
|
||||||
private _expectEditorToContain =
|
private _expectEditorToContain =
|
||||||
(not = false) =>
|
(not = false) =>
|
||||||
(
|
async (
|
||||||
code: string,
|
code: string,
|
||||||
{
|
{
|
||||||
shouldNormalise = false,
|
shouldNormalise = false,
|
||||||
timeout = 5_000,
|
timeout = 5_000,
|
||||||
}: { shouldNormalise?: boolean; timeout?: number } = {}
|
}: { shouldNormalise?: boolean; timeout?: number } = {}
|
||||||
) => {
|
) => {
|
||||||
|
const wasPaneOpen = await this.checkIfPaneIsOpen()
|
||||||
|
if (!wasPaneOpen) {
|
||||||
|
await this.openPane()
|
||||||
|
}
|
||||||
|
const resetPane = async () => {
|
||||||
|
if (!wasPaneOpen) {
|
||||||
|
await this.closePane()
|
||||||
|
}
|
||||||
|
}
|
||||||
if (!shouldNormalise) {
|
if (!shouldNormalise) {
|
||||||
const expectStart = expect(this.codeContent)
|
const expectStart = expect(this.codeContent)
|
||||||
if (not) {
|
if (not) {
|
||||||
return expectStart.not.toContainText(code, { timeout })
|
const result = await expectStart.not.toContainText(code, { timeout })
|
||||||
|
await resetPane()
|
||||||
|
return result
|
||||||
}
|
}
|
||||||
return expectStart.toContainText(code, { timeout })
|
const result = await expectStart.toContainText(code, { timeout })
|
||||||
|
await resetPane()
|
||||||
|
return result
|
||||||
}
|
}
|
||||||
const normalisedCode = code.replaceAll(/\s+/g, '').trim()
|
const normalisedCode = code.replaceAll(/\s+/g, '').trim()
|
||||||
const expectStart = expect.poll(
|
const expectStart = expect.poll(
|
||||||
@ -56,9 +75,13 @@ export class EditorFixture {
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
if (not) {
|
if (not) {
|
||||||
return expectStart.not.toContain(normalisedCode)
|
const result = await expectStart.not.toContain(normalisedCode)
|
||||||
|
await resetPane()
|
||||||
|
return result
|
||||||
}
|
}
|
||||||
return expectStart.toContain(normalisedCode)
|
const result = await expectStart.toContain(normalisedCode)
|
||||||
|
await resetPane()
|
||||||
|
return result
|
||||||
}
|
}
|
||||||
expectEditor = {
|
expectEditor = {
|
||||||
toContain: this._expectEditorToContain(),
|
toContain: this._expectEditorToContain(),
|
||||||
@ -115,4 +138,13 @@ export class EditorFixture {
|
|||||||
code = code.replace(findCode, replaceCode)
|
code = code.replace(findCode, replaceCode)
|
||||||
await this.codeContent.fill(code)
|
await this.codeContent.fill(code)
|
||||||
}
|
}
|
||||||
|
checkIfPaneIsOpen() {
|
||||||
|
return checkIfPaneIsOpen(this.page, this.paneButtonTestId)
|
||||||
|
}
|
||||||
|
closePane() {
|
||||||
|
return closePane(this.page, this.paneButtonTestId)
|
||||||
|
}
|
||||||
|
openPane() {
|
||||||
|
return openPane(this.page, this.paneButtonTestId)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -20,6 +20,7 @@ export class AuthenticatedApp {
|
|||||||
public readonly page: Page
|
public readonly page: Page
|
||||||
public readonly context: BrowserContext
|
public readonly context: BrowserContext
|
||||||
public readonly testInfo: TestInfo
|
public readonly testInfo: TestInfo
|
||||||
|
public readonly viewPortSize = { width: 1000, height: 500 }
|
||||||
|
|
||||||
constructor(context: BrowserContext, page: Page, testInfo: TestInfo) {
|
constructor(context: BrowserContext, page: Page, testInfo: TestInfo) {
|
||||||
this.page = page
|
this.page = page
|
||||||
@ -36,7 +37,7 @@ export class AuthenticatedApp {
|
|||||||
;(window as any).playwrightSkipFilePicker = true
|
;(window as any).playwrightSkipFilePicker = true
|
||||||
}, code)
|
}, code)
|
||||||
|
|
||||||
await this.page.setViewportSize({ width: 1000, height: 500 })
|
await this.page.setViewportSize(this.viewPortSize)
|
||||||
|
|
||||||
await u.waitForAuthSkipAppStart()
|
await u.waitForAuthSkipAppStart()
|
||||||
}
|
}
|
||||||
|
@ -10,7 +10,13 @@ import {
|
|||||||
} from '../test-utils'
|
} from '../test-utils'
|
||||||
|
|
||||||
type mouseParams = {
|
type mouseParams = {
|
||||||
pixelDiff: number
|
pixelDiff?: number
|
||||||
|
}
|
||||||
|
type mouseDragToParams = mouseParams & {
|
||||||
|
fromPoint: { x: number; y: number }
|
||||||
|
}
|
||||||
|
type mouseDragFromParams = mouseParams & {
|
||||||
|
toPoint: { x: number; y: number }
|
||||||
}
|
}
|
||||||
|
|
||||||
type SceneSerialised = {
|
type SceneSerialised = {
|
||||||
@ -20,6 +26,13 @@ type SceneSerialised = {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ClickHandler = (clickParams?: mouseParams) => Promise<void | boolean>
|
||||||
|
type MoveHandler = (moveParams?: mouseParams) => Promise<void | boolean>
|
||||||
|
type DragToHandler = (dragParams: mouseDragToParams) => Promise<void | boolean>
|
||||||
|
type DragFromHandler = (
|
||||||
|
dragParams: mouseDragFromParams
|
||||||
|
) => Promise<void | boolean>
|
||||||
|
|
||||||
export class SceneFixture {
|
export class SceneFixture {
|
||||||
public page: Page
|
public page: Page
|
||||||
|
|
||||||
@ -55,7 +68,7 @@ export class SceneFixture {
|
|||||||
x: number,
|
x: number,
|
||||||
y: number,
|
y: number,
|
||||||
{ steps }: { steps: number } = { steps: 20 }
|
{ steps }: { steps: number } = { steps: 20 }
|
||||||
) =>
|
): [ClickHandler, MoveHandler] =>
|
||||||
[
|
[
|
||||||
(clickParams?: mouseParams) => {
|
(clickParams?: mouseParams) => {
|
||||||
if (clickParams?.pixelDiff) {
|
if (clickParams?.pixelDiff) {
|
||||||
@ -78,6 +91,47 @@ export class SceneFixture {
|
|||||||
return this.page.mouse.move(x, y, { steps })
|
return this.page.mouse.move(x, y, { steps })
|
||||||
},
|
},
|
||||||
] as const
|
] as const
|
||||||
|
makeDragHelpers = (
|
||||||
|
x: number,
|
||||||
|
y: number,
|
||||||
|
{ steps }: { steps: number } = { steps: 20 }
|
||||||
|
): [DragToHandler, DragFromHandler] =>
|
||||||
|
[
|
||||||
|
(dragToParams: mouseDragToParams) => {
|
||||||
|
if (dragToParams?.pixelDiff) {
|
||||||
|
return doAndWaitForImageDiff(
|
||||||
|
this.page,
|
||||||
|
() =>
|
||||||
|
this.page.dragAndDrop('#stream', '#stream', {
|
||||||
|
sourcePosition: dragToParams.fromPoint,
|
||||||
|
targetPosition: { x, y },
|
||||||
|
}),
|
||||||
|
dragToParams.pixelDiff
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return this.page.dragAndDrop('#stream', '#stream', {
|
||||||
|
sourcePosition: dragToParams.fromPoint,
|
||||||
|
targetPosition: { x, y },
|
||||||
|
})
|
||||||
|
},
|
||||||
|
(dragFromParams: mouseDragFromParams) => {
|
||||||
|
if (dragFromParams?.pixelDiff) {
|
||||||
|
return doAndWaitForImageDiff(
|
||||||
|
this.page,
|
||||||
|
() =>
|
||||||
|
this.page.dragAndDrop('#stream', '#stream', {
|
||||||
|
sourcePosition: { x, y },
|
||||||
|
targetPosition: dragFromParams.toPoint,
|
||||||
|
}),
|
||||||
|
dragFromParams.pixelDiff
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return this.page.dragAndDrop('#stream', '#stream', {
|
||||||
|
sourcePosition: { x, y },
|
||||||
|
targetPosition: dragFromParams.toPoint,
|
||||||
|
})
|
||||||
|
},
|
||||||
|
] as const
|
||||||
|
|
||||||
/** Likely no where, there's a chance it will click something in the scene, depending what you have in the scene.
|
/** Likely no where, there's a chance it will click something in the scene, depending what you have in the scene.
|
||||||
*
|
*
|
||||||
|
@ -7,6 +7,7 @@ export class ToolbarFixture {
|
|||||||
|
|
||||||
extrudeButton!: Locator
|
extrudeButton!: Locator
|
||||||
startSketchBtn!: Locator
|
startSketchBtn!: Locator
|
||||||
|
lineBtn!: Locator
|
||||||
rectangleBtn!: Locator
|
rectangleBtn!: Locator
|
||||||
exitSketchBtn!: Locator
|
exitSketchBtn!: Locator
|
||||||
editSketchBtn!: Locator
|
editSketchBtn!: Locator
|
||||||
@ -24,6 +25,7 @@ export class ToolbarFixture {
|
|||||||
this.page = page
|
this.page = page
|
||||||
this.extrudeButton = page.getByTestId('extrude')
|
this.extrudeButton = page.getByTestId('extrude')
|
||||||
this.startSketchBtn = page.getByTestId('sketch')
|
this.startSketchBtn = page.getByTestId('sketch')
|
||||||
|
this.lineBtn = page.getByTestId('line')
|
||||||
this.rectangleBtn = page.getByTestId('corner-rectangle')
|
this.rectangleBtn = page.getByTestId('corner-rectangle')
|
||||||
this.exitSketchBtn = page.getByTestId('sketch-exit')
|
this.exitSketchBtn = page.getByTestId('sketch-exit')
|
||||||
this.editSketchBtn = page.getByText('Edit Sketch')
|
this.editSketchBtn = page.getByText('Edit Sketch')
|
||||||
|
@ -7,6 +7,7 @@ import {
|
|||||||
setupElectron,
|
setupElectron,
|
||||||
tearDown,
|
tearDown,
|
||||||
executorInputPath,
|
executorInputPath,
|
||||||
|
createProject,
|
||||||
} from './test-utils'
|
} from './test-utils'
|
||||||
import { bracket } from 'lib/exampleKcl'
|
import { bracket } from 'lib/exampleKcl'
|
||||||
import { onboardingPaths } from 'routes/Onboarding/paths'
|
import { onboardingPaths } from 'routes/Onboarding/paths'
|
||||||
@ -74,13 +75,8 @@ test.describe('Onboarding tests', () => {
|
|||||||
const viewportSize = { width: 1200, height: 500 }
|
const viewportSize = { width: 1200, height: 500 }
|
||||||
await page.setViewportSize(viewportSize)
|
await page.setViewportSize(viewportSize)
|
||||||
|
|
||||||
// Locators and constants
|
|
||||||
const newProjectButton = page.getByRole('button', { name: 'New project' })
|
|
||||||
const projectLink = page.getByTestId('project-link')
|
|
||||||
|
|
||||||
await test.step(`Create a project and open to the onboarding`, async () => {
|
await test.step(`Create a project and open to the onboarding`, async () => {
|
||||||
await newProjectButton.click()
|
await createProject({ name: 'project-link', page })
|
||||||
await projectLink.click()
|
|
||||||
await test.step(`Ensure the engine connection works by testing the sketch button`, async () => {
|
await test.step(`Ensure the engine connection works by testing the sketch button`, async () => {
|
||||||
await u.waitForPageLoad()
|
await u.waitForPageLoad()
|
||||||
})
|
})
|
||||||
@ -425,7 +421,9 @@ test(
|
|||||||
const restartConfirmationButton = page.getByRole('button', {
|
const restartConfirmationButton = page.getByRole('button', {
|
||||||
name: 'Make a new project',
|
name: 'Make a new project',
|
||||||
})
|
})
|
||||||
const tutorialProjectIndicator = page.getByText('Tutorial Project 00')
|
const tutorialProjectIndicator = page
|
||||||
|
.getByTestId('project-sidebar-toggle')
|
||||||
|
.filter({ hasText: 'Tutorial Project 00' })
|
||||||
const tutorialModalText = page.getByText('Welcome to Modeling App!')
|
const tutorialModalText = page.getByText('Welcome to Modeling App!')
|
||||||
const tutorialDismissButton = page.getByRole('button', { name: 'Dismiss' })
|
const tutorialDismissButton = page.getByRole('button', { name: 'Dismiss' })
|
||||||
const userMenuButton = page.getByTestId('user-sidebar-toggle')
|
const userMenuButton = page.getByTestId('user-sidebar-toggle')
|
||||||
|
@ -451,3 +451,103 @@ sketch002 = startSketchOn(extrude001, seg03)
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test(`Verify axis and origin snapping`, async ({
|
||||||
|
app,
|
||||||
|
editor,
|
||||||
|
toolbar,
|
||||||
|
scene,
|
||||||
|
}) => {
|
||||||
|
// Constants and locators
|
||||||
|
// These are mappings from screenspace to KCL coordinates,
|
||||||
|
// until we merge in our coordinate system helpers
|
||||||
|
const xzPlane = [
|
||||||
|
app.viewPortSize.width * 0.65,
|
||||||
|
app.viewPortSize.height * 0.3,
|
||||||
|
] as const
|
||||||
|
const originSloppy = {
|
||||||
|
screen: [
|
||||||
|
app.viewPortSize.width / 2 + 3, // 3px off the center of the screen
|
||||||
|
app.viewPortSize.height / 2,
|
||||||
|
],
|
||||||
|
kcl: [0, 0],
|
||||||
|
} as const
|
||||||
|
const xAxisSloppy = {
|
||||||
|
screen: [
|
||||||
|
app.viewPortSize.width * 0.75,
|
||||||
|
app.viewPortSize.height / 2 - 3, // 3px off the X-axis
|
||||||
|
],
|
||||||
|
kcl: [16.95, 0],
|
||||||
|
} as const
|
||||||
|
const offYAxis = {
|
||||||
|
screen: [
|
||||||
|
app.viewPortSize.width * 0.6, // Well off the Y-axis, out of snapping range
|
||||||
|
app.viewPortSize.height * 0.3,
|
||||||
|
],
|
||||||
|
kcl: [6.78, 6.78],
|
||||||
|
} as const
|
||||||
|
const yAxisSloppy = {
|
||||||
|
screen: [
|
||||||
|
app.viewPortSize.width / 2 + 5, // 5px off the Y-axis
|
||||||
|
app.viewPortSize.height * 0.3,
|
||||||
|
],
|
||||||
|
kcl: [0, 6.78],
|
||||||
|
} as const
|
||||||
|
const [clickOnXzPlane, moveToXzPlane] = scene.makeMouseHelpers(...xzPlane)
|
||||||
|
const [clickOriginSloppy] = scene.makeMouseHelpers(...originSloppy.screen)
|
||||||
|
const [clickXAxisSloppy, moveXAxisSloppy] = scene.makeMouseHelpers(
|
||||||
|
...xAxisSloppy.screen
|
||||||
|
)
|
||||||
|
const [dragToOffYAxis, dragFromOffAxis] = scene.makeDragHelpers(
|
||||||
|
...offYAxis.screen
|
||||||
|
)
|
||||||
|
|
||||||
|
const expectedCodeSnippets = {
|
||||||
|
sketchOnXzPlane: `sketch001 = startSketchOn('XZ')`,
|
||||||
|
pointAtOrigin: `startProfileAt([${originSloppy.kcl[0]}, ${originSloppy.kcl[1]}], %)`,
|
||||||
|
segmentOnXAxis: `lineTo([${xAxisSloppy.kcl[0]}, ${xAxisSloppy.kcl[1]}], %)`,
|
||||||
|
afterSegmentDraggedOffYAxis: `startProfileAt([${offYAxis.kcl[0]}, ${offYAxis.kcl[1]}], %)`,
|
||||||
|
afterSegmentDraggedOnYAxis: `startProfileAt([${yAxisSloppy.kcl[0]}, ${yAxisSloppy.kcl[1]}], %)`,
|
||||||
|
}
|
||||||
|
|
||||||
|
await app.initialise()
|
||||||
|
|
||||||
|
await test.step(`Start a sketch on the XZ plane`, async () => {
|
||||||
|
await editor.closePane()
|
||||||
|
await toolbar.startSketchPlaneSelection()
|
||||||
|
await moveToXzPlane()
|
||||||
|
await clickOnXzPlane()
|
||||||
|
// timeout wait for engine animation is unavoidable
|
||||||
|
await app.page.waitForTimeout(600)
|
||||||
|
await editor.expectEditor.toContain(expectedCodeSnippets.sketchOnXzPlane)
|
||||||
|
})
|
||||||
|
await test.step(`Place a point a few pixels off the middle, verify it still snaps to 0,0`, async () => {
|
||||||
|
await clickOriginSloppy()
|
||||||
|
await editor.expectEditor.toContain(expectedCodeSnippets.pointAtOrigin)
|
||||||
|
})
|
||||||
|
await test.step(`Add a segment on x-axis after moving the mouse a bit, verify it snaps`, async () => {
|
||||||
|
await moveXAxisSloppy()
|
||||||
|
await clickXAxisSloppy()
|
||||||
|
await editor.expectEditor.toContain(expectedCodeSnippets.segmentOnXAxis)
|
||||||
|
})
|
||||||
|
await test.step(`Unequip line tool`, async () => {
|
||||||
|
await toolbar.lineBtn.click()
|
||||||
|
await expect(toolbar.lineBtn).not.toHaveAttribute('aria-pressed', 'true')
|
||||||
|
})
|
||||||
|
await test.step(`Drag the origin point up and to the right, verify it's past snapping`, async () => {
|
||||||
|
await dragToOffYAxis({
|
||||||
|
fromPoint: { x: originSloppy.screen[0], y: originSloppy.screen[1] },
|
||||||
|
})
|
||||||
|
await editor.expectEditor.toContain(
|
||||||
|
expectedCodeSnippets.afterSegmentDraggedOffYAxis
|
||||||
|
)
|
||||||
|
})
|
||||||
|
await test.step(`Drag the origin point left to the y-axis, verify it snaps back`, async () => {
|
||||||
|
await dragFromOffAxis({
|
||||||
|
toPoint: { x: yAxisSloppy.screen[0], y: yAxisSloppy.screen[1] },
|
||||||
|
})
|
||||||
|
await editor.expectEditor.toContain(
|
||||||
|
expectedCodeSnippets.afterSegmentDraggedOnYAxis
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
@ -7,7 +7,7 @@ import {
|
|||||||
Paths,
|
Paths,
|
||||||
setupElectron,
|
setupElectron,
|
||||||
tearDown,
|
tearDown,
|
||||||
createProjectAndRenameIt,
|
createProject,
|
||||||
} from './test-utils'
|
} from './test-utils'
|
||||||
import fsp from 'fs/promises'
|
import fsp from 'fs/promises'
|
||||||
import fs from 'fs'
|
import fs from 'fs'
|
||||||
@ -503,21 +503,261 @@ test(
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
test.describe(`Project management commands`, () => {
|
||||||
|
test(
|
||||||
|
`Rename from project page`,
|
||||||
|
{ tag: '@electron' },
|
||||||
|
async ({ browserName }, testInfo) => {
|
||||||
|
const projectName = `my_project_to_rename`
|
||||||
|
const { electronApp, page } = await setupElectron({
|
||||||
|
testInfo,
|
||||||
|
folderSetupFn: async (dir) => {
|
||||||
|
await fsp.mkdir(`${dir}/${projectName}`, { recursive: true })
|
||||||
|
await fsp.copyFile(
|
||||||
|
'src/wasm-lib/tests/executor/inputs/router-template-slate.kcl',
|
||||||
|
`${dir}/${projectName}/main.kcl`
|
||||||
|
)
|
||||||
|
},
|
||||||
|
})
|
||||||
|
const u = await getUtils(page)
|
||||||
|
|
||||||
|
// Constants and locators
|
||||||
|
const projectHomeLink = page.getByTestId('project-link')
|
||||||
|
const commandButton = page.getByRole('button', { name: 'Commands' })
|
||||||
|
const commandOption = page.getByRole('option', { name: 'rename project' })
|
||||||
|
const projectNameOption = page.getByRole('option', { name: projectName })
|
||||||
|
const projectRenamedName = `project-000`
|
||||||
|
// const projectMenuButton = page.getByTestId('project-sidebar-toggle')
|
||||||
|
const commandContinueButton = page.getByRole('button', {
|
||||||
|
name: 'Continue',
|
||||||
|
})
|
||||||
|
const commandSubmitButton = page.getByRole('button', {
|
||||||
|
name: 'Submit command',
|
||||||
|
})
|
||||||
|
const toastMessage = page.getByText(`Successfully renamed`)
|
||||||
|
|
||||||
|
await test.step(`Setup`, async () => {
|
||||||
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
|
page.on('console', console.log)
|
||||||
|
|
||||||
|
await projectHomeLink.click()
|
||||||
|
await u.waitForPageLoad()
|
||||||
|
})
|
||||||
|
|
||||||
|
await test.step(`Run rename command via command palette`, async () => {
|
||||||
|
await commandButton.click()
|
||||||
|
await commandOption.click()
|
||||||
|
await projectNameOption.click()
|
||||||
|
|
||||||
|
await expect(commandContinueButton).toBeVisible()
|
||||||
|
await commandContinueButton.click()
|
||||||
|
|
||||||
|
await expect(commandSubmitButton).toBeVisible()
|
||||||
|
await commandSubmitButton.click()
|
||||||
|
|
||||||
|
await expect(toastMessage).toBeVisible()
|
||||||
|
})
|
||||||
|
|
||||||
|
// TODO: in future I'd like the behavior to be to
|
||||||
|
// navigate to the new project's page directly,
|
||||||
|
// see ProjectContextProvider.tsx:158
|
||||||
|
await test.step(`Check the project was renamed and we navigated home`, async () => {
|
||||||
|
await expect(projectHomeLink.first()).toBeVisible()
|
||||||
|
await expect(projectHomeLink.first()).toContainText(projectRenamedName)
|
||||||
|
})
|
||||||
|
|
||||||
|
await electronApp.close()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
test(
|
||||||
|
`Delete from project page`,
|
||||||
|
{ tag: '@electron' },
|
||||||
|
async ({ browserName: _ }, testInfo) => {
|
||||||
|
const projectName = `my_project_to_delete`
|
||||||
|
const { electronApp, page } = await setupElectron({
|
||||||
|
testInfo,
|
||||||
|
folderSetupFn: async (dir) => {
|
||||||
|
await fsp.mkdir(`${dir}/${projectName}`, { recursive: true })
|
||||||
|
await fsp.copyFile(
|
||||||
|
'src/wasm-lib/tests/executor/inputs/router-template-slate.kcl',
|
||||||
|
`${dir}/${projectName}/main.kcl`
|
||||||
|
)
|
||||||
|
},
|
||||||
|
})
|
||||||
|
const u = await getUtils(page)
|
||||||
|
|
||||||
|
// Constants and locators
|
||||||
|
const projectHomeLink = page.getByTestId('project-link')
|
||||||
|
const commandButton = page.getByRole('button', { name: 'Commands' })
|
||||||
|
const commandOption = page.getByRole('option', { name: 'delete project' })
|
||||||
|
const projectNameOption = page.getByRole('option', { name: projectName })
|
||||||
|
const commandWarning = page.getByText('Are you sure you want to delete?')
|
||||||
|
const commandSubmitButton = page.getByRole('button', {
|
||||||
|
name: 'Submit command',
|
||||||
|
})
|
||||||
|
const toastMessage = page.getByText(`Successfully deleted`)
|
||||||
|
const noProjectsMessage = page.getByText('No Projects found')
|
||||||
|
|
||||||
|
await test.step(`Setup`, async () => {
|
||||||
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
|
page.on('console', console.log)
|
||||||
|
|
||||||
|
await projectHomeLink.click()
|
||||||
|
await u.waitForPageLoad()
|
||||||
|
})
|
||||||
|
|
||||||
|
await test.step(`Run delete command via command palette`, async () => {
|
||||||
|
await commandButton.click()
|
||||||
|
await commandOption.click()
|
||||||
|
await projectNameOption.click()
|
||||||
|
|
||||||
|
await expect(commandWarning).toBeVisible()
|
||||||
|
await expect(commandSubmitButton).toBeVisible()
|
||||||
|
await commandSubmitButton.click()
|
||||||
|
|
||||||
|
await expect(toastMessage).toBeVisible()
|
||||||
|
})
|
||||||
|
|
||||||
|
await test.step(`Check the project was deleted and we navigated home`, async () => {
|
||||||
|
await expect(noProjectsMessage).toBeVisible()
|
||||||
|
})
|
||||||
|
|
||||||
|
await electronApp.close()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
test(
|
||||||
|
`Rename from home page`,
|
||||||
|
{ tag: '@electron' },
|
||||||
|
async ({ browserName: _ }, testInfo) => {
|
||||||
|
const projectName = `my_project_to_rename`
|
||||||
|
const { electronApp, page } = await setupElectron({
|
||||||
|
testInfo,
|
||||||
|
folderSetupFn: async (dir) => {
|
||||||
|
await fsp.mkdir(`${dir}/${projectName}`, { recursive: true })
|
||||||
|
await fsp.copyFile(
|
||||||
|
'src/wasm-lib/tests/executor/inputs/router-template-slate.kcl',
|
||||||
|
`${dir}/${projectName}/main.kcl`
|
||||||
|
)
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
// Constants and locators
|
||||||
|
const projectHomeLink = page.getByTestId('project-link')
|
||||||
|
const commandButton = page.getByRole('button', { name: 'Commands' })
|
||||||
|
const commandOption = page.getByRole('option', { name: 'rename project' })
|
||||||
|
const projectNameOption = page.getByRole('option', { name: projectName })
|
||||||
|
const projectRenamedName = `project-000`
|
||||||
|
const commandContinueButton = page.getByRole('button', {
|
||||||
|
name: 'Continue',
|
||||||
|
})
|
||||||
|
const commandSubmitButton = page.getByRole('button', {
|
||||||
|
name: 'Submit command',
|
||||||
|
})
|
||||||
|
const toastMessage = page.getByText(`Successfully renamed`)
|
||||||
|
|
||||||
|
await test.step(`Setup`, async () => {
|
||||||
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
|
page.on('console', console.log)
|
||||||
|
await expect(projectHomeLink).toBeVisible()
|
||||||
|
})
|
||||||
|
|
||||||
|
await test.step(`Run rename command via command palette`, async () => {
|
||||||
|
await commandButton.click()
|
||||||
|
await commandOption.click()
|
||||||
|
await projectNameOption.click()
|
||||||
|
|
||||||
|
await expect(commandContinueButton).toBeVisible()
|
||||||
|
await commandContinueButton.click()
|
||||||
|
|
||||||
|
await expect(commandSubmitButton).toBeVisible()
|
||||||
|
await commandSubmitButton.click()
|
||||||
|
|
||||||
|
await expect(toastMessage).toBeVisible()
|
||||||
|
})
|
||||||
|
|
||||||
|
await test.step(`Check the project was renamed`, async () => {
|
||||||
|
await expect(
|
||||||
|
page.getByRole('link', { name: projectRenamedName })
|
||||||
|
).toBeVisible()
|
||||||
|
await expect(projectHomeLink).not.toHaveText(projectName)
|
||||||
|
})
|
||||||
|
|
||||||
|
await electronApp.close()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
test(
|
||||||
|
`Delete from home page`,
|
||||||
|
{ tag: '@electron' },
|
||||||
|
async ({ browserName: _ }, testInfo) => {
|
||||||
|
const projectName = `my_project_to_delete`
|
||||||
|
const { electronApp, page } = await setupElectron({
|
||||||
|
testInfo,
|
||||||
|
folderSetupFn: async (dir) => {
|
||||||
|
await fsp.mkdir(`${dir}/${projectName}`, { recursive: true })
|
||||||
|
await fsp.copyFile(
|
||||||
|
'src/wasm-lib/tests/executor/inputs/router-template-slate.kcl',
|
||||||
|
`${dir}/${projectName}/main.kcl`
|
||||||
|
)
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
// Constants and locators
|
||||||
|
const projectHomeLink = page.getByTestId('project-link')
|
||||||
|
const commandButton = page.getByRole('button', { name: 'Commands' })
|
||||||
|
const commandOption = page.getByRole('option', { name: 'delete project' })
|
||||||
|
const projectNameOption = page.getByRole('option', { name: projectName })
|
||||||
|
const commandWarning = page.getByText('Are you sure you want to delete?')
|
||||||
|
const commandSubmitButton = page.getByRole('button', {
|
||||||
|
name: 'Submit command',
|
||||||
|
})
|
||||||
|
const toastMessage = page.getByText(`Successfully deleted`)
|
||||||
|
const noProjectsMessage = page.getByText('No Projects found')
|
||||||
|
|
||||||
|
await test.step(`Setup`, async () => {
|
||||||
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
|
page.on('console', console.log)
|
||||||
|
await expect(projectHomeLink).toBeVisible()
|
||||||
|
})
|
||||||
|
|
||||||
|
await test.step(`Run delete command via command palette`, async () => {
|
||||||
|
await commandButton.click()
|
||||||
|
await commandOption.click()
|
||||||
|
await projectNameOption.click()
|
||||||
|
|
||||||
|
await expect(commandWarning).toBeVisible()
|
||||||
|
await expect(commandSubmitButton).toBeVisible()
|
||||||
|
await commandSubmitButton.click()
|
||||||
|
|
||||||
|
await expect(toastMessage).toBeVisible()
|
||||||
|
})
|
||||||
|
|
||||||
|
await test.step(`Check the project was deleted`, async () => {
|
||||||
|
await expect(projectHomeLink).not.toBeVisible()
|
||||||
|
await expect(noProjectsMessage).toBeVisible()
|
||||||
|
})
|
||||||
|
|
||||||
|
await electronApp.close()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
test(
|
test(
|
||||||
'File in the file pane should open with a single click',
|
'File in the file pane should open with a single click',
|
||||||
{ tag: '@electron' },
|
{ tag: '@electron' },
|
||||||
async ({ browserName }, testInfo) => {
|
async ({ browserName }, testInfo) => {
|
||||||
|
const projectName = 'router-template-slate'
|
||||||
const { electronApp, page } = await setupElectron({
|
const { electronApp, page } = await setupElectron({
|
||||||
testInfo,
|
testInfo,
|
||||||
folderSetupFn: async (dir) => {
|
folderSetupFn: async (dir) => {
|
||||||
await fsp.mkdir(`${dir}/router-template-slate`, { recursive: true })
|
await fsp.mkdir(`${dir}/${projectName}`, { recursive: true })
|
||||||
await fsp.copyFile(
|
await fsp.copyFile(
|
||||||
'src/wasm-lib/tests/executor/inputs/router-template-slate.kcl',
|
'src/wasm-lib/tests/executor/inputs/router-template-slate.kcl',
|
||||||
`${dir}/router-template-slate/main.kcl`
|
`${dir}/${projectName}/main.kcl`
|
||||||
)
|
)
|
||||||
await fsp.copyFile(
|
await fsp.copyFile(
|
||||||
'src/wasm-lib/tests/executor/inputs/focusrite_scarlett_mounting_braket.kcl',
|
'src/wasm-lib/tests/executor/inputs/focusrite_scarlett_mounting_braket.kcl',
|
||||||
`${dir}/router-template-slate/otherThingToClickOn.kcl`
|
`${dir}/${projectName}/otherThingToClickOn.kcl`
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
@ -526,7 +766,7 @@ test(
|
|||||||
|
|
||||||
page.on('console', console.log)
|
page.on('console', console.log)
|
||||||
|
|
||||||
await page.getByText('router-template-slate').click()
|
await page.getByText(projectName).click()
|
||||||
await expect(page.getByTestId('loading')).toBeAttached()
|
await expect(page.getByTestId('loading')).toBeAttached()
|
||||||
await expect(page.getByTestId('loading')).not.toBeAttached({
|
await expect(page.getByTestId('loading')).not.toBeAttached({
|
||||||
timeout: 20_000,
|
timeout: 20_000,
|
||||||
@ -643,7 +883,7 @@ test(
|
|||||||
page.on('console', console.log)
|
page.on('console', console.log)
|
||||||
|
|
||||||
await test.step('delete the middle project, i.e. the bracket project', async () => {
|
await test.step('delete the middle project, i.e. the bracket project', async () => {
|
||||||
const project = page.getByText('bracket')
|
const project = page.getByTestId('project-link').getByText('bracket')
|
||||||
|
|
||||||
await project.hover()
|
await project.hover()
|
||||||
await project.focus()
|
await project.focus()
|
||||||
@ -687,10 +927,10 @@ test(
|
|||||||
})
|
})
|
||||||
|
|
||||||
await test.step('Check we can still create a project', async () => {
|
await test.step('Check we can still create a project', async () => {
|
||||||
await page.getByRole('button', { name: 'New project' }).click()
|
await createProject({ name: 'project-000', page, returnHome: true })
|
||||||
await expect(page.getByText('Successfully created')).toBeVisible()
|
await expect(
|
||||||
await expect(page.getByText('Successfully created')).not.toBeVisible()
|
page.getByTestId('project-link').filter({ hasText: 'project-000' })
|
||||||
await expect(page.getByText('project-000')).toBeVisible()
|
).toBeVisible()
|
||||||
})
|
})
|
||||||
|
|
||||||
await electronApp.close()
|
await electronApp.close()
|
||||||
@ -867,17 +1107,16 @@ test.fixme(
|
|||||||
const pointOnModel = { x: 660, y: 250 }
|
const pointOnModel = { x: 660, y: 250 }
|
||||||
const expectedStartCamZPosition = 15633.47
|
const expectedStartCamZPosition = 15633.47
|
||||||
|
|
||||||
|
// Constants and locators
|
||||||
|
const projectLinks = page.getByTestId('project-link')
|
||||||
|
|
||||||
// expect to see text "No Projects found"
|
// expect to see text "No Projects found"
|
||||||
await expect(page.getByText('No Projects found')).toBeVisible()
|
await expect(page.getByText('No Projects found')).toBeVisible()
|
||||||
|
|
||||||
await page.getByRole('button', { name: 'New project' }).click()
|
await createProject({ name: 'project-000', page, returnHome: true })
|
||||||
|
await expect(projectLinks.getByText('project-000')).toBeVisible()
|
||||||
|
|
||||||
await expect(page.getByText('Successfully created')).toBeVisible()
|
await projectLinks.getByText('project-000').click()
|
||||||
await expect(page.getByText('Successfully created')).not.toBeVisible()
|
|
||||||
|
|
||||||
await expect(page.getByText('project-000')).toBeVisible()
|
|
||||||
|
|
||||||
await page.getByText('project-000').click()
|
|
||||||
|
|
||||||
await u.waitForPageLoad()
|
await u.waitForPageLoad()
|
||||||
|
|
||||||
@ -936,16 +1175,10 @@ extrude001 = extrude(200, sketch001)`)
|
|||||||
page.getByRole('button', { name: 'New project' })
|
page.getByRole('button', { name: 'New project' })
|
||||||
).toBeVisible()
|
).toBeVisible()
|
||||||
|
|
||||||
const createProject = async (projectNum: number) => {
|
|
||||||
await page.getByRole('button', { name: 'New project' }).click()
|
|
||||||
await expect(page.getByText('Successfully created')).toBeVisible()
|
|
||||||
await expect(page.getByText('Successfully created')).not.toBeVisible()
|
|
||||||
|
|
||||||
const projectNumStr = projectNum.toString().padStart(3, '0')
|
|
||||||
await expect(page.getByText(`project-${projectNumStr}`)).toBeVisible()
|
|
||||||
}
|
|
||||||
for (let i = 1; i <= 10; i++) {
|
for (let i = 1; i <= 10; i++) {
|
||||||
await createProject(i)
|
const name = `project-${i.toString().padStart(3, '0')}`
|
||||||
|
await createProject({ name, page, returnHome: true })
|
||||||
|
await expect(projectLinks.getByText(name)).toBeVisible()
|
||||||
}
|
}
|
||||||
await electronApp.close()
|
await electronApp.close()
|
||||||
}
|
}
|
||||||
@ -1120,11 +1353,10 @@ test(
|
|||||||
await page.getByTestId('settings-close-button').click()
|
await page.getByTestId('settings-close-button').click()
|
||||||
|
|
||||||
await expect(page.getByText('No Projects found')).toBeVisible()
|
await expect(page.getByText('No Projects found')).toBeVisible()
|
||||||
await page.getByRole('button', { name: 'New project' }).click()
|
await createProject({ name: 'project-000', page, returnHome: true })
|
||||||
await expect(page.getByText('Successfully created')).toBeVisible()
|
await expect(
|
||||||
await expect(page.getByText('Successfully created')).not.toBeVisible()
|
page.getByTestId('project-link').filter({ hasText: 'project-000' })
|
||||||
|
).toBeVisible()
|
||||||
await expect(page.getByText(`project-000`)).toBeVisible()
|
|
||||||
})
|
})
|
||||||
|
|
||||||
await test.step('We can change back to the original root project directory', async () => {
|
await test.step('We can change back to the original root project directory', async () => {
|
||||||
@ -1265,6 +1497,7 @@ test(
|
|||||||
'i_shape.kcl',
|
'i_shape.kcl',
|
||||||
'kittycad_svg.kcl',
|
'kittycad_svg.kcl',
|
||||||
'lego.kcl',
|
'lego.kcl',
|
||||||
|
'lsystem.kcl',
|
||||||
'math.kcl',
|
'math.kcl',
|
||||||
'member_expression_sketch.kcl',
|
'member_expression_sketch.kcl',
|
||||||
'mike_stress_test.kcl',
|
'mike_stress_test.kcl',
|
||||||
@ -1450,7 +1683,7 @@ test(
|
|||||||
page.on('console', console.log)
|
page.on('console', console.log)
|
||||||
|
|
||||||
await test.step('Should create and name a project called wrist brace', async () => {
|
await test.step('Should create and name a project called wrist brace', async () => {
|
||||||
await createProjectAndRenameIt({ name: 'wrist brace', page })
|
await createProject({ name: 'wrist brace', page, returnHome: true })
|
||||||
})
|
})
|
||||||
|
|
||||||
await test.step('Should go through onboarding', async () => {
|
await test.step('Should go through onboarding', async () => {
|
||||||
|
@ -637,7 +637,6 @@ test.describe('Sketch tests', () => {
|
|||||||
|> revolve({ axis: "X" }, %)`)
|
|> revolve({ axis: "X" }, %)`)
|
||||||
})
|
})
|
||||||
test('Can add multiple sketches', async ({ page }) => {
|
test('Can add multiple sketches', async ({ page }) => {
|
||||||
test.skip(process.platform === 'darwin', 'Can add multiple sketches')
|
|
||||||
const u = await getUtils(page)
|
const u = await getUtils(page)
|
||||||
const viewportSize = { width: 1200, height: 500 }
|
const viewportSize = { width: 1200, height: 500 }
|
||||||
await page.setViewportSize(viewportSize)
|
await page.setViewportSize(viewportSize)
|
||||||
@ -675,15 +674,16 @@ test.describe('Sketch tests', () => {
|
|||||||
|
|
||||||
await click00r(50, 0)
|
await click00r(50, 0)
|
||||||
await page.waitForTimeout(100)
|
await page.waitForTimeout(100)
|
||||||
codeStr += ` |> line(${toSU([50, 0])}, %)`
|
codeStr += ` |> lineTo(${toSU([50, 0])}, %)`
|
||||||
await expect(u.codeLocator).toHaveText(codeStr)
|
await expect(u.codeLocator).toHaveText(codeStr)
|
||||||
|
|
||||||
await click00r(0, 50)
|
await click00r(0, 50)
|
||||||
codeStr += ` |> line(${toSU([0, 50])}, %)`
|
codeStr += ` |> line(${toSU([0, 50])}, %)`
|
||||||
await expect(u.codeLocator).toHaveText(codeStr)
|
await expect(u.codeLocator).toHaveText(codeStr)
|
||||||
|
|
||||||
await click00r(-50, 0)
|
let clickCoords = await click00r(-50, 0)
|
||||||
codeStr += ` |> line(${toSU([-50, 0])}, %)`
|
expect(clickCoords).not.toBeUndefined()
|
||||||
|
codeStr += ` |> lineTo(${toSU(clickCoords!)}, %)`
|
||||||
await expect(u.codeLocator).toHaveText(codeStr)
|
await expect(u.codeLocator).toHaveText(codeStr)
|
||||||
|
|
||||||
// exit the sketch, reset relative clicker
|
// exit the sketch, reset relative clicker
|
||||||
@ -709,8 +709,10 @@ test.describe('Sketch tests', () => {
|
|||||||
codeStr += ` |> startProfileAt([2.03, 0], %)`
|
codeStr += ` |> startProfileAt([2.03, 0], %)`
|
||||||
await expect(u.codeLocator).toHaveText(codeStr)
|
await expect(u.codeLocator).toHaveText(codeStr)
|
||||||
|
|
||||||
|
// TODO: I couldn't use `toSU` here because of some rounding error causing
|
||||||
|
// it to be off by 0.01
|
||||||
await click00r(30, 0)
|
await click00r(30, 0)
|
||||||
codeStr += ` |> line([2.04, 0], %)`
|
codeStr += ` |> lineTo([4.07, 0], %)`
|
||||||
await expect(u.codeLocator).toHaveText(codeStr)
|
await expect(u.codeLocator).toHaveText(codeStr)
|
||||||
|
|
||||||
await click00r(0, 30)
|
await click00r(0, 30)
|
||||||
|
Before Width: | Height: | Size: 52 KiB After Width: | Height: | Size: 52 KiB |
Before Width: | Height: | Size: 49 KiB After Width: | Height: | Size: 49 KiB |
Before Width: | Height: | Size: 49 KiB After Width: | Height: | Size: 49 KiB |
Before Width: | Height: | Size: 46 KiB After Width: | Height: | Size: 46 KiB |
Before Width: | Height: | Size: 55 KiB After Width: | Height: | Size: 55 KiB |
Before Width: | Height: | Size: 50 KiB After Width: | Height: | Size: 51 KiB |
Before Width: | Height: | Size: 47 KiB After Width: | Height: | Size: 47 KiB |
Before Width: | Height: | Size: 44 KiB After Width: | Height: | Size: 44 KiB |
Before Width: | Height: | Size: 65 KiB After Width: | Height: | Size: 64 KiB |
Before Width: | Height: | Size: 39 KiB After Width: | Height: | Size: 39 KiB |
@ -219,7 +219,7 @@ test.describe('Test network and connection issues', () => {
|
|||||||
|> startProfileAt([12.34, -12.34], %)
|
|> startProfileAt([12.34, -12.34], %)
|
||||||
|> line([12.34, 0], %)
|
|> line([12.34, 0], %)
|
||||||
|> line([-12.34, 12.34], %)
|
|> line([-12.34, 12.34], %)
|
||||||
|> line([-12.34, 0], %)
|
|> lineTo([0, -12.34], %)
|
||||||
|
|
||||||
`)
|
`)
|
||||||
|
|
||||||
|
@ -45,7 +45,9 @@ export const commonPoints = {
|
|||||||
startAt: '[7.19, -9.7]',
|
startAt: '[7.19, -9.7]',
|
||||||
num1: 7.25,
|
num1: 7.25,
|
||||||
num2: 14.44,
|
num2: 14.44,
|
||||||
}
|
/** The Y-value of a common lineTo move we perform in tests */
|
||||||
|
num3: -2.44,
|
||||||
|
} as const
|
||||||
|
|
||||||
/** A semi-reliable color to check the default XZ plane on
|
/** A semi-reliable color to check the default XZ plane on
|
||||||
* in dark mode in the default camera position
|
* in dark mode in the default camera position
|
||||||
@ -118,15 +120,32 @@ async function waitForDefaultPlanesToBeVisible(page: Page) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
async function openPane(page: Page, testId: string) {
|
export async function checkIfPaneIsOpen(page: Page, testId: string) {
|
||||||
const locator = page.getByTestId(testId)
|
const paneButtonLocator = page.getByTestId(testId)
|
||||||
await expect(locator).toBeVisible()
|
await expect(paneButtonLocator).toBeVisible()
|
||||||
const isOpen = (await locator?.getAttribute('aria-pressed')) === 'true'
|
return (await paneButtonLocator?.getAttribute('aria-pressed')) === 'true'
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function openPane(page: Page, testId: string) {
|
||||||
|
const paneButtonLocator = page.getByTestId(testId)
|
||||||
|
await expect(paneButtonLocator).toBeVisible()
|
||||||
|
const isOpen = await checkIfPaneIsOpen(page, testId)
|
||||||
|
|
||||||
if (!isOpen) {
|
if (!isOpen) {
|
||||||
await locator.click()
|
await paneButtonLocator.click()
|
||||||
await expect(locator).toHaveAttribute('aria-pressed', 'true')
|
|
||||||
}
|
}
|
||||||
|
await expect(paneButtonLocator).toHaveAttribute('aria-pressed', 'true')
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function closePane(page: Page, testId: string) {
|
||||||
|
const paneButtonLocator = page.getByTestId(testId)
|
||||||
|
await expect(paneButtonLocator).toBeVisible()
|
||||||
|
const isOpen = await checkIfPaneIsOpen(page, testId)
|
||||||
|
|
||||||
|
if (isOpen) {
|
||||||
|
await paneButtonLocator.click()
|
||||||
|
}
|
||||||
|
await expect(paneButtonLocator).toHaveAttribute('aria-pressed', 'false')
|
||||||
}
|
}
|
||||||
|
|
||||||
async function openKclCodePanel(page: Page) {
|
async function openKclCodePanel(page: Page) {
|
||||||
@ -467,20 +486,6 @@ export async function getUtils(page: Page, test_?: typeof test) {
|
|||||||
return text.replace(/\s+/g, '')
|
return text.replace(/\s+/g, '')
|
||||||
},
|
},
|
||||||
|
|
||||||
createAndSelectProject: async (hasText: string) => {
|
|
||||||
return test_?.step(
|
|
||||||
`Create and select project with text "${hasText}"`,
|
|
||||||
async () => {
|
|
||||||
// Without this, we get unreliable project creation. It's probably
|
|
||||||
// due to a race between the FS being read and clicking doing something.
|
|
||||||
await page.waitForTimeout(100)
|
|
||||||
await page.getByTestId('home-new-file').click()
|
|
||||||
const projectLinksPost = page.getByTestId('project-link')
|
|
||||||
await projectLinksPost.filter({ hasText }).click()
|
|
||||||
}
|
|
||||||
)
|
|
||||||
},
|
|
||||||
|
|
||||||
editorTextMatches: async (code: string) => {
|
editorTextMatches: async (code: string) => {
|
||||||
const editor = page.locator(editorSelector)
|
const editor = page.locator(editorSelector)
|
||||||
return expect(editor).toHaveText(code, { useInnerText: true })
|
return expect(editor).toHaveText(code, { useInnerText: true })
|
||||||
@ -520,6 +525,9 @@ export async function getUtils(page: Page, test_?: typeof test) {
|
|||||||
.locator('[data-testid="file-pane-scroll-container"] button')
|
.locator('[data-testid="file-pane-scroll-container"] button')
|
||||||
.filter({ hasText: name })
|
.filter({ hasText: name })
|
||||||
.click()
|
.click()
|
||||||
|
await expect(page.getByTestId('project-sidebar-toggle')).toContainText(
|
||||||
|
name
|
||||||
|
)
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -980,30 +988,25 @@ export async function isOutOfViewInScrollContainer(
|
|||||||
return isOutOfView
|
return isOutOfView
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function createProjectAndRenameIt({
|
export async function createProject({
|
||||||
name,
|
name,
|
||||||
page,
|
page,
|
||||||
|
returnHome = false,
|
||||||
}: {
|
}: {
|
||||||
name: string
|
name: string
|
||||||
page: Page
|
page: Page
|
||||||
|
returnHome?: boolean
|
||||||
}) {
|
}) {
|
||||||
|
await test.step(`Create project and navigate to it`, async () => {
|
||||||
await page.getByRole('button', { name: 'New project' }).click()
|
await page.getByRole('button', { name: 'New project' }).click()
|
||||||
await expect(page.getByText('Successfully created')).toBeVisible()
|
await page.getByRole('textbox', { name: 'Name' }).fill(name)
|
||||||
await expect(page.getByText('Successfully created')).not.toBeVisible()
|
await page.getByRole('button', { name: 'Continue' }).click()
|
||||||
|
|
||||||
await expect(page.getByText(`project-000`)).toBeVisible()
|
if (returnHome) {
|
||||||
await page.getByText(`project-000`).hover()
|
await page.waitForURL('**/file/**', { waitUntil: 'domcontentloaded' })
|
||||||
await page.getByText(`project-000`).focus()
|
await page.getByTestId('app-logo').click()
|
||||||
|
}
|
||||||
await page.getByLabel('sketch').first().click()
|
})
|
||||||
|
|
||||||
await page.waitForTimeout(100)
|
|
||||||
|
|
||||||
// type the name passed in
|
|
||||||
await page.keyboard.press('Backspace')
|
|
||||||
await page.keyboard.type(name)
|
|
||||||
|
|
||||||
await page.getByLabel('checkmark').last().click()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function executorInputPath(fileName: string): string {
|
export function executorInputPath(fileName: string): string {
|
||||||
|
@ -96,7 +96,7 @@ test.describe('Testing selections', () => {
|
|||||||
|> startProfileAt(${commonPoints.startAt}, %)
|
|> startProfileAt(${commonPoints.startAt}, %)
|
||||||
|> line([${commonPoints.num1}, 0], %)
|
|> line([${commonPoints.num1}, 0], %)
|
||||||
|> line([0, ${commonPoints.num1 + 0.01}], %)
|
|> line([0, ${commonPoints.num1 + 0.01}], %)
|
||||||
|> line([-${commonPoints.num2}, 0], %)`)
|
|> lineTo([0, ${commonPoints.num3}], %)`)
|
||||||
|
|
||||||
// deselect line tool
|
// deselect line tool
|
||||||
await page.getByRole('button', { name: 'line Line', exact: true }).click()
|
await page.getByRole('button', { name: 'line Line', exact: true }).click()
|
||||||
@ -157,7 +157,9 @@ test.describe('Testing selections', () => {
|
|||||||
await emptySpaceClick()
|
await emptySpaceClick()
|
||||||
|
|
||||||
// check the same selection again by putting cursor in code first then selecting axis
|
// check the same selection again by putting cursor in code first then selecting axis
|
||||||
await page.getByText(` |> line([-${commonPoints.num2}, 0], %)`).click()
|
await page
|
||||||
|
.getByText(` |> lineTo([0, ${commonPoints.num3}], %)`)
|
||||||
|
.click()
|
||||||
await page.keyboard.down('Shift')
|
await page.keyboard.down('Shift')
|
||||||
await constrainButton.click()
|
await constrainButton.click()
|
||||||
await expect(absYButton).toBeDisabled()
|
await expect(absYButton).toBeDisabled()
|
||||||
@ -180,7 +182,9 @@ test.describe('Testing selections', () => {
|
|||||||
process.platform === 'linux' ? 'Control' : 'Meta'
|
process.platform === 'linux' ? 'Control' : 'Meta'
|
||||||
)
|
)
|
||||||
await page.waitForTimeout(100)
|
await page.waitForTimeout(100)
|
||||||
await page.getByText(` |> line([-${commonPoints.num2}, 0], %)`).click()
|
await page
|
||||||
|
.getByText(` |> lineTo([0, ${commonPoints.num3}], %)`)
|
||||||
|
.click()
|
||||||
|
|
||||||
await expect(page.locator('.cm-cursor')).toHaveCount(2)
|
await expect(page.locator('.cm-cursor')).toHaveCount(2)
|
||||||
await page.waitForTimeout(500)
|
await page.waitForTimeout(500)
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import { test, expect } from '@playwright/test'
|
import { test, expect } from '@playwright/test'
|
||||||
import * as fsp from 'fs/promises'
|
import * as fsp from 'fs/promises'
|
||||||
|
import * as fs from 'fs'
|
||||||
import { join } from 'path'
|
import { join } from 'path'
|
||||||
import {
|
import {
|
||||||
getUtils,
|
getUtils,
|
||||||
@ -7,6 +8,7 @@ import {
|
|||||||
setupElectron,
|
setupElectron,
|
||||||
tearDown,
|
tearDown,
|
||||||
executorInputPath,
|
executorInputPath,
|
||||||
|
createProject,
|
||||||
} from './test-utils'
|
} from './test-utils'
|
||||||
import { SaveSettingsPayload, SettingsLevel } from 'lib/settings/settingsTypes'
|
import { SaveSettingsPayload, SettingsLevel } from 'lib/settings/settingsTypes'
|
||||||
import { SETTINGS_FILE_NAME, PROJECT_SETTINGS_FILE_NAME } from 'lib/constants'
|
import { SETTINGS_FILE_NAME, PROJECT_SETTINGS_FILE_NAME } from 'lib/constants'
|
||||||
@ -265,10 +267,15 @@ test.describe('Testing settings', () => {
|
|||||||
process.platform === 'win32',
|
process.platform === 'win32',
|
||||||
'TODO: remove this skip https://github.com/KittyCAD/modeling-app/issues/3557'
|
'TODO: remove this skip https://github.com/KittyCAD/modeling-app/issues/3557'
|
||||||
)
|
)
|
||||||
const { electronApp, page } = await setupElectron({
|
const projectName = 'bracket'
|
||||||
|
const {
|
||||||
|
electronApp,
|
||||||
|
page,
|
||||||
|
dir: projectDirName,
|
||||||
|
} = await setupElectron({
|
||||||
testInfo,
|
testInfo,
|
||||||
folderSetupFn: async (dir) => {
|
folderSetupFn: async (dir) => {
|
||||||
const bracketDir = join(dir, 'bracket')
|
const bracketDir = join(dir, projectName)
|
||||||
await fsp.mkdir(bracketDir, { recursive: true })
|
await fsp.mkdir(bracketDir, { recursive: true })
|
||||||
await fsp.copyFile(
|
await fsp.copyFile(
|
||||||
executorInputPath('focusrite_scarlett_mounting_braket.kcl'),
|
executorInputPath('focusrite_scarlett_mounting_braket.kcl'),
|
||||||
@ -280,6 +287,12 @@ test.describe('Testing settings', () => {
|
|||||||
await page.setViewportSize({ width: 1200, height: 500 })
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
|
|
||||||
// Selectors and constants
|
// Selectors and constants
|
||||||
|
const tempProjectSettingsFilePath = join(
|
||||||
|
projectDirName,
|
||||||
|
projectName,
|
||||||
|
PROJECT_SETTINGS_FILE_NAME
|
||||||
|
)
|
||||||
|
const tempUserSettingsFilePath = join(projectDirName, SETTINGS_FILE_NAME)
|
||||||
const userThemeColor = '120'
|
const userThemeColor = '120'
|
||||||
const projectThemeColor = '50'
|
const projectThemeColor = '50'
|
||||||
const settingsOpenButton = page.getByRole('link', {
|
const settingsOpenButton = page.getByRole('link', {
|
||||||
@ -292,6 +305,21 @@ test.describe('Testing settings', () => {
|
|||||||
const projectLink = page.getByText('bracket')
|
const projectLink = page.getByText('bracket')
|
||||||
const logoLink = page.getByTestId('app-logo')
|
const logoLink = page.getByTestId('app-logo')
|
||||||
|
|
||||||
|
async function confirmThemeWasWritten(filePath: string, value: string) {
|
||||||
|
return expect
|
||||||
|
.poll(
|
||||||
|
async () => {
|
||||||
|
const fileExists = await fs.existsSync(filePath)
|
||||||
|
return fileExists ? fsp.readFile(filePath, 'utf-8') : ''
|
||||||
|
},
|
||||||
|
{
|
||||||
|
message: 'Setting should now be written to the file',
|
||||||
|
timeout: 5_000,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.toContain(`themeColor = "${value}"`)
|
||||||
|
}
|
||||||
|
|
||||||
await test.step('Set user theme color on home', async () => {
|
await test.step('Set user theme color on home', async () => {
|
||||||
await expect(settingsOpenButton).toBeVisible()
|
await expect(settingsOpenButton).toBeVisible()
|
||||||
await settingsOpenButton.click()
|
await settingsOpenButton.click()
|
||||||
@ -299,6 +327,7 @@ test.describe('Testing settings', () => {
|
|||||||
await expect(userSettingsTab).toBeChecked()
|
await expect(userSettingsTab).toBeChecked()
|
||||||
await themeColorSetting.fill(userThemeColor)
|
await themeColorSetting.fill(userThemeColor)
|
||||||
await expect(logoLink).toHaveCSS('--primary-hue', userThemeColor)
|
await expect(logoLink).toHaveCSS('--primary-hue', userThemeColor)
|
||||||
|
await confirmThemeWasWritten(tempUserSettingsFilePath, userThemeColor)
|
||||||
await settingsCloseButton.click()
|
await settingsCloseButton.click()
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -310,6 +339,11 @@ test.describe('Testing settings', () => {
|
|||||||
await expect(projectSettingsTab).toBeChecked()
|
await expect(projectSettingsTab).toBeChecked()
|
||||||
await themeColorSetting.fill(projectThemeColor)
|
await themeColorSetting.fill(projectThemeColor)
|
||||||
await expect(logoLink).toHaveCSS('--primary-hue', projectThemeColor)
|
await expect(logoLink).toHaveCSS('--primary-hue', projectThemeColor)
|
||||||
|
// Make sure that the project settings file has been written to before continuing
|
||||||
|
await confirmThemeWasWritten(
|
||||||
|
tempProjectSettingsFilePath,
|
||||||
|
projectThemeColor
|
||||||
|
)
|
||||||
await settingsCloseButton.click()
|
await settingsCloseButton.click()
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -323,6 +357,7 @@ test.describe('Testing settings', () => {
|
|||||||
|
|
||||||
await test.step(`Navigate back to the home view and see user setting applied`, async () => {
|
await test.step(`Navigate back to the home view and see user setting applied`, async () => {
|
||||||
await logoLink.click()
|
await logoLink.click()
|
||||||
|
await page.screenshot({ path: 'out.png' })
|
||||||
await expect(logoLink).toHaveCSS('--primary-hue', userThemeColor)
|
await expect(logoLink).toHaveCSS('--primary-hue', userThemeColor)
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -428,8 +463,7 @@ test.describe('Testing settings', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
await test.step('Check color of logo changed when in modeling view', async () => {
|
await test.step('Check color of logo changed when in modeling view', async () => {
|
||||||
await page.getByRole('button', { name: 'New project' }).click()
|
await createProject({ name: 'project-000', page })
|
||||||
await page.getByTestId('project-link').first().click()
|
|
||||||
await changeColor('58')
|
await changeColor('58')
|
||||||
await expect(logoLink).toHaveCSS('--primary-hue', '58')
|
await expect(logoLink).toHaveCSS('--primary-hue', '58')
|
||||||
})
|
})
|
||||||
@ -447,7 +481,7 @@ test.describe('Testing settings', () => {
|
|||||||
test(
|
test(
|
||||||
'project settings reload on external change',
|
'project settings reload on external change',
|
||||||
{ tag: '@electron' },
|
{ tag: '@electron' },
|
||||||
async ({ browserName }, testInfo) => {
|
async ({ browserName: _ }, testInfo) => {
|
||||||
const {
|
const {
|
||||||
electronApp,
|
electronApp,
|
||||||
page,
|
page,
|
||||||
@ -465,11 +499,7 @@ test.describe('Testing settings', () => {
|
|||||||
await expect(projectDirLink).toBeVisible()
|
await expect(projectDirLink).toBeVisible()
|
||||||
})
|
})
|
||||||
|
|
||||||
const projectLinks = page.getByTestId('project-link')
|
await createProject({ name: 'project-000', page })
|
||||||
const oldCount = await projectLinks.count()
|
|
||||||
await page.getByRole('button', { name: 'New project' }).click()
|
|
||||||
await expect(projectLinks).toHaveCount(oldCount + 1)
|
|
||||||
await projectLinks.filter({ hasText: 'project-000' }).first().click()
|
|
||||||
|
|
||||||
const changeColorFs = async (color: string) => {
|
const changeColorFs = async (color: string) => {
|
||||||
const tempSettingsFilePath = join(
|
const tempSettingsFilePath = join(
|
||||||
|
@ -1,5 +1,11 @@
|
|||||||
import { test, expect, Page } from '@playwright/test'
|
import { test, expect, Page } from '@playwright/test'
|
||||||
import { getUtils, setup, tearDown, setupElectron } from './test-utils'
|
import {
|
||||||
|
getUtils,
|
||||||
|
setup,
|
||||||
|
tearDown,
|
||||||
|
setupElectron,
|
||||||
|
createProject,
|
||||||
|
} from './test-utils'
|
||||||
import { join } from 'path'
|
import { join } from 'path'
|
||||||
import fs from 'fs'
|
import fs from 'fs'
|
||||||
|
|
||||||
@ -700,17 +706,17 @@ test(
|
|||||||
const fileExists = () =>
|
const fileExists = () =>
|
||||||
fs.existsSync(join(dir, projectName, textToCadFileName))
|
fs.existsSync(join(dir, projectName, textToCadFileName))
|
||||||
|
|
||||||
const {
|
const { openFilePanel, openKclCodePanel, waitForPageLoad } = await getUtils(
|
||||||
createAndSelectProject,
|
page,
|
||||||
openFilePanel,
|
test
|
||||||
openKclCodePanel,
|
)
|
||||||
waitForPageLoad,
|
|
||||||
} = await getUtils(page, test)
|
|
||||||
|
|
||||||
await page.setViewportSize({ width: 1200, height: 500 })
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
|
|
||||||
// Locators
|
// Locators
|
||||||
const projectMenuButton = page.getByRole('button', { name: projectName })
|
const projectMenuButton = page
|
||||||
|
.getByTestId('project-sidebar-toggle')
|
||||||
|
.filter({ hasText: projectName })
|
||||||
const textToCadFileButton = page.getByRole('listitem').filter({
|
const textToCadFileButton = page.getByRole('listitem').filter({
|
||||||
has: page.getByRole('button', { name: textToCadFileName }),
|
has: page.getByRole('button', { name: textToCadFileName }),
|
||||||
})
|
})
|
||||||
@ -719,7 +725,7 @@ test(
|
|||||||
)
|
)
|
||||||
|
|
||||||
// Create and navigate to the project
|
// Create and navigate to the project
|
||||||
await createAndSelectProject('project-000')
|
await createProject({ name: 'project-000', page })
|
||||||
|
|
||||||
// Wait for Start Sketch otherwise you will not have access Text-to-CAD command
|
// Wait for Start Sketch otherwise you will not have access Text-to-CAD command
|
||||||
await waitForPageLoad()
|
await waitForPageLoad()
|
||||||
|
@ -56,7 +56,7 @@
|
|||||||
"react-json-view": "^1.21.3",
|
"react-json-view": "^1.21.3",
|
||||||
"react-modal": "^3.16.1",
|
"react-modal": "^3.16.1",
|
||||||
"react-modal-promise": "^1.0.2",
|
"react-modal-promise": "^1.0.2",
|
||||||
"react-router-dom": "^6.26.1",
|
"react-router-dom": "^6.27.0",
|
||||||
"sketch-helpers": "^0.0.4",
|
"sketch-helpers": "^0.0.4",
|
||||||
"three": "^0.166.1",
|
"three": "^0.166.1",
|
||||||
"ua-parser-js": "^1.0.37",
|
"ua-parser-js": "^1.0.37",
|
||||||
@ -76,7 +76,7 @@
|
|||||||
"build:both": "vite build",
|
"build:both": "vite build",
|
||||||
"build:both:local": "yarn build:wasm && vite build",
|
"build:both:local": "yarn build:wasm && vite build",
|
||||||
"pretest": "yarn remove-importmeta",
|
"pretest": "yarn remove-importmeta",
|
||||||
"test:rust": "(cd src/wasm-lib && cargo test --all && cargo clippy --all --tests --benches)",
|
"test:rust": "(cd src/wasm-lib && cargo test --workspace && cargo clippy --workspace --all-targets)",
|
||||||
"simpleserver": "yarn pretest && http-server ./public --cors -p 3000",
|
"simpleserver": "yarn pretest && http-server ./public --cors -p 3000",
|
||||||
"simpleserver:ci": "yarn pretest && http-server ./public --cors -p 3000 &",
|
"simpleserver:ci": "yarn pretest && http-server ./public --cors -p 3000 &",
|
||||||
"simpleserver:bg": "yarn pretest && http-server ./public --cors -p 3000 &",
|
"simpleserver:bg": "yarn pretest && http-server ./public --cors -p 3000 &",
|
||||||
|
@ -34,6 +34,11 @@
|
|||||||
"title": "Car Wheel Assembly",
|
"title": "Car Wheel Assembly",
|
||||||
"description": "A car wheel assembly with a rotor, tire, and lug nuts."
|
"description": "A car wheel assembly with a rotor, tire, and lug nuts."
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"file": "dodecahedron.kcl",
|
||||||
|
"title": "Hollow Dodecahedron",
|
||||||
|
"description": "A regular dodecahedron or pentagonal dodecahedron is a dodecahedron composed of regular pentagonal faces, three meeting at each vertex. This example shows constructing the individual faces of the dodecahedron and extruding inwards."
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"file": "enclosure.kcl",
|
"file": "enclosure.kcl",
|
||||||
"title": "Enclosure",
|
"title": "Enclosure",
|
||||||
@ -54,6 +59,11 @@
|
|||||||
"title": "A mounting bracket for the Focusrite Scarlett Solo audio interface",
|
"title": "A mounting bracket for the Focusrite Scarlett Solo audio interface",
|
||||||
"description": "This is a bracket that holds an audio device underneath a desk or shelf. The audio device has dimensions of 144mm wide, 80mm length and 45mm depth with fillets of 6mm. This mounting bracket is designed to be 3D printed with PLA material"
|
"description": "This is a bracket that holds an audio device underneath a desk or shelf. The audio device has dimensions of 144mm wide, 80mm length and 45mm depth with fillets of 6mm. This mounting bracket is designed to be 3D printed with PLA material"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"file": "food-service-spatula.kcl",
|
||||||
|
"title": "Food Service Spatula",
|
||||||
|
"description": "Use these spatulas for mixing, flipping, and scraping."
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"file": "french-press.kcl",
|
"file": "french-press.kcl",
|
||||||
"title": "French Press",
|
"title": "French Press",
|
||||||
@ -61,7 +71,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"file": "gear.kcl",
|
"file": "gear.kcl",
|
||||||
"title": "Gear",
|
"title": "Spur Gear",
|
||||||
"description": "A rotating machine part having cut teeth or, in the case of a cogwheel, inserted teeth (called cogs), which mesh with another toothed part to transmit torque. Geared devices can change the speed, torque, and direction of a power source. The two elements that define a gear are its circular shape and the teeth that are integrated into its outer edge, which are designed to fit into the teeth of another gear."
|
"description": "A rotating machine part having cut teeth or, in the case of a cogwheel, inserted teeth (called cogs), which mesh with another toothed part to transmit torque. Geared devices can change the speed, torque, and direction of a power source. The two elements that define a gear are its circular shape and the teeth that are integrated into its outer edge, which are designed to fit into the teeth of another gear."
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
0
release-notes.md
Normal file
@ -43,6 +43,7 @@ import { coreDump } from 'lang/wasm'
|
|||||||
import { useMemo } from 'react'
|
import { useMemo } from 'react'
|
||||||
import { AppStateProvider } from 'AppState'
|
import { AppStateProvider } from 'AppState'
|
||||||
import { reportRejection } from 'lib/trap'
|
import { reportRejection } from 'lib/trap'
|
||||||
|
import { ProjectsContextProvider } from 'components/ProjectsContextProvider'
|
||||||
|
|
||||||
const createRouter = isDesktop() ? createHashRouter : createBrowserRouter
|
const createRouter = isDesktop() ? createHashRouter : createBrowserRouter
|
||||||
|
|
||||||
@ -57,6 +58,7 @@ const router = createRouter([
|
|||||||
<CommandBarProvider>
|
<CommandBarProvider>
|
||||||
<SettingsAuthProvider>
|
<SettingsAuthProvider>
|
||||||
<LspProvider>
|
<LspProvider>
|
||||||
|
<ProjectsContextProvider>
|
||||||
<KclContextProvider>
|
<KclContextProvider>
|
||||||
<AppStateProvider>
|
<AppStateProvider>
|
||||||
<MachineManagerProvider>
|
<MachineManagerProvider>
|
||||||
@ -64,6 +66,7 @@ const router = createRouter([
|
|||||||
</MachineManagerProvider>
|
</MachineManagerProvider>
|
||||||
</AppStateProvider>
|
</AppStateProvider>
|
||||||
</KclContextProvider>
|
</KclContextProvider>
|
||||||
|
</ProjectsContextProvider>
|
||||||
</LspProvider>
|
</LspProvider>
|
||||||
</SettingsAuthProvider>
|
</SettingsAuthProvider>
|
||||||
</CommandBarProvider>
|
</CommandBarProvider>
|
||||||
|
@ -100,6 +100,11 @@ export function Toolbar({
|
|||||||
function resolveItemConfig(
|
function resolveItemConfig(
|
||||||
maybeIconConfig: ToolbarItem
|
maybeIconConfig: ToolbarItem
|
||||||
): ToolbarItemResolved {
|
): ToolbarItemResolved {
|
||||||
|
const isDisabled =
|
||||||
|
disableAllButtons ||
|
||||||
|
maybeIconConfig.status !== 'available' ||
|
||||||
|
maybeIconConfig.disabled?.(state) === true
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...maybeIconConfig,
|
...maybeIconConfig,
|
||||||
title:
|
title:
|
||||||
@ -113,10 +118,11 @@ export function Toolbar({
|
|||||||
typeof maybeIconConfig.hotkey === 'string'
|
typeof maybeIconConfig.hotkey === 'string'
|
||||||
? maybeIconConfig.hotkey
|
? maybeIconConfig.hotkey
|
||||||
: maybeIconConfig.hotkey?.(state),
|
: maybeIconConfig.hotkey?.(state),
|
||||||
disabled:
|
disabled: isDisabled,
|
||||||
disableAllButtons ||
|
disabledReason:
|
||||||
maybeIconConfig.status !== 'available' ||
|
typeof maybeIconConfig.disabledReason === 'function'
|
||||||
maybeIconConfig.disabled?.(state) === true,
|
? maybeIconConfig.disabledReason(state)
|
||||||
|
: maybeIconConfig.disabledReason,
|
||||||
disableHotkey: maybeIconConfig.disableHotkey?.(state),
|
disableHotkey: maybeIconConfig.disableHotkey?.(state),
|
||||||
status: maybeIconConfig.status,
|
status: maybeIconConfig.status,
|
||||||
}
|
}
|
||||||
@ -273,6 +279,8 @@ const ToolbarItemTooltip = memo(function ToolbarItemContents({
|
|||||||
itemConfig: ToolbarItemResolved
|
itemConfig: ToolbarItemResolved
|
||||||
configCallbackProps: ToolbarItemCallbackProps
|
configCallbackProps: ToolbarItemCallbackProps
|
||||||
}) {
|
}) {
|
||||||
|
const { state } = useModelingContext()
|
||||||
|
|
||||||
useHotkeys(
|
useHotkeys(
|
||||||
itemConfig.hotkey || '',
|
itemConfig.hotkey || '',
|
||||||
() => {
|
() => {
|
||||||
@ -336,6 +344,17 @@ const ToolbarItemTooltip = memo(function ToolbarItemContents({
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<p className="px-2 text-ch font-sans">{itemConfig.description}</p>
|
<p className="px-2 text-ch font-sans">{itemConfig.description}</p>
|
||||||
|
{/* Add disabled reason if item is disabled */}
|
||||||
|
{itemConfig.disabled && itemConfig.disabledReason && (
|
||||||
|
<>
|
||||||
|
<hr className="border-chalkboard-20 dark:border-chalkboard-80" />
|
||||||
|
<p className="px-2 text-ch font-sans text-chalkboard-70 dark:text-chalkboard-40">
|
||||||
|
{typeof itemConfig.disabledReason === 'function'
|
||||||
|
? itemConfig.disabledReason(state)
|
||||||
|
: itemConfig.disabledReason}
|
||||||
|
</p>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
{itemConfig.links.length > 0 && (
|
{itemConfig.links.length > 0 && (
|
||||||
<>
|
<>
|
||||||
<hr className="border-chalkboard-20 dark:border-chalkboard-80" />
|
<hr className="border-chalkboard-20 dark:border-chalkboard-80" />
|
||||||
|
@ -44,6 +44,7 @@ import {
|
|||||||
import { ActionButton } from 'components/ActionButton'
|
import { ActionButton } from 'components/ActionButton'
|
||||||
import { err, reportRejection, trap } from 'lib/trap'
|
import { err, reportRejection, trap } from 'lib/trap'
|
||||||
import { useCommandsContext } from 'hooks/useCommandsContext'
|
import { useCommandsContext } from 'hooks/useCommandsContext'
|
||||||
|
import { Node } from 'wasm-lib/kcl/bindings/Node'
|
||||||
|
|
||||||
function useShouldHideScene(): { hideClient: boolean; hideServer: boolean } {
|
function useShouldHideScene(): { hideClient: boolean; hideServer: boolean } {
|
||||||
const [isCamMoving, setIsCamMoving] = useState(false)
|
const [isCamMoving, setIsCamMoving] = useState(false)
|
||||||
@ -201,7 +202,7 @@ const Overlay = ({
|
|||||||
let xAlignment = overlay.angle < 0 ? '0%' : '-100%'
|
let xAlignment = overlay.angle < 0 ? '0%' : '-100%'
|
||||||
let yAlignment = overlay.angle < -90 || overlay.angle >= 90 ? '0%' : '-100%'
|
let yAlignment = overlay.angle < -90 || overlay.angle >= 90 ? '0%' : '-100%'
|
||||||
|
|
||||||
const _node1 = getNodeFromPath<CallExpression>(
|
const _node1 = getNodeFromPath<Node<CallExpression>>(
|
||||||
kclManager.ast,
|
kclManager.ast,
|
||||||
overlay.pathToNode,
|
overlay.pathToNode,
|
||||||
'CallExpression'
|
'CallExpression'
|
||||||
@ -381,7 +382,7 @@ export async function deleteSegment({
|
|||||||
pathToNode: PathToNode
|
pathToNode: PathToNode
|
||||||
sketchDetails: SketchDetails | null
|
sketchDetails: SketchDetails | null
|
||||||
}) {
|
}) {
|
||||||
let modifiedAst: Program | Error = kclManager.ast
|
let modifiedAst: Node<Program> | Error = kclManager.ast
|
||||||
const dependentRanges = findUsesOfTagInPipe(modifiedAst, pathToNode)
|
const dependentRanges = findUsesOfTagInPipe(modifiedAst, pathToNode)
|
||||||
|
|
||||||
const shouldContinueSegDelete = dependentRanges.length
|
const shouldContinueSegDelete = dependentRanges.length
|
||||||
|
@ -19,6 +19,8 @@ import {
|
|||||||
import {
|
import {
|
||||||
ARROWHEAD,
|
ARROWHEAD,
|
||||||
AXIS_GROUP,
|
AXIS_GROUP,
|
||||||
|
DRAFT_POINT,
|
||||||
|
DRAFT_POINT_GROUP,
|
||||||
getSceneScale,
|
getSceneScale,
|
||||||
INTERSECTION_PLANE_LAYER,
|
INTERSECTION_PLANE_LAYER,
|
||||||
OnClickCallbackArgs,
|
OnClickCallbackArgs,
|
||||||
@ -53,7 +55,7 @@ import {
|
|||||||
editorManager,
|
editorManager,
|
||||||
} from 'lib/singletons'
|
} from 'lib/singletons'
|
||||||
import { getNodeFromPath, getNodePathFromSourceRange } from 'lang/queryAst'
|
import { getNodeFromPath, getNodePathFromSourceRange } from 'lang/queryAst'
|
||||||
import { executeAst } from 'lang/langHelpers'
|
import { executeAst, ToolTip } from 'lang/langHelpers'
|
||||||
import {
|
import {
|
||||||
createProfileStartHandle,
|
createProfileStartHandle,
|
||||||
SegmentUtils,
|
SegmentUtils,
|
||||||
@ -92,6 +94,7 @@ import { err, reportRejection, trap } from 'lib/trap'
|
|||||||
import { CSS2DObject } from 'three/examples/jsm/renderers/CSS2DRenderer'
|
import { CSS2DObject } from 'three/examples/jsm/renderers/CSS2DRenderer'
|
||||||
import { Point3d } from 'wasm-lib/kcl/bindings/Point3d'
|
import { Point3d } from 'wasm-lib/kcl/bindings/Point3d'
|
||||||
import { SegmentInputs } from 'lang/std/stdTypes'
|
import { SegmentInputs } from 'lang/std/stdTypes'
|
||||||
|
import { Node } from 'wasm-lib/kcl/bindings/Node'
|
||||||
|
|
||||||
type DraftSegment = 'line' | 'tangentialArcTo'
|
type DraftSegment = 'line' | 'tangentialArcTo'
|
||||||
|
|
||||||
@ -313,6 +316,27 @@ export class SceneEntities {
|
|||||||
const intersectionPlane = this.scene.getObjectByName(RAYCASTABLE_PLANE)
|
const intersectionPlane = this.scene.getObjectByName(RAYCASTABLE_PLANE)
|
||||||
if (intersectionPlane) this.scene.remove(intersectionPlane)
|
if (intersectionPlane) this.scene.remove(intersectionPlane)
|
||||||
}
|
}
|
||||||
|
getDraftPoint() {
|
||||||
|
return this.scene.getObjectByName(DRAFT_POINT)
|
||||||
|
}
|
||||||
|
createDraftPoint({ point, group }: { point: Vector2; group: Group }) {
|
||||||
|
const dummy = new Mesh()
|
||||||
|
dummy.position.set(0, 0, 0)
|
||||||
|
const scale = sceneInfra.getClientSceneScaleFactor(dummy)
|
||||||
|
|
||||||
|
const draftPoint = createProfileStartHandle({
|
||||||
|
isDraft: true,
|
||||||
|
from: [point.x, point.y],
|
||||||
|
scale,
|
||||||
|
theme: sceneInfra._theme,
|
||||||
|
})
|
||||||
|
draftPoint.layers.set(SKETCH_LAYER)
|
||||||
|
group.add(draftPoint)
|
||||||
|
}
|
||||||
|
removeDraftPoint() {
|
||||||
|
const draftPoint = this.getDraftPoint()
|
||||||
|
if (draftPoint) draftPoint.removeFromParent()
|
||||||
|
}
|
||||||
|
|
||||||
setupNoPointsListener({
|
setupNoPointsListener({
|
||||||
sketchDetails,
|
sketchDetails,
|
||||||
@ -321,22 +345,78 @@ export class SceneEntities {
|
|||||||
sketchDetails: SketchDetails
|
sketchDetails: SketchDetails
|
||||||
afterClick: (args: OnClickCallbackArgs) => void
|
afterClick: (args: OnClickCallbackArgs) => void
|
||||||
}) {
|
}) {
|
||||||
// Create a THREEjs plane to raycast clicks onto
|
// TODO: Consolidate shared logic between this and setupSketch
|
||||||
|
// Which should just fire when the sketch mode is entered,
|
||||||
|
// instead of in these two separate XState states.
|
||||||
this.createIntersectionPlane()
|
this.createIntersectionPlane()
|
||||||
|
const draftPointGroup = new Group()
|
||||||
|
draftPointGroup.name = DRAFT_POINT_GROUP
|
||||||
|
sketchDetails.origin &&
|
||||||
|
draftPointGroup.position.set(...sketchDetails.origin)
|
||||||
|
if (!(sketchDetails.yAxis && sketchDetails)) {
|
||||||
|
console.error('No sketch quaternion or sketch details found')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.currentSketchQuaternion = quaternionFromUpNForward(
|
||||||
|
new Vector3(...sketchDetails.yAxis),
|
||||||
|
new Vector3(...sketchDetails.zAxis)
|
||||||
|
)
|
||||||
|
draftPointGroup.setRotationFromQuaternion(this.currentSketchQuaternion)
|
||||||
|
this.scene.add(draftPointGroup)
|
||||||
|
|
||||||
const quaternion = quaternionFromUpNForward(
|
const quaternion = quaternionFromUpNForward(
|
||||||
new Vector3(...sketchDetails.yAxis),
|
new Vector3(...sketchDetails.yAxis),
|
||||||
new Vector3(...sketchDetails.zAxis)
|
new Vector3(...sketchDetails.zAxis)
|
||||||
)
|
)
|
||||||
|
|
||||||
// Position the click raycast plane
|
// Position the click raycast plane
|
||||||
if (this.intersectionPlane) {
|
this.intersectionPlane!.setRotationFromQuaternion(quaternion)
|
||||||
this.intersectionPlane.setRotationFromQuaternion(quaternion)
|
this.intersectionPlane!.position.copy(
|
||||||
this.intersectionPlane.position.copy(
|
|
||||||
new Vector3(...(sketchDetails?.origin || [0, 0, 0]))
|
new Vector3(...(sketchDetails?.origin || [0, 0, 0]))
|
||||||
)
|
)
|
||||||
}
|
|
||||||
sceneInfra.setCallbacks({
|
sceneInfra.setCallbacks({
|
||||||
|
onMove: (args) => {
|
||||||
|
if (!args.intersects.length) return
|
||||||
|
const axisIntersection = args.intersects.find(
|
||||||
|
(sceneObject) =>
|
||||||
|
sceneObject.object.name === X_AXIS ||
|
||||||
|
sceneObject.object.name === Y_AXIS
|
||||||
|
)
|
||||||
|
if (!axisIntersection) return
|
||||||
|
const { intersectionPoint } = args
|
||||||
|
// We're hovering over an axis, so we should show a draft point
|
||||||
|
const snappedPoint = intersectionPoint.twoD.clone()
|
||||||
|
if (axisIntersection.object.name === X_AXIS) {
|
||||||
|
snappedPoint.setComponent(1, 0)
|
||||||
|
} else {
|
||||||
|
snappedPoint.setComponent(0, 0)
|
||||||
|
}
|
||||||
|
// Either create a new one or update the existing one
|
||||||
|
const draftPoint = this.getDraftPoint()
|
||||||
|
|
||||||
|
if (!draftPoint) {
|
||||||
|
this.createDraftPoint({
|
||||||
|
point: snappedPoint,
|
||||||
|
group: draftPointGroup,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
// Ignore if there are huge jumps in the mouse position,
|
||||||
|
// that is likely a strange behavior
|
||||||
|
if (
|
||||||
|
draftPoint.position.distanceTo(
|
||||||
|
new Vector3(snappedPoint.x, snappedPoint.y, 0)
|
||||||
|
) > 100
|
||||||
|
) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
draftPoint.position.set(snappedPoint.x, snappedPoint.y, 0)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onMouseLeave: () => {
|
||||||
|
this.removeDraftPoint()
|
||||||
|
},
|
||||||
onClick: async (args) => {
|
onClick: async (args) => {
|
||||||
|
this.removeDraftPoint()
|
||||||
if (!args) return
|
if (!args) return
|
||||||
// If there is a valid camera interaction that matches, do that instead
|
// If there is a valid camera interaction that matches, do that instead
|
||||||
const interaction = sceneInfra.camControls.getInteractionType(
|
const interaction = sceneInfra.camControls.getInteractionType(
|
||||||
@ -346,10 +426,25 @@ export class SceneEntities {
|
|||||||
if (args.mouseEvent.which !== 1) return
|
if (args.mouseEvent.which !== 1) return
|
||||||
const { intersectionPoint } = args
|
const { intersectionPoint } = args
|
||||||
if (!intersectionPoint?.twoD || !sketchDetails?.sketchPathToNode) return
|
if (!intersectionPoint?.twoD || !sketchDetails?.sketchPathToNode) return
|
||||||
|
|
||||||
|
// Snap to either or both axes
|
||||||
|
// if the click intersects their meshes
|
||||||
|
const yAxisIntersection = args.intersects.find(
|
||||||
|
(sceneObject) => sceneObject.object.name === Y_AXIS
|
||||||
|
)
|
||||||
|
const xAxisIntersection = args.intersects.find(
|
||||||
|
(sceneObject) => sceneObject.object.name === X_AXIS
|
||||||
|
)
|
||||||
|
|
||||||
|
const snappedClickPoint = {
|
||||||
|
x: yAxisIntersection ? 0 : intersectionPoint.twoD.x,
|
||||||
|
y: xAxisIntersection ? 0 : intersectionPoint.twoD.y,
|
||||||
|
}
|
||||||
|
|
||||||
const addStartProfileAtRes = addStartProfileAt(
|
const addStartProfileAtRes = addStartProfileAt(
|
||||||
kclManager.ast,
|
kclManager.ast,
|
||||||
sketchDetails.sketchPathToNode,
|
sketchDetails.sketchPathToNode,
|
||||||
[intersectionPoint.twoD.x, intersectionPoint.twoD.y]
|
[snappedClickPoint.x, snappedClickPoint.y]
|
||||||
)
|
)
|
||||||
|
|
||||||
if (trap(addStartProfileAtRes)) return
|
if (trap(addStartProfileAtRes)) return
|
||||||
@ -357,6 +452,7 @@ export class SceneEntities {
|
|||||||
|
|
||||||
await kclManager.updateAst(modifiedAst, false)
|
await kclManager.updateAst(modifiedAst, false)
|
||||||
this.removeIntersectionPlane()
|
this.removeIntersectionPlane()
|
||||||
|
this.scene.remove(draftPointGroup)
|
||||||
|
|
||||||
// Now perform the caller-specified action
|
// Now perform the caller-specified action
|
||||||
afterClick(args)
|
afterClick(args)
|
||||||
@ -374,14 +470,14 @@ export class SceneEntities {
|
|||||||
selectionRanges,
|
selectionRanges,
|
||||||
}: {
|
}: {
|
||||||
sketchPathToNode: PathToNode
|
sketchPathToNode: PathToNode
|
||||||
maybeModdedAst: Program
|
maybeModdedAst: Node<Program>
|
||||||
draftExpressionsIndices?: { start: number; end: number }
|
draftExpressionsIndices?: { start: number; end: number }
|
||||||
forward: [number, number, number]
|
forward: [number, number, number]
|
||||||
up: [number, number, number]
|
up: [number, number, number]
|
||||||
position?: [number, number, number]
|
position?: [number, number, number]
|
||||||
selectionRanges?: Selections
|
selectionRanges?: Selections
|
||||||
}): Promise<{
|
}): Promise<{
|
||||||
truncatedAst: Program
|
truncatedAst: Node<Program>
|
||||||
programMemoryOverride: ProgramMemory
|
programMemoryOverride: ProgramMemory
|
||||||
sketch: Sketch
|
sketch: Sketch
|
||||||
variableDeclarationName: string
|
variableDeclarationName: string
|
||||||
@ -429,12 +525,7 @@ export class SceneEntities {
|
|||||||
const dummy = new Mesh()
|
const dummy = new Mesh()
|
||||||
// TODO: When we actually have sketch positions and rotations we can use them here.
|
// TODO: When we actually have sketch positions and rotations we can use them here.
|
||||||
dummy.position.set(0, 0, 0)
|
dummy.position.set(0, 0, 0)
|
||||||
const orthoFactor = orthoScale(sceneInfra.camControls.camera)
|
const scale = sceneInfra.getClientSceneScaleFactor(dummy)
|
||||||
const factor =
|
|
||||||
(sceneInfra.camControls.camera instanceof OrthographicCamera
|
|
||||||
? orthoFactor
|
|
||||||
: perspScale(sceneInfra.camControls.camera, dummy)) /
|
|
||||||
sceneInfra._baseUnitMultiplier
|
|
||||||
|
|
||||||
const segPathToNode = getNodePathFromSourceRange(
|
const segPathToNode = getNodePathFromSourceRange(
|
||||||
maybeModdedAst,
|
maybeModdedAst,
|
||||||
@ -445,8 +536,9 @@ export class SceneEntities {
|
|||||||
from: sketch.start.from,
|
from: sketch.start.from,
|
||||||
id: sketch.start.__geoMeta.id,
|
id: sketch.start.__geoMeta.id,
|
||||||
pathToNode: segPathToNode,
|
pathToNode: segPathToNode,
|
||||||
scale: factor,
|
scale,
|
||||||
theme: sceneInfra._theme,
|
theme: sceneInfra._theme,
|
||||||
|
isDraft: false,
|
||||||
})
|
})
|
||||||
_profileStart.layers.set(SKETCH_LAYER)
|
_profileStart.layers.set(SKETCH_LAYER)
|
||||||
_profileStart.traverse((child) => {
|
_profileStart.traverse((child) => {
|
||||||
@ -522,7 +614,7 @@ export class SceneEntities {
|
|||||||
id: segment.__geoMeta.id,
|
id: segment.__geoMeta.id,
|
||||||
pathToNode: segPathToNode,
|
pathToNode: segPathToNode,
|
||||||
isDraftSegment,
|
isDraftSegment,
|
||||||
scale: factor,
|
scale,
|
||||||
texture: sceneInfra.extraSegmentTexture,
|
texture: sceneInfra.extraSegmentTexture,
|
||||||
theme: sceneInfra._theme,
|
theme: sceneInfra._theme,
|
||||||
isSelected,
|
isSelected,
|
||||||
@ -566,7 +658,7 @@ export class SceneEntities {
|
|||||||
}
|
}
|
||||||
updateAstAndRejigSketch = async (
|
updateAstAndRejigSketch = async (
|
||||||
sketchPathToNode: PathToNode,
|
sketchPathToNode: PathToNode,
|
||||||
modifiedAst: Program | Error,
|
modifiedAst: Node<Program> | Error,
|
||||||
forward: [number, number, number],
|
forward: [number, number, number],
|
||||||
up: [number, number, number],
|
up: [number, number, number],
|
||||||
origin: [number, number, number]
|
origin: [number, number, number]
|
||||||
@ -659,12 +751,14 @@ export class SceneEntities {
|
|||||||
|
|
||||||
const { intersectionPoint } = args
|
const { intersectionPoint } = args
|
||||||
let intersection2d = intersectionPoint?.twoD
|
let intersection2d = intersectionPoint?.twoD
|
||||||
const profileStart = args.intersects
|
const intersectsProfileStart = args.intersects
|
||||||
.map(({ object }) => getParentGroup(object, [PROFILE_START]))
|
.map(({ object }) => getParentGroup(object, [PROFILE_START]))
|
||||||
.find((a) => a?.name === PROFILE_START)
|
.find((a) => a?.name === PROFILE_START)
|
||||||
|
|
||||||
let modifiedAst
|
let modifiedAst
|
||||||
if (profileStart) {
|
|
||||||
|
// Snapping logic for the profile start handle
|
||||||
|
if (intersectsProfileStart) {
|
||||||
const lastSegment = sketch.paths.slice(-1)[0]
|
const lastSegment = sketch.paths.slice(-1)[0]
|
||||||
modifiedAst = addCallExpressionsToPipe({
|
modifiedAst = addCallExpressionsToPipe({
|
||||||
node: kclManager.ast,
|
node: kclManager.ast,
|
||||||
@ -697,19 +791,39 @@ export class SceneEntities {
|
|||||||
})
|
})
|
||||||
if (trap(modifiedAst)) return Promise.reject(modifiedAst)
|
if (trap(modifiedAst)) return Promise.reject(modifiedAst)
|
||||||
} else if (intersection2d) {
|
} else if (intersection2d) {
|
||||||
|
const intersectsYAxis = args.intersects.find(
|
||||||
|
(sceneObject) => sceneObject.object.name === Y_AXIS
|
||||||
|
)
|
||||||
|
const intersectsXAxis = args.intersects.find(
|
||||||
|
(sceneObject) => sceneObject.object.name === X_AXIS
|
||||||
|
)
|
||||||
|
|
||||||
const lastSegment = sketch.paths.slice(-1)[0]
|
const lastSegment = sketch.paths.slice(-1)[0]
|
||||||
|
const snappedPoint = {
|
||||||
|
x: intersectsYAxis ? 0 : intersection2d.x,
|
||||||
|
y: intersectsXAxis ? 0 : intersection2d.y,
|
||||||
|
}
|
||||||
|
|
||||||
|
let resolvedFunctionName: ToolTip = 'line'
|
||||||
|
|
||||||
|
// This might need to become its own function if we want more
|
||||||
|
// case-based logic for different segment types
|
||||||
|
if (lastSegment.type === 'TangentialArcTo') {
|
||||||
|
resolvedFunctionName = 'tangentialArcTo'
|
||||||
|
} else if (snappedPoint.x === 0 || snappedPoint.y === 0) {
|
||||||
|
// We consider a point placed on axes or origin to be absolute
|
||||||
|
resolvedFunctionName = 'lineTo'
|
||||||
|
}
|
||||||
|
|
||||||
const tmp = addNewSketchLn({
|
const tmp = addNewSketchLn({
|
||||||
node: kclManager.ast,
|
node: kclManager.ast,
|
||||||
programMemory: kclManager.programMemory,
|
programMemory: kclManager.programMemory,
|
||||||
input: {
|
input: {
|
||||||
type: 'straight-segment',
|
type: 'straight-segment',
|
||||||
from: [lastSegment.to[0], lastSegment.to[1]],
|
from: [lastSegment.to[0], lastSegment.to[1]],
|
||||||
to: [intersection2d.x, intersection2d.y],
|
to: [snappedPoint.x, snappedPoint.y],
|
||||||
},
|
},
|
||||||
fnName:
|
fnName: resolvedFunctionName,
|
||||||
lastSegment.type === 'TangentialArcTo'
|
|
||||||
? 'tangentialArcTo'
|
|
||||||
: 'line',
|
|
||||||
pathToNode: sketchPathToNode,
|
pathToNode: sketchPathToNode,
|
||||||
})
|
})
|
||||||
if (trap(tmp)) return Promise.reject(tmp)
|
if (trap(tmp)) return Promise.reject(tmp)
|
||||||
@ -721,7 +835,7 @@ export class SceneEntities {
|
|||||||
}
|
}
|
||||||
|
|
||||||
await kclManager.executeAstMock(modifiedAst)
|
await kclManager.executeAstMock(modifiedAst)
|
||||||
if (profileStart) {
|
if (intersectsProfileStart) {
|
||||||
sceneInfra.modelingSend({ type: 'CancelSketch' })
|
sceneInfra.modelingSend({ type: 'CancelSketch' })
|
||||||
} else {
|
} else {
|
||||||
await this.setUpDraftSegment(
|
await this.setUpDraftSegment(
|
||||||
@ -1202,7 +1316,7 @@ export class SceneEntities {
|
|||||||
}
|
}
|
||||||
prepareTruncatedMemoryAndAst = (
|
prepareTruncatedMemoryAndAst = (
|
||||||
sketchPathToNode: PathToNode,
|
sketchPathToNode: PathToNode,
|
||||||
ast?: Program,
|
ast?: Node<Program>,
|
||||||
draftSegment?: DraftSegment
|
draftSegment?: DraftSegment
|
||||||
) =>
|
) =>
|
||||||
prepareTruncatedMemoryAndAst(
|
prepareTruncatedMemoryAndAst(
|
||||||
@ -1223,20 +1337,35 @@ export class SceneEntities {
|
|||||||
sketchPathToNode: PathToNode
|
sketchPathToNode: PathToNode
|
||||||
intersects: Intersection<Object3D<Object3DEventMap>>[]
|
intersects: Intersection<Object3D<Object3DEventMap>>[]
|
||||||
draftInfo?: {
|
draftInfo?: {
|
||||||
truncatedAst: Program
|
truncatedAst: Node<Program>
|
||||||
programMemoryOverride: ProgramMemory
|
programMemoryOverride: ProgramMemory
|
||||||
variableDeclarationName: string
|
variableDeclarationName: string
|
||||||
}
|
}
|
||||||
}) {
|
}) {
|
||||||
const profileStart =
|
const intersectsProfileStart =
|
||||||
draftInfo &&
|
draftInfo &&
|
||||||
intersects
|
intersects
|
||||||
.map(({ object }) => getParentGroup(object, [PROFILE_START]))
|
.map(({ object }) => getParentGroup(object, [PROFILE_START]))
|
||||||
.find((a) => a?.name === PROFILE_START)
|
.find((a) => a?.name === PROFILE_START)
|
||||||
const intersection2d = profileStart
|
const intersection2d = intersectsProfileStart
|
||||||
? new Vector2(profileStart.position.x, profileStart.position.y)
|
? new Vector2(
|
||||||
|
intersectsProfileStart.position.x,
|
||||||
|
intersectsProfileStart.position.y
|
||||||
|
)
|
||||||
: _intersection2d
|
: _intersection2d
|
||||||
|
|
||||||
|
const intersectsYAxis = intersects.find(
|
||||||
|
(sceneObject) => sceneObject.object.name === Y_AXIS
|
||||||
|
)
|
||||||
|
const intersectsXAxis = intersects.find(
|
||||||
|
(sceneObject) => sceneObject.object.name === X_AXIS
|
||||||
|
)
|
||||||
|
|
||||||
|
const snappedPoint = new Vector2(
|
||||||
|
intersectsYAxis ? 0 : intersection2d.x,
|
||||||
|
intersectsXAxis ? 0 : intersection2d.y
|
||||||
|
)
|
||||||
|
|
||||||
const group = getParentGroup(object, SEGMENT_BODIES_PLUS_PROFILE_START)
|
const group = getParentGroup(object, SEGMENT_BODIES_PLUS_PROFILE_START)
|
||||||
const subGroup = getParentGroup(object, [ARROWHEAD, CIRCLE_CENTER_HANDLE])
|
const subGroup = getParentGroup(object, [ARROWHEAD, CIRCLE_CENTER_HANDLE])
|
||||||
if (!group) return
|
if (!group) return
|
||||||
@ -1256,10 +1385,10 @@ export class SceneEntities {
|
|||||||
group.userData.from[0],
|
group.userData.from[0],
|
||||||
group.userData.from[1],
|
group.userData.from[1],
|
||||||
]
|
]
|
||||||
const dragTo: [number, number] = [intersection2d.x, intersection2d.y]
|
const dragTo: [number, number] = [snappedPoint.x, snappedPoint.y]
|
||||||
let modifiedAst = draftInfo ? draftInfo.truncatedAst : { ...kclManager.ast }
|
let modifiedAst = draftInfo ? draftInfo.truncatedAst : { ...kclManager.ast }
|
||||||
|
|
||||||
const _node = getNodeFromPath<CallExpression>(
|
const _node = getNodeFromPath<Node<CallExpression>>(
|
||||||
modifiedAst,
|
modifiedAst,
|
||||||
pathToNode,
|
pathToNode,
|
||||||
'CallExpression'
|
'CallExpression'
|
||||||
@ -1271,7 +1400,7 @@ export class SceneEntities {
|
|||||||
|
|
||||||
let modded:
|
let modded:
|
||||||
| {
|
| {
|
||||||
modifiedAst: Program
|
modifiedAst: Node<Program>
|
||||||
pathToNode: PathToNode
|
pathToNode: PathToNode
|
||||||
}
|
}
|
||||||
| Error
|
| Error
|
||||||
@ -1566,7 +1695,7 @@ export class SceneEntities {
|
|||||||
if (parent?.userData?.pathToNode) {
|
if (parent?.userData?.pathToNode) {
|
||||||
const updatedAst = parse(recast(kclManager.ast))
|
const updatedAst = parse(recast(kclManager.ast))
|
||||||
if (trap(updatedAst)) return
|
if (trap(updatedAst)) return
|
||||||
const _node = getNodeFromPath<CallExpression>(
|
const _node = getNodeFromPath<Node<CallExpression>>(
|
||||||
updatedAst,
|
updatedAst,
|
||||||
parent.userData.pathToNode,
|
parent.userData.pathToNode,
|
||||||
'CallExpression'
|
'CallExpression'
|
||||||
@ -1701,12 +1830,12 @@ export type DefaultPlaneStr = 'XY' | 'XZ' | 'YZ' | '-XY' | '-XZ' | '-YZ'
|
|||||||
|
|
||||||
function prepareTruncatedMemoryAndAst(
|
function prepareTruncatedMemoryAndAst(
|
||||||
sketchPathToNode: PathToNode,
|
sketchPathToNode: PathToNode,
|
||||||
ast: Program,
|
ast: Node<Program>,
|
||||||
programMemory: ProgramMemory,
|
programMemory: ProgramMemory,
|
||||||
draftSegment?: DraftSegment
|
draftSegment?: DraftSegment
|
||||||
):
|
):
|
||||||
| {
|
| {
|
||||||
truncatedAst: Program
|
truncatedAst: Node<Program>
|
||||||
programMemoryOverride: ProgramMemory
|
programMemoryOverride: ProgramMemory
|
||||||
variableDeclarationName: string
|
variableDeclarationName: string
|
||||||
}
|
}
|
||||||
@ -1714,7 +1843,7 @@ function prepareTruncatedMemoryAndAst(
|
|||||||
const bodyIndex = Number(sketchPathToNode?.[1]?.[0]) || 0
|
const bodyIndex = Number(sketchPathToNode?.[1]?.[0]) || 0
|
||||||
const _ast = structuredClone(ast)
|
const _ast = structuredClone(ast)
|
||||||
|
|
||||||
const _node = getNodeFromPath<VariableDeclaration>(
|
const _node = getNodeFromPath<Node<VariableDeclaration>>(
|
||||||
_ast,
|
_ast,
|
||||||
sketchPathToNode || [],
|
sketchPathToNode || [],
|
||||||
'VariableDeclaration'
|
'VariableDeclaration'
|
||||||
@ -1764,15 +1893,15 @@ function prepareTruncatedMemoryAndAst(
|
|||||||
).body.slice(-1)[0].start = lastPipeItem.start
|
).body.slice(-1)[0].start = lastPipeItem.start
|
||||||
|
|
||||||
_ast.end = lastPipeItem.end
|
_ast.end = lastPipeItem.end
|
||||||
const varDec = _ast.body[bodyIndex] as VariableDeclaration
|
const varDec = _ast.body[bodyIndex] as Node<VariableDeclaration>
|
||||||
varDec.end = lastPipeItem.end
|
varDec.end = lastPipeItem.end
|
||||||
const declarator = varDec.declarations[0]
|
const declarator = varDec.declarations[0]
|
||||||
declarator.end = lastPipeItem.end
|
declarator.end = lastPipeItem.end
|
||||||
const init = declarator.init as PipeExpression
|
const init = declarator.init as Node<PipeExpression>
|
||||||
init.end = lastPipeItem.end
|
init.end = lastPipeItem.end
|
||||||
init.body.slice(-1)[0].end = lastPipeItem.end
|
init.body.slice(-1)[0].end = lastPipeItem.end
|
||||||
}
|
}
|
||||||
const truncatedAst: Program = {
|
const truncatedAst: Node<Program> = {
|
||||||
..._ast,
|
..._ast,
|
||||||
body: [structuredClone(_ast.body[bodyIndex])],
|
body: [structuredClone(_ast.body[bodyIndex])],
|
||||||
}
|
}
|
||||||
|
@ -30,6 +30,7 @@ import { MouseState, SegmentOverlayPayload } from 'machines/modelingMachine'
|
|||||||
import { getAngle, throttle } from 'lib/utils'
|
import { getAngle, throttle } from 'lib/utils'
|
||||||
import { Themes } from 'lib/theme'
|
import { Themes } from 'lib/theme'
|
||||||
import { CSS2DRenderer } from 'three/examples/jsm/renderers/CSS2DRenderer'
|
import { CSS2DRenderer } from 'three/examples/jsm/renderers/CSS2DRenderer'
|
||||||
|
import { orthoScale, perspScale } from './helpers'
|
||||||
|
|
||||||
type SendType = ReturnType<typeof useModelingContext>['send']
|
type SendType = ReturnType<typeof useModelingContext>['send']
|
||||||
|
|
||||||
@ -49,6 +50,10 @@ export const RAYCASTABLE_PLANE = 'raycastable-plane'
|
|||||||
|
|
||||||
export const X_AXIS = 'xAxis'
|
export const X_AXIS = 'xAxis'
|
||||||
export const Y_AXIS = 'yAxis'
|
export const Y_AXIS = 'yAxis'
|
||||||
|
/** the THREEjs representation of the group surrounding a "snapped" point that is not yet placed */
|
||||||
|
export const DRAFT_POINT_GROUP = 'draft-point-group'
|
||||||
|
/** the THREEjs representation of a "snapped" point that is not yet placed */
|
||||||
|
export const DRAFT_POINT = 'draft-point'
|
||||||
export const AXIS_GROUP = 'axisGroup'
|
export const AXIS_GROUP = 'axisGroup'
|
||||||
export const SKETCH_GROUP_SEGMENTS = 'sketch-group-segments'
|
export const SKETCH_GROUP_SEGMENTS = 'sketch-group-segments'
|
||||||
export const ARROWHEAD = 'arrowhead'
|
export const ARROWHEAD = 'arrowhead'
|
||||||
@ -60,6 +65,11 @@ export interface OnMouseEnterLeaveArgs {
|
|||||||
selected: Object3D<Object3DEventMap>
|
selected: Object3D<Object3DEventMap>
|
||||||
dragSelected?: Object3D<Object3DEventMap>
|
dragSelected?: Object3D<Object3DEventMap>
|
||||||
mouseEvent: MouseEvent
|
mouseEvent: MouseEvent
|
||||||
|
/** The intersection of the mouse with the THREEjs raycast plane */
|
||||||
|
intersectionPoint?: {
|
||||||
|
twoD?: Vector2
|
||||||
|
threeD?: Vector3
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
interface OnDragCallbackArgs extends OnMouseEnterLeaveArgs {
|
interface OnDragCallbackArgs extends OnMouseEnterLeaveArgs {
|
||||||
@ -348,29 +358,42 @@ export class SceneInfra {
|
|||||||
window.removeEventListener('resize', this.onWindowResize)
|
window.removeEventListener('resize', this.onWindowResize)
|
||||||
// Dispose of any other resources like geometries, materials, textures
|
// Dispose of any other resources like geometries, materials, textures
|
||||||
}
|
}
|
||||||
|
getClientSceneScaleFactor(meshOrGroup: Mesh | Group) {
|
||||||
|
const orthoFactor = orthoScale(this.camControls.camera)
|
||||||
|
const factor =
|
||||||
|
(this.camControls.camera instanceof OrthographicCamera
|
||||||
|
? orthoFactor
|
||||||
|
: perspScale(this.camControls.camera, meshOrGroup)) /
|
||||||
|
this._baseUnitMultiplier
|
||||||
|
return factor
|
||||||
|
}
|
||||||
getPlaneIntersectPoint = (): {
|
getPlaneIntersectPoint = (): {
|
||||||
twoD?: Vector2
|
twoD?: Vector2
|
||||||
threeD?: Vector3
|
threeD?: Vector3
|
||||||
intersection: Intersection<Object3D<Object3DEventMap>>
|
intersection: Intersection<Object3D<Object3DEventMap>>
|
||||||
} | null => {
|
} | null => {
|
||||||
|
// Get the orientations from the camera and mouse position
|
||||||
this.planeRaycaster.setFromCamera(
|
this.planeRaycaster.setFromCamera(
|
||||||
this.currentMouseVector,
|
this.currentMouseVector,
|
||||||
this.camControls.camera
|
this.camControls.camera
|
||||||
)
|
)
|
||||||
|
// Get the intersection of the ray with the default planes
|
||||||
const planeIntersects = this.planeRaycaster.intersectObjects(
|
const planeIntersects = this.planeRaycaster.intersectObjects(
|
||||||
this.scene.children,
|
this.scene.children,
|
||||||
true
|
true
|
||||||
)
|
)
|
||||||
const recastablePlaneIntersect = planeIntersects.find(
|
if (!planeIntersects.length) return null
|
||||||
|
|
||||||
|
// Find the intersection with the raycastable (or sketch) plane
|
||||||
|
const raycastablePlaneIntersection = planeIntersects.find(
|
||||||
(intersect) => intersect.object.name === RAYCASTABLE_PLANE
|
(intersect) => intersect.object.name === RAYCASTABLE_PLANE
|
||||||
)
|
)
|
||||||
if (!planeIntersects.length) return null
|
if (!raycastablePlaneIntersection)
|
||||||
if (!recastablePlaneIntersect) return { intersection: planeIntersects[0] }
|
return { intersection: planeIntersects[0] }
|
||||||
const planePosition = planeIntersects[0].object.position
|
const planePosition = raycastablePlaneIntersection.object.position
|
||||||
const inversePlaneQuaternion = planeIntersects[0].object.quaternion
|
const inversePlaneQuaternion =
|
||||||
.clone()
|
raycastablePlaneIntersection.object.quaternion.clone().invert()
|
||||||
.invert()
|
const intersectPoint = raycastablePlaneIntersection.point
|
||||||
const intersectPoint = planeIntersects[0].point
|
|
||||||
let transformedPoint = intersectPoint.clone()
|
let transformedPoint = intersectPoint.clone()
|
||||||
if (transformedPoint) {
|
if (transformedPoint) {
|
||||||
transformedPoint.applyQuaternion(inversePlaneQuaternion)
|
transformedPoint.applyQuaternion(inversePlaneQuaternion)
|
||||||
@ -447,18 +470,26 @@ export class SceneInfra {
|
|||||||
|
|
||||||
if (intersects[0]) {
|
if (intersects[0]) {
|
||||||
const firstIntersectObject = intersects[0].object
|
const firstIntersectObject = intersects[0].object
|
||||||
|
const planeIntersectPoint = this.getPlaneIntersectPoint()
|
||||||
|
const intersectionPoint = {
|
||||||
|
twoD: planeIntersectPoint?.twoD,
|
||||||
|
threeD: planeIntersectPoint?.threeD,
|
||||||
|
}
|
||||||
|
|
||||||
if (this.hoveredObject !== firstIntersectObject) {
|
if (this.hoveredObject !== firstIntersectObject) {
|
||||||
const hoveredObj = this.hoveredObject
|
const hoveredObj = this.hoveredObject
|
||||||
this.hoveredObject = null
|
this.hoveredObject = null
|
||||||
await this.onMouseLeave({
|
await this.onMouseLeave({
|
||||||
selected: hoveredObj,
|
selected: hoveredObj,
|
||||||
mouseEvent: mouseEvent,
|
mouseEvent: mouseEvent,
|
||||||
|
intersectionPoint,
|
||||||
})
|
})
|
||||||
this.hoveredObject = firstIntersectObject
|
this.hoveredObject = firstIntersectObject
|
||||||
await this.onMouseEnter({
|
await this.onMouseEnter({
|
||||||
selected: this.hoveredObject,
|
selected: this.hoveredObject,
|
||||||
dragSelected: this.selected?.object,
|
dragSelected: this.selected?.object,
|
||||||
mouseEvent: mouseEvent,
|
mouseEvent: mouseEvent,
|
||||||
|
intersectionPoint,
|
||||||
})
|
})
|
||||||
if (!this.selected)
|
if (!this.selected)
|
||||||
this.updateMouseState({
|
this.updateMouseState({
|
||||||
|
@ -45,6 +45,7 @@ import {
|
|||||||
import { getTangentPointFromPreviousArc } from 'lib/utils2d'
|
import { getTangentPointFromPreviousArc } from 'lib/utils2d'
|
||||||
import {
|
import {
|
||||||
ARROWHEAD,
|
ARROWHEAD,
|
||||||
|
DRAFT_POINT,
|
||||||
SceneInfra,
|
SceneInfra,
|
||||||
SEGMENT_LENGTH_LABEL,
|
SEGMENT_LENGTH_LABEL,
|
||||||
SEGMENT_LENGTH_LABEL_OFFSET_PX,
|
SEGMENT_LENGTH_LABEL_OFFSET_PX,
|
||||||
@ -686,19 +687,20 @@ class CircleSegment implements SegmentUtils {
|
|||||||
|
|
||||||
export function createProfileStartHandle({
|
export function createProfileStartHandle({
|
||||||
from,
|
from,
|
||||||
id,
|
isDraft = false,
|
||||||
pathToNode,
|
|
||||||
scale = 1,
|
scale = 1,
|
||||||
theme,
|
theme,
|
||||||
isSelected,
|
isSelected,
|
||||||
|
...rest
|
||||||
}: {
|
}: {
|
||||||
from: Coords2d
|
from: Coords2d
|
||||||
id: string
|
|
||||||
pathToNode: PathToNode
|
|
||||||
scale?: number
|
scale?: number
|
||||||
theme: Themes
|
theme: Themes
|
||||||
isSelected?: boolean
|
isSelected?: boolean
|
||||||
}) {
|
} & (
|
||||||
|
| { isDraft: true }
|
||||||
|
| { isDraft: false; id: string; pathToNode: PathToNode }
|
||||||
|
)) {
|
||||||
const group = new Group()
|
const group = new Group()
|
||||||
|
|
||||||
const geometry = new BoxGeometry(12, 12, 12) // in pixels scaled later
|
const geometry = new BoxGeometry(12, 12, 12) // in pixels scaled later
|
||||||
@ -711,13 +713,12 @@ export function createProfileStartHandle({
|
|||||||
|
|
||||||
group.userData = {
|
group.userData = {
|
||||||
type: PROFILE_START,
|
type: PROFILE_START,
|
||||||
id,
|
|
||||||
from,
|
from,
|
||||||
pathToNode,
|
|
||||||
isSelected,
|
isSelected,
|
||||||
baseColor,
|
baseColor,
|
||||||
|
...rest,
|
||||||
}
|
}
|
||||||
group.name = PROFILE_START
|
group.name = isDraft ? DRAFT_POINT : PROFILE_START
|
||||||
group.position.set(from[0], from[1], 0)
|
group.position.set(from[0], from[1], 0)
|
||||||
group.scale.set(scale, scale, scale)
|
group.scale.set(scale, scale, scale)
|
||||||
return group
|
return group
|
||||||
|
@ -538,3 +538,19 @@ export const FileTreeInner = ({
|
|||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const FileTreeRoot = () => {
|
||||||
|
const loaderData = useRouteLoaderData(PATHS.FILE) as IndexLoaderData
|
||||||
|
const { project } = loaderData
|
||||||
|
|
||||||
|
// project.path should never be empty here but I guess during initial loading
|
||||||
|
// it can be.
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className="max-w-xs text-ellipsis overflow-hidden cursor-pointer"
|
||||||
|
title={project?.path ?? ''}
|
||||||
|
>
|
||||||
|
{project?.name ?? ''}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
@ -4,7 +4,7 @@ import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext'
|
|||||||
import { CustomIcon } from './CustomIcon'
|
import { CustomIcon } from './CustomIcon'
|
||||||
import { useLocation, useNavigate } from 'react-router-dom'
|
import { useLocation, useNavigate } from 'react-router-dom'
|
||||||
import { PATHS } from 'lib/paths'
|
import { PATHS } from 'lib/paths'
|
||||||
import { createAndOpenNewProject } from 'lib/desktopFS'
|
import { createAndOpenNewTutorialProject } from 'lib/desktopFS'
|
||||||
import { useAbsoluteFilePath } from 'hooks/useAbsoluteFilePath'
|
import { useAbsoluteFilePath } from 'hooks/useAbsoluteFilePath'
|
||||||
import { useLspContext } from './LspProvider'
|
import { useLspContext } from './LspProvider'
|
||||||
import { openExternalBrowserIfDesktop } from 'lib/openWindow'
|
import { openExternalBrowserIfDesktop } from 'lib/openWindow'
|
||||||
@ -116,9 +116,10 @@ export function HelpMenu(props: React.PropsWithChildren) {
|
|||||||
if (isInProject) {
|
if (isInProject) {
|
||||||
navigate(filePath + PATHS.ONBOARDING.INDEX)
|
navigate(filePath + PATHS.ONBOARDING.INDEX)
|
||||||
} else {
|
} else {
|
||||||
createAndOpenNewProject({ onProjectOpen, navigate }).catch(
|
createAndOpenNewTutorialProject({
|
||||||
reportRejection
|
onProjectOpen,
|
||||||
)
|
navigate,
|
||||||
|
}).catch(reportRejection)
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
@ -91,6 +91,7 @@ import { submitAndAwaitTextToKcl } from 'lib/textToCad'
|
|||||||
import { useFileContext } from 'hooks/useFileContext'
|
import { useFileContext } from 'hooks/useFileContext'
|
||||||
import { uuidv4 } from 'lib/utils'
|
import { uuidv4 } from 'lib/utils'
|
||||||
import { IndexLoaderData } from 'lib/types'
|
import { IndexLoaderData } from 'lib/types'
|
||||||
|
import { Node } from 'wasm-lib/kcl/bindings/Node'
|
||||||
|
|
||||||
type MachineContext<T extends AnyStateMachine> = {
|
type MachineContext<T extends AnyStateMachine> = {
|
||||||
state: StateFrom<T>
|
state: StateFrom<T>
|
||||||
@ -971,7 +972,7 @@ export const ModelingMachineProvider = ({
|
|||||||
})
|
})
|
||||||
let parsed = parse(recast(kclManager.ast))
|
let parsed = parse(recast(kclManager.ast))
|
||||||
if (trap(parsed)) return Promise.reject(parsed)
|
if (trap(parsed)) return Promise.reject(parsed)
|
||||||
parsed = parsed as Program
|
parsed = parsed as Node<Program>
|
||||||
|
|
||||||
const { modifiedAst: _modifiedAst, pathToReplacedNode } =
|
const { modifiedAst: _modifiedAst, pathToReplacedNode } =
|
||||||
moveValueIntoNewVariablePath(
|
moveValueIntoNewVariablePath(
|
||||||
@ -982,7 +983,7 @@ export const ModelingMachineProvider = ({
|
|||||||
)
|
)
|
||||||
parsed = parse(recast(_modifiedAst))
|
parsed = parse(recast(_modifiedAst))
|
||||||
if (trap(parsed)) return Promise.reject(parsed)
|
if (trap(parsed)) return Promise.reject(parsed)
|
||||||
parsed = parsed as Program
|
parsed = parsed as Node<Program>
|
||||||
if (!pathToReplacedNode)
|
if (!pathToReplacedNode)
|
||||||
return Promise.reject(new Error('No path to replaced node'))
|
return Promise.reject(new Error('No path to replaced node'))
|
||||||
|
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import { ReactNode } from 'react'
|
||||||
import styles from './ModelingPane.module.css'
|
import styles from './ModelingPane.module.css'
|
||||||
import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext'
|
import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext'
|
||||||
import { ActionButton } from 'components/ActionButton'
|
import { ActionButton } from 'components/ActionButton'
|
||||||
@ -6,22 +7,24 @@ import { CustomIconName } from 'components/CustomIcon'
|
|||||||
import { IconDefinition } from '@fortawesome/free-solid-svg-icons'
|
import { IconDefinition } from '@fortawesome/free-solid-svg-icons'
|
||||||
import { ActionIcon } from 'components/ActionIcon'
|
import { ActionIcon } from 'components/ActionIcon'
|
||||||
|
|
||||||
export interface ModelingPaneProps
|
export interface ModelingPaneProps {
|
||||||
extends React.PropsWithChildren,
|
id: string
|
||||||
React.HTMLAttributes<HTMLDivElement> {
|
children: ReactNode | ReactNode[]
|
||||||
|
className?: string
|
||||||
icon?: CustomIconName | IconDefinition
|
icon?: CustomIconName | IconDefinition
|
||||||
title: string
|
title: ReactNode
|
||||||
Menu?: React.ReactNode | React.FC
|
Menu?: React.ReactNode | React.FC
|
||||||
detailsTestId?: string
|
detailsTestId?: string
|
||||||
onClose: () => void
|
onClose: () => void
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ModelingPaneHeader = ({
|
export const ModelingPaneHeader = ({
|
||||||
|
id,
|
||||||
icon,
|
icon,
|
||||||
title,
|
title,
|
||||||
Menu,
|
Menu,
|
||||||
onClose,
|
onClose,
|
||||||
}: Pick<ModelingPaneProps, 'icon' | 'title' | 'Menu' | 'onClose'>) => {
|
}: Pick<ModelingPaneProps, 'id' | 'icon' | 'title' | 'Menu' | 'onClose'>) => {
|
||||||
return (
|
return (
|
||||||
<div className={styles.header}>
|
<div className={styles.header}>
|
||||||
<div className="flex gap-2 items-center flex-1">
|
<div className="flex gap-2 items-center flex-1">
|
||||||
@ -34,7 +37,7 @@ export const ModelingPaneHeader = ({
|
|||||||
bgClassName="!bg-transparent"
|
bgClassName="!bg-transparent"
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
<span>{title}</span>
|
<span data-testid={id + '-header'}>{title}</span>
|
||||||
</div>
|
</div>
|
||||||
{Menu instanceof Function ? <Menu /> : Menu}
|
{Menu instanceof Function ? <Menu /> : Menu}
|
||||||
<ActionButton
|
<ActionButton
|
||||||
@ -86,6 +89,7 @@ export const ModelingPane = ({
|
|||||||
}
|
}
|
||||||
>
|
>
|
||||||
<ModelingPaneHeader
|
<ModelingPaneHeader
|
||||||
|
id={id}
|
||||||
icon={icon}
|
icon={icon}
|
||||||
title={title}
|
title={title}
|
||||||
Menu={Menu}
|
Menu={Menu}
|
||||||
|
@ -88,8 +88,12 @@ export const MemoryPane = () => {
|
|||||||
export const processMemory = (programMemory: ProgramMemory) => {
|
export const processMemory = (programMemory: ProgramMemory) => {
|
||||||
const processedMemory: any = {}
|
const processedMemory: any = {}
|
||||||
for (const [key, val] of programMemory?.visibleEntries()) {
|
for (const [key, val] of programMemory?.visibleEntries()) {
|
||||||
if (typeof val.value !== 'function') {
|
if (
|
||||||
const sg = sketchFromKclValue(val, null)
|
(val.type === 'UserVal' && val.value.type === 'Sketch') ||
|
||||||
|
// @ts-ignore
|
||||||
|
(val.type !== 'Function' && val.type !== 'UserVal')
|
||||||
|
) {
|
||||||
|
const sg = sketchFromKclValue(val, key)
|
||||||
if (val.type === 'Solid') {
|
if (val.type === 'Solid') {
|
||||||
processedMemory[key] = val.value.map(({ ...rest }: ExtrudeSurface) => {
|
processedMemory[key] = val.value.map(({ ...rest }: ExtrudeSurface) => {
|
||||||
return rest
|
return rest
|
||||||
@ -98,16 +102,17 @@ export const processMemory = (programMemory: ProgramMemory) => {
|
|||||||
processedMemory[key] = sg.paths.map(({ __geoMeta, ...rest }: Path) => {
|
processedMemory[key] = sg.paths.map(({ __geoMeta, ...rest }: Path) => {
|
||||||
return rest
|
return rest
|
||||||
})
|
})
|
||||||
} else if ((val.type as any) === 'Function') {
|
} else {
|
||||||
|
processedMemory[key] = val.value
|
||||||
|
}
|
||||||
|
//@ts-ignore
|
||||||
|
} else if (val.type === 'Function') {
|
||||||
processedMemory[key] = `__function(${(val as any)?.expression?.params
|
processedMemory[key] = `__function(${(val as any)?.expression?.params
|
||||||
?.map?.(({ identifier }: any) => identifier?.name || '')
|
?.map?.(({ identifier }: any) => identifier?.name || '')
|
||||||
.join(', ')})__`
|
.join(', ')})__`
|
||||||
} else {
|
} else {
|
||||||
processedMemory[key] = val.value
|
processedMemory[key] = val.value
|
||||||
}
|
}
|
||||||
} else if (key !== 'log') {
|
|
||||||
processedMemory[key] = '__function__'
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return processedMemory
|
return processedMemory
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,7 @@ import { MouseEventHandler, ReactNode } from 'react'
|
|||||||
import { MemoryPane, MemoryPaneMenu } from './MemoryPane'
|
import { MemoryPane, MemoryPaneMenu } from './MemoryPane'
|
||||||
import { LogsPane } from './LoggingPanes'
|
import { LogsPane } from './LoggingPanes'
|
||||||
import { DebugPane } from './DebugPane'
|
import { DebugPane } from './DebugPane'
|
||||||
import { FileTreeInner, FileTreeMenu } from 'components/FileTree'
|
import { FileTreeInner, FileTreeMenu, FileTreeRoot } from 'components/FileTree'
|
||||||
import { useKclContext } from 'lang/KclProvider'
|
import { useKclContext } from 'lang/KclProvider'
|
||||||
import { editorManager } from 'lib/singletons'
|
import { editorManager } from 'lib/singletons'
|
||||||
import { ContextFrom } from 'xstate'
|
import { ContextFrom } from 'xstate'
|
||||||
@ -38,7 +38,8 @@ interface PaneCallbackProps {
|
|||||||
|
|
||||||
export type SidebarPane = {
|
export type SidebarPane = {
|
||||||
id: SidebarType
|
id: SidebarType
|
||||||
title: string
|
title: ReactNode
|
||||||
|
sidebarName?: string
|
||||||
icon: CustomIconName | IconDefinition
|
icon: CustomIconName | IconDefinition
|
||||||
keybinding: string
|
keybinding: string
|
||||||
Content: ReactNode | React.FC
|
Content: ReactNode | React.FC
|
||||||
@ -49,7 +50,7 @@ export type SidebarPane = {
|
|||||||
|
|
||||||
export type SidebarAction = {
|
export type SidebarAction = {
|
||||||
id: string
|
id: string
|
||||||
title: string
|
title: ReactNode
|
||||||
icon: CustomIconName
|
icon: CustomIconName
|
||||||
iconClassName?: string // Just until we get rid of FontAwesome icons
|
iconClassName?: string // Just until we get rid of FontAwesome icons
|
||||||
keybinding: string
|
keybinding: string
|
||||||
@ -78,7 +79,8 @@ export const sidebarPanes: SidebarPane[] = [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'files',
|
id: 'files',
|
||||||
title: 'Project Files',
|
title: <FileTreeRoot />,
|
||||||
|
sidebarName: 'Project Files',
|
||||||
icon: 'folder',
|
icon: 'folder',
|
||||||
Content: FileTreeInner,
|
Content: FileTreeInner,
|
||||||
keybinding: 'Shift + F',
|
keybinding: 'Shift + F',
|
@ -5,6 +5,7 @@ import {
|
|||||||
useCallback,
|
useCallback,
|
||||||
useEffect,
|
useEffect,
|
||||||
useMemo,
|
useMemo,
|
||||||
|
ReactNode,
|
||||||
useContext,
|
useContext,
|
||||||
} from 'react'
|
} from 'react'
|
||||||
import { useHotkeys } from 'react-hotkeys-hook'
|
import { useHotkeys } from 'react-hotkeys-hook'
|
||||||
@ -270,7 +271,8 @@ interface ModelingPaneButtonProps
|
|||||||
extends React.HTMLAttributes<HTMLButtonElement> {
|
extends React.HTMLAttributes<HTMLButtonElement> {
|
||||||
paneConfig: {
|
paneConfig: {
|
||||||
id: string
|
id: string
|
||||||
title: string
|
title: ReactNode
|
||||||
|
sidebarName?: string
|
||||||
icon: CustomIconName | IconDefinition
|
icon: CustomIconName | IconDefinition
|
||||||
keybinding: string
|
keybinding: string
|
||||||
iconClassName?: string
|
iconClassName?: string
|
||||||
@ -299,7 +301,10 @@ function ModelingPaneButton({
|
|||||||
<button
|
<button
|
||||||
className="group pointer-events-auto flex items-center justify-center border-transparent dark:border-transparent disabled:!border-transparent p-0 m-0 rounded-sm !outline-0 focus-visible:border-primary"
|
className="group pointer-events-auto flex items-center justify-center border-transparent dark:border-transparent disabled:!border-transparent p-0 m-0 rounded-sm !outline-0 focus-visible:border-primary"
|
||||||
onClick={onClick}
|
onClick={onClick}
|
||||||
name={paneConfig.title}
|
name={
|
||||||
|
paneConfig.sidebarName ??
|
||||||
|
(typeof paneConfig.title === 'string' ? paneConfig.title : '')
|
||||||
|
}
|
||||||
data-testid={paneConfig.id + '-pane-button'}
|
data-testid={paneConfig.id + '-pane-button'}
|
||||||
disabled={disabledText !== undefined}
|
disabled={disabledText !== undefined}
|
||||||
aria-disabled={disabledText !== undefined}
|
aria-disabled={disabledText !== undefined}
|
||||||
@ -315,7 +320,7 @@ function ModelingPaneButton({
|
|||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
<span className="sr-only">
|
<span className="sr-only">
|
||||||
{paneConfig.title}
|
{paneConfig.sidebarName ?? paneConfig.title}
|
||||||
{paneIsOpen !== undefined ? ` pane` : ''}
|
{paneIsOpen !== undefined ? ` pane` : ''}
|
||||||
</span>
|
</span>
|
||||||
<Tooltip
|
<Tooltip
|
||||||
@ -324,7 +329,7 @@ function ModelingPaneButton({
|
|||||||
hoverOnly
|
hoverOnly
|
||||||
>
|
>
|
||||||
<span className="flex-1">
|
<span className="flex-1">
|
||||||
{paneConfig.title}
|
{paneConfig.sidebarName ?? paneConfig.title}
|
||||||
{disabledText !== undefined ? ` (${disabledText})` : ''}
|
{disabledText !== undefined ? ` (${disabledText})` : ''}
|
||||||
{paneIsOpen !== undefined ? ` pane` : ''}
|
{paneIsOpen !== undefined ? ` pane` : ''}
|
||||||
</span>
|
</span>
|
||||||
|
289
src/components/ProjectsContextProvider.tsx
Normal file
@ -0,0 +1,289 @@
|
|||||||
|
import { useMachine } from '@xstate/react'
|
||||||
|
import { useCommandsContext } from 'hooks/useCommandsContext'
|
||||||
|
import { useFileSystemWatcher } from 'hooks/useFileSystemWatcher'
|
||||||
|
import { useProjectsLoader } from 'hooks/useProjectsLoader'
|
||||||
|
import { projectsMachine } from 'machines/projectsMachine'
|
||||||
|
import { createContext, useEffect, useState } from 'react'
|
||||||
|
import { Actor, AnyStateMachine, fromPromise, Prop, StateFrom } from 'xstate'
|
||||||
|
import { useLspContext } from './LspProvider'
|
||||||
|
import toast from 'react-hot-toast'
|
||||||
|
import { useLocation, useNavigate } from 'react-router-dom'
|
||||||
|
import { PATHS } from 'lib/paths'
|
||||||
|
import {
|
||||||
|
createNewProjectDirectory,
|
||||||
|
listProjects,
|
||||||
|
renameProjectDirectory,
|
||||||
|
} from 'lib/desktop'
|
||||||
|
import {
|
||||||
|
getNextProjectIndex,
|
||||||
|
interpolateProjectNameWithIndex,
|
||||||
|
doesProjectNameNeedInterpolated,
|
||||||
|
} from 'lib/desktopFS'
|
||||||
|
import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext'
|
||||||
|
import useStateMachineCommands from 'hooks/useStateMachineCommands'
|
||||||
|
import { projectsCommandBarConfig } from 'lib/commandBarConfigs/projectsCommandConfig'
|
||||||
|
import { isDesktop } from 'lib/isDesktop'
|
||||||
|
|
||||||
|
type MachineContext<T extends AnyStateMachine> = {
|
||||||
|
state?: StateFrom<T>
|
||||||
|
send: Prop<Actor<T>, 'send'>
|
||||||
|
}
|
||||||
|
|
||||||
|
export const ProjectsMachineContext = createContext(
|
||||||
|
{} as MachineContext<typeof projectsMachine>
|
||||||
|
)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Watches the project directory and provides project management-related commands,
|
||||||
|
* like "Create project", "Open project", "Delete project", etc.
|
||||||
|
*
|
||||||
|
* If in the future we implement full-fledge project management in the web version,
|
||||||
|
* we can unify these components but for now, we need this to be only for the desktop version.
|
||||||
|
*/
|
||||||
|
export const ProjectsContextProvider = ({
|
||||||
|
children,
|
||||||
|
}: {
|
||||||
|
children: React.ReactNode
|
||||||
|
}) => {
|
||||||
|
return isDesktop() ? (
|
||||||
|
<ProjectsContextDesktop>{children}</ProjectsContextDesktop>
|
||||||
|
) : (
|
||||||
|
<ProjectsContextWeb>{children}</ProjectsContextWeb>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const ProjectsContextWeb = ({ children }: { children: React.ReactNode }) => {
|
||||||
|
return (
|
||||||
|
<ProjectsMachineContext.Provider
|
||||||
|
value={{
|
||||||
|
state: undefined,
|
||||||
|
send: () => {},
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</ProjectsMachineContext.Provider>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const ProjectsContextDesktop = ({
|
||||||
|
children,
|
||||||
|
}: {
|
||||||
|
children: React.ReactNode
|
||||||
|
}) => {
|
||||||
|
const navigate = useNavigate()
|
||||||
|
const location = useLocation()
|
||||||
|
const { commandBarSend } = useCommandsContext()
|
||||||
|
const { onProjectOpen } = useLspContext()
|
||||||
|
const {
|
||||||
|
settings: { context: settings },
|
||||||
|
} = useSettingsAuthContext()
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
console.log(
|
||||||
|
'project directory changed',
|
||||||
|
settings.app.projectDirectory.current
|
||||||
|
)
|
||||||
|
}, [settings.app.projectDirectory.current])
|
||||||
|
|
||||||
|
const [projectsLoaderTrigger, setProjectsLoaderTrigger] = useState(0)
|
||||||
|
const { projectPaths, projectsDir } = useProjectsLoader([
|
||||||
|
projectsLoaderTrigger,
|
||||||
|
])
|
||||||
|
|
||||||
|
// Re-read projects listing if the projectDir has any updates.
|
||||||
|
useFileSystemWatcher(
|
||||||
|
async () => {
|
||||||
|
return setProjectsLoaderTrigger(projectsLoaderTrigger + 1)
|
||||||
|
},
|
||||||
|
projectsDir ? [projectsDir] : []
|
||||||
|
)
|
||||||
|
|
||||||
|
const [state, send, actor] = useMachine(
|
||||||
|
projectsMachine.provide({
|
||||||
|
actions: {
|
||||||
|
navigateToProject: ({ context, event }) => {
|
||||||
|
const nameFromEventData =
|
||||||
|
'data' in event &&
|
||||||
|
event.data &&
|
||||||
|
'name' in event.data &&
|
||||||
|
event.data.name
|
||||||
|
const nameFromOutputData =
|
||||||
|
'output' in event &&
|
||||||
|
event.output &&
|
||||||
|
'name' in event.output &&
|
||||||
|
event.output.name
|
||||||
|
|
||||||
|
const name = nameFromEventData || nameFromOutputData
|
||||||
|
|
||||||
|
if (name) {
|
||||||
|
let projectPath =
|
||||||
|
context.defaultDirectory + window.electron.path.sep + name
|
||||||
|
onProjectOpen(
|
||||||
|
{
|
||||||
|
name,
|
||||||
|
path: projectPath,
|
||||||
|
},
|
||||||
|
null
|
||||||
|
)
|
||||||
|
commandBarSend({ type: 'Close' })
|
||||||
|
const newPathName = `${PATHS.FILE}/${encodeURIComponent(
|
||||||
|
projectPath
|
||||||
|
)}`
|
||||||
|
navigate(newPathName)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
navigateToProjectIfNeeded: ({ event }) => {
|
||||||
|
if (
|
||||||
|
event.type.startsWith('xstate.done.actor.') &&
|
||||||
|
'output' in event
|
||||||
|
) {
|
||||||
|
const isInAProject = location.pathname.startsWith(PATHS.FILE)
|
||||||
|
const isInDeletedProject =
|
||||||
|
event.type === 'xstate.done.actor.delete-project' &&
|
||||||
|
isInAProject &&
|
||||||
|
decodeURIComponent(location.pathname).includes(event.output.name)
|
||||||
|
if (isInDeletedProject) {
|
||||||
|
navigate(PATHS.HOME)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const isInRenamedProject =
|
||||||
|
event.type === 'xstate.done.actor.rename-project' &&
|
||||||
|
isInAProject &&
|
||||||
|
decodeURIComponent(location.pathname).includes(
|
||||||
|
event.output.oldName
|
||||||
|
)
|
||||||
|
|
||||||
|
if (isInRenamedProject) {
|
||||||
|
// TODO: In future, we can navigate to the new project path
|
||||||
|
// directly, but we need to coordinate with
|
||||||
|
// @lf94's useFileSystemWatcher in SettingsAuthProvider.tsx:224
|
||||||
|
// Because it's beating us to the punch and updating the route
|
||||||
|
// const newPathName = location.pathname.replace(
|
||||||
|
// encodeURIComponent(event.output.oldName),
|
||||||
|
// encodeURIComponent(event.output.newName)
|
||||||
|
// )
|
||||||
|
// navigate(newPathName)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
toastSuccess: ({ event }) =>
|
||||||
|
toast.success(
|
||||||
|
('data' in event && typeof event.data === 'string' && event.data) ||
|
||||||
|
('output' in event &&
|
||||||
|
'message' in event.output &&
|
||||||
|
typeof event.output.message === 'string' &&
|
||||||
|
event.output.message) ||
|
||||||
|
''
|
||||||
|
),
|
||||||
|
toastError: ({ event }) =>
|
||||||
|
toast.error(
|
||||||
|
('data' in event && typeof event.data === 'string' && event.data) ||
|
||||||
|
('output' in event &&
|
||||||
|
typeof event.output === 'string' &&
|
||||||
|
event.output) ||
|
||||||
|
''
|
||||||
|
),
|
||||||
|
},
|
||||||
|
actors: {
|
||||||
|
readProjects: fromPromise(() => listProjects()),
|
||||||
|
createProject: fromPromise(async ({ input }) => {
|
||||||
|
let name = (
|
||||||
|
input && 'name' in input && input.name
|
||||||
|
? input.name
|
||||||
|
: settings.projects.defaultProjectName.current
|
||||||
|
).trim()
|
||||||
|
|
||||||
|
if (doesProjectNameNeedInterpolated(name)) {
|
||||||
|
const nextIndex = getNextProjectIndex(name, input.projects)
|
||||||
|
name = interpolateProjectNameWithIndex(name, nextIndex)
|
||||||
|
}
|
||||||
|
|
||||||
|
await createNewProjectDirectory(name)
|
||||||
|
|
||||||
|
return {
|
||||||
|
message: `Successfully created "${name}"`,
|
||||||
|
name,
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
renameProject: fromPromise(async ({ input }) => {
|
||||||
|
const {
|
||||||
|
oldName,
|
||||||
|
newName,
|
||||||
|
defaultProjectName,
|
||||||
|
defaultDirectory,
|
||||||
|
projects,
|
||||||
|
} = input
|
||||||
|
let name = newName ? newName : defaultProjectName
|
||||||
|
if (doesProjectNameNeedInterpolated(name)) {
|
||||||
|
const nextIndex = getNextProjectIndex(name, projects)
|
||||||
|
name = interpolateProjectNameWithIndex(name, nextIndex)
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('from Project')
|
||||||
|
|
||||||
|
await renameProjectDirectory(
|
||||||
|
window.electron.path.join(defaultDirectory, oldName),
|
||||||
|
name
|
||||||
|
)
|
||||||
|
return {
|
||||||
|
message: `Successfully renamed "${oldName}" to "${name}"`,
|
||||||
|
oldName: oldName,
|
||||||
|
newName: name,
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
deleteProject: fromPromise(async ({ input }) => {
|
||||||
|
await window.electron.rm(
|
||||||
|
window.electron.path.join(input.defaultDirectory, input.name),
|
||||||
|
{
|
||||||
|
recursive: true,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
return {
|
||||||
|
message: `Successfully deleted "${input.name}"`,
|
||||||
|
name: input.name,
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
guards: {
|
||||||
|
'Has at least 1 project': ({ event }) => {
|
||||||
|
if (event.type !== 'xstate.done.actor.read-projects') return false
|
||||||
|
console.log(`from has at least 1 project: ${event.output.length}`)
|
||||||
|
return event.output.length ? event.output.length >= 1 : false
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
input: {
|
||||||
|
projects: projectPaths,
|
||||||
|
defaultProjectName: settings.projects.defaultProjectName.current,
|
||||||
|
defaultDirectory: settings.app.projectDirectory.current,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
send({ type: 'Read projects', data: {} })
|
||||||
|
}, [projectPaths])
|
||||||
|
|
||||||
|
// register all project-related command palette commands
|
||||||
|
useStateMachineCommands({
|
||||||
|
machineId: 'projects',
|
||||||
|
send,
|
||||||
|
state,
|
||||||
|
commandBarConfig: projectsCommandBarConfig,
|
||||||
|
actor,
|
||||||
|
})
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ProjectsMachineContext.Provider
|
||||||
|
value={{
|
||||||
|
state,
|
||||||
|
send,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</ProjectsMachineContext.Provider>
|
||||||
|
)
|
||||||
|
}
|
@ -15,7 +15,10 @@ import { SettingsFieldInput } from './SettingsFieldInput'
|
|||||||
import toast from 'react-hot-toast'
|
import toast from 'react-hot-toast'
|
||||||
import { APP_VERSION } from 'routes/Settings'
|
import { APP_VERSION } from 'routes/Settings'
|
||||||
import { PATHS } from 'lib/paths'
|
import { PATHS } from 'lib/paths'
|
||||||
import { createAndOpenNewProject, getSettingsFolderPaths } from 'lib/desktopFS'
|
import {
|
||||||
|
createAndOpenNewTutorialProject,
|
||||||
|
getSettingsFolderPaths,
|
||||||
|
} from 'lib/desktopFS'
|
||||||
import { useDotDotSlash } from 'hooks/useDotDotSlash'
|
import { useDotDotSlash } from 'hooks/useDotDotSlash'
|
||||||
import { ForwardedRef, forwardRef, useEffect } from 'react'
|
import { ForwardedRef, forwardRef, useEffect } from 'react'
|
||||||
import { useLspContext } from 'components/LspProvider'
|
import { useLspContext } from 'components/LspProvider'
|
||||||
@ -79,7 +82,7 @@ export const AllSettingsFields = forwardRef(
|
|||||||
} else {
|
} else {
|
||||||
// If we're in the global settings, create a new project and navigate
|
// If we're in the global settings, create a new project and navigate
|
||||||
// to the onboarding start in that project
|
// to the onboarding start in that project
|
||||||
await createAndOpenNewProject({ onProjectOpen, navigate })
|
await createAndOpenNewTutorialProject({ onProjectOpen, navigate })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,6 +14,7 @@ import {
|
|||||||
import { TransformInfo } from 'lang/std/stdTypes'
|
import { TransformInfo } from 'lang/std/stdTypes'
|
||||||
import { kclManager } from 'lib/singletons'
|
import { kclManager } from 'lib/singletons'
|
||||||
import { err } from 'lib/trap'
|
import { err } from 'lib/trap'
|
||||||
|
import { Node } from 'wasm-lib/kcl/bindings/Node'
|
||||||
|
|
||||||
export function setEqualLengthInfo({
|
export function setEqualLengthInfo({
|
||||||
selectionRanges,
|
selectionRanges,
|
||||||
@ -86,7 +87,7 @@ export function applyConstraintEqualLength({
|
|||||||
selectionRanges: Selections
|
selectionRanges: Selections
|
||||||
}):
|
}):
|
||||||
| {
|
| {
|
||||||
modifiedAst: Program
|
modifiedAst: Node<Program>
|
||||||
pathToNodeMap: PathToNodeMap
|
pathToNodeMap: PathToNodeMap
|
||||||
}
|
}
|
||||||
| Error {
|
| Error {
|
||||||
|
@ -13,6 +13,7 @@ import {
|
|||||||
import { TransformInfo } from 'lang/std/stdTypes'
|
import { TransformInfo } from 'lang/std/stdTypes'
|
||||||
import { kclManager } from 'lib/singletons'
|
import { kclManager } from 'lib/singletons'
|
||||||
import { err } from 'lib/trap'
|
import { err } from 'lib/trap'
|
||||||
|
import { Node } from 'wasm-lib/kcl/bindings/Node'
|
||||||
|
|
||||||
export function horzVertInfo(
|
export function horzVertInfo(
|
||||||
selectionRanges: Selections,
|
selectionRanges: Selections,
|
||||||
@ -55,11 +56,11 @@ export function horzVertInfo(
|
|||||||
export function applyConstraintHorzVert(
|
export function applyConstraintHorzVert(
|
||||||
selectionRanges: Selections,
|
selectionRanges: Selections,
|
||||||
horOrVert: 'vertical' | 'horizontal',
|
horOrVert: 'vertical' | 'horizontal',
|
||||||
ast: Program,
|
ast: Node<Program>,
|
||||||
programMemory: ProgramMemory
|
programMemory: ProgramMemory
|
||||||
):
|
):
|
||||||
| {
|
| {
|
||||||
modifiedAst: Program
|
modifiedAst: Node<Program>
|
||||||
pathToNodeMap: PathToNodeMap
|
pathToNodeMap: PathToNodeMap
|
||||||
}
|
}
|
||||||
| Error {
|
| Error {
|
||||||
|
@ -19,6 +19,7 @@ import { createVariableDeclaration } from '../../lang/modifyAst'
|
|||||||
import { removeDoubleNegatives } from '../AvailableVarsHelpers'
|
import { removeDoubleNegatives } from '../AvailableVarsHelpers'
|
||||||
import { kclManager } from 'lib/singletons'
|
import { kclManager } from 'lib/singletons'
|
||||||
import { err } from 'lib/trap'
|
import { err } from 'lib/trap'
|
||||||
|
import { Node } from 'wasm-lib/kcl/bindings/Node'
|
||||||
|
|
||||||
const getModalInfo = createInfoModal(GetInfoModal)
|
const getModalInfo = createInfoModal(GetInfoModal)
|
||||||
|
|
||||||
@ -136,7 +137,7 @@ export async function applyConstraintIntersect({
|
|||||||
}: {
|
}: {
|
||||||
selectionRanges: Selections
|
selectionRanges: Selections
|
||||||
}): Promise<{
|
}): Promise<{
|
||||||
modifiedAst: Program
|
modifiedAst: Node<Program>
|
||||||
pathToNodeMap: PathToNodeMap
|
pathToNodeMap: PathToNodeMap
|
||||||
}> {
|
}> {
|
||||||
const info = intersectInfo({
|
const info = intersectInfo({
|
||||||
|
@ -13,6 +13,7 @@ import {
|
|||||||
import { TransformInfo } from 'lang/std/stdTypes'
|
import { TransformInfo } from 'lang/std/stdTypes'
|
||||||
import { kclManager } from 'lib/singletons'
|
import { kclManager } from 'lib/singletons'
|
||||||
import { err } from 'lib/trap'
|
import { err } from 'lib/trap'
|
||||||
|
import { Node } from 'wasm-lib/kcl/bindings/Node'
|
||||||
|
|
||||||
export function removeConstrainingValuesInfo({
|
export function removeConstrainingValuesInfo({
|
||||||
selectionRanges,
|
selectionRanges,
|
||||||
@ -77,7 +78,7 @@ export function applyRemoveConstrainingValues({
|
|||||||
pathToNodes?: Array<PathToNode>
|
pathToNodes?: Array<PathToNode>
|
||||||
}):
|
}):
|
||||||
| {
|
| {
|
||||||
modifiedAst: Program
|
modifiedAst: Node<Program>
|
||||||
pathToNodeMap: PathToNodeMap
|
pathToNodeMap: PathToNodeMap
|
||||||
}
|
}
|
||||||
| Error {
|
| Error {
|
||||||
|
@ -23,6 +23,7 @@ import {
|
|||||||
import { removeDoubleNegatives } from '../AvailableVarsHelpers'
|
import { removeDoubleNegatives } from '../AvailableVarsHelpers'
|
||||||
import { kclManager } from 'lib/singletons'
|
import { kclManager } from 'lib/singletons'
|
||||||
import { err } from 'lib/trap'
|
import { err } from 'lib/trap'
|
||||||
|
import { Node } from 'wasm-lib/kcl/bindings/Node'
|
||||||
|
|
||||||
const getModalInfo = createSetAngleLengthModal(SetAngleLengthModal)
|
const getModalInfo = createSetAngleLengthModal(SetAngleLengthModal)
|
||||||
|
|
||||||
@ -161,7 +162,7 @@ export function applyConstraintAxisAlign({
|
|||||||
constraint: 'snapToYAxis' | 'snapToXAxis'
|
constraint: 'snapToYAxis' | 'snapToXAxis'
|
||||||
}):
|
}):
|
||||||
| {
|
| {
|
||||||
modifiedAst: Program
|
modifiedAst: Node<Program>
|
||||||
pathToNodeMap: PathToNodeMap
|
pathToNodeMap: PathToNodeMap
|
||||||
}
|
}
|
||||||
| Error {
|
| Error {
|
||||||
|
@ -18,6 +18,7 @@ import { removeDoubleNegatives } from '../AvailableVarsHelpers'
|
|||||||
import { kclManager } from 'lib/singletons'
|
import { kclManager } from 'lib/singletons'
|
||||||
import { Selections } from 'lib/selections'
|
import { Selections } from 'lib/selections'
|
||||||
import { cleanErrs, err } from 'lib/trap'
|
import { cleanErrs, err } from 'lib/trap'
|
||||||
|
import { Node } from 'wasm-lib/kcl/bindings/Node'
|
||||||
|
|
||||||
const getModalInfo = createInfoModal(GetInfoModal)
|
const getModalInfo = createInfoModal(GetInfoModal)
|
||||||
|
|
||||||
@ -185,7 +186,7 @@ export function applyConstraintHorzVertAlign({
|
|||||||
constraint: 'setHorzDistance' | 'setVertDistance'
|
constraint: 'setHorzDistance' | 'setVertDistance'
|
||||||
}):
|
}):
|
||||||
| {
|
| {
|
||||||
modifiedAst: Program
|
modifiedAst: Node<Program>
|
||||||
pathToNodeMap: PathToNodeMap
|
pathToNodeMap: PathToNodeMap
|
||||||
}
|
}
|
||||||
| Error {
|
| Error {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { fireEvent, render, screen } from '@testing-library/react'
|
import { fireEvent, render, screen, waitFor } from '@testing-library/react'
|
||||||
import UserSidebarMenu from './UserSidebarMenu'
|
import UserSidebarMenu from './UserSidebarMenu'
|
||||||
import {
|
import {
|
||||||
Route,
|
Route,
|
||||||
@ -13,7 +13,7 @@ import { CommandBarProvider } from './CommandBar/CommandBarProvider'
|
|||||||
type User = Models['User_type']
|
type User = Models['User_type']
|
||||||
|
|
||||||
describe('UserSidebarMenu tests', () => {
|
describe('UserSidebarMenu tests', () => {
|
||||||
test("Renders user's name and email if available", () => {
|
test("Renders user's name and email if available", async () => {
|
||||||
const userWellFormed: User = {
|
const userWellFormed: User = {
|
||||||
id: '8675309',
|
id: '8675309',
|
||||||
name: 'Test User',
|
name: 'Test User',
|
||||||
@ -39,13 +39,19 @@ describe('UserSidebarMenu tests', () => {
|
|||||||
|
|
||||||
fireEvent.click(screen.getByTestId('user-sidebar-toggle'))
|
fireEvent.click(screen.getByTestId('user-sidebar-toggle'))
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
expect(screen.getByTestId('username')).toHaveTextContent(
|
expect(screen.getByTestId('username')).toHaveTextContent(
|
||||||
userWellFormed.name || ''
|
userWellFormed.name || ''
|
||||||
)
|
)
|
||||||
expect(screen.getByTestId('email')).toHaveTextContent(userWellFormed.email)
|
})
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(screen.getByTestId('email')).toHaveTextContent(
|
||||||
|
userWellFormed.email
|
||||||
|
)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
test("Renders just the user's email if no name is available", () => {
|
test("Renders just the user's email if no name is available", async () => {
|
||||||
const userNoName: User = {
|
const userNoName: User = {
|
||||||
id: '8675309',
|
id: '8675309',
|
||||||
email: 'kittycad.sidebar.test@example.com',
|
email: 'kittycad.sidebar.test@example.com',
|
||||||
@ -71,10 +77,12 @@ describe('UserSidebarMenu tests', () => {
|
|||||||
|
|
||||||
fireEvent.click(screen.getByTestId('user-sidebar-toggle'))
|
fireEvent.click(screen.getByTestId('user-sidebar-toggle'))
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
expect(screen.getByTestId('username')).toHaveTextContent(userNoName.email)
|
expect(screen.getByTestId('username')).toHaveTextContent(userNoName.email)
|
||||||
})
|
})
|
||||||
|
})
|
||||||
|
|
||||||
test('Renders a menu button if no user avatar is available', () => {
|
test('Renders a menu button if no user avatar is available', async () => {
|
||||||
const userNoAvatar: User = {
|
const userNoAvatar: User = {
|
||||||
id: '8675309',
|
id: '8675309',
|
||||||
name: 'Test User',
|
name: 'Test User',
|
||||||
@ -98,10 +106,12 @@ describe('UserSidebarMenu tests', () => {
|
|||||||
</TestWrap>
|
</TestWrap>
|
||||||
)
|
)
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
expect(screen.getByTestId('user-sidebar-toggle')).toHaveTextContent(
|
expect(screen.getByTestId('user-sidebar-toggle')).toHaveTextContent(
|
||||||
'User menu'
|
'User menu'
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
function TestWrap({ children }: { children: React.ReactNode }) {
|
function TestWrap({ children }: { children: React.ReactNode }) {
|
||||||
|
6
src/hooks/useProjectsContext.ts
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
import { ProjectsMachineContext } from 'components/ProjectsContextProvider'
|
||||||
|
import { useContext } from 'react'
|
||||||
|
|
||||||
|
export const useProjectsContext = () => {
|
||||||
|
return useContext(ProjectsMachineContext)
|
||||||
|
}
|
@ -5,7 +5,7 @@ import { useCommandsContext } from './useCommandsContext'
|
|||||||
import { modelingMachine } from 'machines/modelingMachine'
|
import { modelingMachine } from 'machines/modelingMachine'
|
||||||
import { authMachine } from 'machines/authMachine'
|
import { authMachine } from 'machines/authMachine'
|
||||||
import { settingsMachine } from 'machines/settingsMachine'
|
import { settingsMachine } from 'machines/settingsMachine'
|
||||||
import { homeMachine } from 'machines/homeMachine'
|
import { projectsMachine } from 'machines/projectsMachine'
|
||||||
import {
|
import {
|
||||||
Command,
|
Command,
|
||||||
StateMachineCommandSetConfig,
|
StateMachineCommandSetConfig,
|
||||||
@ -22,7 +22,7 @@ export type AllMachines =
|
|||||||
| typeof modelingMachine
|
| typeof modelingMachine
|
||||||
| typeof settingsMachine
|
| typeof settingsMachine
|
||||||
| typeof authMachine
|
| typeof authMachine
|
||||||
| typeof homeMachine
|
| typeof projectsMachine
|
||||||
|
|
||||||
interface UseStateMachineCommandsArgs<
|
interface UseStateMachineCommandsArgs<
|
||||||
T extends AllMachines,
|
T extends AllMachines,
|
||||||
|
@ -21,9 +21,10 @@ import {
|
|||||||
import { getNodeFromPath } from './queryAst'
|
import { getNodeFromPath } from './queryAst'
|
||||||
import { codeManager, editorManager, sceneInfra } from 'lib/singletons'
|
import { codeManager, editorManager, sceneInfra } from 'lib/singletons'
|
||||||
import { Diagnostic } from '@codemirror/lint'
|
import { Diagnostic } from '@codemirror/lint'
|
||||||
|
import { Node } from 'wasm-lib/kcl/bindings/Node'
|
||||||
|
|
||||||
interface ExecuteArgs {
|
interface ExecuteArgs {
|
||||||
ast?: Program
|
ast?: Node<Program>
|
||||||
zoomToFit?: boolean
|
zoomToFit?: boolean
|
||||||
executionId?: number
|
executionId?: number
|
||||||
zoomOnRangeAndType?: {
|
zoomOnRangeAndType?: {
|
||||||
@ -33,13 +34,13 @@ interface ExecuteArgs {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class KclManager {
|
export class KclManager {
|
||||||
private _ast: Program = {
|
private _ast: Node<Program> = {
|
||||||
body: [],
|
body: [],
|
||||||
start: 0,
|
start: 0,
|
||||||
end: 0,
|
end: 0,
|
||||||
nonCodeMeta: {
|
nonCodeMeta: {
|
||||||
nonCodeNodes: {},
|
nonCodeNodes: {},
|
||||||
start: [],
|
startNodes: [],
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
private _execState: ExecState = emptyExecState()
|
private _execState: ExecState = emptyExecState()
|
||||||
@ -55,7 +56,7 @@ export class KclManager {
|
|||||||
engineCommandManager: EngineCommandManager
|
engineCommandManager: EngineCommandManager
|
||||||
|
|
||||||
private _isExecutingCallback: (arg: boolean) => void = () => {}
|
private _isExecutingCallback: (arg: boolean) => void = () => {}
|
||||||
private _astCallBack: (arg: Program) => void = () => {}
|
private _astCallBack: (arg: Node<Program>) => void = () => {}
|
||||||
private _programMemoryCallBack: (arg: ProgramMemory) => void = () => {}
|
private _programMemoryCallBack: (arg: ProgramMemory) => void = () => {}
|
||||||
private _logsCallBack: (arg: string[]) => void = () => {}
|
private _logsCallBack: (arg: string[]) => void = () => {}
|
||||||
private _kclErrorsCallBack: (arg: KCLError[]) => void = () => {}
|
private _kclErrorsCallBack: (arg: KCLError[]) => void = () => {}
|
||||||
@ -181,7 +182,7 @@ export class KclManager {
|
|||||||
setWasmInitFailed,
|
setWasmInitFailed,
|
||||||
}: {
|
}: {
|
||||||
setProgramMemory: (arg: ProgramMemory) => void
|
setProgramMemory: (arg: ProgramMemory) => void
|
||||||
setAst: (arg: Program) => void
|
setAst: (arg: Node<Program>) => void
|
||||||
setLogs: (arg: string[]) => void
|
setLogs: (arg: string[]) => void
|
||||||
setKclErrors: (arg: KCLError[]) => void
|
setKclErrors: (arg: KCLError[]) => void
|
||||||
setIsExecuting: (arg: boolean) => void
|
setIsExecuting: (arg: boolean) => void
|
||||||
@ -205,12 +206,12 @@ export class KclManager {
|
|||||||
end: 0,
|
end: 0,
|
||||||
nonCodeMeta: {
|
nonCodeMeta: {
|
||||||
nonCodeNodes: {},
|
nonCodeNodes: {},
|
||||||
start: [],
|
startNodes: [],
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
safeParse(code: string): Program | null {
|
safeParse(code: string): Node<Program> | null {
|
||||||
const ast = parse(code)
|
const ast = parse(code)
|
||||||
this.lints = []
|
this.lints = []
|
||||||
this.kclErrors = []
|
this.kclErrors = []
|
||||||
@ -377,7 +378,7 @@ export class KclManager {
|
|||||||
Array.from(this.engineCommandManager.artifactGraph).forEach(
|
Array.from(this.engineCommandManager.artifactGraph).forEach(
|
||||||
([commandId, artifact]) => {
|
([commandId, artifact]) => {
|
||||||
if (!('codeRef' in artifact)) return
|
if (!('codeRef' in artifact)) return
|
||||||
const _node1 = getNodeFromPath<CallExpression>(
|
const _node1 = getNodeFromPath<Node<CallExpression>>(
|
||||||
this.ast,
|
this.ast,
|
||||||
artifact.codeRef.pathToNode,
|
artifact.codeRef.pathToNode,
|
||||||
'CallExpression'
|
'CallExpression'
|
||||||
@ -441,7 +442,7 @@ export class KclManager {
|
|||||||
// but should probably have think about which of the function to keep
|
// but should probably have think about which of the function to keep
|
||||||
// This always updates the code state and editor and writes to the file system.
|
// This always updates the code state and editor and writes to the file system.
|
||||||
async updateAst(
|
async updateAst(
|
||||||
ast: Program,
|
ast: Node<Program>,
|
||||||
execute: boolean,
|
execute: boolean,
|
||||||
optionalParams?: {
|
optionalParams?: {
|
||||||
focusPath?: Array<PathToNode>
|
focusPath?: Array<PathToNode>
|
||||||
@ -452,7 +453,7 @@ export class KclManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
): Promise<{
|
): Promise<{
|
||||||
newAst: Program
|
newAst: Node<Program>
|
||||||
selections?: Selections
|
selections?: Selections
|
||||||
}> {
|
}> {
|
||||||
const newCode = recast(ast)
|
const newCode = recast(ast)
|
||||||
@ -588,7 +589,7 @@ export class KclManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Determines if there is no KCL code which means it is executing a blank KCL file
|
// Determines if there is no KCL code which means it is executing a blank KCL file
|
||||||
_isAstEmpty(ast: Program) {
|
_isAstEmpty(ast: Node<Program>) {
|
||||||
return ast.start === 0 && ast.end === 0 && ast.body.length === 0
|
return ast.start === 0 && ast.end === 0 && ast.body.length === 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,6 +12,7 @@ import { EngineCommandManager } from 'lang/std/engineConnection'
|
|||||||
import { KCLError } from 'lang/errors'
|
import { KCLError } from 'lang/errors'
|
||||||
import { Diagnostic } from '@codemirror/lint'
|
import { Diagnostic } from '@codemirror/lint'
|
||||||
import { IdGenerator } from 'wasm-lib/kcl/bindings/IdGenerator'
|
import { IdGenerator } from 'wasm-lib/kcl/bindings/IdGenerator'
|
||||||
|
import { Node } from 'wasm-lib/kcl/bindings/Node'
|
||||||
|
|
||||||
export type ToolTip =
|
export type ToolTip =
|
||||||
| 'lineTo'
|
| 'lineTo'
|
||||||
@ -52,7 +53,7 @@ export async function executeAst({
|
|||||||
programMemoryOverride,
|
programMemoryOverride,
|
||||||
idGenerator,
|
idGenerator,
|
||||||
}: {
|
}: {
|
||||||
ast: Program
|
ast: Node<Program>
|
||||||
engineCommandManager: EngineCommandManager
|
engineCommandManager: EngineCommandManager
|
||||||
useFakeExecutor?: boolean
|
useFakeExecutor?: boolean
|
||||||
programMemoryOverride?: ProgramMemory
|
programMemoryOverride?: ProgramMemory
|
||||||
|
@ -21,6 +21,7 @@ import { enginelessExecutor } from '../lib/testHelpers'
|
|||||||
import { findUsesOfTagInPipe, getNodePathFromSourceRange } from './queryAst'
|
import { findUsesOfTagInPipe, getNodePathFromSourceRange } from './queryAst'
|
||||||
import { err } from 'lib/trap'
|
import { err } from 'lib/trap'
|
||||||
import { SimplifiedArgDetails } from './std/stdTypes'
|
import { SimplifiedArgDetails } from './std/stdTypes'
|
||||||
|
import { Node } from 'wasm-lib/kcl/bindings/Node'
|
||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
await initPromise
|
await initPromise
|
||||||
@ -109,7 +110,7 @@ describe('Testing findUniqueName', () => {
|
|||||||
{ type: 'Identifier', name: 'yo07', start: 0, end: 0 },
|
{ type: 'Identifier', name: 'yo07', start: 0, end: 0 },
|
||||||
{ type: 'Identifier', name: 'yo08', start: 0, end: 0 },
|
{ type: 'Identifier', name: 'yo08', start: 0, end: 0 },
|
||||||
{ type: 'Identifier', name: 'yo09', start: 0, end: 0 },
|
{ type: 'Identifier', name: 'yo09', start: 0, end: 0 },
|
||||||
] satisfies Identifier[]),
|
] satisfies Node<Identifier>[]),
|
||||||
'yo',
|
'yo',
|
||||||
2
|
2
|
||||||
)
|
)
|
||||||
@ -123,7 +124,7 @@ describe('Testing addSketchTo', () => {
|
|||||||
body: [],
|
body: [],
|
||||||
start: 0,
|
start: 0,
|
||||||
end: 0,
|
end: 0,
|
||||||
nonCodeMeta: { nonCodeNodes: {}, start: [] },
|
nonCodeMeta: { nonCodeNodes: {}, startNodes: [] },
|
||||||
},
|
},
|
||||||
'yz'
|
'yz'
|
||||||
)
|
)
|
||||||
|
@ -42,12 +42,13 @@ import { SimplifiedArgDetails } from './std/stdTypes'
|
|||||||
import { TagDeclarator } from 'wasm-lib/kcl/bindings/TagDeclarator'
|
import { TagDeclarator } from 'wasm-lib/kcl/bindings/TagDeclarator'
|
||||||
import { Models } from '@kittycad/lib'
|
import { Models } from '@kittycad/lib'
|
||||||
import { ExtrudeFacePlane } from 'machines/modelingMachine'
|
import { ExtrudeFacePlane } from 'machines/modelingMachine'
|
||||||
|
import { Node } from 'wasm-lib/kcl/bindings/Node'
|
||||||
|
|
||||||
export function startSketchOnDefault(
|
export function startSketchOnDefault(
|
||||||
node: Program,
|
node: Node<Program>,
|
||||||
axis: DefaultPlaneStr,
|
axis: DefaultPlaneStr,
|
||||||
name = ''
|
name = ''
|
||||||
): { modifiedAst: Program; id: string; pathToNode: PathToNode } {
|
): { modifiedAst: Node<Program>; id: string; pathToNode: PathToNode } {
|
||||||
const _node = { ...node }
|
const _node = { ...node }
|
||||||
const _name =
|
const _name =
|
||||||
name || findUniqueName(node, KCL_DEFAULT_CONSTANT_PREFIXES.SKETCH)
|
name || findUniqueName(node, KCL_DEFAULT_CONSTANT_PREFIXES.SKETCH)
|
||||||
@ -76,10 +77,10 @@ export function startSketchOnDefault(
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function addStartProfileAt(
|
export function addStartProfileAt(
|
||||||
node: Program,
|
node: Node<Program>,
|
||||||
pathToNode: PathToNode,
|
pathToNode: PathToNode,
|
||||||
at: [number, number]
|
at: [number, number]
|
||||||
): { modifiedAst: Program; pathToNode: PathToNode } | Error {
|
): { modifiedAst: Node<Program>; pathToNode: PathToNode } | Error {
|
||||||
const _node1 = getNodeFromPath<VariableDeclaration>(
|
const _node1 = getNodeFromPath<VariableDeclaration>(
|
||||||
node,
|
node,
|
||||||
pathToNode,
|
pathToNode,
|
||||||
@ -114,7 +115,7 @@ export function addStartProfileAt(
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function addSketchTo(
|
export function addSketchTo(
|
||||||
node: Program,
|
node: Node<Program>,
|
||||||
axis: 'xy' | 'xz' | 'yz',
|
axis: 'xy' | 'xz' | 'yz',
|
||||||
name = ''
|
name = ''
|
||||||
): { modifiedAst: Program; id: string; pathToNode: PathToNode } {
|
): { modifiedAst: Program; id: string; pathToNode: PathToNode } {
|
||||||
@ -210,7 +211,7 @@ export function mutateArrExp(node: Expr, updateWith: ArrayExpression): boolean {
|
|||||||
|
|
||||||
export function mutateObjExpProp(
|
export function mutateObjExpProp(
|
||||||
node: Expr,
|
node: Expr,
|
||||||
updateWith: Literal | ArrayExpression,
|
updateWith: Node<Literal> | Node<ArrayExpression>,
|
||||||
key: string
|
key: string
|
||||||
): boolean {
|
): boolean {
|
||||||
if (node.type === 'ObjectExpression') {
|
if (node.type === 'ObjectExpression') {
|
||||||
@ -248,13 +249,13 @@ export function mutateObjExpProp(
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function extrudeSketch(
|
export function extrudeSketch(
|
||||||
node: Program,
|
node: Node<Program>,
|
||||||
pathToNode: PathToNode,
|
pathToNode: PathToNode,
|
||||||
shouldPipe = false,
|
shouldPipe = false,
|
||||||
distance: Expr = createLiteral(4)
|
distance: Expr = createLiteral(4)
|
||||||
):
|
):
|
||||||
| {
|
| {
|
||||||
modifiedAst: Program
|
modifiedAst: Node<Program>
|
||||||
pathToNode: PathToNode
|
pathToNode: PathToNode
|
||||||
pathToExtrudeArg: PathToNode
|
pathToExtrudeArg: PathToNode
|
||||||
}
|
}
|
||||||
@ -343,13 +344,13 @@ export function extrudeSketch(
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function revolveSketch(
|
export function revolveSketch(
|
||||||
node: Program,
|
node: Node<Program>,
|
||||||
pathToNode: PathToNode,
|
pathToNode: PathToNode,
|
||||||
shouldPipe = false,
|
shouldPipe = false,
|
||||||
angle: Expr = createLiteral(4)
|
angle: Expr = createLiteral(4)
|
||||||
):
|
):
|
||||||
| {
|
| {
|
||||||
modifiedAst: Program
|
modifiedAst: Node<Program>
|
||||||
pathToNode: PathToNode
|
pathToNode: PathToNode
|
||||||
pathToRevolveArg: PathToNode
|
pathToRevolveArg: PathToNode
|
||||||
}
|
}
|
||||||
@ -439,7 +440,7 @@ export function revolveSketch(
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function sketchOnExtrudedFace(
|
export function sketchOnExtrudedFace(
|
||||||
node: Program,
|
node: Node<Program>,
|
||||||
sketchPathToNode: PathToNode,
|
sketchPathToNode: PathToNode,
|
||||||
extrudePathToNode: PathToNode,
|
extrudePathToNode: PathToNode,
|
||||||
info: ExtrudeFacePlane['faceInfo'] = { type: 'wall' }
|
info: ExtrudeFacePlane['faceInfo'] = { type: 'wall' }
|
||||||
@ -571,7 +572,7 @@ export function splitPathAtPipeExpression(pathToNode: PathToNode): {
|
|||||||
return splitPathAtPipeExpression(pathToNode.slice(0, -1))
|
return splitPathAtPipeExpression(pathToNode.slice(0, -1))
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createLiteral(value: string | number): Literal {
|
export function createLiteral(value: string | number): Node<Literal> {
|
||||||
return {
|
return {
|
||||||
type: 'Literal',
|
type: 'Literal',
|
||||||
start: 0,
|
start: 0,
|
||||||
@ -581,7 +582,7 @@ export function createLiteral(value: string | number): Literal {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createTagDeclarator(value: string): TagDeclarator {
|
export function createTagDeclarator(value: string): Node<TagDeclarator> {
|
||||||
return {
|
return {
|
||||||
type: 'TagDeclarator',
|
type: 'TagDeclarator',
|
||||||
start: 0,
|
start: 0,
|
||||||
@ -591,7 +592,7 @@ export function createTagDeclarator(value: string): TagDeclarator {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createIdentifier(name: string): Identifier {
|
export function createIdentifier(name: string): Node<Identifier> {
|
||||||
return {
|
return {
|
||||||
type: 'Identifier',
|
type: 'Identifier',
|
||||||
start: 0,
|
start: 0,
|
||||||
@ -601,7 +602,7 @@ export function createIdentifier(name: string): Identifier {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createPipeSubstitution(): PipeSubstitution {
|
export function createPipeSubstitution(): Node<PipeSubstitution> {
|
||||||
return {
|
return {
|
||||||
type: 'PipeSubstitution',
|
type: 'PipeSubstitution',
|
||||||
start: 0,
|
start: 0,
|
||||||
@ -612,7 +613,7 @@ export function createPipeSubstitution(): PipeSubstitution {
|
|||||||
export function createCallExpressionStdLib(
|
export function createCallExpressionStdLib(
|
||||||
name: string,
|
name: string,
|
||||||
args: CallExpression['arguments']
|
args: CallExpression['arguments']
|
||||||
): CallExpression {
|
): Node<CallExpression> {
|
||||||
return {
|
return {
|
||||||
type: 'CallExpression',
|
type: 'CallExpression',
|
||||||
start: 0,
|
start: 0,
|
||||||
@ -632,7 +633,7 @@ export function createCallExpressionStdLib(
|
|||||||
export function createCallExpression(
|
export function createCallExpression(
|
||||||
name: string,
|
name: string,
|
||||||
args: CallExpression['arguments']
|
args: CallExpression['arguments']
|
||||||
): CallExpression {
|
): Node<CallExpression> {
|
||||||
return {
|
return {
|
||||||
type: 'CallExpression',
|
type: 'CallExpression',
|
||||||
start: 0,
|
start: 0,
|
||||||
@ -651,7 +652,7 @@ export function createCallExpression(
|
|||||||
|
|
||||||
export function createArrayExpression(
|
export function createArrayExpression(
|
||||||
elements: ArrayExpression['elements']
|
elements: ArrayExpression['elements']
|
||||||
): ArrayExpression {
|
): Node<ArrayExpression> {
|
||||||
return {
|
return {
|
||||||
type: 'ArrayExpression',
|
type: 'ArrayExpression',
|
||||||
start: 0,
|
start: 0,
|
||||||
@ -664,7 +665,7 @@ export function createArrayExpression(
|
|||||||
|
|
||||||
export function createPipeExpression(
|
export function createPipeExpression(
|
||||||
body: PipeExpression['body']
|
body: PipeExpression['body']
|
||||||
): PipeExpression {
|
): Node<PipeExpression> {
|
||||||
return {
|
return {
|
||||||
type: 'PipeExpression',
|
type: 'PipeExpression',
|
||||||
start: 0,
|
start: 0,
|
||||||
@ -680,7 +681,7 @@ export function createVariableDeclaration(
|
|||||||
init: VariableDeclarator['init'],
|
init: VariableDeclarator['init'],
|
||||||
visibility: VariableDeclaration['visibility'] = 'default',
|
visibility: VariableDeclaration['visibility'] = 'default',
|
||||||
kind: VariableDeclaration['kind'] = 'const'
|
kind: VariableDeclaration['kind'] = 'const'
|
||||||
): VariableDeclaration {
|
): Node<VariableDeclaration> {
|
||||||
return {
|
return {
|
||||||
type: 'VariableDeclaration',
|
type: 'VariableDeclaration',
|
||||||
start: 0,
|
start: 0,
|
||||||
@ -703,7 +704,7 @@ export function createVariableDeclaration(
|
|||||||
|
|
||||||
export function createObjectExpression(properties: {
|
export function createObjectExpression(properties: {
|
||||||
[key: string]: Expr
|
[key: string]: Expr
|
||||||
}): ObjectExpression {
|
}): Node<ObjectExpression> {
|
||||||
return {
|
return {
|
||||||
type: 'ObjectExpression',
|
type: 'ObjectExpression',
|
||||||
start: 0,
|
start: 0,
|
||||||
@ -724,7 +725,7 @@ export function createObjectExpression(properties: {
|
|||||||
export function createUnaryExpression(
|
export function createUnaryExpression(
|
||||||
argument: UnaryExpression['argument'],
|
argument: UnaryExpression['argument'],
|
||||||
operator: UnaryExpression['operator'] = '-'
|
operator: UnaryExpression['operator'] = '-'
|
||||||
): UnaryExpression {
|
): Node<UnaryExpression> {
|
||||||
return {
|
return {
|
||||||
type: 'UnaryExpression',
|
type: 'UnaryExpression',
|
||||||
start: 0,
|
start: 0,
|
||||||
@ -739,7 +740,7 @@ export function createBinaryExpression([left, operator, right]: [
|
|||||||
BinaryExpression['left'],
|
BinaryExpression['left'],
|
||||||
BinaryExpression['operator'],
|
BinaryExpression['operator'],
|
||||||
BinaryExpression['right']
|
BinaryExpression['right']
|
||||||
]): BinaryExpression {
|
]): Node<BinaryExpression> {
|
||||||
return {
|
return {
|
||||||
type: 'BinaryExpression',
|
type: 'BinaryExpression',
|
||||||
start: 0,
|
start: 0,
|
||||||
@ -754,19 +755,19 @@ export function createBinaryExpression([left, operator, right]: [
|
|||||||
export function createBinaryExpressionWithUnary([left, right]: [
|
export function createBinaryExpressionWithUnary([left, right]: [
|
||||||
BinaryExpression['left'],
|
BinaryExpression['left'],
|
||||||
BinaryExpression['right']
|
BinaryExpression['right']
|
||||||
]): BinaryExpression {
|
]): Node<BinaryExpression> {
|
||||||
if (right.type === 'UnaryExpression' && right.operator === '-')
|
if (right.type === 'UnaryExpression' && right.operator === '-')
|
||||||
return createBinaryExpression([left, '-', right.argument])
|
return createBinaryExpression([left, '-', right.argument])
|
||||||
return createBinaryExpression([left, '+', right])
|
return createBinaryExpression([left, '+', right])
|
||||||
}
|
}
|
||||||
|
|
||||||
export function giveSketchFnCallTag(
|
export function giveSketchFnCallTag(
|
||||||
ast: Program,
|
ast: Node<Program>,
|
||||||
range: Selection['range'],
|
range: Selection['range'],
|
||||||
tag?: string
|
tag?: string
|
||||||
):
|
):
|
||||||
| {
|
| {
|
||||||
modifiedAst: Program
|
modifiedAst: Node<Program>
|
||||||
tag: string
|
tag: string
|
||||||
isTagExisting: boolean
|
isTagExisting: boolean
|
||||||
pathToNode: PathToNode
|
pathToNode: PathToNode
|
||||||
@ -801,7 +802,7 @@ export function giveSketchFnCallTag(
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function moveValueIntoNewVariablePath(
|
export function moveValueIntoNewVariablePath(
|
||||||
ast: Program,
|
ast: Node<Program>,
|
||||||
programMemory: ProgramMemory,
|
programMemory: ProgramMemory,
|
||||||
pathToNode: PathToNode,
|
pathToNode: PathToNode,
|
||||||
variableName: string
|
variableName: string
|
||||||
@ -834,12 +835,12 @@ export function moveValueIntoNewVariablePath(
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function moveValueIntoNewVariable(
|
export function moveValueIntoNewVariable(
|
||||||
ast: Program,
|
ast: Node<Program>,
|
||||||
programMemory: ProgramMemory,
|
programMemory: ProgramMemory,
|
||||||
sourceRange: Selection['range'],
|
sourceRange: Selection['range'],
|
||||||
variableName: string
|
variableName: string
|
||||||
): {
|
): {
|
||||||
modifiedAst: Program
|
modifiedAst: Node<Program>
|
||||||
pathToReplacedNode?: PathToNode
|
pathToReplacedNode?: PathToNode
|
||||||
} {
|
} {
|
||||||
const meta = isNodeSafeToReplace(ast, sourceRange)
|
const meta = isNodeSafeToReplace(ast, sourceRange)
|
||||||
@ -872,17 +873,17 @@ export function moveValueIntoNewVariable(
|
|||||||
*/
|
*/
|
||||||
export function deleteSegmentFromPipeExpression(
|
export function deleteSegmentFromPipeExpression(
|
||||||
dependentRanges: SourceRange[],
|
dependentRanges: SourceRange[],
|
||||||
modifiedAst: Program,
|
modifiedAst: Node<Program>,
|
||||||
programMemory: ProgramMemory,
|
programMemory: ProgramMemory,
|
||||||
code: string,
|
code: string,
|
||||||
pathToNode: PathToNode
|
pathToNode: PathToNode
|
||||||
): Program | Error {
|
): Node<Program> | Error {
|
||||||
let _modifiedAst = structuredClone(modifiedAst)
|
let _modifiedAst = structuredClone(modifiedAst)
|
||||||
|
|
||||||
dependentRanges.forEach((range) => {
|
dependentRanges.forEach((range) => {
|
||||||
const path = getNodePathFromSourceRange(_modifiedAst, range)
|
const path = getNodePathFromSourceRange(_modifiedAst, range)
|
||||||
|
|
||||||
const callExp = getNodeFromPath<CallExpression>(
|
const callExp = getNodeFromPath<Node<CallExpression>>(
|
||||||
_modifiedAst,
|
_modifiedAst,
|
||||||
path,
|
path,
|
||||||
'CallExpression',
|
'CallExpression',
|
||||||
@ -928,11 +929,11 @@ export function deleteSegmentFromPipeExpression(
|
|||||||
export function removeSingleConstraintInfo(
|
export function removeSingleConstraintInfo(
|
||||||
pathToCallExp: PathToNode,
|
pathToCallExp: PathToNode,
|
||||||
argDetails: SimplifiedArgDetails,
|
argDetails: SimplifiedArgDetails,
|
||||||
ast: Program,
|
ast: Node<Program>,
|
||||||
programMemory: ProgramMemory
|
programMemory: ProgramMemory
|
||||||
):
|
):
|
||||||
| {
|
| {
|
||||||
modifiedAst: Program
|
modifiedAst: Node<Program>
|
||||||
pathToNodeMap: PathToNodeMap
|
pathToNodeMap: PathToNodeMap
|
||||||
}
|
}
|
||||||
| false {
|
| false {
|
||||||
@ -954,12 +955,12 @@ export function removeSingleConstraintInfo(
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function deleteFromSelection(
|
export async function deleteFromSelection(
|
||||||
ast: Program,
|
ast: Node<Program>,
|
||||||
selection: Selection,
|
selection: Selection,
|
||||||
programMemory: ProgramMemory,
|
programMemory: ProgramMemory,
|
||||||
getFaceDetails: (id: string) => Promise<Models['FaceIsPlanar_type']> = () =>
|
getFaceDetails: (id: string) => Promise<Models['FaceIsPlanar_type']> = () =>
|
||||||
({} as any)
|
({} as any)
|
||||||
): Promise<Program | Error> {
|
): Promise<Node<Program> | Error> {
|
||||||
const astClone = structuredClone(ast)
|
const astClone = structuredClone(ast)
|
||||||
const range = selection.range
|
const range = selection.range
|
||||||
const path = getNodePathFromSourceRange(ast, range)
|
const path = getNodePathFromSourceRange(ast, range)
|
||||||
@ -1134,5 +1135,5 @@ export async function deleteFromSelection(
|
|||||||
}
|
}
|
||||||
|
|
||||||
const nonCodeMetaEmpty = () => {
|
const nonCodeMetaEmpty = () => {
|
||||||
return { nonCodeNodes: {}, start: [] }
|
return { nonCodeNodes: {}, startNodes: [], start: 0, end: 0 }
|
||||||
}
|
}
|
||||||
|
@ -36,11 +36,12 @@ import {
|
|||||||
getSweepFromSuspectedPath,
|
getSweepFromSuspectedPath,
|
||||||
} from 'lang/std/artifactGraph'
|
} from 'lang/std/artifactGraph'
|
||||||
import { kclManager, engineCommandManager, editorManager } from 'lib/singletons'
|
import { kclManager, engineCommandManager, editorManager } from 'lib/singletons'
|
||||||
|
import { Node } from 'wasm-lib/kcl/bindings/Node'
|
||||||
|
|
||||||
// Apply Fillet To Selection
|
// Apply Fillet To Selection
|
||||||
|
|
||||||
export function applyFilletToSelection(
|
export function applyFilletToSelection(
|
||||||
ast: Program,
|
ast: Node<Program>,
|
||||||
selection: Selections,
|
selection: Selections,
|
||||||
radius: KclCommandValue
|
radius: KclCommandValue
|
||||||
): void | Error {
|
): void | Error {
|
||||||
@ -55,10 +56,10 @@ export function applyFilletToSelection(
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function modifyAstCloneWithFilletAndTag(
|
export function modifyAstCloneWithFilletAndTag(
|
||||||
ast: Program,
|
ast: Node<Program>,
|
||||||
selection: Selections,
|
selection: Selections,
|
||||||
radius: KclCommandValue
|
radius: KclCommandValue
|
||||||
): { modifiedAst: Program; pathToFilletNode: Array<PathToNode> } | Error {
|
): { modifiedAst: Node<Program>; pathToFilletNode: Array<PathToNode> } | Error {
|
||||||
let clonedAst = structuredClone(ast)
|
let clonedAst = structuredClone(ast)
|
||||||
const clonedAstForGetExtrude = structuredClone(ast)
|
const clonedAstForGetExtrude = structuredClone(ast)
|
||||||
|
|
||||||
@ -246,7 +247,7 @@ export function getPathToExtrudeForSegmentSelection(
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function updateAstAndFocus(
|
async function updateAstAndFocus(
|
||||||
modifiedAst: Program,
|
modifiedAst: Node<Program>,
|
||||||
pathToFilletNode: Array<PathToNode>
|
pathToFilletNode: Array<PathToNode>
|
||||||
) {
|
) {
|
||||||
const updatedAst = await kclManager.updateAst(modifiedAst, true, {
|
const updatedAst = await kclManager.updateAst(modifiedAst, true, {
|
||||||
@ -258,7 +259,7 @@ async function updateAstAndFocus(
|
|||||||
}
|
}
|
||||||
|
|
||||||
function mutateAstWithTagForSketchSegment(
|
function mutateAstWithTagForSketchSegment(
|
||||||
astClone: Program,
|
astClone: Node<Program>,
|
||||||
pathToSegmentNode: PathToNode
|
pathToSegmentNode: PathToNode
|
||||||
): { modifiedAst: Program; tag: string } | Error {
|
): { modifiedAst: Program; tag: string } | Error {
|
||||||
const segmentNode = getNodeFromPath<CallExpression>(
|
const segmentNode = getNodeFromPath<CallExpression>(
|
||||||
@ -292,7 +293,7 @@ function mutateAstWithTagForSketchSegment(
|
|||||||
function getEdgeTagCall(
|
function getEdgeTagCall(
|
||||||
tag: string,
|
tag: string,
|
||||||
selectionType: string
|
selectionType: string
|
||||||
): Identifier | CallExpression {
|
): Node<Identifier | CallExpression> {
|
||||||
let tagCall: Expr = createIdentifier(tag)
|
let tagCall: Expr = createIdentifier(tag)
|
||||||
|
|
||||||
// Modify the tag based on selectionType
|
// Modify the tag based on selectionType
|
||||||
@ -426,7 +427,7 @@ export const hasValidFilletSelection = ({
|
|||||||
code,
|
code,
|
||||||
}: {
|
}: {
|
||||||
selectionRanges: Selections
|
selectionRanges: Selections
|
||||||
ast: Program
|
ast: Node<Program>
|
||||||
code: string
|
code: string
|
||||||
}) => {
|
}) => {
|
||||||
// check if there is anything filletable in the scene
|
// check if there is anything filletable in the scene
|
||||||
@ -454,7 +455,7 @@ export const hasValidFilletSelection = ({
|
|||||||
for (const selection of selectionRanges.codeBasedSelections) {
|
for (const selection of selectionRanges.codeBasedSelections) {
|
||||||
// check if all selections are in sketchLineHelperMap
|
// check if all selections are in sketchLineHelperMap
|
||||||
const path = getNodePathFromSourceRange(ast, selection.range)
|
const path = getNodePathFromSourceRange(ast, selection.range)
|
||||||
const segmentNode = getNodeFromPath<CallExpression>(
|
const segmentNode = getNodeFromPath<Node<CallExpression>>(
|
||||||
ast,
|
ast,
|
||||||
path,
|
path,
|
||||||
'CallExpression'
|
'CallExpression'
|
||||||
@ -534,7 +535,7 @@ export const isTagUsedInFillet = ({
|
|||||||
ast,
|
ast,
|
||||||
callExp,
|
callExp,
|
||||||
}: {
|
}: {
|
||||||
ast: Program
|
ast: Node<Program>
|
||||||
callExp: CallExpression
|
callExp: CallExpression
|
||||||
}): Array<EdgeTypes> => {
|
}): Array<EdgeTypes> => {
|
||||||
const tag = getTagFromCallExpression(callExp)
|
const tag = getTagFromCallExpression(callExp)
|
||||||
|
@ -29,6 +29,7 @@ import {
|
|||||||
} from './std/sketchcombos'
|
} from './std/sketchcombos'
|
||||||
import { err } from 'lib/trap'
|
import { err } from 'lib/trap'
|
||||||
import { ImportStatement } from 'wasm-lib/kcl/bindings/ImportStatement'
|
import { ImportStatement } from 'wasm-lib/kcl/bindings/ImportStatement'
|
||||||
|
import { Node } from 'wasm-lib/kcl/bindings/Node'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves a node from a given path within a Program node structure, optionally stopping at a specified node type.
|
* Retrieves a node from a given path within a Program node structure, optionally stopping at a specified node type.
|
||||||
@ -121,12 +122,13 @@ export function getNodeFromPathCurry(
|
|||||||
}
|
}
|
||||||
|
|
||||||
function moreNodePathFromSourceRange(
|
function moreNodePathFromSourceRange(
|
||||||
node:
|
node: Node<
|
||||||
| Expr
|
| Expr
|
||||||
| ImportStatement
|
| ImportStatement
|
||||||
| ExpressionStatement
|
| ExpressionStatement
|
||||||
| VariableDeclaration
|
| VariableDeclaration
|
||||||
| ReturnStatement,
|
| ReturnStatement
|
||||||
|
>,
|
||||||
sourceRange: Selection['range'],
|
sourceRange: Selection['range'],
|
||||||
previousPath: PathToNode = [['body', '']]
|
previousPath: PathToNode = [['body', '']]
|
||||||
): PathToNode {
|
): PathToNode {
|
||||||
@ -344,15 +346,16 @@ export function getNodePathFromSourceRange(
|
|||||||
return path
|
return path
|
||||||
}
|
}
|
||||||
|
|
||||||
type KCLNode =
|
type KCLNode = Node<
|
||||||
| Expr
|
| Expr
|
||||||
| ExpressionStatement
|
| ExpressionStatement
|
||||||
| VariableDeclaration
|
| VariableDeclaration
|
||||||
| VariableDeclarator
|
| VariableDeclarator
|
||||||
| ReturnStatement
|
| ReturnStatement
|
||||||
|
>
|
||||||
|
|
||||||
export function traverse(
|
export function traverse(
|
||||||
node: KCLNode | Program,
|
node: KCLNode | Node<Program>,
|
||||||
option: {
|
option: {
|
||||||
enter?: (node: KCLNode, pathToNode: PathToNode) => void
|
enter?: (node: KCLNode, pathToNode: PathToNode) => void
|
||||||
leave?: (node: KCLNode) => void
|
leave?: (node: KCLNode) => void
|
||||||
@ -512,9 +515,9 @@ export function findAllPreviousVariables(
|
|||||||
}
|
}
|
||||||
|
|
||||||
type ReplacerFn = (
|
type ReplacerFn = (
|
||||||
_ast: Program,
|
_ast: Node<Program>,
|
||||||
varName: string
|
varName: string
|
||||||
) => { modifiedAst: Program; pathToReplaced: PathToNode } | Error
|
) => { modifiedAst: Node<Program>; pathToReplaced: PathToNode } | Error
|
||||||
|
|
||||||
export function isNodeSafeToReplacePath(
|
export function isNodeSafeToReplacePath(
|
||||||
ast: Program,
|
ast: Program,
|
||||||
@ -583,12 +586,12 @@ export function isNodeSafeToReplacePath(
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function isNodeSafeToReplace(
|
export function isNodeSafeToReplace(
|
||||||
ast: Program,
|
ast: Node<Program>,
|
||||||
sourceRange: [number, number]
|
sourceRange: [number, number]
|
||||||
):
|
):
|
||||||
| {
|
| {
|
||||||
isSafe: boolean
|
isSafe: boolean
|
||||||
value: Expr
|
value: Node<Expr>
|
||||||
replacer: ReplacerFn
|
replacer: ReplacerFn
|
||||||
}
|
}
|
||||||
| Error {
|
| Error {
|
||||||
@ -837,7 +840,7 @@ export function findUsesOfTagInPipe(
|
|||||||
? String(thirdParam.value)
|
? String(thirdParam.value)
|
||||||
: thirdParam.name
|
: thirdParam.name
|
||||||
|
|
||||||
const varDec = getNodeFromPath<VariableDeclaration>(
|
const varDec = getNodeFromPath<Node<VariableDeclaration>>(
|
||||||
ast,
|
ast,
|
||||||
pathToNode,
|
pathToNode,
|
||||||
'VariableDeclaration'
|
'VariableDeclaration'
|
||||||
@ -898,7 +901,7 @@ export function hasSketchPipeBeenExtruded(selection: Selection, ast: Program) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** File must contain at least one sketch that has not been extruded already */
|
/** File must contain at least one sketch that has not been extruded already */
|
||||||
export function doesSceneHaveSweepableSketch(ast: Program) {
|
export function doesSceneHaveSweepableSketch(ast: Node<Program>) {
|
||||||
const theMap: any = {}
|
const theMap: any = {}
|
||||||
traverse(ast as any, {
|
traverse(ast as any, {
|
||||||
enter(node) {
|
enter(node) {
|
||||||
|
@ -17,6 +17,7 @@ import {
|
|||||||
import { getNodeFromPath, getNodePathFromSourceRange } from '../queryAst'
|
import { getNodeFromPath, getNodePathFromSourceRange } from '../queryAst'
|
||||||
import { enginelessExecutor } from '../../lib/testHelpers'
|
import { enginelessExecutor } from '../../lib/testHelpers'
|
||||||
import { err } from 'lib/trap'
|
import { err } from 'lib/trap'
|
||||||
|
import { Node } from 'wasm-lib/kcl/bindings/Node'
|
||||||
|
|
||||||
const eachQuad: [number, [number, number]][] = [
|
const eachQuad: [number, [number, number]][] = [
|
||||||
[-315, [1, 1]],
|
[-315, [1, 1]],
|
||||||
@ -687,7 +688,7 @@ describe('testing getConstraintInfo', () => {
|
|||||||
]
|
]
|
||||||
if (err(ast)) return ast
|
if (err(ast)) return ast
|
||||||
const pathToNode = getNodePathFromSourceRange(ast, sourceRange)
|
const pathToNode = getNodePathFromSourceRange(ast, sourceRange)
|
||||||
const callExp = getNodeFromPath<CallExpression>(
|
const callExp = getNodeFromPath<Node<CallExpression>>(
|
||||||
ast,
|
ast,
|
||||||
pathToNode,
|
pathToNode,
|
||||||
'CallExpression'
|
'CallExpression'
|
||||||
@ -841,7 +842,7 @@ describe('testing getConstraintInfo', () => {
|
|||||||
]
|
]
|
||||||
if (err(ast)) return ast
|
if (err(ast)) return ast
|
||||||
const pathToNode = getNodePathFromSourceRange(ast, sourceRange)
|
const pathToNode = getNodePathFromSourceRange(ast, sourceRange)
|
||||||
const callExp = getNodeFromPath<CallExpression>(
|
const callExp = getNodeFromPath<Node<CallExpression>>(
|
||||||
ast,
|
ast,
|
||||||
pathToNode,
|
pathToNode,
|
||||||
'CallExpression'
|
'CallExpression'
|
||||||
@ -1197,7 +1198,7 @@ describe('testing getConstraintInfo', () => {
|
|||||||
]
|
]
|
||||||
if (err(ast)) return ast
|
if (err(ast)) return ast
|
||||||
const pathToNode = getNodePathFromSourceRange(ast, sourceRange)
|
const pathToNode = getNodePathFromSourceRange(ast, sourceRange)
|
||||||
const callExp = getNodeFromPath<CallExpression>(
|
const callExp = getNodeFromPath<Node<CallExpression>>(
|
||||||
ast,
|
ast,
|
||||||
pathToNode,
|
pathToNode,
|
||||||
'CallExpression'
|
'CallExpression'
|
||||||
|
@ -55,6 +55,7 @@ import { err } from 'lib/trap'
|
|||||||
import { perpendicularDistance } from 'sketch-helpers'
|
import { perpendicularDistance } from 'sketch-helpers'
|
||||||
import { TagDeclarator } from 'wasm-lib/kcl/bindings/TagDeclarator'
|
import { TagDeclarator } from 'wasm-lib/kcl/bindings/TagDeclarator'
|
||||||
import { EdgeCutInfo } from 'machines/modelingMachine'
|
import { EdgeCutInfo } from 'machines/modelingMachine'
|
||||||
|
import { Node } from 'wasm-lib/kcl/bindings/Node'
|
||||||
|
|
||||||
const STRAIGHT_SEGMENT_ERR = new Error(
|
const STRAIGHT_SEGMENT_ERR = new Error(
|
||||||
'Invalid input, expected "straight-segment"'
|
'Invalid input, expected "straight-segment"'
|
||||||
@ -1785,7 +1786,7 @@ export const angledLineThatIntersects: SketchLineHelper = {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
if (intersectTag !== -1) {
|
if (intersectTag !== -1) {
|
||||||
const tag = firstArg.properties[intersectTag]?.value as Identifier
|
const tag = firstArg.properties[intersectTag]?.value as Node<Identifier>
|
||||||
const pathToTagProp: PathToNode = [
|
const pathToTagProp: PathToNode = [
|
||||||
...pathToObjectExp,
|
...pathToObjectExp,
|
||||||
[intersectTag, 'index'],
|
[intersectTag, 'index'],
|
||||||
@ -1825,7 +1826,9 @@ export const updateStartProfileAtArgs: SketchLineHelper['updateArgs'] = ({
|
|||||||
body: [],
|
body: [],
|
||||||
|
|
||||||
nonCodeMeta: {
|
nonCodeMeta: {
|
||||||
start: [],
|
start: 0,
|
||||||
|
end: 0,
|
||||||
|
startNodes: [],
|
||||||
nonCodeNodes: [],
|
nonCodeNodes: [],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -1865,7 +1868,7 @@ export const sketchLineHelperMap: { [key: string]: SketchLineHelper } = {
|
|||||||
} as const
|
} as const
|
||||||
|
|
||||||
export function changeSketchArguments(
|
export function changeSketchArguments(
|
||||||
node: Program,
|
node: Node<Program>,
|
||||||
programMemory: ProgramMemory,
|
programMemory: ProgramMemory,
|
||||||
sourceRangeOrPath:
|
sourceRangeOrPath:
|
||||||
| {
|
| {
|
||||||
@ -1877,7 +1880,7 @@ export function changeSketchArguments(
|
|||||||
pathToNode: PathToNode
|
pathToNode: PathToNode
|
||||||
},
|
},
|
||||||
input: SegmentInputs
|
input: SegmentInputs
|
||||||
): { modifiedAst: Program; pathToNode: PathToNode } | Error {
|
): { modifiedAst: Node<Program>; pathToNode: PathToNode } | Error {
|
||||||
const _node = { ...node }
|
const _node = { ...node }
|
||||||
const thePath =
|
const thePath =
|
||||||
sourceRangeOrPath.type === 'sourceRange'
|
sourceRangeOrPath.type === 'sourceRange'
|
||||||
@ -1906,7 +1909,7 @@ export function changeSketchArguments(
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function getConstraintInfo(
|
export function getConstraintInfo(
|
||||||
callExpression: CallExpression,
|
callExpression: Node<CallExpression>,
|
||||||
code: string,
|
code: string,
|
||||||
pathToNode: PathToNode
|
pathToNode: PathToNode
|
||||||
): ConstrainInfo[] {
|
): ConstrainInfo[] {
|
||||||
@ -1944,7 +1947,7 @@ export function compareVec2Epsilon2(
|
|||||||
}
|
}
|
||||||
|
|
||||||
interface CreateLineFnCallArgs {
|
interface CreateLineFnCallArgs {
|
||||||
node: Program
|
node: Node<Program>
|
||||||
programMemory: ProgramMemory
|
programMemory: ProgramMemory
|
||||||
input: SegmentInputs
|
input: SegmentInputs
|
||||||
fnName: ToolTip
|
fnName: ToolTip
|
||||||
@ -1961,7 +1964,7 @@ export function addNewSketchLn({
|
|||||||
spliceBetween = false,
|
spliceBetween = false,
|
||||||
}: CreateLineFnCallArgs):
|
}: CreateLineFnCallArgs):
|
||||||
| {
|
| {
|
||||||
modifiedAst: Program
|
modifiedAst: Node<Program>
|
||||||
pathToNode: PathToNode
|
pathToNode: PathToNode
|
||||||
}
|
}
|
||||||
| Error {
|
| Error {
|
||||||
@ -1971,8 +1974,12 @@ export function addNewSketchLn({
|
|||||||
return new Error('not a sketch line helper')
|
return new Error('not a sketch line helper')
|
||||||
}
|
}
|
||||||
|
|
||||||
getNodeFromPath<VariableDeclarator>(node, pathToNode, 'VariableDeclarator')
|
getNodeFromPath<Node<VariableDeclarator>>(
|
||||||
getNodeFromPath<PipeExpression | CallExpression>(
|
node,
|
||||||
|
pathToNode,
|
||||||
|
'VariableDeclarator'
|
||||||
|
)
|
||||||
|
getNodeFromPath<Node<PipeExpression | CallExpression>>(
|
||||||
node,
|
node,
|
||||||
pathToNode,
|
pathToNode,
|
||||||
'PipeExpression'
|
'PipeExpression'
|
||||||
@ -1991,13 +1998,13 @@ export function addCallExpressionsToPipe({
|
|||||||
pathToNode,
|
pathToNode,
|
||||||
expressions,
|
expressions,
|
||||||
}: {
|
}: {
|
||||||
node: Program
|
node: Node<Program>
|
||||||
programMemory: ProgramMemory
|
programMemory: ProgramMemory
|
||||||
pathToNode: PathToNode
|
pathToNode: PathToNode
|
||||||
expressions: CallExpression[]
|
expressions: Node<CallExpression>[]
|
||||||
}) {
|
}) {
|
||||||
const _node = { ...node }
|
const _node = { ...node }
|
||||||
const pipeExpression = getNodeFromPath<PipeExpression>(
|
const pipeExpression = getNodeFromPath<Node<PipeExpression>>(
|
||||||
_node,
|
_node,
|
||||||
pathToNode,
|
pathToNode,
|
||||||
'PipeExpression'
|
'PipeExpression'
|
||||||
@ -2046,7 +2053,7 @@ export function replaceSketchLine({
|
|||||||
replaceExistingCallback,
|
replaceExistingCallback,
|
||||||
referencedSegment,
|
referencedSegment,
|
||||||
}: {
|
}: {
|
||||||
node: Program
|
node: Node<Program>
|
||||||
programMemory: ProgramMemory
|
programMemory: ProgramMemory
|
||||||
pathToNode: PathToNode
|
pathToNode: PathToNode
|
||||||
fnName: ToolTip
|
fnName: ToolTip
|
||||||
@ -2055,7 +2062,7 @@ export function replaceSketchLine({
|
|||||||
referencedSegment?: Path
|
referencedSegment?: Path
|
||||||
}):
|
}):
|
||||||
| {
|
| {
|
||||||
modifiedAst: Program
|
modifiedAst: Node<Program>
|
||||||
valueUsedInTransform?: number
|
valueUsedInTransform?: number
|
||||||
pathToNode: PathToNode
|
pathToNode: PathToNode
|
||||||
}
|
}
|
||||||
@ -2107,7 +2114,7 @@ function addTagToChamfer(
|
|||||||
edgeCutMeta: EdgeCutInfo | null
|
edgeCutMeta: EdgeCutInfo | null
|
||||||
):
|
):
|
||||||
| {
|
| {
|
||||||
modifiedAst: Program
|
modifiedAst: Node<Program>
|
||||||
tag: string
|
tag: string
|
||||||
}
|
}
|
||||||
| Error {
|
| Error {
|
||||||
@ -2234,7 +2241,7 @@ export function addTagForSketchOnFace(
|
|||||||
edgeCutMeta: EdgeCutInfo | null
|
edgeCutMeta: EdgeCutInfo | null
|
||||||
):
|
):
|
||||||
| {
|
| {
|
||||||
modifiedAst: Program
|
modifiedAst: Node<Program>
|
||||||
tag: string
|
tag: string
|
||||||
}
|
}
|
||||||
| Error {
|
| Error {
|
||||||
@ -2272,12 +2279,14 @@ function isAngleLiteral(lineArugement: Expr): boolean {
|
|||||||
: false
|
: false
|
||||||
}
|
}
|
||||||
|
|
||||||
type addTagFn = (a: AddTagInfo) => { modifiedAst: Program; tag: string } | Error
|
type addTagFn = (
|
||||||
|
a: AddTagInfo
|
||||||
|
) => { modifiedAst: Node<Program>; tag: string } | Error
|
||||||
|
|
||||||
function addTag(tagIndex = 2): addTagFn {
|
function addTag(tagIndex = 2): addTagFn {
|
||||||
return ({ node, pathToNode }) => {
|
return ({ node, pathToNode }) => {
|
||||||
const _node = { ...node }
|
const _node = { ...node }
|
||||||
const callExpr = getNodeFromPath<CallExpression>(
|
const callExpr = getNodeFromPath<Node<CallExpression>>(
|
||||||
_node,
|
_node,
|
||||||
pathToNode,
|
pathToNode,
|
||||||
'CallExpression'
|
'CallExpression'
|
||||||
|
@ -49,6 +49,7 @@ import {
|
|||||||
getSketchSegmentFromSourceRange,
|
getSketchSegmentFromSourceRange,
|
||||||
} from './sketchConstraints'
|
} from './sketchConstraints'
|
||||||
import { getAngle, roundOff, normaliseAngle } from '../../lib/utils'
|
import { getAngle, roundOff, normaliseAngle } from '../../lib/utils'
|
||||||
|
import { Node } from 'wasm-lib/kcl/bindings/Node'
|
||||||
|
|
||||||
export type LineInputsType =
|
export type LineInputsType =
|
||||||
| 'xAbsolute'
|
| 'xAbsolute'
|
||||||
@ -325,7 +326,7 @@ const setHorzVertDistanceCreateNode =
|
|||||||
if (isUndef(refNum) || err(literalArg)) return REF_NUM_ERR
|
if (isUndef(refNum) || err(literalArg)) return REF_NUM_ERR
|
||||||
|
|
||||||
const valueUsedInTransform = roundOff(literalArg - refNum, 2)
|
const valueUsedInTransform = roundOff(literalArg - refNum, 2)
|
||||||
let finalValue: Expr = createBinaryExpressionWithUnary([
|
let finalValue: Node<Expr> = createBinaryExpressionWithUnary([
|
||||||
createSegEnd(referenceSegName, !index),
|
createSegEnd(referenceSegName, !index),
|
||||||
forceValueUsedInTransform || createLiteral(valueUsedInTransform),
|
forceValueUsedInTransform || createLiteral(valueUsedInTransform),
|
||||||
])
|
])
|
||||||
@ -683,6 +684,14 @@ const transformMap: TransformMap = {
|
|||||||
tag
|
tag
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
|
xAbs: {
|
||||||
|
tooltip: 'lineTo',
|
||||||
|
createNode: setAbsDistanceCreateNode('x'),
|
||||||
|
},
|
||||||
|
yAbs: {
|
||||||
|
tooltip: 'lineTo',
|
||||||
|
createNode: setAbsDistanceCreateNode('y'),
|
||||||
|
},
|
||||||
},
|
},
|
||||||
xAbsolute: {
|
xAbsolute: {
|
||||||
equalLength: {
|
equalLength: {
|
||||||
@ -1541,7 +1550,7 @@ export function transformSecondarySketchLinesTagFirst({
|
|||||||
forceSegName,
|
forceSegName,
|
||||||
forceValueUsedInTransform,
|
forceValueUsedInTransform,
|
||||||
}: {
|
}: {
|
||||||
ast: Program
|
ast: Node<Program>
|
||||||
selectionRanges: Selections
|
selectionRanges: Selections
|
||||||
transformInfos: TransformInfo[]
|
transformInfos: TransformInfo[]
|
||||||
programMemory: ProgramMemory
|
programMemory: ProgramMemory
|
||||||
@ -1549,7 +1558,7 @@ export function transformSecondarySketchLinesTagFirst({
|
|||||||
forceValueUsedInTransform?: BinaryPart
|
forceValueUsedInTransform?: BinaryPart
|
||||||
}):
|
}):
|
||||||
| {
|
| {
|
||||||
modifiedAst: Program
|
modifiedAst: Node<Program>
|
||||||
valueUsedInTransform?: number
|
valueUsedInTransform?: number
|
||||||
pathToNodeMap: PathToNodeMap
|
pathToNodeMap: PathToNodeMap
|
||||||
tagInfo: {
|
tagInfo: {
|
||||||
@ -1620,7 +1629,7 @@ export function transformAstSketchLines({
|
|||||||
forceValueUsedInTransform,
|
forceValueUsedInTransform,
|
||||||
referencedSegmentRange,
|
referencedSegmentRange,
|
||||||
}: {
|
}: {
|
||||||
ast: Program
|
ast: Node<Program>
|
||||||
selectionRanges: Selections | PathToNode[]
|
selectionRanges: Selections | PathToNode[]
|
||||||
transformInfos: TransformInfo[]
|
transformInfos: TransformInfo[]
|
||||||
programMemory: ProgramMemory
|
programMemory: ProgramMemory
|
||||||
@ -1629,7 +1638,7 @@ export function transformAstSketchLines({
|
|||||||
referencedSegmentRange?: Selection['range']
|
referencedSegmentRange?: Selection['range']
|
||||||
}):
|
}):
|
||||||
| {
|
| {
|
||||||
modifiedAst: Program
|
modifiedAst: Node<Program>
|
||||||
valueUsedInTransform?: number
|
valueUsedInTransform?: number
|
||||||
pathToNodeMap: PathToNodeMap
|
pathToNodeMap: PathToNodeMap
|
||||||
}
|
}
|
||||||
@ -1647,7 +1656,7 @@ export function transformAstSketchLines({
|
|||||||
|
|
||||||
const getNode = getNodeFromPathCurry(node, _pathToNode)
|
const getNode = getNodeFromPathCurry(node, _pathToNode)
|
||||||
|
|
||||||
const callExp = getNode<CallExpression>('CallExpression')
|
const callExp = getNode<Node<CallExpression>>('CallExpression')
|
||||||
if (err(callExp)) return callExp
|
if (err(callExp)) return callExp
|
||||||
const varDec = getNode<VariableDeclarator>('VariableDeclarator')
|
const varDec = getNode<VariableDeclarator>('VariableDeclarator')
|
||||||
if (err(varDec)) return varDec
|
if (err(varDec)) return varDec
|
||||||
@ -1806,13 +1815,16 @@ function createSegAngle(referenceSegName: string): BinaryPart {
|
|||||||
return createCallExpression('segAng', [createIdentifier(referenceSegName)])
|
return createCallExpression('segAng', [createIdentifier(referenceSegName)])
|
||||||
}
|
}
|
||||||
|
|
||||||
function createSegEnd(referenceSegName: string, isX: boolean): CallExpression {
|
function createSegEnd(
|
||||||
|
referenceSegName: string,
|
||||||
|
isX: boolean
|
||||||
|
): Node<CallExpression> {
|
||||||
return createCallExpression(isX ? 'segEndX' : 'segEndY', [
|
return createCallExpression(isX ? 'segEndX' : 'segEndY', [
|
||||||
createIdentifier(referenceSegName),
|
createIdentifier(referenceSegName),
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
|
|
||||||
function createLastSeg(isX: boolean): CallExpression {
|
function createLastSeg(isX: boolean): Node<CallExpression> {
|
||||||
return createCallExpression(isX ? 'lastSegX' : 'lastSegY', [
|
return createCallExpression(isX ? 'lastSegX' : 'lastSegY', [
|
||||||
createPipeSubstitution(),
|
createPipeSubstitution(),
|
||||||
])
|
])
|
||||||
@ -1830,7 +1842,7 @@ export function getConstraintLevelFromSourceRange(
|
|||||||
ast: Program | Error
|
ast: Program | Error
|
||||||
): Error | { range: [number, number]; level: ConstraintLevel } {
|
): Error | { range: [number, number]; level: ConstraintLevel } {
|
||||||
if (err(ast)) return ast
|
if (err(ast)) return ast
|
||||||
const nodeMeta = getNodeFromPath<CallExpression>(
|
const nodeMeta = getNodeFromPath<Node<CallExpression>>(
|
||||||
ast,
|
ast,
|
||||||
getNodePathFromSourceRange(ast, cursorRange),
|
getNodePathFromSourceRange(ast, cursorRange),
|
||||||
'CallExpression'
|
'CallExpression'
|
||||||
|
@ -11,16 +11,17 @@ import {
|
|||||||
BinaryPart,
|
BinaryPart,
|
||||||
} from '../wasm'
|
} from '../wasm'
|
||||||
import { LineInputsType } from './sketchcombos'
|
import { LineInputsType } from './sketchcombos'
|
||||||
|
import { Node } from 'wasm-lib/kcl/bindings/Node'
|
||||||
|
|
||||||
export interface ModifyAstBase {
|
export interface ModifyAstBase {
|
||||||
node: Program
|
node: Node<Program>
|
||||||
// TODO #896: Remove ProgramMemory from this interface
|
// TODO #896: Remove ProgramMemory from this interface
|
||||||
previousProgramMemory: ProgramMemory
|
previousProgramMemory: ProgramMemory
|
||||||
pathToNode: PathToNode
|
pathToNode: PathToNode
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AddTagInfo {
|
export interface AddTagInfo {
|
||||||
node: Program
|
node: Node<Program>
|
||||||
pathToNode: PathToNode
|
pathToNode: PathToNode
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -134,7 +135,7 @@ type _InputArg<T> =
|
|||||||
* Which is why a union type is used that can be type narrowed using the {@link RawArg.type} property
|
* Which is why a union type is used that can be type narrowed using the {@link RawArg.type} property
|
||||||
* {@link RawArg.expr} is common to all of these types
|
* {@link RawArg.expr} is common to all of these types
|
||||||
*/
|
*/
|
||||||
export type InputArg = _InputArg<Expr>
|
export type InputArg = _InputArg<Node<Expr>>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@link RawArg.expr} is the literal equivalent of whatever current expression is
|
* {@link RawArg.expr} is the literal equivalent of whatever current expression is
|
||||||
@ -142,7 +143,7 @@ export type InputArg = _InputArg<Expr>
|
|||||||
* but of course works for expressions like myVar + someFn() etc too
|
* but of course works for expressions like myVar + someFn() etc too
|
||||||
* This is useful in cases where we want to "un-constrain" inputs to segments
|
* This is useful in cases where we want to "un-constrain" inputs to segments
|
||||||
*/
|
*/
|
||||||
type RawArg = _InputArg<Literal>
|
type RawArg = _InputArg<Node<Literal>>
|
||||||
|
|
||||||
export type InputArgs = Array<InputArg>
|
export type InputArgs = Array<InputArg>
|
||||||
|
|
||||||
@ -186,7 +187,7 @@ export type CreateStdLibSketchCallExpr = (args: {
|
|||||||
inputs: InputArgs
|
inputs: InputArgs
|
||||||
rawArgs: RawArgs
|
rawArgs: RawArgs
|
||||||
referenceSegName: string
|
referenceSegName: string
|
||||||
tag?: Expr
|
tag?: Node<Expr>
|
||||||
forceValueUsedInTransform?: BinaryPart
|
forceValueUsedInTransform?: BinaryPart
|
||||||
referencedSegment?: Path
|
referencedSegment?: Path
|
||||||
}) => CreatedSketchExprResult | Error
|
}) => CreatedSketchExprResult | Error
|
||||||
@ -215,26 +216,26 @@ export interface ConstrainInfo {
|
|||||||
export interface SketchLineHelper {
|
export interface SketchLineHelper {
|
||||||
add: (a: addCall) =>
|
add: (a: addCall) =>
|
||||||
| {
|
| {
|
||||||
modifiedAst: Program
|
modifiedAst: Node<Program>
|
||||||
pathToNode: PathToNode
|
pathToNode: PathToNode
|
||||||
valueUsedInTransform?: number
|
valueUsedInTransform?: number
|
||||||
}
|
}
|
||||||
| Error
|
| Error
|
||||||
updateArgs: (a: updateArgs) =>
|
updateArgs: (a: updateArgs) =>
|
||||||
| {
|
| {
|
||||||
modifiedAst: Program
|
modifiedAst: Node<Program>
|
||||||
pathToNode: PathToNode
|
pathToNode: PathToNode
|
||||||
}
|
}
|
||||||
| Error
|
| Error
|
||||||
getTag: (a: CallExpression) => string | Error
|
getTag: (a: CallExpression) => string | Error
|
||||||
addTag: (a: AddTagInfo) =>
|
addTag: (a: AddTagInfo) =>
|
||||||
| {
|
| {
|
||||||
modifiedAst: Program
|
modifiedAst: Node<Program>
|
||||||
tag: string
|
tag: string
|
||||||
}
|
}
|
||||||
| Error
|
| Error
|
||||||
getConstraintInfo: (
|
getConstraintInfo: (
|
||||||
callExp: CallExpression,
|
callExp: Node<CallExpression>,
|
||||||
code: string,
|
code: string,
|
||||||
pathToNode: PathToNode
|
pathToNode: PathToNode
|
||||||
) => ConstrainInfo[]
|
) => ConstrainInfo[]
|
||||||
|
13
src/lang/wasm.test.ts
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
import { err } from 'lib/trap'
|
||||||
|
import { parse } from './wasm'
|
||||||
|
import { enginelessExecutor } from 'lib/testHelpers'
|
||||||
|
|
||||||
|
it('can execute parsed AST', async () => {
|
||||||
|
const code = `x = 1
|
||||||
|
// A comment.`
|
||||||
|
const ast = parse(code)
|
||||||
|
expect(err(ast)).toEqual(false)
|
||||||
|
const execState = await enginelessExecutor(ast)
|
||||||
|
expect(err(ast)).toEqual(false)
|
||||||
|
expect(execState.memory.get('x')?.value).toEqual(1)
|
||||||
|
})
|
@ -42,6 +42,7 @@ import { ExecState as RawExecState } from '../wasm-lib/kcl/bindings/ExecState'
|
|||||||
import { ProgramMemory as RawProgramMemory } from '../wasm-lib/kcl/bindings/ProgramMemory'
|
import { ProgramMemory as RawProgramMemory } from '../wasm-lib/kcl/bindings/ProgramMemory'
|
||||||
import { EnvironmentRef } from '../wasm-lib/kcl/bindings/EnvironmentRef'
|
import { EnvironmentRef } from '../wasm-lib/kcl/bindings/EnvironmentRef'
|
||||||
import { Environment } from '../wasm-lib/kcl/bindings/Environment'
|
import { Environment } from '../wasm-lib/kcl/bindings/Environment'
|
||||||
|
import { Node } from 'wasm-lib/kcl/bindings/Node'
|
||||||
|
|
||||||
export type { Program } from '../wasm-lib/kcl/bindings/Program'
|
export type { Program } from '../wasm-lib/kcl/bindings/Program'
|
||||||
export type { Expr } from '../wasm-lib/kcl/bindings/Expr'
|
export type { Expr } from '../wasm-lib/kcl/bindings/Expr'
|
||||||
@ -122,11 +123,11 @@ export const initPromise = initialise()
|
|||||||
export const rangeTypeFix = (ranges: number[][]): [number, number][] =>
|
export const rangeTypeFix = (ranges: number[][]): [number, number][] =>
|
||||||
ranges.map(([start, end]) => [start, end])
|
ranges.map(([start, end]) => [start, end])
|
||||||
|
|
||||||
export const parse = (code: string | Error): Program | Error => {
|
export const parse = (code: string | Error): Node<Program> | Error => {
|
||||||
if (err(code)) return code
|
if (err(code)) return code
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const program: Program = parse_wasm(code)
|
const program: Node<Program> = parse_wasm(code)
|
||||||
return program
|
return program
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
// throw e
|
// throw e
|
||||||
@ -378,7 +379,7 @@ export function sketchFromKclValue(
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const executor = async (
|
export const executor = async (
|
||||||
node: Program,
|
node: Node<Program>,
|
||||||
programMemory: ProgramMemory | Error = ProgramMemory.empty(),
|
programMemory: ProgramMemory | Error = ProgramMemory.empty(),
|
||||||
idGenerator: IdGenerator = defaultIdGenerator(),
|
idGenerator: IdGenerator = defaultIdGenerator(),
|
||||||
engineCommandManager: EngineCommandManager,
|
engineCommandManager: EngineCommandManager,
|
||||||
@ -402,7 +403,7 @@ export const executor = async (
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const _executor = async (
|
export const _executor = async (
|
||||||
node: Program,
|
node: Node<Program>,
|
||||||
programMemory: ProgramMemory | Error = ProgramMemory.empty(),
|
programMemory: ProgramMemory | Error = ProgramMemory.empty(),
|
||||||
idGenerator: IdGenerator = defaultIdGenerator(),
|
idGenerator: IdGenerator = defaultIdGenerator(),
|
||||||
engineCommandManager: EngineCommandManager,
|
engineCommandManager: EngineCommandManager,
|
||||||
@ -493,13 +494,13 @@ export function lexer(str: string): Token[] | Error {
|
|||||||
|
|
||||||
export const modifyAstForSketch = async (
|
export const modifyAstForSketch = async (
|
||||||
engineCommandManager: EngineCommandManager,
|
engineCommandManager: EngineCommandManager,
|
||||||
ast: Program,
|
ast: Node<Program>,
|
||||||
variableName: string,
|
variableName: string,
|
||||||
currentPlane: string,
|
currentPlane: string,
|
||||||
engineId: string
|
engineId: string
|
||||||
): Promise<Program> => {
|
): Promise<Node<Program>> => {
|
||||||
try {
|
try {
|
||||||
const updatedAst: Program = await modify_ast_for_sketch_wasm(
|
const updatedAst: Node<Program> = await modify_ast_for_sketch_wasm(
|
||||||
engineCommandManager,
|
engineCommandManager,
|
||||||
JSON.stringify(ast),
|
JSON.stringify(ast),
|
||||||
variableName,
|
variableName,
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
|
import { CommandBarOverwriteWarning } from 'components/CommandBarOverwriteWarning'
|
||||||
import { StateMachineCommandSetConfig } from 'lib/commandTypes'
|
import { StateMachineCommandSetConfig } from 'lib/commandTypes'
|
||||||
import { homeMachine } from 'machines/homeMachine'
|
import { projectsMachine } from 'machines/projectsMachine'
|
||||||
|
|
||||||
export type HomeCommandSchema = {
|
export type ProjectsCommandSchema = {
|
||||||
'Read projects': {}
|
'Read projects': {}
|
||||||
'Create project': {
|
'Create project': {
|
||||||
name: string
|
name: string
|
||||||
@ -18,9 +19,9 @@ export type HomeCommandSchema = {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const homeCommandBarConfig: StateMachineCommandSetConfig<
|
export const projectsCommandBarConfig: StateMachineCommandSetConfig<
|
||||||
typeof homeMachine,
|
typeof projectsMachine,
|
||||||
HomeCommandSchema
|
ProjectsCommandSchema
|
||||||
> = {
|
> = {
|
||||||
'Open project': {
|
'Open project': {
|
||||||
icon: 'arrowRight',
|
icon: 'arrowRight',
|
||||||
@ -53,6 +54,11 @@ export const homeCommandBarConfig: StateMachineCommandSetConfig<
|
|||||||
icon: 'close',
|
icon: 'close',
|
||||||
description: 'Delete a project',
|
description: 'Delete a project',
|
||||||
needsReview: true,
|
needsReview: true,
|
||||||
|
reviewMessage: ({ argumentsToSubmit }) =>
|
||||||
|
CommandBarOverwriteWarning({
|
||||||
|
heading: 'Are you sure you want to delete?',
|
||||||
|
message: `This will permanently delete the project "${argumentsToSubmit.name}" and all its contents.`,
|
||||||
|
}),
|
||||||
args: {
|
args: {
|
||||||
name: {
|
name: {
|
||||||
inputType: 'options',
|
inputType: 'options',
|
@ -6,6 +6,7 @@ import { Identifier, Expr, VariableDeclaration } from 'lang/wasm'
|
|||||||
import { commandBarMachine } from 'machines/commandBarMachine'
|
import { commandBarMachine } from 'machines/commandBarMachine'
|
||||||
import { ReactNode } from 'react'
|
import { ReactNode } from 'react'
|
||||||
import { MachineManager } from 'components/MachineManagerProvider'
|
import { MachineManager } from 'components/MachineManagerProvider'
|
||||||
|
import { Node } from 'wasm-lib/kcl/bindings/Node'
|
||||||
|
|
||||||
type Icon = CustomIconName
|
type Icon = CustomIconName
|
||||||
const PLATFORMS = ['both', 'web', 'desktop'] as const
|
const PLATFORMS = ['both', 'web', 'desktop'] as const
|
||||||
@ -24,8 +25,8 @@ export interface KclExpression {
|
|||||||
}
|
}
|
||||||
export interface KclExpressionWithVariable extends KclExpression {
|
export interface KclExpressionWithVariable extends KclExpression {
|
||||||
variableName: string
|
variableName: string
|
||||||
variableDeclarationAst: VariableDeclaration
|
variableDeclarationAst: Node<VariableDeclaration>
|
||||||
variableIdentifierAst: Identifier
|
variableIdentifierAst: Node<Identifier>
|
||||||
insertIndex: number
|
insertIndex: number
|
||||||
}
|
}
|
||||||
export type KclCommandValue = KclExpression | KclExpressionWithVariable
|
export type KclCommandValue = KclExpression | KclExpressionWithVariable
|
||||||
|
@ -111,6 +111,9 @@ export function createMachineCommand<
|
|||||||
if ('displayName' in commandConfig) {
|
if ('displayName' in commandConfig) {
|
||||||
command.displayName = commandConfig.displayName
|
command.displayName = commandConfig.displayName
|
||||||
}
|
}
|
||||||
|
if ('reviewMessage' in commandConfig) {
|
||||||
|
command.reviewMessage = commandConfig.reviewMessage
|
||||||
|
}
|
||||||
|
|
||||||
return command
|
return command
|
||||||
}
|
}
|
||||||
|
131
src/lib/desktop.test.ts
Normal file
@ -0,0 +1,131 @@
|
|||||||
|
import { vi, describe, it, expect, beforeEach } from 'vitest'
|
||||||
|
import { listProjects } from './desktop'
|
||||||
|
import { DeepPartial } from './types'
|
||||||
|
import { Configuration } from 'wasm-lib/kcl/bindings/Configuration'
|
||||||
|
|
||||||
|
// Mock the electron window global
|
||||||
|
const mockElectron = {
|
||||||
|
readdir: vi.fn(),
|
||||||
|
path: {
|
||||||
|
join: vi.fn(),
|
||||||
|
basename: vi.fn(),
|
||||||
|
dirname: vi.fn(),
|
||||||
|
},
|
||||||
|
stat: vi.fn(),
|
||||||
|
statIsDirectory: vi.fn(),
|
||||||
|
exists: vi.fn(),
|
||||||
|
writeFile: vi.fn(),
|
||||||
|
readFile: vi.fn(),
|
||||||
|
os: {
|
||||||
|
isMac: false,
|
||||||
|
isWindows: false,
|
||||||
|
},
|
||||||
|
process: {
|
||||||
|
env: {},
|
||||||
|
},
|
||||||
|
getPath: vi.fn(),
|
||||||
|
kittycad: vi.fn(),
|
||||||
|
}
|
||||||
|
|
||||||
|
vi.stubGlobal('window', { electron: mockElectron })
|
||||||
|
|
||||||
|
describe('desktop utilities', () => {
|
||||||
|
const mockConfig: DeepPartial<Configuration> = {
|
||||||
|
settings: {
|
||||||
|
project: {
|
||||||
|
directory: '/test/projects',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
const mockFileSystem: { [key: string]: string[] } = {
|
||||||
|
'/test/projects': [
|
||||||
|
'.hidden-project',
|
||||||
|
'valid-project',
|
||||||
|
'.git',
|
||||||
|
'project-without-kcl-files',
|
||||||
|
'another-valid-project',
|
||||||
|
],
|
||||||
|
'/test/projects/valid-project': ['file1.kcl', 'file2.stp'],
|
||||||
|
'/test/projects/project-without-kcl-files': ['file3.glb'],
|
||||||
|
'/test/projects/another-valid-project': ['file4.kcl'],
|
||||||
|
}
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
vi.clearAllMocks()
|
||||||
|
|
||||||
|
// Setup default mock implementations
|
||||||
|
mockElectron.path.join.mockImplementation((...parts: string[]) =>
|
||||||
|
parts.join('/')
|
||||||
|
)
|
||||||
|
mockElectron.path.basename.mockImplementation((path: string) =>
|
||||||
|
path.split('/').pop()
|
||||||
|
)
|
||||||
|
mockElectron.path.dirname.mockImplementation((path: string) =>
|
||||||
|
path.split('/').slice(0, -1).join('/')
|
||||||
|
)
|
||||||
|
|
||||||
|
// Mock readdir to return the entries for the given path
|
||||||
|
mockElectron.readdir.mockImplementation(async (path: string) => {
|
||||||
|
return mockFileSystem[path] || []
|
||||||
|
})
|
||||||
|
|
||||||
|
// Mock statIsDirectory to return true if the path exists in mockFileSystem
|
||||||
|
mockElectron.statIsDirectory.mockImplementation(async (path: string) => {
|
||||||
|
return path in mockFileSystem
|
||||||
|
})
|
||||||
|
|
||||||
|
// Mock stat to always resolve with dummy metadata
|
||||||
|
mockElectron.stat.mockResolvedValue({
|
||||||
|
mtimeMs: 123,
|
||||||
|
atimeMs: 456,
|
||||||
|
ctimeMs: 789,
|
||||||
|
size: 100,
|
||||||
|
mode: 0o666,
|
||||||
|
})
|
||||||
|
|
||||||
|
mockElectron.exists.mockResolvedValue(true)
|
||||||
|
mockElectron.readFile.mockResolvedValue('')
|
||||||
|
mockElectron.writeFile.mockResolvedValue(undefined)
|
||||||
|
mockElectron.getPath.mockResolvedValue('/appData')
|
||||||
|
mockElectron.kittycad.mockResolvedValue({})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('listProjects', () => {
|
||||||
|
it('does not list .git directories', async () => {
|
||||||
|
const projects = await listProjects(mockConfig)
|
||||||
|
expect(projects.map((p) => p.name)).not.toContain('.git')
|
||||||
|
})
|
||||||
|
it('lists projects excluding hidden and without .kcl files', async () => {
|
||||||
|
const projects = await listProjects(mockConfig)
|
||||||
|
|
||||||
|
// Verify only non-dot projects with .kcl files were included
|
||||||
|
expect(projects.map((p) => p.name)).toEqual([
|
||||||
|
'valid-project',
|
||||||
|
'another-valid-project',
|
||||||
|
])
|
||||||
|
|
||||||
|
// Verify we didn't try to get project info for dot directories
|
||||||
|
expect(mockElectron.stat).not.toHaveBeenCalledWith(
|
||||||
|
expect.stringContaining('/.hidden-project')
|
||||||
|
)
|
||||||
|
expect(mockElectron.stat).not.toHaveBeenCalledWith(
|
||||||
|
expect.stringContaining('/.git')
|
||||||
|
)
|
||||||
|
|
||||||
|
// Verify that projects without .kcl files are not included
|
||||||
|
expect(projects.map((p) => p.name)).not.toContain(
|
||||||
|
'project-without-kcl-files'
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('handles empty project directory', async () => {
|
||||||
|
// Adjust mockFileSystem to simulate empty directory
|
||||||
|
mockFileSystem['/test/projects'] = []
|
||||||
|
|
||||||
|
const projects = await listProjects(mockConfig)
|
||||||
|
|
||||||
|
expect(projects).toEqual([])
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
@ -139,6 +139,11 @@ export async function listProjects(
|
|||||||
|
|
||||||
const entries = await window.electron.readdir(projectDir)
|
const entries = await window.electron.readdir(projectDir)
|
||||||
for (let entry of entries) {
|
for (let entry of entries) {
|
||||||
|
// Skip directories that start with a dot
|
||||||
|
if (entry.startsWith('.')) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
const projectPath = window.electron.path.join(projectDir, entry)
|
const projectPath = window.electron.path.join(projectDir, entry)
|
||||||
// if it's not a directory ignore.
|
// if it's not a directory ignore.
|
||||||
const isDirectory = await window.electron.statIsDirectory(projectPath)
|
const isDirectory = await window.electron.statIsDirectory(projectPath)
|
||||||
|
@ -120,7 +120,7 @@ export async function getSettingsFolderPaths(projectPath?: string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function createAndOpenNewProject({
|
export async function createAndOpenNewTutorialProject({
|
||||||
onProjectOpen,
|
onProjectOpen,
|
||||||
navigate,
|
navigate,
|
||||||
}: {
|
}: {
|
||||||
@ -144,6 +144,22 @@ export async function createAndOpenNewProject({
|
|||||||
ONBOARDING_PROJECT_NAME,
|
ONBOARDING_PROJECT_NAME,
|
||||||
nextIndex
|
nextIndex
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Delete the tutorial project if it already exists.
|
||||||
|
if (isDesktop()) {
|
||||||
|
if (configuration.settings?.project?.directory === undefined) {
|
||||||
|
return Promise.reject(new Error('configuration settings are undefined'))
|
||||||
|
}
|
||||||
|
|
||||||
|
const fullPath = window.electron.join(
|
||||||
|
configuration.settings.project.directory,
|
||||||
|
name
|
||||||
|
)
|
||||||
|
if (window.electron.exists(fullPath)) {
|
||||||
|
await window.electron.rm(fullPath)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const newProject = await createNewProjectDirectory(
|
const newProject = await createNewProjectDirectory(
|
||||||
name,
|
name,
|
||||||
bracket,
|
bracket,
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import * as path from 'path'
|
import * as path from 'path'
|
||||||
import * as fs from 'fs/promises'
|
import * as fs from 'fs/promises'
|
||||||
|
import { Stats } from 'fs'
|
||||||
import { Models } from '@kittycad/lib/dist/types/src'
|
import { Models } from '@kittycad/lib/dist/types/src'
|
||||||
import { PROJECT_ENTRYPOINT } from './constants'
|
import { PROJECT_ENTRYPOINT } from './constants'
|
||||||
|
|
||||||
@ -43,8 +44,16 @@ export default async function getCurrentProjectFile(
|
|||||||
? sourcePath
|
? sourcePath
|
||||||
: path.join(process.cwd(), sourcePath)
|
: path.join(process.cwd(), sourcePath)
|
||||||
|
|
||||||
|
let stats: Stats
|
||||||
|
try {
|
||||||
|
stats = await fs.stat(sourcePath)
|
||||||
|
} catch (error) {
|
||||||
|
return new Error(
|
||||||
|
`Unable to access the path: ${sourcePath}. Error: ${error}`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
// If the path is a directory, let's assume it is a project directory.
|
// If the path is a directory, let's assume it is a project directory.
|
||||||
const stats = await fs.stat(sourcePath)
|
|
||||||
if (stats.isDirectory()) {
|
if (stats.isDirectory()) {
|
||||||
// Walk the directory and look for a kcl file.
|
// Walk the directory and look for a kcl file.
|
||||||
const files = await fs.readdir(sourcePath)
|
const files = await fs.readdir(sourcePath)
|
||||||
|
@ -36,6 +36,7 @@ import {
|
|||||||
getWallCodeRef,
|
getWallCodeRef,
|
||||||
ArtifactId,
|
ArtifactId,
|
||||||
} from 'lang/std/artifactGraph'
|
} from 'lang/std/artifactGraph'
|
||||||
|
import { Node } from 'wasm-lib/kcl/bindings/Node'
|
||||||
|
|
||||||
export const X_AXIS_UUID = 'ad792545-7fd3-482a-a602-a93924e3055b'
|
export const X_AXIS_UUID = 'ad792545-7fd3-482a-a602-a93924e3055b'
|
||||||
export const Y_AXIS_UUID = '680fd157-266f-4b8a-984f-cdf46b8bdf01'
|
export const Y_AXIS_UUID = '680fd157-266f-4b8a-984f-cdf46b8bdf01'
|
||||||
@ -244,7 +245,7 @@ export function getEventForSegmentSelection(
|
|||||||
const updatedAst = parse(codeManager.code)
|
const updatedAst = parse(codeManager.code)
|
||||||
if (err(updatedAst)) return null
|
if (err(updatedAst)) return null
|
||||||
|
|
||||||
const nodeMeta = getNodeFromPath<CallExpression>(
|
const nodeMeta = getNodeFromPath<Node<CallExpression>>(
|
||||||
updatedAst,
|
updatedAst,
|
||||||
pathToNode,
|
pathToNode,
|
||||||
'CallExpression'
|
'CallExpression'
|
||||||
@ -362,7 +363,7 @@ function updateSceneObjectColors(codeBasedSelections: Selection[]) {
|
|||||||
|
|
||||||
Object.values(sceneEntitiesManager.activeSegments).forEach((segmentGroup) => {
|
Object.values(sceneEntitiesManager.activeSegments).forEach((segmentGroup) => {
|
||||||
if (!SEGMENT_BODIES_PLUS_PROFILE_START.includes(segmentGroup?.name)) return
|
if (!SEGMENT_BODIES_PLUS_PROFILE_START.includes(segmentGroup?.name)) return
|
||||||
const nodeMeta = getNodeFromPath<CallExpression>(
|
const nodeMeta = getNodeFromPath<Node<CallExpression>>(
|
||||||
updated,
|
updated,
|
||||||
segmentGroup.userData.pathToNode,
|
segmentGroup.userData.pathToNode,
|
||||||
'CallExpression'
|
'CallExpression'
|
||||||
|
@ -17,6 +17,7 @@ import { DefaultPlanes } from 'wasm-lib/kcl/bindings/DefaultPlanes'
|
|||||||
import { err, reportRejection } from 'lib/trap'
|
import { err, reportRejection } from 'lib/trap'
|
||||||
import { toSync } from './utils'
|
import { toSync } from './utils'
|
||||||
import { IdGenerator } from 'wasm-lib/kcl/bindings/IdGenerator'
|
import { IdGenerator } from 'wasm-lib/kcl/bindings/IdGenerator'
|
||||||
|
import { Node } from 'wasm-lib/kcl/bindings/Node'
|
||||||
|
|
||||||
type WebSocketResponse = Models['WebSocketResponse_type']
|
type WebSocketResponse = Models['WebSocketResponse_type']
|
||||||
|
|
||||||
@ -84,7 +85,7 @@ class MockEngineCommandManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function enginelessExecutor(
|
export async function enginelessExecutor(
|
||||||
ast: Program | Error,
|
ast: Node<Program> | Error,
|
||||||
pm: ProgramMemory | Error = ProgramMemory.empty(),
|
pm: ProgramMemory | Error = ProgramMemory.empty(),
|
||||||
idGenerator: IdGenerator = defaultIdGenerator()
|
idGenerator: IdGenerator = defaultIdGenerator()
|
||||||
): Promise<ExecState> {
|
): Promise<ExecState> {
|
||||||
@ -109,7 +110,7 @@ export async function enginelessExecutor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function executor(
|
export async function executor(
|
||||||
ast: Program,
|
ast: Node<Program>,
|
||||||
pm: ProgramMemory = ProgramMemory.empty(),
|
pm: ProgramMemory = ProgramMemory.empty(),
|
||||||
idGenerator: IdGenerator = defaultIdGenerator()
|
idGenerator: IdGenerator = defaultIdGenerator()
|
||||||
): Promise<ExecState> {
|
): Promise<ExecState> {
|
||||||
|
@ -39,6 +39,9 @@ export type ToolbarItem = {
|
|||||||
description: string
|
description: string
|
||||||
links: { label: string; url: string }[]
|
links: { label: string; url: string }[]
|
||||||
isActive?: (state: StateFrom<typeof modelingMachine>) => boolean
|
isActive?: (state: StateFrom<typeof modelingMachine>) => boolean
|
||||||
|
disabledReason?:
|
||||||
|
| string
|
||||||
|
| ((state: StateFrom<typeof modelingMachine>) => string | undefined)
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ToolbarItemResolved = Omit<
|
export type ToolbarItemResolved = Omit<
|
||||||
@ -349,6 +352,11 @@ export const toolbarConfig: Record<ToolbarModeName, ToolbarMode> = {
|
|||||||
(!isEditingExistingSketch(state.context) &&
|
(!isEditingExistingSketch(state.context) &&
|
||||||
!state.matches({ Sketch: 'Tangential arc to' })) ||
|
!state.matches({ Sketch: 'Tangential arc to' })) ||
|
||||||
pipeHasCircle(state.context),
|
pipeHasCircle(state.context),
|
||||||
|
disabledReason: (state) =>
|
||||||
|
!isEditingExistingSketch(state.context) &&
|
||||||
|
!state.matches({ Sketch: 'Tangential arc to' })
|
||||||
|
? "Cannot start a tangential arc because there's no previous line to be tangential to. Try drawing a line first or selecting an existing sketch to edit."
|
||||||
|
: undefined,
|
||||||
title: 'Tangential Arc',
|
title: 'Tangential Arc',
|
||||||
hotkey: (state) =>
|
hotkey: (state) =>
|
||||||
state.matches({ Sketch: 'Tangential arc to' }) ? ['Esc', 'A'] : 'A',
|
state.matches({ Sketch: 'Tangential arc to' }) ? ['Esc', 'A'] : 'A',
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
import { assign, fromPromise, setup } from 'xstate'
|
import { assign, fromPromise, setup } from 'xstate'
|
||||||
import { HomeCommandSchema } from 'lib/commandBarConfigs/homeCommandConfig'
|
import { ProjectsCommandSchema } from 'lib/commandBarConfigs/projectsCommandConfig'
|
||||||
import { Project } from 'lib/project'
|
import { Project } from 'lib/project'
|
||||||
|
import { isArray } from 'lib/utils'
|
||||||
|
|
||||||
export const homeMachine = setup({
|
export const projectsMachine = setup({
|
||||||
types: {
|
types: {
|
||||||
context: {} as {
|
context: {} as {
|
||||||
projects: Project[]
|
projects: Project[]
|
||||||
@ -11,15 +12,36 @@ export const homeMachine = setup({
|
|||||||
},
|
},
|
||||||
events: {} as
|
events: {} as
|
||||||
| { type: 'Read projects'; data: {} }
|
| { type: 'Read projects'; data: {} }
|
||||||
| { type: 'Open project'; data: HomeCommandSchema['Open project'] }
|
| { type: 'Open project'; data: ProjectsCommandSchema['Open project'] }
|
||||||
| { type: 'Rename project'; data: HomeCommandSchema['Rename project'] }
|
| {
|
||||||
| { type: 'Create project'; data: HomeCommandSchema['Create project'] }
|
type: 'Rename project'
|
||||||
| { type: 'Delete project'; data: HomeCommandSchema['Delete project'] }
|
data: ProjectsCommandSchema['Rename project']
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
type: 'Create project'
|
||||||
|
data: ProjectsCommandSchema['Create project']
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
type: 'Delete project'
|
||||||
|
data: ProjectsCommandSchema['Delete project']
|
||||||
|
}
|
||||||
| { type: 'navigate'; data: { name: string } }
|
| { type: 'navigate'; data: { name: string } }
|
||||||
| {
|
| {
|
||||||
type: 'xstate.done.actor.read-projects'
|
type: 'xstate.done.actor.read-projects'
|
||||||
output: Project[]
|
output: Project[]
|
||||||
}
|
}
|
||||||
|
| {
|
||||||
|
type: 'xstate.done.actor.delete-project'
|
||||||
|
output: { message: string; name: string }
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
type: 'xstate.done.actor.create-project'
|
||||||
|
output: { message: string; name: string }
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
type: 'xstate.done.actor.rename-project'
|
||||||
|
output: { message: string; oldName: string; newName: string }
|
||||||
|
}
|
||||||
| { type: 'assign'; data: { [key: string]: any } },
|
| { type: 'assign'; data: { [key: string]: any } },
|
||||||
input: {} as {
|
input: {} as {
|
||||||
projects: Project[]
|
projects: Project[]
|
||||||
@ -30,16 +52,20 @@ export const homeMachine = setup({
|
|||||||
actions: {
|
actions: {
|
||||||
setProjects: assign({
|
setProjects: assign({
|
||||||
projects: ({ context, event }) =>
|
projects: ({ context, event }) =>
|
||||||
'output' in event ? event.output : context.projects,
|
'output' in event && isArray(event.output)
|
||||||
|
? event.output
|
||||||
|
: context.projects,
|
||||||
}),
|
}),
|
||||||
toastSuccess: () => {},
|
toastSuccess: () => {},
|
||||||
toastError: () => {},
|
toastError: () => {},
|
||||||
navigateToProject: () => {},
|
navigateToProject: () => {},
|
||||||
|
navigateToProjectIfNeeded: () => {},
|
||||||
},
|
},
|
||||||
actors: {
|
actors: {
|
||||||
readProjects: fromPromise(() => Promise.resolve([] as Project[])),
|
readProjects: fromPromise(() => Promise.resolve([] as Project[])),
|
||||||
createProject: fromPromise((_: { input: { name: string } }) =>
|
createProject: fromPromise(
|
||||||
Promise.resolve('')
|
(_: { input: { name: string; projects: Project[] } }) =>
|
||||||
|
Promise.resolve({ message: '' })
|
||||||
),
|
),
|
||||||
renameProject: fromPromise(
|
renameProject: fromPromise(
|
||||||
(_: {
|
(_: {
|
||||||
@ -48,28 +74,35 @@ export const homeMachine = setup({
|
|||||||
newName: string
|
newName: string
|
||||||
defaultProjectName: string
|
defaultProjectName: string
|
||||||
defaultDirectory: string
|
defaultDirectory: string
|
||||||
|
projects: Project[]
|
||||||
}
|
}
|
||||||
}) => Promise.resolve('')
|
}) =>
|
||||||
|
Promise.resolve({
|
||||||
|
message: '',
|
||||||
|
oldName: '',
|
||||||
|
newName: '',
|
||||||
|
})
|
||||||
),
|
),
|
||||||
deleteProject: fromPromise(
|
deleteProject: fromPromise(
|
||||||
(_: { input: { defaultDirectory: string; name: string } }) =>
|
(_: { input: { defaultDirectory: string; name: string } }) =>
|
||||||
Promise.resolve('')
|
Promise.resolve({
|
||||||
|
message: '',
|
||||||
|
name: '',
|
||||||
|
})
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
guards: {
|
guards: {
|
||||||
'Has at least 1 project': () => false,
|
'Has at least 1 project': () => false,
|
||||||
},
|
},
|
||||||
}).createMachine({
|
}).createMachine({
|
||||||
/** @xstate-layout N4IgpgJg5mDOIC5QAkD2BbMACdBDAxgBYCWAdmAHTK6xampYAOATqgFZj4AusAxAMLMwuLthbtOXANoAGALqJQjVLGJdiqUopAAPRAHYAbPooAWABwBGUwE5zAJgeGArM-MAaEAE9EN0wGYKGX97GX1nGVNDS0MbfwBfeM80TBwCEnIqGiZWDm4+ACUwUlxU8TzpeW1lVXVNbT0EcJNg02d-fzt7fU77Tx8EQ0iKCPtnfUsjGRtLGXtE5IxsPCIySmpacsk+QWFRHIluWQUkEBq1DS1TxqN7ChjzOxtXf0t7a37EcwsRibH-ZzRezA8wLEApZbpNZZTa5ba8AAiYAANmB9lsjlVTuc6ldQDdDOYKP5bm0os5TDJDJ8mlEzPpzIZHA4bO9umCIWlVpkNgcKnwAPKMYp8yTHaoqC71a6IEmBUz6BkWZzWDq2Uw0qzOIJAwz+PXWfSmeZJcFLLkZSi7ERkKCi7i8CCaShkABuqAA1pR8EIRGAALQYyonJSS3ENRDA2wUeyvd6dPVhGw0-RhGOp8IA8xGFkc80rS0Ua3qUh2oO8MDMVjMCiMZEiABmqGY6AoPr2AaD4uxYcuEYQoQpQWNNjsMnMgLGKbT3TC7TcOfsNjzqQL0KKJXQtvtXEdzoobs9lCEm87cMxIbOvel+MQqtMQRmS5ks31sZpAUsZkcIX+cQZJIrpC3KUBupTbuWlbVrW9ZcE2LYUCepRnocwYSrUfYyggbzvBQ+jMq49imLYwTUt4iCft+5i-u0-7UfoQEWtCSKoiWZbnruTqZIeXoUBAKJoihFTdqGGE3rod7UdqsQTI8hiGAqrIauRA7RvYeoqhO1jtAqjFrpkLFohBHEVlWzYwY2zatvxrFCWKWKiVKeISdh4yBJE-jGs4fhhA4zg0kRNgxhplhaW0nn4XpUKZEUuAQMZqF8FxLqkO6vG+hAgYcbAIlXmJzmNERdy0RYNiKgpthxDSEU6q8MSTJYjWGFFIEULF8WljuSX7jxx7CJlQY5ZYl44pht4IP61gyPc8njt0lIuH51UKrVVITEyMy2C1hbtQl-KmdBdaWQhGVZYluWjeJjSTf402shMEyuEyljPAFL0UNmMiuN86lWHMiSmvQ-HwKcnL6WA6FOf2k3mESMRDA4RpUm4U4qf6gSEt0QIvvqfjOCaiyrtF6zZPQXWQ+GWFlUEsbmNMf1TV9NLeXDcqRIySnNaaYPEzC5M9vl-b+IyFCjupryPF9jKWP5Kks-cbMWLERHRNt0LFntkgU2NLk4dqsz43YsTK++Kk2C+MbTOOcxzOMrhqzFxTgZ1Qba1dd6BUE1jGsLMxxK9KlDNqm3tMLUQvqYlgO5QhlsTubsFXesTTUuPTfHExshDS0RftRftGgEnTZtHbX9Zr+QJ-2S4Y3qnmTC+4tMyp1EfeOnmeQqdOhyXQrFOXXCV1hCkmLDOnBJYvRRDSsyRzGjiKj0lKdAkANAA */
|
/** @xstate-layout N4IgpgJg5mDOIC5QAkD2BbMACdBDAxgBYCWAdmAMS6yzFSkDaADALqKgAOqtALsaqXYgAHogAsAJgA0IAJ6IAjBIkA2AHQAOCUw0qNkjQE4xYjQF8zMtJhwES5NcmpZSqLBwBOqAFZh8PWAoAJTBcCHcvX39YZjYkEC5efkF40QQFFQBmAHY1Qwz9QwBWU0NMrRl5BGyxBTUVJlVM01rtBXNLEGtsPCIyMEdnVwifPwCKAGEPUJ5sT1H-WKFE4j4BITSMzMzNIsyJDSZMhSYmFWzKxAkTNSYxIuU9zOKT7IsrDB67fsHYEajxiEwv8xjFWMtuKtkhsrpJboZsioincFMdTIjLggVGJ1GImMVMg0JISjEV3l1PrY+g4nH95gDAiFSLgbPSxkt4is1ilQGlrhJ4YjkbU0RoMXJEIYkWoikV8hJinjdOdyd0qfYBrSQdFJtNcLNtTwOZxIdyYQh+YKkSjReKqqYBQoEQpsgiSi9MqrKb0Nb9DYEACJgAA2YANbMW4M5puhqVhAvxQptCnRKkxCleuw0Gm2+2aRVRXpsPp+Woj4wA8hwwKRDcaEjH1nGLXDE9aRSmxWmJVipWocmdsbL8kxC501SWHFMZmQoIaKBABAMyAA3VAAawG+D1swAtOX61zY7zENlEWoMkoatcdPoipjCWIZRIirozsPiYYi19qQNp-rZ3nMAPC8Dw1A4YN9QAM1QDx0DUbcZjAfdInZKMTSSJsT2qc9Lwka8lTvTFTB2WUMiyQwc3yPRv3VH4mRZQDywXJc1FXDcBmmZlMBQhYjXQhtMJ5ERT1wlQr0kQj7kxKiZTxM5s2zbQEVoycBgY9AmNQ-wKGA0DwMgngYLgtQuJZZCDwEo8sJEnD1Dwgjb2kntig0GUkWVfZDEMfFVO+Bwg1DPhSDnZjFwcdjNzUCAQzDCztP4uIMKhGy0jPezxPwySnPvHsTjlPIhyOHRsnwlM-N-NRArDLS+N0kDYIM6DYPgmKgvivjD0bYS+VbBF21RTs7UUUcimfRpXyYRF8LFCrfSBCBaoZFiItINcor1CBeIZLqhPNdKL0yxzs2cqoNDqdpSo0EoSoLOb6NCRaQv9FblzWjjTMe7bQQYBQksElKesUMRjFubRMllCHsm2a5MVldRDBfFpmj0IpxPuhwFqW0F6v0iDmpMzbvuiXbAfNFNQcaI5IaKaH9jETFsUMNRVDxOU5TxBULE6VwYvgeIJ38sAIT25td27KpdzG7yZeho5slHVmMc1IY3HLfnkrNZt2hOW5smRCQM2uhQHkZ6VtkRBFnmu0qFGVv11ZFsnm0kdN8QFJVjlUPZ9co+3-2C0KEqdrXsJMJ8ryc86iUyYiCvxFNzldb3jHtjTsf8EPj1ssQchZlR8jR5EmDRrIGZcuF7maF1aZtslx29IWqtiwPDSz1LxDxepnlOfYDghp03eyOpngzXuYdHNPHozgJ26B9IXR2cTvP0BVkSRXKqgLtyq8OfQcXOwlubMIA */
|
||||||
id: 'Home machine',
|
id: 'Home machine',
|
||||||
|
|
||||||
initial: 'Reading projects',
|
initial: 'Reading projects',
|
||||||
|
|
||||||
context: {
|
context: ({ input }) => ({
|
||||||
projects: [],
|
...input,
|
||||||
defaultProjectName: '',
|
}),
|
||||||
defaultDirectory: '',
|
|
||||||
},
|
|
||||||
|
|
||||||
on: {
|
on: {
|
||||||
assign: {
|
assign: {
|
||||||
@ -110,7 +143,9 @@ export const homeMachine = setup({
|
|||||||
},
|
},
|
||||||
|
|
||||||
'Open project': {
|
'Open project': {
|
||||||
target: 'Opening project',
|
target: 'Reading projects',
|
||||||
|
actions: 'navigateToProject',
|
||||||
|
reenter: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -119,20 +154,22 @@ export const homeMachine = setup({
|
|||||||
invoke: {
|
invoke: {
|
||||||
id: 'create-project',
|
id: 'create-project',
|
||||||
src: 'createProject',
|
src: 'createProject',
|
||||||
input: ({ event }) => {
|
input: ({ event, context }) => {
|
||||||
if (event.type !== 'Create project') {
|
if (event.type !== 'Create project') {
|
||||||
return {
|
return {
|
||||||
name: '',
|
name: '',
|
||||||
|
projects: context.projects,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
name: event.data.name,
|
name: event.data.name,
|
||||||
|
projects: context.projects,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onDone: [
|
onDone: [
|
||||||
{
|
{
|
||||||
target: 'Reading projects',
|
target: 'Reading projects',
|
||||||
actions: ['toastSuccess'],
|
actions: ['toastSuccess', 'navigateToProject'],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
onError: [
|
onError: [
|
||||||
@ -156,6 +193,7 @@ export const homeMachine = setup({
|
|||||||
defaultDirectory: context.defaultDirectory,
|
defaultDirectory: context.defaultDirectory,
|
||||||
oldName: '',
|
oldName: '',
|
||||||
newName: '',
|
newName: '',
|
||||||
|
projects: context.projects,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
@ -163,12 +201,13 @@ export const homeMachine = setup({
|
|||||||
defaultDirectory: context.defaultDirectory,
|
defaultDirectory: context.defaultDirectory,
|
||||||
oldName: event.data.oldName,
|
oldName: event.data.oldName,
|
||||||
newName: event.data.newName,
|
newName: event.data.newName,
|
||||||
|
projects: context.projects,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onDone: [
|
onDone: [
|
||||||
{
|
{
|
||||||
target: '#Home machine.Reading projects',
|
target: '#Home machine.Reading projects',
|
||||||
actions: ['toastSuccess'],
|
actions: ['toastSuccess', 'navigateToProjectIfNeeded'],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
onError: [
|
onError: [
|
||||||
@ -199,7 +238,7 @@ export const homeMachine = setup({
|
|||||||
},
|
},
|
||||||
onDone: [
|
onDone: [
|
||||||
{
|
{
|
||||||
actions: ['toastSuccess'],
|
actions: ['toastSuccess', 'navigateToProjectIfNeeded'],
|
||||||
target: '#Home machine.Reading projects',
|
target: '#Home machine.Reading projects',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@ -233,9 +272,5 @@ export const homeMachine = setup({
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
'Opening project': {
|
|
||||||
entry: ['navigateToProject'],
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
})
|
})
|