Compare commits
61 Commits
remove-old
...
jtran/fix-
Author | SHA1 | Date | |
---|---|---|---|
0c2f63b399 | |||
363ae10658 | |||
ac4a6c84cf | |||
c6fad2e2dc | |||
013cb10961 | |||
6261083cb1 | |||
2b0ba37ed0 | |||
96174f3cf6 | |||
aed62ff912 | |||
9334d64608 | |||
4fa7d2d8c8 | |||
3e615dfdbc | |||
c9860af29f | |||
23a42f0195 | |||
a77fa639f3 | |||
0a5ad7c95b | |||
4a654523d2 | |||
73a7e2bfd6 | |||
eb0850fea9 | |||
029f76f273 | |||
28b5f7080c | |||
5b1dcfecd6 | |||
f89d191425 | |||
2f4e4b62a8 | |||
5ebd5c8dbb | |||
a9ceaf2678 | |||
c8afd3399b | |||
5dda4828c6 | |||
72acab752c | |||
81df38ad1c | |||
0576a2bef1 | |||
4b2f6b4647 | |||
69edaa4183 | |||
2eb7c382bf | |||
38913ecb98 | |||
debd06129f | |||
d38bd342a0 | |||
f026f10335 | |||
895d7ebc6d | |||
65edf17a44 | |||
0c2a0a8c07 | |||
97cef4d16c | |||
9358278f7b | |||
a174e084d4 | |||
df7246897a | |||
0c9f64dd7c | |||
d2b9d3a058 | |||
7e54f08778 | |||
d9c2dd376e | |||
275a2150e7 | |||
8b8feb8d68 | |||
e21ef3f122 | |||
66834931aa | |||
06c1bcaf2e | |||
fc7df7ecbe | |||
67cb7b33bb | |||
ea74b94fac | |||
529833c63f | |||
92da86391a | |||
e7cb390db4 | |||
8a66bbbdbd |
@ -1,3 +1,3 @@
|
|||||||
[codespell]
|
[codespell]
|
||||||
ignore-words-list: crate,everytime,inout,co-ordinate,ot,nwo,atleast,ue,afterall
|
ignore-words-list: crate,everytime,inout,co-ordinate,ot,nwo,atleast,ue,afterall
|
||||||
skip: **/target,node_modules,build,**/Cargo.lock,./docs/kcl/*.md,.yarn.lock,**/yarn.lock,./openapi/*.json,./src/lib/machine-api.d.ts,./packages/codemirror-lang-kcl/test/all.test.ts
|
skip: **/target,node_modules,build,dist,./out,**/Cargo.lock,./docs/kcl/*.md,.yarn.lock,**/yarn.lock,./openapi/*.json,./packages/codemirror-lang-kcl/test/all.test.ts,tsconfig.tsbuildinfo
|
||||||
|
12
.eslintrc
@ -5,16 +5,24 @@
|
|||||||
},
|
},
|
||||||
"plugins": [
|
"plugins": [
|
||||||
"css-modules",
|
"css-modules",
|
||||||
|
"jest",
|
||||||
|
"react",
|
||||||
"suggest-no-throw",
|
"suggest-no-throw",
|
||||||
|
"@typescript-eslint"
|
||||||
],
|
],
|
||||||
"extends": [
|
"extends": [
|
||||||
"react-app",
|
|
||||||
"react-app/jest",
|
|
||||||
"plugin:css-modules/recommended"
|
"plugin:css-modules/recommended"
|
||||||
],
|
],
|
||||||
"rules": {
|
"rules": {
|
||||||
"@typescript-eslint/no-floating-promises": "error",
|
"@typescript-eslint/no-floating-promises": "error",
|
||||||
"@typescript-eslint/no-misused-promises": "error",
|
"@typescript-eslint/no-misused-promises": "error",
|
||||||
|
"no-restricted-globals": [
|
||||||
|
"error",
|
||||||
|
{
|
||||||
|
"name": "isNaN",
|
||||||
|
"message": "Use Number.isNaN() instead."
|
||||||
|
}
|
||||||
|
],
|
||||||
"semi": [
|
"semi": [
|
||||||
"error",
|
"error",
|
||||||
"never"
|
"never"
|
||||||
|
2
.github/ci-cd-scripts/playwright-electron.sh
vendored
@ -21,7 +21,7 @@ if [[ ! -f "test-results/.last-run.json" ]]; then
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
retry=1
|
retry=1
|
||||||
max_retrys=4
|
max_retrys=5
|
||||||
|
|
||||||
# retry failed tests, doing our own retries because using inbuilt playwright retries causes connection issues
|
# retry failed tests, doing our own retries because using inbuilt playwright retries causes connection issues
|
||||||
while [[ $retry -le $max_retrys ]]; do
|
while [[ $retry -le $max_retrys ]]; do
|
||||||
|
4
.github/dependabot.yml
vendored
@ -26,3 +26,7 @@ updates:
|
|||||||
reviewers:
|
reviewers:
|
||||||
- adamchalmers
|
- adamchalmers
|
||||||
- jessfraz
|
- jessfraz
|
||||||
|
groups:
|
||||||
|
serde-dependencies:
|
||||||
|
patterns:
|
||||||
|
- "serde*"
|
||||||
|
6
.github/workflows/e2e-tests.yml
vendored
@ -127,9 +127,12 @@ jobs:
|
|||||||
shell: bash
|
shell: bash
|
||||||
run: yarn tron:package
|
run: yarn tron:package
|
||||||
- name: Run ubuntu/chrome snapshots
|
- name: Run ubuntu/chrome snapshots
|
||||||
|
if: ${{ matrix.os == 'namespace-profile-ubuntu-8-cores' && matrix.shardIndex == 1 }}
|
||||||
shell: bash
|
shell: bash
|
||||||
|
# TODO: break this in its own job, for now it's not slowing down the overall execution as ubuntu is the quickest,
|
||||||
|
# but we could do better. This forces a large 1/1 shard of all 20 snapshot tests that runs in about 3 minutes.
|
||||||
run: |
|
run: |
|
||||||
PLATFORM=web yarn playwright test --config=playwright.config.ts --retries="3" --update-snapshots --grep=@snapshot --shard=${{ matrix.shardIndex }}/${{ matrix.shardTotal }}
|
PLATFORM=web yarn playwright test --config=playwright.config.ts --retries="3" --update-snapshots --grep=@snapshot --shard=1/1
|
||||||
env:
|
env:
|
||||||
CI: true
|
CI: true
|
||||||
NODE_ENV: development
|
NODE_ENV: development
|
||||||
@ -150,6 +153,7 @@ jobs:
|
|||||||
continue-on-error: true
|
continue-on-error: true
|
||||||
run: rm -r test-results
|
run: rm -r test-results
|
||||||
- name: check for changes
|
- name: check for changes
|
||||||
|
if: ${{ matrix.os == 'namespace-profile-ubuntu-8-cores' && matrix.shardIndex == 1 }}
|
||||||
shell: bash
|
shell: bash
|
||||||
id: git-check
|
id: git-check
|
||||||
run: |
|
run: |
|
||||||
|
40
README.md
@ -337,13 +337,47 @@ For individual testing:
|
|||||||
yarn test abstractSyntaxTree -t "unexpected closed curly brace" --silent=false
|
yarn test abstractSyntaxTree -t "unexpected closed curly brace" --silent=false
|
||||||
```
|
```
|
||||||
|
|
||||||
Which will run our suite of [Vitest unit](https://vitest.dev/) and [React Testing Library E2E](https://testing-library.com/docs/react-testing-library/intro/) tests, in interactive mode by default.
|
Which will run our suite of [Vitest unit](https://vitest.dev/) and [React Testing Library E2E](https://testing-library.com/docs/react-testing-library/intro) tests, in interactive mode by default.
|
||||||
|
|
||||||
### Rust tests
|
### Rust tests
|
||||||
|
|
||||||
```bash
|
**Dependencies**
|
||||||
|
|
||||||
|
- `KITTYCAD_API_TOKEN`
|
||||||
|
- `cargo-nextest`
|
||||||
|
- `just`
|
||||||
|
|
||||||
|
#### Setting KITTYCAD_API_TOKEN
|
||||||
|
Use the production zoo.dev token, set this environment variable before running the tests
|
||||||
|
|
||||||
|
#### Installing cargonextest
|
||||||
|
|
||||||
|
```
|
||||||
cd src/wasm-lib
|
cd src/wasm-lib
|
||||||
KITTYCAD_API_TOKEN=XXX cargo test -- --test-threads=1
|
cargo search cargo-nextest
|
||||||
|
cargo install cargo-nextest
|
||||||
|
```
|
||||||
|
|
||||||
|
#### just
|
||||||
|
install [`just`](https://github.com/casey/just?tab=readme-ov-file#pre-built-binaries)
|
||||||
|
|
||||||
|
#### Running the tests
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# With just
|
||||||
|
# Make sure KITTYCAD_API_TOKEN=<prod zoo.dev token> is set
|
||||||
|
# Make sure you installed cargo-nextest
|
||||||
|
# Make sure you installed just
|
||||||
|
cd src/wasm-lib
|
||||||
|
just test
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Without just
|
||||||
|
# Make sure KITTYCAD_API_TOKEN=<prod zoo.dev token> is set
|
||||||
|
# Make sure you installed cargo-nextest
|
||||||
|
cd src/wasm-lib
|
||||||
|
export RUST_BRACKTRACE="full" && cargo nextest run --workspace --test-threads=1
|
||||||
```
|
```
|
||||||
|
|
||||||
Where `XXX` is an API token from the production engine (NOT the dev environment).
|
Where `XXX` is an API token from the production engine (NOT the dev environment).
|
||||||
|
@ -24,3 +24,5 @@ once fixed in engine will just start working here with no language changes.
|
|||||||
chamfer cases work currently.
|
chamfer cases work currently.
|
||||||
|
|
||||||
- **Appearance**: Changing the appearance on a loft does not work.
|
- **Appearance**: Changing the appearance on a loft does not work.
|
||||||
|
|
||||||
|
- **Helix**: Currently sweeping a helix does not work.
|
||||||
|
43
docs/kcl/helixRevolutions.md
Normal file
@ -48,6 +48,7 @@ layout: manual
|
|||||||
* [`getOppositeEdge`](kcl/getOppositeEdge)
|
* [`getOppositeEdge`](kcl/getOppositeEdge)
|
||||||
* [`getPreviousAdjacentEdge`](kcl/getPreviousAdjacentEdge)
|
* [`getPreviousAdjacentEdge`](kcl/getPreviousAdjacentEdge)
|
||||||
* [`helix`](kcl/helix)
|
* [`helix`](kcl/helix)
|
||||||
|
* [`helixRevolutions`](kcl/helixRevolutions)
|
||||||
* [`hole`](kcl/hole)
|
* [`hole`](kcl/hole)
|
||||||
* [`hollow`](kcl/hollow)
|
* [`hollow`](kcl/hollow)
|
||||||
* [`import`](kcl/import)
|
* [`import`](kcl/import)
|
||||||
@ -81,6 +82,7 @@ layout: manual
|
|||||||
* [`pi`](kcl/pi)
|
* [`pi`](kcl/pi)
|
||||||
* [`polar`](kcl/polar)
|
* [`polar`](kcl/polar)
|
||||||
* [`polygon`](kcl/polygon)
|
* [`polygon`](kcl/polygon)
|
||||||
|
* [`pop`](kcl/pop)
|
||||||
* [`pow`](kcl/pow)
|
* [`pow`](kcl/pow)
|
||||||
* [`profileStart`](kcl/profileStart)
|
* [`profileStart`](kcl/profileStart)
|
||||||
* [`profileStartX`](kcl/profileStartX)
|
* [`profileStartX`](kcl/profileStartX)
|
||||||
|
39
docs/kcl/pop.md
Normal file
6781
docs/kcl/std.json
@ -1,19 +1,19 @@
|
|||||||
---
|
---
|
||||||
title: "AxisOrEdgeReference"
|
title: "Axis2dOrEdgeReference"
|
||||||
excerpt: "Axis or tagged edge."
|
excerpt: "A 2D axis or tagged edge."
|
||||||
layout: manual
|
layout: manual
|
||||||
---
|
---
|
||||||
|
|
||||||
Axis or tagged edge.
|
A 2D axis or tagged edge.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
**This schema accepts any of the following:**
|
**This schema accepts any of the following:**
|
||||||
|
|
||||||
Axis and origin.
|
2D axis and origin.
|
||||||
|
|
||||||
[`AxisAndOrigin`](/docs/kcl/types/AxisAndOrigin)
|
[`AxisAndOrigin2d`](/docs/kcl/types/AxisAndOrigin2d)
|
||||||
|
|
||||||
|
|
||||||
|
|
42
docs/kcl/types/Axis3dOrEdgeReference.md
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
---
|
||||||
|
title: "Axis3dOrEdgeReference"
|
||||||
|
excerpt: "A 3D axis or tagged edge."
|
||||||
|
layout: manual
|
||||||
|
---
|
||||||
|
|
||||||
|
A 3D axis or tagged edge.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
**This schema accepts any of the following:**
|
||||||
|
|
||||||
|
3D axis and origin.
|
||||||
|
|
||||||
|
[`AxisAndOrigin3d`](/docs/kcl/types/AxisAndOrigin3d)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
Tagged edge.
|
||||||
|
|
||||||
|
[`EdgeReference`](/docs/kcl/types/EdgeReference)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1,10 +1,10 @@
|
|||||||
---
|
---
|
||||||
title: "AxisAndOrigin"
|
title: "AxisAndOrigin2d"
|
||||||
excerpt: "Axis and origin."
|
excerpt: "A 2D axis and origin."
|
||||||
layout: manual
|
layout: manual
|
||||||
---
|
---
|
||||||
|
|
||||||
Axis and origin.
|
A 2D axis and origin.
|
||||||
|
|
||||||
|
|
||||||
|
|
105
docs/kcl/types/AxisAndOrigin3d.md
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
---
|
||||||
|
title: "AxisAndOrigin3d"
|
||||||
|
excerpt: "A 3D axis and origin."
|
||||||
|
layout: manual
|
||||||
|
---
|
||||||
|
|
||||||
|
A 3D axis and origin.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
**This schema accepts exactly one of the following:**
|
||||||
|
|
||||||
|
X-axis.
|
||||||
|
|
||||||
|
**enum:** `X`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
Y-axis.
|
||||||
|
|
||||||
|
**enum:** `Y`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
Z-axis.
|
||||||
|
|
||||||
|
**enum:** `Z`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
Flip the X-axis.
|
||||||
|
|
||||||
|
**enum:** `-X`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
Flip the Y-axis.
|
||||||
|
|
||||||
|
**enum:** `-Y`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
Flip the Z-axis.
|
||||||
|
|
||||||
|
**enum:** `-Z`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
**Type:** `object`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
| Property | Type | Description | Required |
|
||||||
|
|----------|------|-------------|----------|
|
||||||
|
| `custom` |`object`| | No |
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
25
docs/kcl/types/Helix.md
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
---
|
||||||
|
title: "Helix"
|
||||||
|
excerpt: "A helix."
|
||||||
|
layout: manual
|
||||||
|
---
|
||||||
|
|
||||||
|
A helix.
|
||||||
|
|
||||||
|
**Type:** `object`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
| Property | Type | Description | Required |
|
||||||
|
|----------|------|-------------|----------|
|
||||||
|
| `value` |`string`| The id of the helix. | No |
|
||||||
|
| `revolutions` |`number`| Number of revolutions. | No |
|
||||||
|
| `angleStart` |`number`| Start angle (in degrees). | No |
|
||||||
|
| `ccw` |`boolean`| Is the helix rotation counter clockwise? | No |
|
||||||
|
| `__meta` |`[` [`Metadata`](/docs/kcl/types/Metadata) `]`| | No |
|
||||||
|
|
||||||
|
|
@ -1,10 +1,10 @@
|
|||||||
---
|
---
|
||||||
title: "HelixData"
|
title: "HelixData"
|
||||||
excerpt: "Data for helices."
|
excerpt: "Data for a helix."
|
||||||
layout: manual
|
layout: manual
|
||||||
---
|
---
|
||||||
|
|
||||||
Data for helices.
|
Data for a helix.
|
||||||
|
|
||||||
**Type:** `object`
|
**Type:** `object`
|
||||||
|
|
||||||
@ -19,6 +19,8 @@ Data for helices.
|
|||||||
| `revolutions` |`number`| Number of revolutions. | No |
|
| `revolutions` |`number`| Number of revolutions. | No |
|
||||||
| `angleStart` |`number`| Start angle (in degrees). | No |
|
| `angleStart` |`number`| Start angle (in degrees). | No |
|
||||||
| `ccw` |`boolean`| Is the helix rotation counter clockwise? The default is `false`. | No |
|
| `ccw` |`boolean`| Is the helix rotation counter clockwise? The default is `false`. | No |
|
||||||
| `length` |`number`| Length of the helix. If this argument is not provided, the height of the solid is used. | No |
|
| `length` |`number`| Length of the helix. | No |
|
||||||
|
| `radius` |`number`| Radius of the helix. | No |
|
||||||
|
| `axis` |[`Axis3dOrEdgeReference`](/docs/kcl/types/Axis3dOrEdgeReference)| Axis to use as mirror. | No |
|
||||||
|
|
||||||
|
|
||||||
|
24
docs/kcl/types/HelixRevolutionsData.md
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
---
|
||||||
|
title: "HelixRevolutionsData"
|
||||||
|
excerpt: "Data for helix revolutions."
|
||||||
|
layout: manual
|
||||||
|
---
|
||||||
|
|
||||||
|
Data for helix revolutions.
|
||||||
|
|
||||||
|
**Type:** `object`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
| Property | Type | Description | Required |
|
||||||
|
|----------|------|-------------|----------|
|
||||||
|
| `revolutions` |`number`| Number of revolutions. | No |
|
||||||
|
| `angleStart` |`number`| Start angle (in degrees). | No |
|
||||||
|
| `ccw` |`boolean`| Is the helix rotation counter clockwise? The default is `false`. | No |
|
||||||
|
| `length` |`number`| Length of the helix. If this argument is not provided, the height of the solid is used. | No |
|
||||||
|
|
||||||
|
|
25
docs/kcl/types/HelixValue.md
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
---
|
||||||
|
title: "HelixValue"
|
||||||
|
excerpt: "A helix."
|
||||||
|
layout: manual
|
||||||
|
---
|
||||||
|
|
||||||
|
A helix.
|
||||||
|
|
||||||
|
**Type:** `object`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
| Property | Type | Description | Required |
|
||||||
|
|----------|------|-------------|----------|
|
||||||
|
| `value` |`string`| The id of the helix. | No |
|
||||||
|
| `revolutions` |`number`| Number of revolutions. | No |
|
||||||
|
| `angleStart` |`number`| Start angle (in degrees). | No |
|
||||||
|
| `ccw` |`boolean`| Is the helix rotation counter clockwise? | No |
|
||||||
|
| `__meta` |`[` [`Metadata`](/docs/kcl/types/Metadata) `]`| | No |
|
||||||
|
|
||||||
|
|
@ -285,6 +285,27 @@ An solid is a collection of extrude surfaces.
|
|||||||
| `value` |`[` [`Solid`](/docs/kcl/types/Solid) `]`| | No |
|
| `value` |`[` [`Solid`](/docs/kcl/types/Solid) `]`| | No |
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
A helix.
|
||||||
|
|
||||||
|
**Type:** `object`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
| Property | Type | Description | Required |
|
||||||
|
|----------|------|-------------|----------|
|
||||||
|
| `type` |enum: [`Helix`](/docs/kcl/types/Helix)| | No |
|
||||||
|
| `value` |`string`| The id of the helix. | No |
|
||||||
|
| `revolutions` |`number`| Number of revolutions. | No |
|
||||||
|
| `angleStart` |`number`| Start angle (in degrees). | No |
|
||||||
|
| `ccw` |`boolean`| Is the helix rotation counter clockwise? | No |
|
||||||
|
| `__meta` |`[` [`Metadata`](/docs/kcl/types/Metadata) `]`| | No |
|
||||||
|
|
||||||
|
|
||||||
----
|
----
|
||||||
Data for an imported geometry.
|
Data for an imported geometry.
|
||||||
|
|
||||||
|
@ -16,6 +16,6 @@ Data for a mirror.
|
|||||||
|
|
||||||
| Property | Type | Description | Required |
|
| Property | Type | Description | Required |
|
||||||
|----------|------|-------------|----------|
|
|----------|------|-------------|----------|
|
||||||
| `axis` |[`AxisOrEdgeReference`](/docs/kcl/types/AxisOrEdgeReference)| Axis to use as mirror. | No |
|
| `axis` |[`Axis2dOrEdgeReference`](/docs/kcl/types/Axis2dOrEdgeReference)| Axis to use as mirror. | No |
|
||||||
|
|
||||||
|
|
||||||
|
@ -17,7 +17,7 @@ Data for revolution surfaces.
|
|||||||
| Property | Type | Description | Required |
|
| Property | Type | Description | Required |
|
||||||
|----------|------|-------------|----------|
|
|----------|------|-------------|----------|
|
||||||
| `angle` |`number` (**maximum:** 360.0) (**minimum:** -360.0)| Angle to revolve (in degrees). Default is 360. | No |
|
| `angle` |`number` (**maximum:** 360.0) (**minimum:** -360.0)| Angle to revolve (in degrees). Default is 360. | No |
|
||||||
| `axis` |[`AxisOrEdgeReference`](/docs/kcl/types/AxisOrEdgeReference)| Axis of revolution. | No |
|
| `axis` |[`Axis2dOrEdgeReference`](/docs/kcl/types/Axis2dOrEdgeReference)| Axis of revolution. | No |
|
||||||
| `tolerance` |`number`| Tolerance for the revolve operation. | No |
|
| `tolerance` |`number`| Tolerance for the revolve operation. | No |
|
||||||
|
|
||||||
|
|
||||||
|
@ -16,7 +16,7 @@ Data for a sweep.
|
|||||||
|
|
||||||
| Property | Type | Description | Required |
|
| Property | Type | Description | Required |
|
||||||
|----------|------|-------------|----------|
|
|----------|------|-------------|----------|
|
||||||
| `path` |[`Sketch`](/docs/kcl/types/Sketch)| The path to sweep along. | No |
|
| `path` |[`SweepPath`](/docs/kcl/types/SweepPath)| The path to sweep along. | No |
|
||||||
| `sectional` |`boolean`| If true, the sweep will be broken up into sub-sweeps (extrusions, revolves, sweeps) based on the trajectory path components. | No |
|
| `sectional` |`boolean`| If true, the sweep will be broken up into sub-sweeps (extrusions, revolves, sweeps) based on the trajectory path components. | No |
|
||||||
| `tolerance` |`number`| Tolerance for the sweep operation. | No |
|
| `tolerance` |`number`| Tolerance for the sweep operation. | No |
|
||||||
|
|
||||||
|
42
docs/kcl/types/SweepPath.md
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
---
|
||||||
|
title: "SweepPath"
|
||||||
|
excerpt: "A path to sweep along."
|
||||||
|
layout: manual
|
||||||
|
---
|
||||||
|
|
||||||
|
A path to sweep along.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
**This schema accepts any of the following:**
|
||||||
|
|
||||||
|
A path to sweep along.
|
||||||
|
|
||||||
|
[`Sketch`](/docs/kcl/types/Sketch)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
A path to sweep along.
|
||||||
|
|
||||||
|
[`Helix`](/docs/kcl/types/Helix)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -12,10 +12,9 @@ import { CmdBarFixture } from './cmdBarFixture'
|
|||||||
import { EditorFixture } from './editorFixture'
|
import { EditorFixture } from './editorFixture'
|
||||||
import { ToolbarFixture } from './toolbarFixture'
|
import { ToolbarFixture } from './toolbarFixture'
|
||||||
import { SceneFixture } from './sceneFixture'
|
import { SceneFixture } from './sceneFixture'
|
||||||
|
import { SaveSettingsPayload } from 'lib/settings/settingsTypes'
|
||||||
import { HomePageFixture } from './homePageFixture'
|
import { HomePageFixture } from './homePageFixture'
|
||||||
import { unsafeTypedKeys } from 'lib/utils'
|
import { unsafeTypedKeys } from 'lib/utils'
|
||||||
import { DeepPartial } from 'lib/types'
|
|
||||||
import { Settings } from 'wasm-lib/kcl/bindings/Settings'
|
|
||||||
|
|
||||||
export class AuthenticatedApp {
|
export class AuthenticatedApp {
|
||||||
public readonly page: Page
|
public readonly page: Page
|
||||||
@ -79,7 +78,7 @@ export class AuthenticatedTronApp {
|
|||||||
fixtures: Partial<Fixtures>
|
fixtures: Partial<Fixtures>
|
||||||
folderSetupFn?: (projectDirName: string) => Promise<void>
|
folderSetupFn?: (projectDirName: string) => Promise<void>
|
||||||
cleanProjectDir?: boolean
|
cleanProjectDir?: boolean
|
||||||
appSettings?: DeepPartial<Settings>
|
appSettings?: Partial<SaveSettingsPayload>
|
||||||
} = { fixtures: {} }
|
} = { fixtures: {} }
|
||||||
) {
|
) {
|
||||||
const { electronApp, page, context, dir } = await setupElectron({
|
const { electronApp, page, context, dir } = await setupElectron({
|
||||||
|
@ -36,7 +36,8 @@ type DragFromHandler = (
|
|||||||
|
|
||||||
export class SceneFixture {
|
export class SceneFixture {
|
||||||
public page: Page
|
public page: Page
|
||||||
|
public streamWrapper!: Locator
|
||||||
|
public loadingIndicator!: Locator
|
||||||
private exeIndicator!: Locator
|
private exeIndicator!: Locator
|
||||||
|
|
||||||
constructor(page: Page) {
|
constructor(page: Page) {
|
||||||
@ -64,6 +65,8 @@ export class SceneFixture {
|
|||||||
this.page = page
|
this.page = page
|
||||||
|
|
||||||
this.exeIndicator = page.getByTestId('model-state-indicator-execution-done')
|
this.exeIndicator = page.getByTestId('model-state-indicator-execution-done')
|
||||||
|
this.streamWrapper = page.getByTestId('stream')
|
||||||
|
this.loadingIndicator = this.streamWrapper.getByTestId('loading')
|
||||||
}
|
}
|
||||||
|
|
||||||
makeMouseHelpers = (
|
makeMouseHelpers = (
|
||||||
|
@ -14,6 +14,7 @@ export class ToolbarFixture {
|
|||||||
|
|
||||||
extrudeButton!: Locator
|
extrudeButton!: Locator
|
||||||
loftButton!: Locator
|
loftButton!: Locator
|
||||||
|
sweepButton!: Locator
|
||||||
shellButton!: Locator
|
shellButton!: Locator
|
||||||
offsetPlaneButton!: Locator
|
offsetPlaneButton!: Locator
|
||||||
startSketchBtn!: Locator
|
startSketchBtn!: Locator
|
||||||
@ -40,6 +41,7 @@ export class ToolbarFixture {
|
|||||||
this.page = page
|
this.page = page
|
||||||
this.extrudeButton = page.getByTestId('extrude')
|
this.extrudeButton = page.getByTestId('extrude')
|
||||||
this.loftButton = page.getByTestId('loft')
|
this.loftButton = page.getByTestId('loft')
|
||||||
|
this.sweepButton = page.getByTestId('sweep')
|
||||||
this.shellButton = page.getByTestId('shell')
|
this.shellButton = page.getByTestId('shell')
|
||||||
this.offsetPlaneButton = page.getByTestId('plane-offset')
|
this.offsetPlaneButton = page.getByTestId('plane-offset')
|
||||||
this.startSketchBtn = page.getByTestId('sketch')
|
this.startSketchBtn = page.getByTestId('sketch')
|
||||||
|
@ -1,12 +1,7 @@
|
|||||||
import { test, expect } from './zoo-test'
|
import { test, expect } from './zoo-test'
|
||||||
import { join } from 'path'
|
import { join } from 'path'
|
||||||
import fsp from 'fs/promises'
|
import fsp from 'fs/promises'
|
||||||
import {
|
import { getUtils, executorInputPath, createProject } from './test-utils'
|
||||||
getUtils,
|
|
||||||
executorInputPath,
|
|
||||||
createProject,
|
|
||||||
settingsToToml,
|
|
||||||
} 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'
|
||||||
import {
|
import {
|
||||||
@ -15,6 +10,7 @@ import {
|
|||||||
TEST_SETTINGS_ONBOARDING_EXPORT,
|
TEST_SETTINGS_ONBOARDING_EXPORT,
|
||||||
TEST_SETTINGS_ONBOARDING_USER_MENU,
|
TEST_SETTINGS_ONBOARDING_USER_MENU,
|
||||||
} from './storageStates'
|
} from './storageStates'
|
||||||
|
import * as TOML from '@iarna/toml'
|
||||||
import { expectPixelColor } from './fixtures/sceneFixture'
|
import { expectPixelColor } from './fixtures/sceneFixture'
|
||||||
|
|
||||||
// Because onboarding relies on an app setting we need to set it as incompletel
|
// Because onboarding relies on an app setting we need to set it as incompletel
|
||||||
@ -26,7 +22,7 @@ test.describe('Onboarding tests', () => {
|
|||||||
{
|
{
|
||||||
appSettings: {
|
appSettings: {
|
||||||
app: {
|
app: {
|
||||||
onboarding_status: 'incomplete',
|
onboardingStatus: 'incomplete',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
cleanProjectDir: true,
|
cleanProjectDir: true,
|
||||||
@ -67,7 +63,7 @@ test.describe('Onboarding tests', () => {
|
|||||||
tag: '@electron',
|
tag: '@electron',
|
||||||
appSettings: {
|
appSettings: {
|
||||||
app: {
|
app: {
|
||||||
onboarding_status: 'incomplete',
|
onboardingStatus: 'incomplete',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
cleanProjectDir: true,
|
cleanProjectDir: true,
|
||||||
@ -112,7 +108,7 @@ test.describe('Onboarding tests', () => {
|
|||||||
{
|
{
|
||||||
appSettings: {
|
appSettings: {
|
||||||
app: {
|
app: {
|
||||||
onboarding_status: 'incomplete',
|
onboardingStatus: 'incomplete',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
cleanProjectDir: true,
|
cleanProjectDir: true,
|
||||||
@ -162,7 +158,7 @@ test.describe('Onboarding tests', () => {
|
|||||||
{
|
{
|
||||||
appSettings: {
|
appSettings: {
|
||||||
app: {
|
app: {
|
||||||
onboarding_status: 'incomplete',
|
onboardingStatus: 'incomplete',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -176,7 +172,7 @@ test.describe('Onboarding tests', () => {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
settingsKey: TEST_SETTINGS_KEY,
|
settingsKey: TEST_SETTINGS_KEY,
|
||||||
settings: settingsToToml({
|
settings: TOML.stringify({
|
||||||
settings: TEST_SETTINGS_ONBOARDING_START,
|
settings: TEST_SETTINGS_ONBOARDING_START,
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
@ -212,7 +208,7 @@ test.describe('Onboarding tests', () => {
|
|||||||
{
|
{
|
||||||
appSettings: {
|
appSettings: {
|
||||||
app: {
|
app: {
|
||||||
onboarding_status: '/export',
|
onboardingStatus: '/export',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
cleanProjectDir: true,
|
cleanProjectDir: true,
|
||||||
@ -229,7 +225,7 @@ test.describe('Onboarding tests', () => {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
settingsKey: TEST_SETTINGS_KEY,
|
settingsKey: TEST_SETTINGS_KEY,
|
||||||
settings: settingsToToml({
|
settings: TOML.stringify({
|
||||||
settings: TEST_SETTINGS_ONBOARDING_EXPORT,
|
settings: TEST_SETTINGS_ONBOARDING_EXPORT,
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
@ -267,7 +263,7 @@ test.describe('Onboarding tests', () => {
|
|||||||
{
|
{
|
||||||
appSettings: {
|
appSettings: {
|
||||||
app: {
|
app: {
|
||||||
onboarding_status: '/parametric-modeling',
|
onboardingStatus: '/parametric-modeling',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
cleanProjectDir: true,
|
cleanProjectDir: true,
|
||||||
@ -317,7 +313,7 @@ test.describe('Onboarding tests', () => {
|
|||||||
{
|
{
|
||||||
appSettings: {
|
appSettings: {
|
||||||
app: {
|
app: {
|
||||||
onboarding_status: 'incomplete',
|
onboardingStatus: 'incomplete',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
cleanProjectDir: true,
|
cleanProjectDir: true,
|
||||||
@ -330,7 +326,7 @@ test.describe('Onboarding tests', () => {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
settingsKey: TEST_SETTINGS_KEY,
|
settingsKey: TEST_SETTINGS_KEY,
|
||||||
settings: settingsToToml({
|
settings: TOML.stringify({
|
||||||
settings: TEST_SETTINGS_ONBOARDING_USER_MENU,
|
settings: TEST_SETTINGS_ONBOARDING_USER_MENU,
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
@ -390,7 +386,7 @@ test.describe('Onboarding tests', () => {
|
|||||||
{
|
{
|
||||||
appSettings: {
|
appSettings: {
|
||||||
app: {
|
app: {
|
||||||
onboarding_status: 'incomplete',
|
onboardingStatus: 'incomplete',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
cleanProjectDir: true,
|
cleanProjectDir: true,
|
||||||
@ -404,7 +400,7 @@ test.describe('Onboarding tests', () => {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
settingsKey: TEST_SETTINGS_KEY,
|
settingsKey: TEST_SETTINGS_KEY,
|
||||||
settings: settingsToToml({
|
settings: TOML.stringify({
|
||||||
settings: TEST_SETTINGS_ONBOARDING_USER_MENU,
|
settings: TEST_SETTINGS_ONBOARDING_USER_MENU,
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
@ -446,7 +442,7 @@ test.fixme(
|
|||||||
{
|
{
|
||||||
appSettings: {
|
appSettings: {
|
||||||
app: {
|
app: {
|
||||||
onboarding_status: 'dismissed',
|
onboardingStatus: 'dismissed',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
cleanProjectDir: true,
|
cleanProjectDir: true,
|
||||||
|
@ -756,6 +756,17 @@ test(`Offset plane point-and-click`, async ({
|
|||||||
})
|
})
|
||||||
await scene.expectPixelColor([74, 74, 74], testPoint, 15)
|
await scene.expectPixelColor([74, 74, 74], testPoint, 15)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
await test.step('Delete offset plane via feature tree selection', async () => {
|
||||||
|
await editor.closePane()
|
||||||
|
const operationButton = await toolbar.getFeatureTreeOperation(
|
||||||
|
'Offset Plane',
|
||||||
|
0
|
||||||
|
)
|
||||||
|
await operationButton.click({ button: 'left' })
|
||||||
|
await page.keyboard.press('Backspace')
|
||||||
|
await scene.expectPixelColor([50, 51, 96], testPoint, 15)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
const loftPointAndClickCases = [
|
const loftPointAndClickCases = [
|
||||||
@ -851,6 +862,173 @@ loftPointAndClickCases.forEach(({ shouldPreselect }) => {
|
|||||||
})
|
})
|
||||||
await scene.expectPixelColor([89, 89, 89], testPoint, 15)
|
await scene.expectPixelColor([89, 89, 89], testPoint, 15)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
await test.step('Delete loft via feature tree selection', async () => {
|
||||||
|
await editor.closePane()
|
||||||
|
const operationButton = await toolbar.getFeatureTreeOperation('Loft', 0)
|
||||||
|
await operationButton.click({ button: 'left' })
|
||||||
|
await page.keyboard.press('Backspace')
|
||||||
|
await scene.expectPixelColor([254, 254, 254], testPoint, 15)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
// TODO: merge with above test. Right now we're not able to delete a loft
|
||||||
|
// right after creation via selection for some reason, so we go with a new instance
|
||||||
|
test('Loft and offset plane deletion via selection', async ({
|
||||||
|
context,
|
||||||
|
page,
|
||||||
|
homePage,
|
||||||
|
scene,
|
||||||
|
}) => {
|
||||||
|
const initialCode = `sketch001 = startSketchOn('XZ')
|
||||||
|
|> circle({ center = [0, 0], radius = 30 }, %)
|
||||||
|
plane001 = offsetPlane('XZ', 50)
|
||||||
|
sketch002 = startSketchOn(plane001)
|
||||||
|
|> circle({ center = [0, 0], radius = 20 }, %)
|
||||||
|
loft001 = loft([sketch001, sketch002])
|
||||||
|
`
|
||||||
|
await context.addInitScript((initialCode) => {
|
||||||
|
localStorage.setItem('persistCode', initialCode)
|
||||||
|
}, initialCode)
|
||||||
|
await page.setBodyDimensions({ width: 1000, height: 500 })
|
||||||
|
await homePage.goToModelingScene()
|
||||||
|
|
||||||
|
// One dumb hardcoded screen pixel value
|
||||||
|
const testPoint = { x: 575, y: 200 }
|
||||||
|
const [clickOnSketch1] = scene.makeMouseHelpers(testPoint.x, testPoint.y)
|
||||||
|
const [clickOnSketch2] = scene.makeMouseHelpers(testPoint.x, testPoint.y + 80)
|
||||||
|
|
||||||
|
await test.step(`Delete loft`, async () => {
|
||||||
|
// Check for loft
|
||||||
|
await scene.expectPixelColor([89, 89, 89], testPoint, 15)
|
||||||
|
await clickOnSketch1()
|
||||||
|
await expect(page.locator('.cm-activeLine')).toHaveText(`
|
||||||
|
|> circle({ center = [0, 0], radius = 30 }, %)
|
||||||
|
`)
|
||||||
|
await page.keyboard.press('Backspace')
|
||||||
|
// Check for sketch 1
|
||||||
|
await scene.expectPixelColor([254, 254, 254], testPoint, 15)
|
||||||
|
})
|
||||||
|
|
||||||
|
await test.step('Delete sketch002', async () => {
|
||||||
|
await page.waitForTimeout(1000)
|
||||||
|
await clickOnSketch2()
|
||||||
|
await expect(page.locator('.cm-activeLine')).toHaveText(`
|
||||||
|
|> circle({ center = [0, 0], radius = 20 }, %)
|
||||||
|
`)
|
||||||
|
await page.keyboard.press('Backspace')
|
||||||
|
// Check for plane001
|
||||||
|
await scene.expectPixelColor([228, 228, 228], testPoint, 15)
|
||||||
|
})
|
||||||
|
|
||||||
|
await test.step('Delete plane001', async () => {
|
||||||
|
await page.waitForTimeout(1000)
|
||||||
|
await clickOnSketch2()
|
||||||
|
await expect(page.locator('.cm-activeLine')).toHaveText(`
|
||||||
|
plane001 = offsetPlane('XZ', 50)
|
||||||
|
`)
|
||||||
|
await page.keyboard.press('Backspace')
|
||||||
|
// Check for sketch 1
|
||||||
|
await scene.expectPixelColor([254, 254, 254], testPoint, 15)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
test(`Sweep point-and-click`, async ({
|
||||||
|
context,
|
||||||
|
page,
|
||||||
|
homePage,
|
||||||
|
scene,
|
||||||
|
editor,
|
||||||
|
toolbar,
|
||||||
|
cmdBar,
|
||||||
|
}) => {
|
||||||
|
const initialCode = `sketch001 = startSketchOn('YZ')
|
||||||
|
|> circle({
|
||||||
|
center = [0, 0],
|
||||||
|
radius = 500
|
||||||
|
}, %)
|
||||||
|
sketch002 = startSketchOn('XZ')
|
||||||
|
|> startProfileAt([0, 0], %)
|
||||||
|
|> xLine(-500, %)
|
||||||
|
|> tangentialArcTo([-2000, 500], %)
|
||||||
|
`
|
||||||
|
await context.addInitScript((initialCode) => {
|
||||||
|
localStorage.setItem('persistCode', initialCode)
|
||||||
|
}, initialCode)
|
||||||
|
await page.setBodyDimensions({ width: 1000, height: 500 })
|
||||||
|
await homePage.goToModelingScene()
|
||||||
|
await scene.waitForExecutionDone()
|
||||||
|
|
||||||
|
// One dumb hardcoded screen pixel value
|
||||||
|
const testPoint = { x: 700, y: 250 }
|
||||||
|
const [clickOnSketch1] = scene.makeMouseHelpers(testPoint.x, testPoint.y)
|
||||||
|
const [clickOnSketch2] = scene.makeMouseHelpers(testPoint.x - 50, testPoint.y)
|
||||||
|
const sweepDeclaration = 'sweep001 = sweep({ path = sketch002 }, sketch001)'
|
||||||
|
|
||||||
|
await test.step(`Look for sketch001`, async () => {
|
||||||
|
await toolbar.closePane('code')
|
||||||
|
await scene.expectPixelColor([53, 53, 53], testPoint, 15)
|
||||||
|
})
|
||||||
|
|
||||||
|
await test.step(`Go through the command bar flow`, async () => {
|
||||||
|
await toolbar.sweepButton.click()
|
||||||
|
await cmdBar.expectState({
|
||||||
|
commandName: 'Sweep',
|
||||||
|
currentArgKey: 'profile',
|
||||||
|
currentArgValue: '',
|
||||||
|
headerArguments: {
|
||||||
|
Path: '',
|
||||||
|
Profile: '',
|
||||||
|
},
|
||||||
|
highlightedHeaderArg: 'profile',
|
||||||
|
stage: 'arguments',
|
||||||
|
})
|
||||||
|
await clickOnSketch1()
|
||||||
|
await cmdBar.expectState({
|
||||||
|
commandName: 'Sweep',
|
||||||
|
currentArgKey: 'path',
|
||||||
|
currentArgValue: '',
|
||||||
|
headerArguments: {
|
||||||
|
Path: '',
|
||||||
|
Profile: '1 face',
|
||||||
|
},
|
||||||
|
highlightedHeaderArg: 'path',
|
||||||
|
stage: 'arguments',
|
||||||
|
})
|
||||||
|
await clickOnSketch2()
|
||||||
|
await cmdBar.expectState({
|
||||||
|
commandName: 'Sweep',
|
||||||
|
headerArguments: {
|
||||||
|
Path: '1 face',
|
||||||
|
Profile: '1 face',
|
||||||
|
},
|
||||||
|
stage: 'review',
|
||||||
|
})
|
||||||
|
await cmdBar.progressCmdBar()
|
||||||
|
})
|
||||||
|
|
||||||
|
await test.step(`Confirm code is added to the editor, scene has changed`, async () => {
|
||||||
|
await scene.expectPixelColor([135, 64, 73], testPoint, 15)
|
||||||
|
await toolbar.openPane('code')
|
||||||
|
await editor.expectEditor.toContain(sweepDeclaration)
|
||||||
|
await editor.expectState({
|
||||||
|
diagnostics: [],
|
||||||
|
activeLines: [sweepDeclaration],
|
||||||
|
highlightedCode: '',
|
||||||
|
})
|
||||||
|
await toolbar.closePane('code')
|
||||||
|
})
|
||||||
|
|
||||||
|
await test.step('Delete sweep via feature tree selection', async () => {
|
||||||
|
await toolbar.openPane('feature-tree')
|
||||||
|
await page.waitForTimeout(500)
|
||||||
|
const operationButton = await toolbar.getFeatureTreeOperation('Sweep', 0)
|
||||||
|
await operationButton.click({ button: 'left' })
|
||||||
|
await page.keyboard.press('Backspace')
|
||||||
|
await page.waitForTimeout(500)
|
||||||
|
await toolbar.closePane('feature-tree')
|
||||||
|
await scene.expectPixelColor([53, 53, 53], testPoint, 15)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -1030,4 +1208,104 @@ extrude001 = extrude(40, sketch001)
|
|||||||
})
|
})
|
||||||
await scene.expectPixelColor([49, 49, 49], testPoint, 15)
|
await scene.expectPixelColor([49, 49, 49], testPoint, 15)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
await test.step('Delete shell via feature tree selection', async () => {
|
||||||
|
await editor.closePane()
|
||||||
|
const operationButton = await toolbar.getFeatureTreeOperation('Shell', 0)
|
||||||
|
await operationButton.click({ button: 'left' })
|
||||||
|
await page.keyboard.press('Backspace')
|
||||||
|
await scene.expectPixelColor([99, 99, 99], testPoint, 15)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
const shellSketchOnFacesCases = [
|
||||||
|
`sketch001 = startSketchOn('XZ')
|
||||||
|
|> circle({ center = [0, 0], radius = 100 }, %)
|
||||||
|
|> extrude(100, %)
|
||||||
|
|
||||||
|
sketch002 = startSketchOn(sketch001, 'END')
|
||||||
|
|> circle({ center = [0, 0], radius = 50 }, %)
|
||||||
|
|> extrude(50, %)
|
||||||
|
`,
|
||||||
|
`sketch001 = startSketchOn('XZ')
|
||||||
|
|> circle({ center = [0, 0], radius = 100 }, %)
|
||||||
|
extrude001 = extrude(100, sketch001)
|
||||||
|
|
||||||
|
sketch002 = startSketchOn(extrude001, 'END')
|
||||||
|
|> circle({ center = [0, 0], radius = 50 }, %)
|
||||||
|
extrude002 = extrude(50, sketch002)
|
||||||
|
`,
|
||||||
|
]
|
||||||
|
shellSketchOnFacesCases.forEach((initialCode, index) => {
|
||||||
|
const hasExtrudesInPipe = index === 0
|
||||||
|
test(`Shell point-and-click sketch on face (extrudes in pipes: ${hasExtrudesInPipe})`, async ({
|
||||||
|
context,
|
||||||
|
page,
|
||||||
|
homePage,
|
||||||
|
scene,
|
||||||
|
editor,
|
||||||
|
toolbar,
|
||||||
|
cmdBar,
|
||||||
|
}) => {
|
||||||
|
await context.addInitScript((initialCode) => {
|
||||||
|
localStorage.setItem('persistCode', initialCode)
|
||||||
|
}, initialCode)
|
||||||
|
await page.setBodyDimensions({ width: 1000, height: 500 })
|
||||||
|
await homePage.goToModelingScene()
|
||||||
|
await scene.waitForExecutionDone()
|
||||||
|
|
||||||
|
// One dumb hardcoded screen pixel value
|
||||||
|
const testPoint = { x: 550, y: 295 }
|
||||||
|
const [clickOnCap] = scene.makeMouseHelpers(testPoint.x, testPoint.y)
|
||||||
|
const shellDeclaration = `shell001 = shell({ faces = ['end'], thickness = 5 }, ${
|
||||||
|
hasExtrudesInPipe ? 'sketch002' : 'extrude002'
|
||||||
|
})`
|
||||||
|
|
||||||
|
await test.step(`Look for the grey of the shape`, async () => {
|
||||||
|
await toolbar.closePane('code')
|
||||||
|
await scene.expectPixelColor([128, 128, 128], testPoint, 15)
|
||||||
|
})
|
||||||
|
|
||||||
|
await test.step(`Go through the command bar flow, selecting a cap and keeping default thickness`, async () => {
|
||||||
|
await toolbar.shellButton.click()
|
||||||
|
await cmdBar.expectState({
|
||||||
|
stage: 'arguments',
|
||||||
|
currentArgKey: 'selection',
|
||||||
|
currentArgValue: '',
|
||||||
|
headerArguments: {
|
||||||
|
Selection: '',
|
||||||
|
Thickness: '',
|
||||||
|
},
|
||||||
|
highlightedHeaderArg: 'selection',
|
||||||
|
commandName: 'Shell',
|
||||||
|
})
|
||||||
|
await clickOnCap()
|
||||||
|
await page.waitForTimeout(500)
|
||||||
|
await cmdBar.progressCmdBar()
|
||||||
|
await page.waitForTimeout(500)
|
||||||
|
await cmdBar.progressCmdBar()
|
||||||
|
await page.waitForTimeout(500)
|
||||||
|
await cmdBar.expectState({
|
||||||
|
stage: 'review',
|
||||||
|
headerArguments: {
|
||||||
|
Selection: '1 cap',
|
||||||
|
Thickness: '5',
|
||||||
|
},
|
||||||
|
commandName: 'Shell',
|
||||||
|
})
|
||||||
|
await cmdBar.progressCmdBar()
|
||||||
|
})
|
||||||
|
|
||||||
|
await test.step(`Confirm code is added to the editor, scene has changed`, async () => {
|
||||||
|
await toolbar.openPane('code')
|
||||||
|
await editor.expectEditor.toContain(shellDeclaration)
|
||||||
|
await editor.expectState({
|
||||||
|
diagnostics: [],
|
||||||
|
activeLines: [shellDeclaration],
|
||||||
|
highlightedCode: '',
|
||||||
|
})
|
||||||
|
await toolbar.closePane('code')
|
||||||
|
await scene.expectPixelColor([73, 73, 73], testPoint, 15)
|
||||||
|
})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
@ -115,7 +115,7 @@ test(
|
|||||||
)
|
)
|
||||||
|
|
||||||
test(
|
test(
|
||||||
'yyyyyyyyy open a file in a project works and renders, open another file in different project with errors, it should clear the scene',
|
'open a file in a project works and renders, open another file in different project with errors, it should clear the scene',
|
||||||
{ tag: '@electron' },
|
{ tag: '@electron' },
|
||||||
async ({ context, page }, testInfo) => {
|
async ({ context, page }, testInfo) => {
|
||||||
await context.folderSetupFn(async (dir) => {
|
await context.folderSetupFn(async (dir) => {
|
||||||
@ -199,7 +199,7 @@ test(
|
|||||||
)
|
)
|
||||||
|
|
||||||
test(
|
test(
|
||||||
'aaayyyyyyyy open a file in a project works and renders, open another file in different project that is empty, it should clear the scene',
|
'open a file in a project works and renders, open another file in different project that is empty, it should clear the scene',
|
||||||
{ tag: '@electron' },
|
{ tag: '@electron' },
|
||||||
async ({ context, page }, testInfo) => {
|
async ({ context, page }, testInfo) => {
|
||||||
await context.folderSetupFn(async (dir) => {
|
await context.folderSetupFn(async (dir) => {
|
||||||
@ -276,7 +276,7 @@ test(
|
|||||||
)
|
)
|
||||||
|
|
||||||
test(
|
test(
|
||||||
'nooooooooooooo open a file in a project works and renders, open empty file, it should clear the scene',
|
'open a file in a project works and renders, open empty file, it should clear the scene',
|
||||||
{ tag: '@electron' },
|
{ tag: '@electron' },
|
||||||
async ({ context, page }, testInfo) => {
|
async ({ context, page }, testInfo) => {
|
||||||
await context.folderSetupFn(async (dir) => {
|
await context.folderSetupFn(async (dir) => {
|
||||||
@ -1885,3 +1885,48 @@ test.fixme(
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
test(
|
||||||
|
'project name with foreign characters should open',
|
||||||
|
{ tag: '@electron' },
|
||||||
|
async ({ context, page }, testInfo) => {
|
||||||
|
await context.folderSetupFn(async (dir) => {
|
||||||
|
const bracketDir = path.join(dir, 'اَلْعَرَبِيَّةُ')
|
||||||
|
await fsp.mkdir(bracketDir, { recursive: true })
|
||||||
|
await fsp.copyFile(
|
||||||
|
executorInputPath('focusrite_scarlett_mounting_braket.kcl'),
|
||||||
|
path.join(bracketDir, 'main.kcl')
|
||||||
|
)
|
||||||
|
|
||||||
|
await fsp.writeFile(path.join(bracketDir, 'empty.kcl'), '')
|
||||||
|
})
|
||||||
|
|
||||||
|
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||||
|
const u = await getUtils(page)
|
||||||
|
|
||||||
|
page.on('console', console.log)
|
||||||
|
|
||||||
|
const pointOnModel = { x: 630, y: 280 }
|
||||||
|
|
||||||
|
await test.step('Opening the اَلْعَرَبِيَّةُ project should load the stream', async () => {
|
||||||
|
// expect to see the text bracket
|
||||||
|
await expect(page.getByText('اَلْعَرَبِيَّةُ')).toBeVisible()
|
||||||
|
|
||||||
|
await page.getByText('اَلْعَرَبِيَّةُ').click()
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
page.getByRole('button', { name: 'Start Sketch' })
|
||||||
|
).toBeEnabled({
|
||||||
|
timeout: 20_000,
|
||||||
|
})
|
||||||
|
|
||||||
|
// gray at this pixel means the stream has loaded in the most
|
||||||
|
// user way we can verify it (pixel color)
|
||||||
|
await expect
|
||||||
|
.poll(() => u.getGreatestPixDiff(pointOnModel, [85, 85, 85]), {
|
||||||
|
timeout: 10_000,
|
||||||
|
})
|
||||||
|
.toBeLessThan(15)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
)
|
||||||
|
@ -614,6 +614,38 @@ extrude001 = extrude(50, sketch001)
|
|||||||
await expect(gizmo).toBeVisible()
|
await expect(gizmo).toBeVisible()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test(`Refreshing the app doesn't cause the stream to pause on long-executing files`, async ({
|
||||||
|
context,
|
||||||
|
homePage,
|
||||||
|
scene,
|
||||||
|
toolbar,
|
||||||
|
viewport,
|
||||||
|
}) => {
|
||||||
|
await context.folderSetupFn(async (dir) => {
|
||||||
|
const legoDir = path.join(dir, 'lego')
|
||||||
|
await fsp.mkdir(legoDir, { recursive: true })
|
||||||
|
await fsp.copyFile(
|
||||||
|
executorInputPath('lego.kcl'),
|
||||||
|
path.join(legoDir, 'main.kcl')
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
await test.step(`Test setup`, async () => {
|
||||||
|
await homePage.openProject('lego')
|
||||||
|
await toolbar.closePane('code')
|
||||||
|
})
|
||||||
|
await test.step(`Waiting for the loading spinner to disappear`, async () => {
|
||||||
|
await scene.loadingIndicator.waitFor({ state: 'detached' })
|
||||||
|
})
|
||||||
|
await test.step(`The part should start loading quickly, not waiting until execution is complete`, async () => {
|
||||||
|
await scene.expectPixelColor(
|
||||||
|
[143, 143, 143],
|
||||||
|
{ x: (viewport?.width ?? 1200) / 2, y: (viewport?.height ?? 500) / 2 },
|
||||||
|
15
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
async function clickExportButton(page: Page) {
|
async function clickExportButton(page: Page) {
|
||||||
|
@ -39,8 +39,8 @@ test.describe('Sketch tests', () => {
|
|||||||
${startProfileAt1}
|
${startProfileAt1}
|
||||||
|> arc({
|
|> arc({
|
||||||
radius = screwRadius,
|
radius = screwRadius,
|
||||||
angle_start = 0,
|
angleStart = 0,
|
||||||
angle_end = 360
|
angleEnd = 360
|
||||||
}, %)
|
}, %)
|
||||||
|
|
||||||
part001 = startSketchOn('XY')
|
part001 = startSketchOn('XY')
|
||||||
@ -60,8 +60,8 @@ test.describe('Sketch tests', () => {
|
|||||||
|> yLine(wireOffset, %)
|
|> yLine(wireOffset, %)
|
||||||
|> arc({
|
|> arc({
|
||||||
radius = wireRadius,
|
radius = wireRadius,
|
||||||
angle_start = 0,
|
angleStart = 0,
|
||||||
angle_end = 180
|
angleEnd = 180
|
||||||
}, %)
|
}, %)
|
||||||
|> yLine(-wireOffset, %)
|
|> yLine(-wireOffset, %)
|
||||||
|> xLine(-width / 4, %)
|
|> xLine(-width / 4, %)
|
||||||
@ -1323,3 +1323,85 @@ test.describe(`Sketching with offset planes`, () => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Regression test for https://github.com/KittyCAD/modeling-app/issues/4891
|
||||||
|
test.describe(`Click based selection don't brick the app when clicked out of range after format using cache`, () => {
|
||||||
|
test(`Can select a line that reformmed after entering sketch mode`, async ({
|
||||||
|
context,
|
||||||
|
page,
|
||||||
|
scene,
|
||||||
|
toolbar,
|
||||||
|
editor,
|
||||||
|
homePage,
|
||||||
|
}) => {
|
||||||
|
// We seed the scene with a single offset plane
|
||||||
|
await context.addInitScript(() => {
|
||||||
|
localStorage.setItem(
|
||||||
|
'persistCode',
|
||||||
|
`sketch001 = startSketchOn('XZ')
|
||||||
|
|> startProfileAt([0, 0], %)
|
||||||
|
|> line([3.14, 3.14], %)
|
||||||
|
|> arcTo({
|
||||||
|
end = [4, 2],
|
||||||
|
interior = [1, 2]
|
||||||
|
}, %)
|
||||||
|
`
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
await homePage.goToModelingScene()
|
||||||
|
await scene.waitForExecutionDone()
|
||||||
|
|
||||||
|
await test.step(`format the code`, async () => {
|
||||||
|
// doesn't contain condensed version
|
||||||
|
await editor.expectEditor.not.toContain(
|
||||||
|
`arcTo({ end = [4, 2], interior = [1, 2] }, %)`
|
||||||
|
)
|
||||||
|
// click the code to enter sketch mode
|
||||||
|
await page.getByText(`arcTo`).click()
|
||||||
|
// Format the code.
|
||||||
|
await page.locator('#code-pane button:first-child').click()
|
||||||
|
await page.locator('button:has-text("Format code")').click()
|
||||||
|
})
|
||||||
|
|
||||||
|
await test.step(`Ensure the code reformatted`, async () => {
|
||||||
|
await editor.expectEditor.toContain(
|
||||||
|
`arcTo({ end = [4, 2], interior = [1, 2] }, %)`
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
const [arcClick, arcHover] = scene.makeMouseHelpers(699, 337)
|
||||||
|
await test.step('Ensure we can hover the arc', async () => {
|
||||||
|
await arcHover()
|
||||||
|
|
||||||
|
// Check that the code is highlighted
|
||||||
|
await editor.expectState({
|
||||||
|
activeLines: ["sketch001=startSketchOn('XZ')"],
|
||||||
|
diagnostics: [],
|
||||||
|
highlightedCode: 'arcTo({end = [4, 2], interior = [1, 2]}, %)',
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
await test.step('reset the selection', async () => {
|
||||||
|
// Move the mouse out of the way
|
||||||
|
await page.mouse.move(655, 337)
|
||||||
|
|
||||||
|
await editor.expectState({
|
||||||
|
activeLines: ["sketch001=startSketchOn('XZ')"],
|
||||||
|
diagnostics: [],
|
||||||
|
highlightedCode: '',
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
await test.step('Ensure we can click the arc', async () => {
|
||||||
|
await arcClick()
|
||||||
|
|
||||||
|
// Check that the code is highlighted
|
||||||
|
await editor.expectState({
|
||||||
|
activeLines: [],
|
||||||
|
diagnostics: [],
|
||||||
|
highlightedCode: 'arcTo({end = [4, 2], interior = [1, 2]}, %)',
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { test, expect } from '@playwright/test'
|
import { test, expect } from '@playwright/test'
|
||||||
import { secrets } from './secrets'
|
import { secrets } from './secrets'
|
||||||
import { Paths, doExport, getUtils, settingsToToml } from './test-utils'
|
import { Paths, doExport, getUtils } from './test-utils'
|
||||||
import { Models } from '@kittycad/lib'
|
import { Models } from '@kittycad/lib'
|
||||||
import fsp from 'fs/promises'
|
import fsp from 'fs/promises'
|
||||||
import { spawn } from 'child_process'
|
import { spawn } from 'child_process'
|
||||||
@ -12,6 +12,7 @@ import {
|
|||||||
TEST_SETTINGS,
|
TEST_SETTINGS,
|
||||||
TEST_SETTINGS_KEY,
|
TEST_SETTINGS_KEY,
|
||||||
} from './storageStates'
|
} from './storageStates'
|
||||||
|
import * as TOML from '@iarna/toml'
|
||||||
|
|
||||||
test.beforeEach(async ({ page }) => {
|
test.beforeEach(async ({ page }) => {
|
||||||
// reducedMotion kills animations, which speeds up tests and reduces flakiness
|
// reducedMotion kills animations, which speeds up tests and reduces flakiness
|
||||||
@ -28,7 +29,7 @@ test.beforeEach(async ({ page }) => {
|
|||||||
{
|
{
|
||||||
token: secrets.token,
|
token: secrets.token,
|
||||||
settingsKey: TEST_SETTINGS_KEY,
|
settingsKey: TEST_SETTINGS_KEY,
|
||||||
settings: settingsToToml({ settings: TEST_SETTINGS }),
|
settings: TOML.stringify({ settings: TEST_SETTINGS }),
|
||||||
IS_PLAYWRIGHT_KEY: IS_PLAYWRIGHT_KEY,
|
IS_PLAYWRIGHT_KEY: IS_PLAYWRIGHT_KEY,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@ -703,12 +704,12 @@ test.describe(
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
settingsKey: TEST_SETTINGS_KEY,
|
settingsKey: TEST_SETTINGS_KEY,
|
||||||
settings: settingsToToml({
|
settings: TOML.stringify({
|
||||||
settings: {
|
settings: {
|
||||||
...TEST_SETTINGS,
|
...TEST_SETTINGS,
|
||||||
modeling: {
|
modeling: {
|
||||||
...TEST_SETTINGS.modeling,
|
...TEST_SETTINGS.modeling,
|
||||||
base_unit: 'mm',
|
defaultUnit: 'mm',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
@ -1061,12 +1062,12 @@ test.describe('Grid visibility', { tag: '@snapshot' }, () => {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
settingsKey: TEST_SETTINGS_KEY,
|
settingsKey: TEST_SETTINGS_KEY,
|
||||||
settings: settingsToToml({
|
settings: TOML.stringify({
|
||||||
settings: {
|
settings: {
|
||||||
...TEST_SETTINGS,
|
...TEST_SETTINGS,
|
||||||
modeling: {
|
modeling: {
|
||||||
...TEST_SETTINGS.modeling,
|
...TEST_SETTINGS.modeling,
|
||||||
show_scale_grid: true,
|
showScaleGrid: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
|
Before Width: | Height: | Size: 49 KiB |
Before Width: | Height: | Size: 51 KiB After Width: | Height: | Size: 52 KiB |
Before Width: | Height: | Size: 47 KiB |
Before Width: | Height: | Size: 55 KiB |
Before Width: | Height: | Size: 53 KiB After Width: | Height: | Size: 53 KiB |
Before Width: | Height: | Size: 48 KiB |
Before Width: | Height: | Size: 43 KiB |
Before Width: | Height: | Size: 44 KiB |
Before Width: | Height: | Size: 41 KiB |
Before Width: | Height: | Size: 39 KiB |
Before Width: | Height: | Size: 47 KiB |
Before Width: | Height: | Size: 35 KiB |
Before Width: | Height: | Size: 34 KiB |
Before Width: | Height: | Size: 57 KiB |
Before Width: | Height: | Size: 55 KiB After Width: | Height: | Size: 42 KiB |
Before Width: | Height: | Size: 54 KiB |
Before Width: | Height: | Size: 52 KiB After Width: | Height: | Size: 52 KiB |
Before Width: | Height: | Size: 52 KiB |
Before Width: | Height: | Size: 74 KiB After Width: | Height: | Size: 74 KiB |
Before Width: | Height: | Size: 65 KiB |
Before Width: | Height: | Size: 50 KiB After Width: | Height: | Size: 50 KiB |
Before Width: | Height: | Size: 46 KiB After Width: | Height: | Size: 47 KiB |
Before Width: | Height: | Size: 65 KiB After Width: | Height: | Size: 66 KiB |
Before Width: | Height: | Size: 62 KiB |
Before Width: | Height: | Size: 152 KiB |
Before Width: | Height: | Size: 144 KiB After Width: | Height: | Size: 144 KiB |
Before Width: | Height: | Size: 130 KiB |
Before Width: | Height: | Size: 136 KiB |
Before Width: | Height: | Size: 128 KiB After Width: | Height: | Size: 128 KiB |
Before Width: | Height: | Size: 112 KiB |
Before Width: | Height: | Size: 38 KiB After Width: | Height: | Size: 48 KiB |
Before Width: | Height: | Size: 38 KiB |
Before Width: | Height: | Size: 42 KiB After Width: | Height: | Size: 53 KiB |
Before Width: | Height: | Size: 41 KiB |
Before Width: | Height: | Size: 41 KiB After Width: | Height: | Size: 48 KiB |
Before Width: | Height: | Size: 40 KiB |
Before Width: | Height: | Size: 40 KiB After Width: | Height: | Size: 52 KiB |
Before Width: | Height: | Size: 40 KiB |
Before Width: | Height: | Size: 44 KiB After Width: | Height: | Size: 53 KiB |
Before Width: | Height: | Size: 44 KiB |
Before Width: | Height: | Size: 38 KiB After Width: | Height: | Size: 47 KiB |
Before Width: | Height: | Size: 38 KiB |
@ -1,83 +1,78 @@
|
|||||||
|
import { SaveSettingsPayload } from 'lib/settings/settingsTypes'
|
||||||
import { Themes } from 'lib/theme'
|
import { Themes } from 'lib/theme'
|
||||||
import { DeepPartial } from 'lib/types'
|
|
||||||
import { onboardingPaths } from 'routes/Onboarding/paths'
|
import { onboardingPaths } from 'routes/Onboarding/paths'
|
||||||
import { Settings } from 'wasm-lib/kcl/bindings/Settings'
|
|
||||||
|
|
||||||
export const IS_PLAYWRIGHT_KEY = 'playwright'
|
export const IS_PLAYWRIGHT_KEY = 'playwright'
|
||||||
|
|
||||||
export const TEST_SETTINGS_KEY = '/settings.toml'
|
export const TEST_SETTINGS_KEY = '/settings.toml'
|
||||||
export const TEST_SETTINGS = {
|
export const TEST_SETTINGS = {
|
||||||
app: {
|
app: {
|
||||||
onboarding_status: 'dismissed',
|
|
||||||
appearance: {
|
|
||||||
theme: Themes.Dark,
|
theme: Themes.Dark,
|
||||||
},
|
onboardingStatus: 'dismissed',
|
||||||
show_debug_panel: true,
|
projectDirectory: '',
|
||||||
|
enableSSAO: false,
|
||||||
},
|
},
|
||||||
modeling: {
|
modeling: {
|
||||||
base_unit: 'in',
|
defaultUnit: 'in',
|
||||||
mouse_controls: 'zoo',
|
mouseControls: 'Zoo',
|
||||||
camera_projection: 'perspective',
|
cameraProjection: 'perspective',
|
||||||
enable_ssao: false,
|
showDebugPanel: true,
|
||||||
},
|
},
|
||||||
project: {
|
projects: {
|
||||||
default_project_name: 'project-$nnn',
|
defaultProjectName: 'project-$nnn',
|
||||||
directory: '',
|
|
||||||
},
|
},
|
||||||
text_editor: {
|
textEditor: {
|
||||||
text_wrapping: true,
|
textWrapping: true,
|
||||||
},
|
},
|
||||||
} satisfies DeepPartial<Settings>
|
} satisfies Partial<SaveSettingsPayload>
|
||||||
|
|
||||||
export const TEST_SETTINGS_ONBOARDING_USER_MENU = {
|
export const TEST_SETTINGS_ONBOARDING_USER_MENU = {
|
||||||
...TEST_SETTINGS,
|
...TEST_SETTINGS,
|
||||||
app: { ...TEST_SETTINGS.app, onboarding_status: onboardingPaths.USER_MENU },
|
app: { ...TEST_SETTINGS.app, onboardingStatus: onboardingPaths.USER_MENU },
|
||||||
} satisfies DeepPartial<Settings>
|
} satisfies Partial<SaveSettingsPayload>
|
||||||
|
|
||||||
export const TEST_SETTINGS_ONBOARDING_EXPORT = {
|
export const TEST_SETTINGS_ONBOARDING_EXPORT = {
|
||||||
...TEST_SETTINGS,
|
...TEST_SETTINGS,
|
||||||
app: { ...TEST_SETTINGS.app, onboarding_status: onboardingPaths.EXPORT },
|
app: { ...TEST_SETTINGS.app, onboardingStatus: onboardingPaths.EXPORT },
|
||||||
} satisfies DeepPartial<Settings>
|
} satisfies Partial<SaveSettingsPayload>
|
||||||
|
|
||||||
export const TEST_SETTINGS_ONBOARDING_PARAMETRIC_MODELING = {
|
export const TEST_SETTINGS_ONBOARDING_PARAMETRIC_MODELING = {
|
||||||
...TEST_SETTINGS,
|
...TEST_SETTINGS,
|
||||||
app: {
|
app: {
|
||||||
...TEST_SETTINGS.app,
|
...TEST_SETTINGS.app,
|
||||||
onboarding_status: onboardingPaths.PARAMETRIC_MODELING,
|
onboardingStatus: onboardingPaths.PARAMETRIC_MODELING,
|
||||||
},
|
},
|
||||||
} satisfies DeepPartial<Settings>
|
} satisfies Partial<SaveSettingsPayload>
|
||||||
|
|
||||||
export const TEST_SETTINGS_ONBOARDING_START = {
|
export const TEST_SETTINGS_ONBOARDING_START = {
|
||||||
...TEST_SETTINGS,
|
...TEST_SETTINGS,
|
||||||
app: { ...TEST_SETTINGS.app, onboarding_status: '' },
|
app: { ...TEST_SETTINGS.app, onboardingStatus: '' },
|
||||||
} satisfies DeepPartial<Settings>
|
} satisfies Partial<SaveSettingsPayload>
|
||||||
|
|
||||||
export const TEST_SETTINGS_DEFAULT_THEME = {
|
export const TEST_SETTINGS_DEFAULT_THEME = {
|
||||||
...TEST_SETTINGS,
|
...TEST_SETTINGS,
|
||||||
app: { ...TEST_SETTINGS.app, appearance: { theme: Themes.System } },
|
app: { ...TEST_SETTINGS.app, theme: Themes.System },
|
||||||
} satisfies DeepPartial<Settings>
|
} satisfies Partial<SaveSettingsPayload>
|
||||||
|
|
||||||
export const TEST_SETTINGS_CORRUPTED = {
|
export const TEST_SETTINGS_CORRUPTED = {
|
||||||
app: {
|
app: {
|
||||||
onboarding_status: 'dismissed',
|
|
||||||
appearance: {
|
|
||||||
theme: Themes.Dark,
|
theme: Themes.Dark,
|
||||||
},
|
onboardingStatus: 'dismissed',
|
||||||
show_debug_panel: true,
|
projectDirectory: 123 as any,
|
||||||
},
|
},
|
||||||
modeling: {
|
modeling: {
|
||||||
base_unit: 'invalid' as any,
|
defaultUnit: 'invalid' as any,
|
||||||
mouse_controls: `() => alert('hack the planet')` as any,
|
mouseControls: `() => alert('hack the planet')` as any,
|
||||||
camera_projection: 'perspective',
|
cameraProjection: 'perspective',
|
||||||
|
showDebugPanel: true,
|
||||||
},
|
},
|
||||||
project: {
|
projects: {
|
||||||
default_project_name: false as any,
|
defaultProjectName: false as any,
|
||||||
directory: 123 as any,
|
|
||||||
},
|
},
|
||||||
text_editor: {
|
textEditor: {
|
||||||
text_wrapping: true,
|
textWrapping: true,
|
||||||
},
|
},
|
||||||
} satisfies DeepPartial<Settings>
|
} satisfies Partial<SaveSettingsPayload>
|
||||||
|
|
||||||
export const TEST_CODE_GIZMO = `part001 = startSketchOn('XZ')
|
export const TEST_CODE_GIZMO = `part001 = startSketchOn('XZ')
|
||||||
|> startProfileAt([20, 0], %)
|
|> startProfileAt([20, 0], %)
|
||||||
|
@ -23,13 +23,11 @@ import {
|
|||||||
IS_PLAYWRIGHT_KEY,
|
IS_PLAYWRIGHT_KEY,
|
||||||
} from './storageStates'
|
} from './storageStates'
|
||||||
import * as TOML from '@iarna/toml'
|
import * as TOML from '@iarna/toml'
|
||||||
|
import { SaveSettingsPayload } from 'lib/settings/settingsTypes'
|
||||||
import { SETTINGS_FILE_NAME } from 'lib/constants'
|
import { SETTINGS_FILE_NAME } from 'lib/constants'
|
||||||
import { isErrorWhitelisted } from './lib/console-error-whitelist'
|
import { isErrorWhitelisted } from './lib/console-error-whitelist'
|
||||||
import { isArray } from 'lib/utils'
|
import { isArray } from 'lib/utils'
|
||||||
import { reportRejection } from 'lib/trap'
|
import { reportRejection } from 'lib/trap'
|
||||||
import { Configuration } from 'lang/wasm'
|
|
||||||
import { DeepPartial } from 'lib/types'
|
|
||||||
import { Settings } from 'wasm-lib/kcl/bindings/Settings'
|
|
||||||
|
|
||||||
const toNormalizedCode = (text: string) => {
|
const toNormalizedCode = (text: string) => {
|
||||||
return text.replace(/\s+/g, '')
|
return text.replace(/\s+/g, '')
|
||||||
@ -886,23 +884,19 @@ export async function setup(
|
|||||||
{
|
{
|
||||||
token: secrets.token,
|
token: secrets.token,
|
||||||
settingsKey: TEST_SETTINGS_KEY,
|
settingsKey: TEST_SETTINGS_KEY,
|
||||||
settings: settingsToToml({
|
settings: TOML.stringify({
|
||||||
settings: {
|
settings: {
|
||||||
...TEST_SETTINGS,
|
...TEST_SETTINGS,
|
||||||
app: {
|
app: {
|
||||||
...TEST_SETTINGS.app,
|
...TEST_SETTINGS.projects,
|
||||||
onboarding_status: 'dismissed',
|
projectDirectory: TEST_SETTINGS.app.projectDirectory,
|
||||||
appearance: {
|
onboardingStatus: 'dismissed',
|
||||||
theme: 'dark',
|
theme: 'dark',
|
||||||
},
|
},
|
||||||
},
|
} as Partial<SaveSettingsPayload>,
|
||||||
project: {
|
|
||||||
directory: TEST_SETTINGS.project.directory,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}),
|
}),
|
||||||
IS_PLAYWRIGHT_KEY,
|
IS_PLAYWRIGHT_KEY,
|
||||||
PLAYWRIGHT_TEST_DIR: TEST_SETTINGS.project.directory,
|
PLAYWRIGHT_TEST_DIR: TEST_SETTINGS.app.projectDirectory,
|
||||||
PERSIST_MODELING_CONTEXT,
|
PERSIST_MODELING_CONTEXT,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@ -937,7 +931,7 @@ export async function setupElectron({
|
|||||||
testInfo: TestInfo
|
testInfo: TestInfo
|
||||||
folderSetupFn?: (projectDirName: string) => Promise<void>
|
folderSetupFn?: (projectDirName: string) => Promise<void>
|
||||||
cleanProjectDir?: boolean
|
cleanProjectDir?: boolean
|
||||||
appSettings?: DeepPartial<Settings>
|
appSettings?: Partial<SaveSettingsPayload>
|
||||||
}): Promise<{
|
}): Promise<{
|
||||||
electronApp: ElectronApplication
|
electronApp: ElectronApplication
|
||||||
context: BrowserContext
|
context: BrowserContext
|
||||||
@ -984,7 +978,7 @@ export async function setupElectron({
|
|||||||
|
|
||||||
if (cleanProjectDir) {
|
if (cleanProjectDir) {
|
||||||
const tempSettingsFilePath = path.join(projectDirName, SETTINGS_FILE_NAME)
|
const tempSettingsFilePath = path.join(projectDirName, SETTINGS_FILE_NAME)
|
||||||
const settingsOverrides = settingsToToml(
|
const settingsOverrides = TOML.stringify(
|
||||||
appSettings
|
appSettings
|
||||||
? {
|
? {
|
||||||
settings: {
|
settings: {
|
||||||
@ -992,11 +986,9 @@ export async function setupElectron({
|
|||||||
...appSettings,
|
...appSettings,
|
||||||
app: {
|
app: {
|
||||||
...TEST_SETTINGS.app,
|
...TEST_SETTINGS.app,
|
||||||
|
projectDirectory: projectDirName,
|
||||||
...appSettings.app,
|
...appSettings.app,
|
||||||
},
|
},
|
||||||
project: {
|
|
||||||
directory: projectDirName,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
: {
|
: {
|
||||||
@ -1004,9 +996,7 @@ export async function setupElectron({
|
|||||||
...TEST_SETTINGS,
|
...TEST_SETTINGS,
|
||||||
app: {
|
app: {
|
||||||
...TEST_SETTINGS.app,
|
...TEST_SETTINGS.app,
|
||||||
},
|
projectDirectory: projectDirName,
|
||||||
project: {
|
|
||||||
directory: projectDirName,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -1200,7 +1190,3 @@ export async function pollEditorLinesSelectedLength(page: Page, lines: number) {
|
|||||||
})
|
})
|
||||||
.toBe(lines)
|
.toBe(lines)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function settingsToToml(settings: DeepPartial<Configuration>) {
|
|
||||||
return TOML.stringify(settings)
|
|
||||||
}
|
|
||||||
|
@ -389,25 +389,25 @@ test.describe('Testing selections', () => {
|
|||||||
await expect(u.codeLocator).toContainText(`sketch005 = startSketchOn({
|
await expect(u.codeLocator).toContainText(`sketch005 = startSketchOn({
|
||||||
plane = {
|
plane = {
|
||||||
origin = { x = 0, y = -50, z = 0 },
|
origin = { x = 0, y = -50, z = 0 },
|
||||||
x_axis = { x = 1, y = 0, z = 0 },
|
xAxis = { x = 1, y = 0, z = 0 },
|
||||||
y_axis = { x = 0, y = 0, z = 1 },
|
yAxis = { x = 0, y = 0, z = 1 },
|
||||||
z_axis = { x = 0, y = -1, z = 0 }
|
zAxis = { x = 0, y = -1, z = 0 }
|
||||||
}
|
}
|
||||||
})`)
|
})`)
|
||||||
await expect(u.codeLocator).toContainText(`sketch003 = startSketchOn({
|
await expect(u.codeLocator).toContainText(`sketch003 = startSketchOn({
|
||||||
plane = {
|
plane = {
|
||||||
origin = { x = 116.53, y = 0, z = 163.25 },
|
origin = { x = 116.53, y = 0, z = 163.25 },
|
||||||
x_axis = { x = -0.81, y = 0, z = 0.58 },
|
xAxis = { x = -0.81, y = 0, z = 0.58 },
|
||||||
y_axis = { x = 0, y = -1, z = 0 },
|
yAxis = { x = 0, y = -1, z = 0 },
|
||||||
z_axis = { x = 0.58, y = 0, z = 0.81 }
|
zAxis = { x = 0.58, y = 0, z = 0.81 }
|
||||||
}
|
}
|
||||||
})`)
|
})`)
|
||||||
await expect(u.codeLocator).toContainText(`sketch002 = startSketchOn({
|
await expect(u.codeLocator).toContainText(`sketch002 = startSketchOn({
|
||||||
plane = {
|
plane = {
|
||||||
origin = { x = -91.74, y = 0, z = 80.89 },
|
origin = { x = -91.74, y = 0, z = 80.89 },
|
||||||
x_axis = { x = -0.66, y = 0, z = -0.75 },
|
xAxis = { x = -0.66, y = 0, z = -0.75 },
|
||||||
y_axis = { x = 0, y = -1, z = 0 },
|
yAxis = { x = 0, y = -1, z = 0 },
|
||||||
z_axis = { x = -0.75, y = 0, z = 0.66 }
|
zAxis = { x = -0.75, y = 0, z = 0.66 }
|
||||||
}
|
}
|
||||||
})`)
|
})`)
|
||||||
|
|
||||||
|
@ -31,13 +31,13 @@ test.describe('Testing settings', () => {
|
|||||||
)
|
)
|
||||||
) as { settings: SaveSettingsPayload }
|
) as { settings: SaveSettingsPayload }
|
||||||
|
|
||||||
expect(storedSettings.settings?.app?.appearance?.theme).toBe('dark')
|
expect(storedSettings.settings?.app?.theme).toBe('dark')
|
||||||
|
|
||||||
// Check that the invalid settings were changed to good defaults
|
// Check that the invalid settings were changed to good defaults
|
||||||
expect(storedSettings.settings?.modeling?.base_unit).toBe('in')
|
expect(storedSettings.settings?.modeling?.defaultUnit).toBe('in')
|
||||||
expect(storedSettings.settings?.modeling?.mouse_controls).toBe('Zoo')
|
expect(storedSettings.settings?.modeling?.mouseControls).toBe('Zoo')
|
||||||
expect(storedSettings.settings?.project?.directory).toBe('')
|
expect(storedSettings.settings?.app?.projectDirectory).toBe('')
|
||||||
expect(storedSettings.settings?.project?.default_project_name).toBe(
|
expect(storedSettings.settings?.projects?.defaultProjectName).toBe(
|
||||||
'project-$nnn'
|
'project-$nnn'
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -374,9 +374,7 @@ test.describe('Testing settings', () => {
|
|||||||
tag: '@electron',
|
tag: '@electron',
|
||||||
appSettings: {
|
appSettings: {
|
||||||
app: {
|
app: {
|
||||||
appearance: {
|
themeColor: '259',
|
||||||
color: 259,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -402,11 +400,9 @@ test.describe('Testing settings', () => {
|
|||||||
tag: '@electron',
|
tag: '@electron',
|
||||||
appSettings: {
|
appSettings: {
|
||||||
app: {
|
app: {
|
||||||
appearance: {
|
|
||||||
// Doesn't matter what you set it to. It will
|
// Doesn't matter what you set it to. It will
|
||||||
// default to 264.5
|
// default to 264.5
|
||||||
color: 0,
|
themeColor: '0',
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -836,7 +832,7 @@ test.describe('Testing settings', () => {
|
|||||||
// but "show debug panel" set to false
|
// but "show debug panel" set to false
|
||||||
appSettings: {
|
appSettings: {
|
||||||
...TEST_SETTINGS,
|
...TEST_SETTINGS,
|
||||||
app: { ...TEST_SETTINGS.app, show_debug_panel: false },
|
modeling: { ...TEST_SETTINGS.modeling, showDebugPanel: false },
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
async ({ context, page, homePage }) => {
|
async ({ context, page, homePage }) => {
|
||||||
|
@ -156,13 +156,13 @@ test.describe('Text-to-CAD tests', () => {
|
|||||||
const cmdSearchBar = page.getByPlaceholder('Search commands')
|
const cmdSearchBar = page.getByPlaceholder('Search commands')
|
||||||
await expect(cmdSearchBar).toBeVisible()
|
await expect(cmdSearchBar).toBeVisible()
|
||||||
|
|
||||||
const textToCadCommand = page.getByText('Text-to-CAD')
|
const textToCadCommand = page.getByRole('option', { name: 'Text-to-CAD' })
|
||||||
await expect(textToCadCommand.first()).toBeVisible()
|
await expect(textToCadCommand.first()).toBeVisible()
|
||||||
// Click the Text-to-CAD command
|
// Click the Text-to-CAD command
|
||||||
await textToCadCommand.first().click()
|
await textToCadCommand.first().click()
|
||||||
|
|
||||||
// Enter the prompt.
|
// Enter the prompt.
|
||||||
const prompt = page.getByText('Prompt')
|
const prompt = page.getByRole('textbox', { name: 'Prompt' })
|
||||||
await expect(prompt.first()).toBeVisible()
|
await expect(prompt.first()).toBeVisible()
|
||||||
|
|
||||||
// Type the prompt.
|
// Type the prompt.
|
||||||
@ -224,13 +224,13 @@ test.describe('Text-to-CAD tests', () => {
|
|||||||
const cmdSearchBar = page.getByPlaceholder('Search commands')
|
const cmdSearchBar = page.getByPlaceholder('Search commands')
|
||||||
await expect(cmdSearchBar).toBeVisible()
|
await expect(cmdSearchBar).toBeVisible()
|
||||||
|
|
||||||
const textToCadCommand = page.getByText('Text-to-CAD')
|
const textToCadCommand = page.getByRole('option', { name: 'Text-to-CAD' })
|
||||||
await expect(textToCadCommand.first()).toBeVisible()
|
await expect(textToCadCommand.first()).toBeVisible()
|
||||||
// Click the Text-to-CAD command
|
// Click the Text-to-CAD command
|
||||||
await textToCadCommand.first().click()
|
await textToCadCommand.first().click()
|
||||||
|
|
||||||
// Enter the prompt.
|
// Enter the prompt.
|
||||||
const prompt = page.getByText('Prompt')
|
const prompt = page.getByRole('textbox', { name: 'Prompt' })
|
||||||
await expect(prompt.first()).toBeVisible()
|
await expect(prompt.first()).toBeVisible()
|
||||||
|
|
||||||
const badPrompt = 'akjsndladf lajbhflauweyfaaaljhr472iouafyvsssssss'
|
const badPrompt = 'akjsndladf lajbhflauweyfaaaljhr472iouafyvsssssss'
|
||||||
@ -314,13 +314,13 @@ test.describe('Text-to-CAD tests', () => {
|
|||||||
const cmdSearchBar = page.getByPlaceholder('Search commands')
|
const cmdSearchBar = page.getByPlaceholder('Search commands')
|
||||||
await expect(cmdSearchBar).toBeVisible()
|
await expect(cmdSearchBar).toBeVisible()
|
||||||
|
|
||||||
const textToCadCommand = page.getByText('Text-to-CAD')
|
const textToCadCommand = page.getByRole('option', { name: 'Text-to-CAD' })
|
||||||
await expect(textToCadCommand.first()).toBeVisible()
|
await expect(textToCadCommand.first()).toBeVisible()
|
||||||
// Click the Text-to-CAD command
|
// Click the Text-to-CAD command
|
||||||
await textToCadCommand.first().click()
|
await textToCadCommand.first().click()
|
||||||
|
|
||||||
// Enter the prompt.
|
// Enter the prompt.
|
||||||
const prompt = page.getByText('Prompt')
|
const prompt = page.getByRole('textbox', { name: 'Prompt' })
|
||||||
await expect(prompt.first()).toBeVisible()
|
await expect(prompt.first()).toBeVisible()
|
||||||
|
|
||||||
const badPrompt = 'akjsndladflajbhflauweyf15;'
|
const badPrompt = 'akjsndladflajbhflauweyf15;'
|
||||||
@ -392,13 +392,13 @@ test.describe('Text-to-CAD tests', () => {
|
|||||||
const cmdSearchBar = page.getByPlaceholder('Search commands')
|
const cmdSearchBar = page.getByPlaceholder('Search commands')
|
||||||
await expect(cmdSearchBar).toBeVisible()
|
await expect(cmdSearchBar).toBeVisible()
|
||||||
|
|
||||||
const textToCadCommand = page.getByText('Text-to-CAD')
|
const textToCadCommand = page.getByRole('option', { name: 'Text-to-CAD' })
|
||||||
await expect(textToCadCommand.first()).toBeVisible()
|
await expect(textToCadCommand.first()).toBeVisible()
|
||||||
// Click the Text-to-CAD command
|
// Click the Text-to-CAD command
|
||||||
await textToCadCommand.first().click()
|
await textToCadCommand.first().click()
|
||||||
|
|
||||||
// Enter the prompt.
|
// Enter the prompt.
|
||||||
const prompt = page.getByText('Prompt')
|
const prompt = page.getByRole('textbox', { name: 'Prompt' })
|
||||||
await expect(prompt.first()).toBeVisible()
|
await expect(prompt.first()).toBeVisible()
|
||||||
|
|
||||||
// Type the prompt.
|
// Type the prompt.
|
||||||
@ -604,7 +604,7 @@ async function sendPromptFromCommandBar(page: Page, promptStr: string) {
|
|||||||
await page.waitForTimeout(1000)
|
await page.waitForTimeout(1000)
|
||||||
|
|
||||||
// Enter the prompt.
|
// Enter the prompt.
|
||||||
const prompt = page.getByText('Prompt')
|
const prompt = page.getByRole('textbox', { name: 'Prompt' })
|
||||||
await expect(prompt.first()).toBeVisible()
|
await expect(prompt.first()).toBeVisible()
|
||||||
|
|
||||||
// Type the prompt.
|
// Type the prompt.
|
||||||
|
@ -18,8 +18,7 @@ import {
|
|||||||
AuthenticatedApp,
|
AuthenticatedApp,
|
||||||
} from './fixtures/fixtureSetup'
|
} from './fixtures/fixtureSetup'
|
||||||
|
|
||||||
import { Settings } from 'wasm-lib/kcl/bindings/Settings'
|
import { SaveSettingsPayload } from 'lib/settings/settingsTypes'
|
||||||
import { DeepPartial } from 'lib/types'
|
|
||||||
export { expect } from '@playwright/test'
|
export { expect } from '@playwright/test'
|
||||||
|
|
||||||
declare module '@playwright/test' {
|
declare module '@playwright/test' {
|
||||||
@ -46,7 +45,7 @@ export type BrowserContext = BrowserContextPlaywright
|
|||||||
export type Page = PagePlaywright
|
export type Page = PagePlaywright
|
||||||
export type TestDetails = TestDetailsPlaywright & {
|
export type TestDetails = TestDetailsPlaywright & {
|
||||||
cleanProjectDir?: boolean
|
cleanProjectDir?: boolean
|
||||||
appSettings?: DeepPartial<Settings>
|
appSettings?: Partial<SaveSettingsPayload>
|
||||||
}
|
}
|
||||||
|
|
||||||
// Our custom decorated Zoo test object. Makes it easier to add fixtures, and
|
// Our custom decorated Zoo test object. Makes it easier to add fixtures, and
|
||||||
|
18
flake.lock
generated
@ -2,11 +2,11 @@
|
|||||||
"nodes": {
|
"nodes": {
|
||||||
"nixpkgs": {
|
"nixpkgs": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1721933792,
|
"lastModified": 1736320768,
|
||||||
"narHash": "sha256-zYVwABlQnxpbaHMfX6Wt9jhyQstFYwN2XjleOJV3VVg=",
|
"narHash": "sha256-nIYdTAiKIGnFNugbomgBJR+Xv5F1ZQU+HfaBqJKroC0=",
|
||||||
"owner": "NixOS",
|
"owner": "NixOS",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "2122a9b35b35719ad9a395fe783eabb092df01b1",
|
"rev": "4bc9c909d9ac828a039f288cf872d16d38185db8",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@ -18,11 +18,11 @@
|
|||||||
},
|
},
|
||||||
"nixpkgs_2": {
|
"nixpkgs_2": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1718428119,
|
"lastModified": 1728538411,
|
||||||
"narHash": "sha256-WdWDpNaq6u1IPtxtYHHWpl5BmabtpmLnMAx0RdJ/vo8=",
|
"narHash": "sha256-f0SBJz1eZ2yOuKUr5CA9BHULGXVSn6miBuUWdTyhUhU=",
|
||||||
"owner": "NixOS",
|
"owner": "NixOS",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "e6cea36f83499eb4e9cd184c8a8e823296b50ad5",
|
"rev": "b69de56fac8c2b6f8fd27f2eca01dcda8e0a4221",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@ -43,11 +43,11 @@
|
|||||||
"nixpkgs": "nixpkgs_2"
|
"nixpkgs": "nixpkgs_2"
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1721960387,
|
"lastModified": 1736476219,
|
||||||
"narHash": "sha256-o21ax+745ETGXrcgc/yUuLw1SI77ymp3xEpJt+w/kks=",
|
"narHash": "sha256-+qyv3QqdZCdZ3cSO/cbpEY6tntyYjfe1bB12mdpNFaY=",
|
||||||
"owner": "oxalica",
|
"owner": "oxalica",
|
||||||
"repo": "rust-overlay",
|
"repo": "rust-overlay",
|
||||||
"rev": "9cbf831c5b20a53354fc12758abd05966f9f1699",
|
"rev": "de30cc5963da22e9742bbbbb9a3344570ed237b9",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
8
interface.d.ts
vendored
@ -11,6 +11,13 @@ export interface IElectronAPI {
|
|||||||
open: typeof dialog.showOpenDialog
|
open: typeof dialog.showOpenDialog
|
||||||
save: typeof dialog.showSaveDialog
|
save: typeof dialog.showSaveDialog
|
||||||
openExternal: typeof shell.openExternal
|
openExternal: typeof shell.openExternal
|
||||||
|
takeElectronWindowScreenshot: ({
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
}: {
|
||||||
|
width: number
|
||||||
|
height: number
|
||||||
|
}) => Promise<string>
|
||||||
showInFolder: typeof shell.showItemInFolder
|
showInFolder: typeof shell.showItemInFolder
|
||||||
/** Require to be called first before {@link loginWithDeviceFlow} */
|
/** Require to be called first before {@link loginWithDeviceFlow} */
|
||||||
startDeviceFlow: (host: string) => Promise<string>
|
startDeviceFlow: (host: string) => Promise<string>
|
||||||
@ -86,5 +93,6 @@ export interface IElectronAPI {
|
|||||||
declare global {
|
declare global {
|
||||||
interface Window {
|
interface Window {
|
||||||
electron: IElectronAPI
|
electron: IElectronAPI
|
||||||
|
openExternalLink: (e: React.MouseEvent<HTMLAnchorElement>) => void
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
26
package.json
@ -15,18 +15,18 @@
|
|||||||
"@codemirror/autocomplete": "^6.17.0",
|
"@codemirror/autocomplete": "^6.17.0",
|
||||||
"@codemirror/commands": "^6.6.0",
|
"@codemirror/commands": "^6.6.0",
|
||||||
"@codemirror/language": "^6.10.3",
|
"@codemirror/language": "^6.10.3",
|
||||||
"@codemirror/lint": "^6.8.1",
|
"@codemirror/lint": "^6.8.4",
|
||||||
"@codemirror/search": "^6.5.6",
|
"@codemirror/search": "^6.5.6",
|
||||||
"@codemirror/state": "^6.4.1",
|
"@codemirror/state": "^6.4.1",
|
||||||
"@codemirror/theme-one-dark": "^6.1.2",
|
"@codemirror/theme-one-dark": "^6.1.2",
|
||||||
"@csstools/postcss-oklab-function": "^4.0.2",
|
"@csstools/postcss-oklab-function": "^4.0.7",
|
||||||
"@fortawesome/fontawesome-svg-core": "^6.5.2",
|
"@fortawesome/fontawesome-svg-core": "^6.5.2",
|
||||||
"@fortawesome/free-brands-svg-icons": "^6.5.2",
|
"@fortawesome/free-brands-svg-icons": "^6.5.2",
|
||||||
"@fortawesome/free-solid-svg-icons": "^6.4.2",
|
"@fortawesome/free-solid-svg-icons": "^6.4.2",
|
||||||
"@fortawesome/react-fontawesome": "^0.2.0",
|
"@fortawesome/react-fontawesome": "^0.2.0",
|
||||||
"@headlessui/react": "^1.7.19",
|
"@headlessui/react": "^1.7.19",
|
||||||
"@headlessui/tailwindcss": "^0.2.0",
|
"@headlessui/tailwindcss": "^0.2.0",
|
||||||
"@kittycad/lib": "2.0.7",
|
"@kittycad/lib": "2.0.13",
|
||||||
"@lezer/highlight": "^1.2.1",
|
"@lezer/highlight": "^1.2.1",
|
||||||
"@lezer/lr": "^1.4.1",
|
"@lezer/lr": "^1.4.1",
|
||||||
"@react-hook/resize-observer": "^2.0.1",
|
"@react-hook/resize-observer": "^2.0.1",
|
||||||
@ -52,13 +52,13 @@
|
|||||||
"react": "^18.3.1",
|
"react": "^18.3.1",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
"react-hot-toast": "^2.4.1",
|
"react-hot-toast": "^2.4.1",
|
||||||
"react-hotkeys-hook": "^4.5.1",
|
"react-hotkeys-hook": "^4.6.1",
|
||||||
"react-json-view": "^1.21.3",
|
"react-json-view": "^1.21.3",
|
||||||
"react-modal": "^3.16.1",
|
"react-modal": "^3.16.3",
|
||||||
"react-modal-promise": "^1.0.2",
|
"react-modal-promise": "^1.0.2",
|
||||||
"react-router-dom": "^6.28.0",
|
"react-router-dom": "^6.28.0",
|
||||||
"sketch-helpers": "^0.0.4",
|
"sketch-helpers": "^0.0.4",
|
||||||
"three": "^0.166.1",
|
"three": "^0.172.0",
|
||||||
"ua-parser-js": "^1.0.37",
|
"ua-parser-js": "^1.0.37",
|
||||||
"uuid": "^11.0.2",
|
"uuid": "^11.0.2",
|
||||||
"vscode-jsonrpc": "^8.2.1",
|
"vscode-jsonrpc": "^8.2.1",
|
||||||
@ -91,8 +91,8 @@
|
|||||||
"build:wasm": "yarn wasm-prep && cd src/wasm-lib && wasm-pack build --release --target web --out-dir pkg && cargo test -p kcl-lib export_bindings && cd ../.. && yarn isomorphic-copy-wasm && yarn fmt",
|
"build:wasm": "yarn wasm-prep && cd src/wasm-lib && wasm-pack build --release --target web --out-dir pkg && cargo test -p kcl-lib export_bindings && cd ../.. && yarn isomorphic-copy-wasm && yarn fmt",
|
||||||
"remove-importmeta": "sed -i 's/import.meta.url/window.location.origin/g' \"./src/wasm-lib/pkg/wasm_lib.js\"; sed -i '' 's/import.meta.url/window.location.origin/g' \"./src/wasm-lib/pkg/wasm_lib.js\" || echo \"sed for both mac and linux\"",
|
"remove-importmeta": "sed -i 's/import.meta.url/window.location.origin/g' \"./src/wasm-lib/pkg/wasm_lib.js\"; sed -i '' 's/import.meta.url/window.location.origin/g' \"./src/wasm-lib/pkg/wasm_lib.js\" || echo \"sed for both mac and linux\"",
|
||||||
"wasm-prep": "rimraf src/wasm-lib/pkg && mkdirp src/wasm-lib/pkg && rimraf src/wasm-lib/kcl/bindings",
|
"wasm-prep": "rimraf src/wasm-lib/pkg && mkdirp src/wasm-lib/pkg && rimraf src/wasm-lib/kcl/bindings",
|
||||||
"lint-fix": "eslint --fix src e2e packages/codemirror-lsp-client",
|
"lint-fix": "eslint --fix --ext .ts --ext .tsx src e2e packages/codemirror-lsp-client/src",
|
||||||
"lint": "eslint --max-warnings 0 src e2e packages/codemirror-lsp-client",
|
"lint": "eslint --max-warnings 0 --ext .ts --ext .tsx src e2e packages/codemirror-lsp-client/src",
|
||||||
"files:set-version": "echo \"$(jq --arg v \"$VERSION\" '.version=$v' package.json --indent 2)\" > package.json",
|
"files:set-version": "echo \"$(jq --arg v \"$VERSION\" '.version=$v' package.json --indent 2)\" > package.json",
|
||||||
"files:set-notes": "./scripts/set-files-notes.sh",
|
"files:set-notes": "./scripts/set-files-notes.sh",
|
||||||
"files:flip-to-nightly": "./scripts/flip-files-to-nightly.sh",
|
"files:flip-to-nightly": "./scripts/flip-files-to-nightly.sh",
|
||||||
@ -166,13 +166,11 @@
|
|||||||
"@types/react": "^18.3.4",
|
"@types/react": "^18.3.4",
|
||||||
"@types/react-dom": "^18.3.1",
|
"@types/react-dom": "^18.3.1",
|
||||||
"@types/react-modal": "^3.16.3",
|
"@types/react-modal": "^3.16.3",
|
||||||
"@types/three": "^0.163.0",
|
"@types/three": "^0.172.0",
|
||||||
"@types/ua-parser-js": "^0.7.39",
|
"@types/ua-parser-js": "^0.7.39",
|
||||||
"@types/uuid": "^9.0.8",
|
"@types/uuid": "^9.0.8",
|
||||||
"@types/wicg-file-system-access": "^2023.10.5",
|
"@types/wicg-file-system-access": "^2023.10.5",
|
||||||
"@types/ws": "^8.5.13",
|
"@types/ws": "^8.5.13",
|
||||||
"@typescript-eslint/eslint-plugin": "^5.0.0",
|
|
||||||
"@typescript-eslint/parser": "^5.0.0",
|
|
||||||
"@vitejs/plugin-react": "^4.3.0",
|
"@vitejs/plugin-react": "^4.3.0",
|
||||||
"@vitest/web-worker": "^1.5.0",
|
"@vitest/web-worker": "^1.5.0",
|
||||||
"@xstate/cli": "^0.5.17",
|
"@xstate/cli": "^0.5.17",
|
||||||
@ -182,11 +180,12 @@
|
|||||||
"electron-builder": "24.13.3",
|
"electron-builder": "24.13.3",
|
||||||
"electron-notarize": "1.2.2",
|
"electron-notarize": "1.2.2",
|
||||||
"eslint": "^8.0.1",
|
"eslint": "^8.0.1",
|
||||||
"eslint-config-react-app": "^7.0.1",
|
|
||||||
"eslint-plugin-css-modules": "^2.12.0",
|
"eslint-plugin-css-modules": "^2.12.0",
|
||||||
"eslint-plugin-import": "^2.30.0",
|
"eslint-plugin-import": "^2.30.0",
|
||||||
|
"eslint-plugin-jest": "^28.10.0",
|
||||||
|
"eslint-plugin-react": "^7.37.3",
|
||||||
"eslint-plugin-suggest-no-throw": "^1.0.0",
|
"eslint-plugin-suggest-no-throw": "^1.0.0",
|
||||||
"happy-dom": "^15.11.7",
|
"happy-dom": "^16.3.0",
|
||||||
"http-server": "^14.1.1",
|
"http-server": "^14.1.1",
|
||||||
"husky": "^9.1.5",
|
"husky": "^9.1.5",
|
||||||
"kill-port": "^2.0.1",
|
"kill-port": "^2.0.1",
|
||||||
@ -200,6 +199,7 @@
|
|||||||
"tailwindcss": "^3.4.1",
|
"tailwindcss": "^3.4.1",
|
||||||
"ts-node": "^10.0.0",
|
"ts-node": "^10.0.0",
|
||||||
"typescript": "^5.7.2",
|
"typescript": "^5.7.2",
|
||||||
|
"typescript-eslint": "^8.19.1",
|
||||||
"vite": "^5.4.6",
|
"vite": "^5.4.6",
|
||||||
"vite-plugin-package-version": "^1.1.0",
|
"vite-plugin-package-version": "^1.1.0",
|
||||||
"vite-tsconfig-paths": "^4.3.2",
|
"vite-tsconfig-paths": "^4.3.2",
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
|
|
||||||
statement[@isGroup=Statement] {
|
statement[@isGroup=Statement] {
|
||||||
ImportStatement { kw<"import"> ImportItems ImportFrom String } |
|
ImportStatement { kw<"import"> ImportItems ImportFrom String } |
|
||||||
FunctionDeclaration { kw<"export">? kw<"fn"> VariableDefinition Equals ParamList Arrow Body } |
|
FunctionDeclaration { kw<"export">? kw<"fn"> VariableDefinition Equals? ParamList Arrow? Body } |
|
||||||
VariableDeclaration { kw<"export">? (kw<"var"> | kw<"let"> | kw<"const">)? VariableDefinition Equals expression } |
|
VariableDeclaration { kw<"export">? (kw<"var"> | kw<"let"> | kw<"const">)? VariableDefinition Equals expression } |
|
||||||
ReturnStatement { kw<"return"> expression } |
|
ReturnStatement { kw<"return"> expression } |
|
||||||
ExpressionStatement { expression }
|
ExpressionStatement { expression }
|
||||||
@ -57,7 +57,7 @@ expression[@isGroup=Expression] {
|
|||||||
|
|
||||||
UnaryOp { AddOp | BangOp }
|
UnaryOp { AddOp | BangOp }
|
||||||
|
|
||||||
ObjectProperty { PropertyName ":" expression }
|
ObjectProperty { PropertyName (":" | Equals) expression }
|
||||||
|
|
||||||
ArgumentList { "(" commaSep<expression> ")" }
|
ArgumentList { "(" commaSep<expression> ")" }
|
||||||
|
|
||||||
@ -85,7 +85,7 @@ commaSep1NoTrailingComma<term> { term ("," term)* }
|
|||||||
@tokens {
|
@tokens {
|
||||||
String[isolate] { "'" ("\\" _ | !['\\])* "'" | '"' ("\\" _ | !["\\])* '"' }
|
String[isolate] { "'" ("\\" _ | !['\\])* "'" | '"' ("\\" _ | !["\\])* '"' }
|
||||||
|
|
||||||
Number { "." @digit+ | @digit+ ("." @digit*)? }
|
Number { "." @digit+ | @digit+ ("." @digit+)? }
|
||||||
@precedence { Number, "." }
|
@precedence { Number, "." }
|
||||||
|
|
||||||
AddOp { "+" | "-" }
|
AddOp { "+" | "-" }
|
||||||
|
60
packages/codemirror-lang-kcl/test/fn.txt
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
# full
|
||||||
|
|
||||||
|
fn two = () => {
|
||||||
|
return 2
|
||||||
|
}
|
||||||
|
|
||||||
|
==>
|
||||||
|
|
||||||
|
Program(FunctionDeclaration(fn,
|
||||||
|
VariableDefinition,
|
||||||
|
Equals,
|
||||||
|
ParamList,
|
||||||
|
Arrow,
|
||||||
|
Body(ReturnStatement(return,
|
||||||
|
Number))))
|
||||||
|
|
||||||
|
# = is optional
|
||||||
|
|
||||||
|
fn one () => {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
==>
|
||||||
|
|
||||||
|
Program(FunctionDeclaration(fn,
|
||||||
|
VariableDefinition,
|
||||||
|
ParamList,
|
||||||
|
Arrow,
|
||||||
|
Body(ReturnStatement(return,
|
||||||
|
Number))))
|
||||||
|
|
||||||
|
# => is optional
|
||||||
|
|
||||||
|
fn one = () {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
==>
|
||||||
|
|
||||||
|
Program(FunctionDeclaration(fn,
|
||||||
|
VariableDefinition,
|
||||||
|
Equals,
|
||||||
|
ParamList,
|
||||||
|
Body(ReturnStatement(return,
|
||||||
|
Number))))
|
||||||
|
|
||||||
|
# terse
|
||||||
|
|
||||||
|
fn two() {
|
||||||
|
return 2
|
||||||
|
}
|
||||||
|
|
||||||
|
==>
|
||||||
|
|
||||||
|
Program(FunctionDeclaration(fn,
|
||||||
|
VariableDefinition,
|
||||||
|
ParamList,
|
||||||
|
Body(ReturnStatement(return,
|
||||||
|
Number))))
|
||||||
|
|
20
packages/codemirror-lang-kcl/test/key.txt
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
# colon (deprecated)
|
||||||
|
|
||||||
|
x = { k: 123 }
|
||||||
|
|
||||||
|
==>
|
||||||
|
Program(VariableDeclaration(VariableDefinition,
|
||||||
|
Equals,
|
||||||
|
ObjectExpression(ObjectProperty(PropertyName,
|
||||||
|
Number))))
|
||||||
|
|
||||||
|
# equal
|
||||||
|
|
||||||
|
x = { k = 123 }
|
||||||
|
|
||||||
|
==>
|
||||||
|
Program(VariableDeclaration(VariableDefinition,
|
||||||
|
Equals,
|
||||||
|
ObjectExpression(ObjectProperty(PropertyName,
|
||||||
|
Equals,
|
||||||
|
Number))))
|
43
packages/codemirror-lang-kcl/test/range.txt
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
# spaced
|
||||||
|
|
||||||
|
a = [0 .. 1]
|
||||||
|
|
||||||
|
==>
|
||||||
|
Program(VariableDeclaration(VariableDefinition,
|
||||||
|
Equals,
|
||||||
|
ArrayExpression(IntegerRange(Number,
|
||||||
|
Number))))
|
||||||
|
|
||||||
|
# compact
|
||||||
|
|
||||||
|
a = [0..1]
|
||||||
|
|
||||||
|
==>
|
||||||
|
Program(VariableDeclaration(VariableDefinition,
|
||||||
|
Equals,
|
||||||
|
ArrayExpression(IntegerRange(Number,
|
||||||
|
Number))))
|
||||||
|
|
||||||
|
# expr spaced
|
||||||
|
|
||||||
|
a = [start .. start + 10]
|
||||||
|
|
||||||
|
==>
|
||||||
|
Program(VariableDeclaration(VariableDefinition,
|
||||||
|
Equals,
|
||||||
|
ArrayExpression(IntegerRange(VariableName,
|
||||||
|
BinaryExpression(VariableName,
|
||||||
|
AddOp,
|
||||||
|
Number)))))
|
||||||
|
|
||||||
|
# expr compact
|
||||||
|
|
||||||
|
a = [start..start + 10]
|
||||||
|
|
||||||
|
==>
|
||||||
|
Program(VariableDeclaration(VariableDefinition,
|
||||||
|
Equals,
|
||||||
|
ArrayExpression(IntegerRange(VariableName,
|
||||||
|
BinaryExpression(VariableName,
|
||||||
|
AddOp,
|
||||||
|
Number)))))
|
@ -42,7 +42,7 @@ export default class StreamDemuxer extends Queue<Uint8Array> {
|
|||||||
// try to parse the content-length from the headers
|
// try to parse the content-length from the headers
|
||||||
const length = parseInt(match[1])
|
const length = parseInt(match[1])
|
||||||
|
|
||||||
if (isNaN(length))
|
if (Number.isNaN(length))
|
||||||
return Promise.reject(new Error('invalid content length'))
|
return Promise.reject(new Error('invalid content length'))
|
||||||
|
|
||||||
// slice the headers since we now have the content length
|
// slice the headers since we now have the content length
|
||||||
|
@ -368,13 +368,20 @@ export class LanguageServerPlugin implements PluginValue {
|
|||||||
sortText,
|
sortText,
|
||||||
filterText,
|
filterText,
|
||||||
}) => {
|
}) => {
|
||||||
|
const detailText = [
|
||||||
|
deprecated ? 'Deprecated' : undefined,
|
||||||
|
labelDetails ? labelDetails.detail : detail,
|
||||||
|
]
|
||||||
|
// Don't let undefined appear.
|
||||||
|
.filter(Boolean)
|
||||||
|
.join(' ')
|
||||||
const completion: Completion & {
|
const completion: Completion & {
|
||||||
filterText: string
|
filterText: string
|
||||||
sortText?: string
|
sortText?: string
|
||||||
apply: string
|
apply: string
|
||||||
} = {
|
} = {
|
||||||
label,
|
label,
|
||||||
detail: labelDetails ? labelDetails.detail : detail,
|
detail: detailText,
|
||||||
apply: label,
|
apply: label,
|
||||||
type: kind && CompletionItemKindMap[kind].toLowerCase(),
|
type: kind && CompletionItemKindMap[kind].toLowerCase(),
|
||||||
sortText: sortText ?? label,
|
sortText: sortText ?? label,
|
||||||
@ -382,7 +389,11 @@ export class LanguageServerPlugin implements PluginValue {
|
|||||||
}
|
}
|
||||||
if (documentation) {
|
if (documentation) {
|
||||||
completion.info = () => {
|
completion.info = () => {
|
||||||
const htmlString = formatMarkdownContents(documentation)
|
const deprecatedHtml = deprecated
|
||||||
|
? '<p><strong>Deprecated</strong></p>'
|
||||||
|
: ''
|
||||||
|
const htmlString =
|
||||||
|
deprecatedHtml + formatMarkdownContents(documentation)
|
||||||
const htmlNode = document.createElement('div')
|
const htmlNode = document.createElement('div')
|
||||||
htmlNode.style.display = 'contents'
|
htmlNode.style.display = 'contents'
|
||||||
htmlNode.innerHTML = htmlString
|
htmlNode.innerHTML = htmlString
|
||||||
|
@ -32,10 +32,9 @@ export default defineConfig({
|
|||||||
},
|
},
|
||||||
projects: [
|
projects: [
|
||||||
{
|
{
|
||||||
name: 'Google Chrome',
|
name: 'chromium',
|
||||||
use: {
|
use: {
|
||||||
...devices['Desktop Chrome'],
|
...devices['Desktop Chrome'],
|
||||||
channel: 'chrome',
|
|
||||||
contextOptions: {
|
contextOptions: {
|
||||||
/* Chromium is the only one with these permission types */
|
/* Chromium is the only one with these permission types */
|
||||||
permissions: ['clipboard-write', 'clipboard-read'],
|
permissions: ['clipboard-write', 'clipboard-read'],
|
||||||
|
@ -54,7 +54,7 @@ export function App() {
|
|||||||
)
|
)
|
||||||
|
|
||||||
const {
|
const {
|
||||||
app: { onboarding_status },
|
app: { onboardingStatus },
|
||||||
} = settings.context
|
} = settings.context
|
||||||
|
|
||||||
useHotkeys('backspace', (e) => {
|
useHotkeys('backspace', (e) => {
|
||||||
@ -69,7 +69,7 @@ export function App() {
|
|||||||
)
|
)
|
||||||
|
|
||||||
const paneOpacity = [onboardingPaths.CAMERA, onboardingPaths.STREAMING].some(
|
const paneOpacity = [onboardingPaths.CAMERA, onboardingPaths.STREAMING].some(
|
||||||
(p) => p === onboarding_status.current
|
(p) => p === onboardingStatus.current
|
||||||
)
|
)
|
||||||
? 'opacity-20'
|
? 'opacity-20'
|
||||||
: ''
|
: ''
|
||||||
|
@ -77,7 +77,7 @@ export const ClientSideScene = ({
|
|||||||
}: {
|
}: {
|
||||||
cameraControls: ReturnType<
|
cameraControls: ReturnType<
|
||||||
typeof useSettingsAuthContext
|
typeof useSettingsAuthContext
|
||||||
>['settings']['context']['modeling']['mouse_controls']['current']
|
>['settings']['context']['modeling']['mouseControls']['current']
|
||||||
}) => {
|
}) => {
|
||||||
const canvasRef = useRef<HTMLDivElement>(null)
|
const canvasRef = useRef<HTMLDivElement>(null)
|
||||||
const { state, send, context } = useModelingContext()
|
const { state, send, context } = useModelingContext()
|
||||||
|
@ -1230,12 +1230,13 @@ export class SceneEntities {
|
|||||||
// lee: Well, it appears all our code in sceneEntities each act as their own
|
// lee: Well, it appears all our code in sceneEntities each act as their own
|
||||||
// kind of classes. In this case, I'll keep utility functions pertaining to
|
// kind of classes. In this case, I'll keep utility functions pertaining to
|
||||||
// circle3Point here. Feel free to extract as needed.
|
// circle3Point here. Feel free to extract as needed.
|
||||||
entryDraftCircle3Point = async (
|
entryDraftCircle3Point = (
|
||||||
|
done: () => void,
|
||||||
startSketchOnASTNodePath: PathToNode,
|
startSketchOnASTNodePath: PathToNode,
|
||||||
forward: Vector3,
|
forward: Vector3,
|
||||||
up: Vector3,
|
up: Vector3,
|
||||||
sketchOrigin: Vector3
|
sketchOrigin: Vector3
|
||||||
) => {
|
): (() => void) => {
|
||||||
// lee: Not a fan we need to re-iterate this dummy object all over the place
|
// lee: Not a fan we need to re-iterate this dummy object all over the place
|
||||||
// just to get the scale but okie dokie.
|
// just to get the scale but okie dokie.
|
||||||
const dummy = new Mesh()
|
const dummy = new Mesh()
|
||||||
@ -1374,13 +1375,13 @@ export class SceneEntities {
|
|||||||
groupOfDrafts.add(groupCircle)
|
groupOfDrafts.add(groupCircle)
|
||||||
}
|
}
|
||||||
|
|
||||||
const cleanup = () => {
|
|
||||||
this.scene.remove(groupOfDrafts)
|
|
||||||
}
|
|
||||||
|
|
||||||
// The target of our dragging
|
// The target of our dragging
|
||||||
let target: Object3D | undefined = undefined
|
let target: Object3D | undefined = undefined
|
||||||
|
|
||||||
|
const cleanupFn = () => {
|
||||||
|
this.scene.remove(groupOfDrafts)
|
||||||
|
}
|
||||||
|
|
||||||
sceneInfra.setCallbacks({
|
sceneInfra.setCallbacks({
|
||||||
async onDrag(args) {
|
async onDrag(args) {
|
||||||
const draftPointsIntersected = args.intersects.filter(
|
const draftPointsIntersected = args.intersects.filter(
|
||||||
@ -1444,9 +1445,11 @@ export class SceneEntities {
|
|||||||
await kclManager.executeAstMock(astSnapshot)
|
await kclManager.executeAstMock(astSnapshot)
|
||||||
await codeManager.updateEditorWithAstAndWriteToFile(astSnapshot)
|
await codeManager.updateEditorWithAstAndWriteToFile(astSnapshot)
|
||||||
|
|
||||||
sceneInfra.modelingSend({ type: 'circle3PointsFinished', cleanup })
|
done()
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
return cleanupFn
|
||||||
}
|
}
|
||||||
setupDraftCircle = async (
|
setupDraftCircle = async (
|
||||||
sketchPathToNode: PathToNode,
|
sketchPathToNode: PathToNode,
|
||||||
|
@ -5,21 +5,21 @@ import { useEffect, useState } from 'react'
|
|||||||
export function CameraProjectionToggle() {
|
export function CameraProjectionToggle() {
|
||||||
const { settings } = useSettingsAuthContext()
|
const { settings } = useSettingsAuthContext()
|
||||||
const isCameraProjectionPerspective =
|
const isCameraProjectionPerspective =
|
||||||
settings.context.modeling.camera_projection.current === 'perspective'
|
settings.context.modeling.cameraProjection.current === 'perspective'
|
||||||
const [checked, setChecked] = useState(isCameraProjectionPerspective)
|
const [checked, setChecked] = useState(isCameraProjectionPerspective)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setChecked(
|
setChecked(
|
||||||
settings.context.modeling.camera_projection.current === 'perspective'
|
settings.context.modeling.cameraProjection.current === 'perspective'
|
||||||
)
|
)
|
||||||
}, [settings.context.modeling.camera_projection.current])
|
}, [settings.context.modeling.cameraProjection.current])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Switch
|
<Switch
|
||||||
checked={checked}
|
checked={checked}
|
||||||
onChange={(newValue) => {
|
onChange={(newValue) => {
|
||||||
settings.send({
|
settings.send({
|
||||||
type: 'set.modeling.camera_projection',
|
type: 'set.modeling.cameraProjection',
|
||||||
data: {
|
data: {
|
||||||
level: 'user',
|
level: 'user',
|
||||||
value: newValue ? 'perspective' : 'orthographic',
|
value: newValue ? 'perspective' : 'orthographic',
|
||||||
|
@ -115,9 +115,9 @@ function CommandBarKclInput({
|
|||||||
: defaultValue.length,
|
: defaultValue.length,
|
||||||
},
|
},
|
||||||
theme:
|
theme:
|
||||||
settings.context.app.appearance.theme.current === 'system'
|
settings.context.app.theme.current === 'system'
|
||||||
? getSystemTheme()
|
? getSystemTheme()
|
||||||
: settings.context.app.appearance.theme.current,
|
: settings.context.app.theme.current,
|
||||||
extensions: [
|
extensions: [
|
||||||
varMentionsExtension,
|
varMentionsExtension,
|
||||||
EditorView.updateListener.of((vu: ViewUpdate) => {
|
EditorView.updateListener.of((vu: ViewUpdate) => {
|
||||||
|
@ -75,6 +75,7 @@ function CommandBarTextareaInput({
|
|||||||
target.selectionStart = selectionStart + 1
|
target.selectionStart = selectionStart + 1
|
||||||
target.selectionEnd = selectionStart + 1
|
target.selectionEnd = selectionStart + 1
|
||||||
} else if (event.key === 'Enter') {
|
} else if (event.key === 'Enter') {
|
||||||
|
event.preventDefault()
|
||||||
formRef.current?.dispatchEvent(
|
formRef.current?.dispatchEvent(
|
||||||
new Event('submit', { bubbles: true })
|
new Event('submit', { bubbles: true })
|
||||||
)
|
)
|
||||||
|
@ -6,7 +6,7 @@ import { useState } from 'react'
|
|||||||
const DownloadAppBanner = () => {
|
const DownloadAppBanner = () => {
|
||||||
const { settings } = useSettingsAuthContext()
|
const { settings } = useSettingsAuthContext()
|
||||||
const [isBannerDismissed, setIsBannerDismissed] = useState(
|
const [isBannerDismissed, setIsBannerDismissed] = useState(
|
||||||
settings.context.app.dismiss_web_banner.current
|
settings.context.app.dismissWebBanner.current
|
||||||
)
|
)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|