Compare commits

..

7 Commits

Author SHA1 Message Date
14e3311050 Update snapshots 2025-06-19 01:52:08 +00:00
d4c5cef78a More warnings
Signed-off-by: Nick Cameron <nrc@ncameron.org>
2025-06-19 13:32:02 +12:00
4b4960ac2c autocomplete
Signed-off-by: Nick Cameron <nrc@ncameron.org>
2025-06-19 11:12:21 +12:00
8c36105113 Generate angle suffixes in the frontend
Signed-off-by: Nick Cameron <nrc@ncameron.org>
2025-06-19 11:12:20 +12:00
77e5d6e3a5 Update samples
Signed-off-by: Nick Cameron <nrc@ncameron.org>
2025-06-19 11:12:20 +12:00
bfaec5c04e Warn on inferred angle units
Signed-off-by: Nick Cameron <nrc@ncameron.org>
2025-06-19 11:12:20 +12:00
8395869b2e Allow or deny warnings
Signed-off-by: Nick Cameron <nrc@ncameron.org>
2025-06-19 11:12:20 +12:00
1027 changed files with 9806 additions and 94007 deletions

View File

@ -9,7 +9,6 @@ VITE_KC_SITE_BASE_URL=https://dev.zoo.dev
VITE_KC_SITE_APP_URL=https://app.dev.zoo.dev VITE_KC_SITE_APP_URL=https://app.dev.zoo.dev
VITE_KC_SKIP_AUTH=false VITE_KC_SKIP_AUTH=false
VITE_KC_CONNECTION_TIMEOUT_MS=5000 VITE_KC_CONNECTION_TIMEOUT_MS=5000
#VITE_WASM_URL="optional way of overriding the wasm url, particular for unit tests which need this if you running not on the default 3000 port"
#VITE_KC_DEV_TOKEN="optional token to skip auth in the app" #VITE_KC_DEV_TOKEN="optional token to skip auth in the app"
#token="required token for playwright. TODO: clean up env vars in #3973" #token="required token for playwright. TODO: clean up env vars in #3973"

View File

@ -2,7 +2,7 @@
name: Release name: Release
about: Create a new release for the Zoo Design Studio about: Create a new release for the Zoo Design Studio
title: "Cut release v1.?.?" title: "Cut release v1.?.?"
labels: [meta/release] labels: [release]
--- ---
> Instructions: https://github.com/KittyCAD/modeling-app/blob/main/CONTRIBUTING.md#shipping-releases > Instructions: https://github.com/KittyCAD/modeling-app/blob/main/CONTRIBUTING.md#shipping-releases
@ -19,8 +19,7 @@ Release builds URL: ???
* [ ] Confirm the application opens (dismiss the updater) * [ ] Confirm the application opens (dismiss the updater)
* [ ] Create a project with a basic Text-to-CAD prompt * [ ] Create a project with a basic Text-to-CAD prompt
* [ ] Confirm the result is viewable in an engine stream * [ ] Confirm the result is viewable in an engine stream
* [ ] Use 'Check for updates' to bring back the updater toast * [ ] Open the application again and confirm the updater can downgrade
* [ ] Confirm the app can update to the previous release
## macOS via ??? ## macOS via ???
@ -28,8 +27,7 @@ Release builds URL: ???
* [ ] Confirm the application opens (dismiss the updater) * [ ] Confirm the application opens (dismiss the updater)
* [ ] Create a project with a basic Text-to-CAD prompt * [ ] Create a project with a basic Text-to-CAD prompt
* [ ] Confirm the result is viewable in an engine stream * [ ] Confirm the result is viewable in an engine stream
* [ ] Use 'Check for updates' to bring back the updater toast * [ ] Open the application again and confirm the updater can downgrade
* [ ] Confirm the app can update to the previous release
## Linux via ??? ## Linux via ???
@ -37,5 +35,4 @@ Release builds URL: ???
* [ ] Confirm the application opens (dismiss the updater) * [ ] Confirm the application opens (dismiss the updater)
* [ ] Create a project with a basic Text-to-CAD prompt * [ ] Create a project with a basic Text-to-CAD prompt
* [ ] Confirm the result is viewable in an engine stream * [ ] Confirm the result is viewable in an engine stream
* [ ] Use 'Check for updates' to bring back the updater toast * [ ] Open the application again and confirm the updater can downgrade
* [ ] Confirm the app can update to the previous release

View File

@ -43,7 +43,7 @@ jobs:
- name: Download Wasm Cache - name: Download Wasm Cache
id: download-wasm id: download-wasm
if: ${{ github.event_name == 'pull_request' && steps.filter.outputs.rust == 'false' }} if: ${{ github.event_name == 'pull_request' && steps.filter.outputs.rust == 'false' }}
uses: dawidd6/action-download-artifact@v11 uses: dawidd6/action-download-artifact@v7
continue-on-error: true continue-on-error: true
with: with:
github_token: ${{secrets.GITHUB_TOKEN}} github_token: ${{secrets.GITHUB_TOKEN}}
@ -362,7 +362,7 @@ jobs:
- name: Authenticate to Google Cloud - name: Authenticate to Google Cloud
if: ${{ env.IS_STAGING == 'true' }} if: ${{ env.IS_STAGING == 'true' }}
uses: 'google-github-actions/auth@v2.1.10' uses: 'google-github-actions/auth@v2.1.8'
with: with:
credentials_json: '${{ secrets.GOOGLE_CLOUD_DL_SA }}' credentials_json: '${{ secrets.GOOGLE_CLOUD_DL_SA }}'

View File

@ -25,8 +25,8 @@ jobs:
- runner=8cpu-linux-x64 - runner=8cpu-linux-x64
- extras=s3-cache - extras=s3-cache
steps: steps:
- uses: runs-on/action@v2 - uses: runs-on/action@v1
- uses: actions/create-github-app-token@v2 - uses: actions/create-github-app-token@v1
id: app-token id: app-token
with: with:
app-id: ${{ secrets.MODELING_APP_GH_APP_ID }} app-id: ${{ secrets.MODELING_APP_GH_APP_ID }}
@ -149,8 +149,8 @@ jobs:
partitionIndex: [1, 2, 3, 4, 5, 6] partitionIndex: [1, 2, 3, 4, 5, 6]
partitionTotal: [6] partitionTotal: [6]
steps: steps:
- uses: runs-on/action@v2 - uses: runs-on/action@v1
- uses: actions/create-github-app-token@v2 - uses: actions/create-github-app-token@v1
id: app-token id: app-token
with: with:
app-id: ${{ secrets.MODELING_APP_GH_APP_ID }} app-id: ${{ secrets.MODELING_APP_GH_APP_ID }}
@ -207,8 +207,8 @@ jobs:
- runner=32cpu-linux-x64 - runner=32cpu-linux-x64
- extras=s3-cache - extras=s3-cache
steps: steps:
- uses: runs-on/action@v2 - uses: runs-on/action@v1
- uses: actions/create-github-app-token@v2 - uses: actions/create-github-app-token@v1
id: app-token id: app-token
with: with:
app-id: ${{ secrets.MODELING_APP_GH_APP_ID }} app-id: ${{ secrets.MODELING_APP_GH_APP_ID }}

View File

@ -46,7 +46,7 @@ jobs:
- name: Download Wasm cache - name: Download Wasm cache
id: download-wasm id: download-wasm
if: ${{ github.event_name != 'schedule' && steps.filter.outputs.rust == 'false' }} if: ${{ github.event_name != 'schedule' && steps.filter.outputs.rust == 'false' }}
uses: dawidd6/action-download-artifact@v11 uses: dawidd6/action-download-artifact@v7
continue-on-error: true continue-on-error: true
with: with:
github_token: ${{secrets.GITHUB_TOKEN}} github_token: ${{secrets.GITHUB_TOKEN}}
@ -110,7 +110,7 @@ jobs:
steps: steps:
- uses: actions/create-github-app-token@v2 - uses: actions/create-github-app-token@v1
id: app-token id: app-token
with: with:
app-id: ${{ secrets.MODELING_APP_GH_APP_ID }} app-id: ${{ secrets.MODELING_APP_GH_APP_ID }}
@ -230,7 +230,7 @@ jobs:
steps: steps:
- uses: actions/create-github-app-token@v2 - uses: actions/create-github-app-token@v1
id: app-token id: app-token
with: with:
app-id: ${{ secrets.MODELING_APP_GH_APP_ID }} app-id: ${{ secrets.MODELING_APP_GH_APP_ID }}

View File

@ -20,7 +20,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- uses: actions/create-github-app-token@v2 - uses: actions/create-github-app-token@v1
id: app-token id: app-token
with: with:
# required # required

View File

@ -328,7 +328,7 @@ jobs:
mkdir -p releases/language-server/${{ env.TAG }} mkdir -p releases/language-server/${{ env.TAG }}
cp -r build/* releases/language-server/${{ env.TAG }} cp -r build/* releases/language-server/${{ env.TAG }}
- name: "Authenticate to Google Cloud" - name: "Authenticate to Google Cloud"
uses: "google-github-actions/auth@v2.1.10" uses: "google-github-actions/auth@v2.1.8"
with: with:
credentials_json: "${{ secrets.GOOGLE_CLOUD_DL_SA }}" credentials_json: "${{ secrets.GOOGLE_CLOUD_DL_SA }}"
- name: Set up Cloud SDK - name: Set up Cloud SDK

View File

@ -113,7 +113,7 @@ jobs:
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- name: Install uv - name: Install uv
uses: astral-sh/setup-uv@v6 uses: astral-sh/setup-uv@v5
- uses: actions-rust-lang/setup-rust-toolchain@v1 - uses: actions-rust-lang/setup-rust-toolchain@v1
- uses: taiki-e/install-action@just - uses: taiki-e/install-action@just
- name: Run tests - name: Run tests
@ -130,7 +130,7 @@ jobs:
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- name: Install the latest version of uv - name: Install the latest version of uv
uses: astral-sh/setup-uv@v6 uses: astral-sh/setup-uv@v5
- name: Install codespell - name: Install codespell
run: | run: |
uv venv .venv uv venv .venv
@ -161,7 +161,7 @@ jobs:
with: with:
path: rust/kcl-python-bindings path: rust/kcl-python-bindings
- name: Install the latest version of uv - name: Install the latest version of uv
uses: astral-sh/setup-uv@v6 uses: astral-sh/setup-uv@v5
- name: do uv things - name: do uv things
run: | run: |
cd rust/kcl-python-bindings cd rust/kcl-python-bindings

View File

@ -108,7 +108,7 @@ jobs:
run: npm run files:set-notes run: npm run files:set-notes
- name: Authenticate to Google Cloud - name: Authenticate to Google Cloud
uses: 'google-github-actions/auth@v2.1.10' uses: 'google-github-actions/auth@v2.1.8'
with: with:
credentials_json: '${{ secrets.GOOGLE_CLOUD_DL_SA }}' credentials_json: '${{ secrets.GOOGLE_CLOUD_DL_SA }}'

View File

@ -120,36 +120,6 @@ jobs:
- run: npm run circular-deps:diff - run: npm run circular-deps:diff
npm-url-checker:
runs-on: ubuntu-latest
needs: npm-build-wasm
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version-file: '.nvmrc'
cache: 'npm'
- run: npm install
- name: Download all artifacts
uses: actions/download-artifact@v4
- name: Copy prepared wasm
run: |
ls -R prepared-wasm
cp prepared-wasm/kcl_wasm_lib_bg.wasm public
mkdir rust/kcl-wasm-lib/pkg
cp prepared-wasm/kcl_wasm_lib* rust/kcl-wasm-lib/pkg
- name: Copy prepared ts-rs bindings
run: |
ls -R prepared-ts-rs-bindings
mkdir rust/kcl-lib/bindings
cp -r prepared-ts-rs-bindings/* rust/kcl-lib/bindings/
- run: npm run url-checker:diff
python-codespell: python-codespell:
runs-on: ubuntu-22.04 runs-on: ubuntu-22.04
steps: steps:

2
.gitignore vendored
View File

@ -87,4 +87,4 @@ venv
.vscode-test .vscode-test
.biome/ .biome/
.million .million

View File

@ -235,47 +235,6 @@ To display logging (to the terminal or console) set `ZOO_LOG=1`. This will log s
To enable memory metrics, build with `--features dhat-heap`. To enable memory metrics, build with `--features dhat-heap`.
## Running scripts
There are multiple scripts under the folder path `./scripts` which can be used in various settings.
### Pattern for a static file, npm run commands, and CI-CD checks
If you want to implement a static checker follow this pattern. Two static checkers we have are circular dependency checks in our typescript code and url checker to see if any hard coded URL is the typescript application 404s. We have a set of known files in `./scripts/known/*.txt` which is the baseline.
If you improve the baseline, run the overwrite command and commit the new smaller baseline. Try not to make the baseline bigger, the CI CD will complain.
These baselines are to hold us to higher standards and help implement automated testing against the repository
#### Output result to stdout
- `npm run circular-deps`
- `npm run url-checker`
- create a `<name>.sh` file that will run the static checker then output the result to `stdout`
#### Overwrite result to known .txt file on disk
If the application needs to overwrite the known file on disk use this pattern. This known .txt file will be source controlled as the baseline
- `npm run circular-deps:overwrite`
- `npm run url-checker:overwrite`
#### Diff baseline and current
These commands will write a /tmp/ file on disk and compare it to the known file in the repository. This command will also be used in the CI CD pipeline for automated checks
- create a `diff-<name>.sh` file that is the script to diff your tmp file to the baseline
e.g. `diff-url-checker.sh`
```bash
#!/bin/bash
set -euo pipefail
npm run url-checker > /tmp/urls.txt
diff --ignore-blank-lines -w /tmp/urls.txt ./scripts/known/urls.txt
```
- `npm run circular-deps:diff`
- `npm run url-checker:diff`
## Proposing changes ## Proposing changes
Before you submit a contribution PR to this repo, please ensure that: Before you submit a contribution PR to this repo, please ensure that:
@ -321,9 +280,6 @@ Assign someone to each section of the manual checklist generated by the issue te
Follow the instructions [here](./rust/README.md) to publish new crates. Follow the instructions [here](./rust/README.md) to publish new crates.
This ensures that the KCL accepted by the app is also accepted by the CLI. This ensures that the KCL accepted by the app is also accepted by the CLI.
If there are documentation changes, merge the corresponding Dependabot PRs [here](https://github.com/KittyCAD/website/pulls/app%2Fdependabot) for the website.
You can trigger Dependabot to check for updates [here](https://github.com/KittyCAD/website/network/updates/17261214/jobs).
#### 5. Publish the release #### 5. Publish the release
Head over to https://github.com/KittyCAD/modeling-app/releases/new, pick the newly created tag and type it in the **Release title** field as well. Head over to https://github.com/KittyCAD/modeling-app/releases/new, pick the newly created tag and type it in the **Release title** field as well.

View File

@ -62,10 +62,7 @@ else
endif endif
public/kcl-samples/manifest.json: $(KCL_SOURCES) public/kcl-samples/manifest.json: $(KCL_SOURCES)
ifndef WINDOWS
cd rust/kcl-lib && EXPECTORATE=overwrite cargo test generate_manifest cd rust/kcl-lib && EXPECTORATE=overwrite cargo test generate_manifest
@ touch $@
endif
.vite/build/main.js: $(REACT_SOURCES) $(TYPESCRIPT_SOURCES) $(VITE_SOURCES) .vite/build/main.js: $(REACT_SOURCES) $(TYPESCRIPT_SOURCES) $(VITE_SOURCES)
npm run tronb:vite:dev npm run tronb:vite:dev

View File

@ -83,13 +83,6 @@ Allow orbiting in sketch mode.
Whether to show the debug panel, which lets you see various states of the app to aid in development. Whether to show the debug panel, which lets you see various states of the app to aid in development.
**Default:** None
##### fixed_size_grid
If true, the grid cells will be fixed-size, where the width is your default length unit. If false, the grid will get larger as you zoom out, and smaller as you zoom in.
**Default:** None **Default:** None

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -10,11 +10,9 @@ Extend the current sketch with a new involute circular curve.
```kcl ```kcl
involuteCircular( involuteCircular(
@sketch: Sketch, @sketch: Sketch,
startRadius: number(Length),
endRadius: number(Length),
angle: number(Angle), angle: number(Angle),
startRadius?: number(Length),
endRadius?: number(Length),
startDiameter?: number(Length),
endDiameter?: number(Length),
reverse?: bool, reverse?: bool,
tag?: TagDecl, tag?: TagDecl,
): Sketch ): Sketch
@ -27,11 +25,9 @@ involuteCircular(
| Name | Type | Description | Required | | Name | Type | Description | Required |
|----------|------|-------------|----------| |----------|------|-------------|----------|
| `sketch` | [`Sketch`](/docs/kcl-std/types/std-types-Sketch) | Which sketch should this path be added to? | Yes | | `sketch` | [`Sketch`](/docs/kcl-std/types/std-types-Sketch) | Which sketch should this path be added to? | Yes |
| `startRadius` | [`number(Length)`](/docs/kcl-std/types/std-types-number) | The involute is described between two circles, start_radius is the radius of the inner circle. | Yes |
| `endRadius` | [`number(Length)`](/docs/kcl-std/types/std-types-number) | The involute is described between two circles, end_radius is the radius of the outer circle. | Yes |
| `angle` | [`number(Angle)`](/docs/kcl-std/types/std-types-number) | The angle to rotate the involute by. A value of zero will produce a curve with a tangent along the x-axis at the start point of the curve. | Yes | | `angle` | [`number(Angle)`](/docs/kcl-std/types/std-types-number) | The angle to rotate the involute by. A value of zero will produce a curve with a tangent along the x-axis at the start point of the curve. | Yes |
| `startRadius` | [`number(Length)`](/docs/kcl-std/types/std-types-number) | The involute is described between two circles, startRadius is the radius of the inner circle. Either `startRadius` or `startDiameter` must be given (but not both). | No |
| `endRadius` | [`number(Length)`](/docs/kcl-std/types/std-types-number) | The involute is described between two circles, endRadius is the radius of the outer circle. Either `endRadius` or `endDiameter` must be given (but not both). | No |
| `startDiameter` | [`number(Length)`](/docs/kcl-std/types/std-types-number) | The involute is described between two circles, startDiameter describes the inner circle. Either `startRadius` or `startDiameter` must be given (but not both). | No |
| `endDiameter` | [`number(Length)`](/docs/kcl-std/types/std-types-number) | The involute is described between two circles, endDiameter describes the outer circle. Either `endRadius` or `endDiameter` must be given (but not both). | No |
| `reverse` | [`bool`](/docs/kcl-std/types/std-types-bool) | If reverse is true, the segment will start from the end of the involute, otherwise it will start from that start. | No | | `reverse` | [`bool`](/docs/kcl-std/types/std-types-bool) | If reverse is true, the segment will start from the end of the involute, otherwise it will start from that start. | No |
| `tag` | [`TagDecl`](/docs/kcl-std/types/std-types-TagDecl) | Create a new tag which refers to this line. | No | | `tag` | [`TagDecl`](/docs/kcl-std/types/std-types-TagDecl) | Create a new tag which refers to this line. | No |

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -38,7 +38,7 @@ revolved around the same axis.
| `sketches` | [`[Sketch; 1+]`](/docs/kcl-std/types/std-types-Sketch) | The sketch or set of sketches that should be revolved | Yes | | `sketches` | [`[Sketch; 1+]`](/docs/kcl-std/types/std-types-Sketch) | The sketch or set of sketches that should be revolved | Yes |
| `axis` | [`Axis2d`](/docs/kcl-std/types/std-types-Axis2d) or [`Edge`](/docs/kcl-std/types/std-types-Edge) | Axis of revolution. | Yes | | `axis` | [`Axis2d`](/docs/kcl-std/types/std-types-Axis2d) or [`Edge`](/docs/kcl-std/types/std-types-Edge) | Axis of revolution. | Yes |
| `angle` | [`number(Angle)`](/docs/kcl-std/types/std-types-number) | Angle to revolve (in degrees). Default is 360. | No | | `angle` | [`number(Angle)`](/docs/kcl-std/types/std-types-number) | Angle to revolve (in degrees). Default is 360. | No |
| `tolerance` | [`number(Length)`](/docs/kcl-std/types/std-types-number) | Defines the smallest distance below which two entities are considered coincident, intersecting, coplanar, or similar. For most use cases, it should not be changed from its default value of 10^-7 millimeters. | No | | `tolerance` | [`number(Length)`](/docs/kcl-std/types/std-types-number) | Tolerance for the revolve operation. | No |
| `symmetric` | [`bool`](/docs/kcl-std/types/std-types-bool) | If true, the extrusion will happen symmetrically around the sketch. Otherwise, the extrusion will happen on only one side of the sketch. | No | | `symmetric` | [`bool`](/docs/kcl-std/types/std-types-bool) | If true, the extrusion will happen symmetrically around the sketch. Otherwise, the extrusion will happen on only one side of the sketch. | No |
| `bidirectionalAngle` | [`number(Angle)`](/docs/kcl-std/types/std-types-number) | If specified, will also revolve in the opposite direction to 'angle' to the specified angle. If 'symmetric' is true, this value is ignored. | No | | `bidirectionalAngle` | [`number(Angle)`](/docs/kcl-std/types/std-types-number) | If specified, will also revolve in the opposite direction to 'angle' to the specified angle. If 'symmetric' is true, this value is ignored. | No |
| `tagStart` | [`TagDecl`](/docs/kcl-std/types/std-types-TagDecl) | A named tag for the face at the start of the revolve, i.e. the original sketch. | No | | `tagStart` | [`TagDecl`](/docs/kcl-std/types/std-types-TagDecl) | A named tag for the face at the start of the revolve, i.e. the original sketch. | No |

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -28,7 +28,7 @@ will smoothly blend the transition.
| `solid` | [`Solid`](/docs/kcl-std/types/std-types-Solid) | The solid whose edges should be filletted | Yes | | `solid` | [`Solid`](/docs/kcl-std/types/std-types-Solid) | The solid whose edges should be filletted | Yes |
| `radius` | [`number(Length)`](/docs/kcl-std/types/std-types-number) | The radius of the fillet | Yes | | `radius` | [`number(Length)`](/docs/kcl-std/types/std-types-number) | The radius of the fillet | Yes |
| `tags` | [`[Edge; 1+]`](/docs/kcl-std/types/std-types-Edge) | The paths you want to fillet | Yes | | `tags` | [`[Edge; 1+]`](/docs/kcl-std/types/std-types-Edge) | The paths you want to fillet | Yes |
| `tolerance` | [`number(Length)`](/docs/kcl-std/types/std-types-number) | Defines the smallest distance below which two entities are considered coincident, intersecting, coplanar, or similar. For most use cases, it should not be changed from its default value of 10^-7 millimeters. | No | | `tolerance` | [`number(Length)`](/docs/kcl-std/types/std-types-number) | The tolerance for this fillet | No |
| `tag` | [`TagDecl`](/docs/kcl-std/types/std-types-TagDecl) | Create a new tag which refers to this fillet | No | | `tag` | [`TagDecl`](/docs/kcl-std/types/std-types-TagDecl) | Create a new tag which refers to this fillet | No |
### Returns ### Returns

File diff suppressed because one or more lines are too long

View File

@ -24,7 +24,7 @@ verifying fit, and analyzing overlapping geometries in assemblies.
| Name | Type | Description | Required | | Name | Type | Description | Required |
|----------|------|-------------|----------| |----------|------|-------------|----------|
| `solids` | `[Solid; 2+]` | The solids to intersect. | Yes | | `solids` | `[Solid; 2+]` | The solids to intersect. | Yes |
| `tolerance` | [`number(Length)`](/docs/kcl-std/types/std-types-number) | Defines the smallest distance below which two entities are considered coincident, intersecting, coplanar, or similar. For most use cases, it should not be changed from its default value of 10^-7 millimeters. | No | | `tolerance` | [`number(Length)`](/docs/kcl-std/types/std-types-number) | The tolerance to use for the intersection operation. | No |
### Returns ### Returns

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -27,7 +27,7 @@ and complex multi-body part modeling.
|----------|------|-------------|----------| |----------|------|-------------|----------|
| `solids` | [`[Solid; 1+]`](/docs/kcl-std/types/std-types-Solid) | The solids to use as the base to subtract from. | Yes | | `solids` | [`[Solid; 1+]`](/docs/kcl-std/types/std-types-Solid) | The solids to use as the base to subtract from. | Yes |
| `tools` | [`[Solid]`](/docs/kcl-std/types/std-types-Solid) | The solids to subtract. | Yes | | `tools` | [`[Solid]`](/docs/kcl-std/types/std-types-Solid) | The solids to subtract. | Yes |
| `tolerance` | [`number(Length)`](/docs/kcl-std/types/std-types-number) | Defines the smallest distance below which two entities are considered coincident, intersecting, coplanar, or similar. For most use cases, it should not be changed from its default value of 10^-7 millimeters. | No | | `tolerance` | [`number(Length)`](/docs/kcl-std/types/std-types-number) | The tolerance to use for the subtraction operation. | No |
### Returns ### Returns

View File

@ -21,7 +21,7 @@ union(
| Name | Type | Description | Required | | Name | Type | Description | Required |
|----------|------|-------------|----------| |----------|------|-------------|----------|
| `solids` | `[Solid; 2+]` | The solids to union. | Yes | | `solids` | `[Solid; 2+]` | The solids to union. | Yes |
| `tolerance` | [`number(Length)`](/docs/kcl-std/types/std-types-number) | Defines the smallest distance below which two entities are considered coincident, intersecting, coplanar, or similar. For most use cases, it should not be changed from its default value of 10^-7 millimeters. | No | | `tolerance` | [`number(Length)`](/docs/kcl-std/types/std-types-number) | The tolerance to use for the union operation. | No |
### Returns ### Returns

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -67,12 +67,10 @@ layout: manual
* [`patternCircular2d`](/docs/kcl-std/functions/std-sketch-patternCircular2d) * [`patternCircular2d`](/docs/kcl-std/functions/std-sketch-patternCircular2d)
* [`patternLinear2d`](/docs/kcl-std/functions/std-sketch-patternLinear2d) * [`patternLinear2d`](/docs/kcl-std/functions/std-sketch-patternLinear2d)
* [`patternTransform2d`](/docs/kcl-std/functions/std-sketch-patternTransform2d) * [`patternTransform2d`](/docs/kcl-std/functions/std-sketch-patternTransform2d)
* [`planeOf`](/docs/kcl-std/functions/std-sketch-planeOf)
* [`polygon`](/docs/kcl-std/functions/std-sketch-polygon) * [`polygon`](/docs/kcl-std/functions/std-sketch-polygon)
* [`profileStart`](/docs/kcl-std/functions/std-sketch-profileStart) * [`profileStart`](/docs/kcl-std/functions/std-sketch-profileStart)
* [`profileStartX`](/docs/kcl-std/functions/std-sketch-profileStartX) * [`profileStartX`](/docs/kcl-std/functions/std-sketch-profileStartX)
* [`profileStartY`](/docs/kcl-std/functions/std-sketch-profileStartY) * [`profileStartY`](/docs/kcl-std/functions/std-sketch-profileStartY)
* [`rectangle`](/docs/kcl-std/functions/std-sketch-rectangle)
* [`revolve`](/docs/kcl-std/functions/std-sketch-revolve) * [`revolve`](/docs/kcl-std/functions/std-sketch-revolve)
* [`segAng`](/docs/kcl-std/functions/std-sketch-segAng) * [`segAng`](/docs/kcl-std/functions/std-sketch-segAng)
* [`segEnd`](/docs/kcl-std/functions/std-sketch-segEnd) * [`segEnd`](/docs/kcl-std/functions/std-sketch-segEnd)

View File

@ -32,12 +32,10 @@ This module contains functions for creating and manipulating sketches, and makin
* [`patternCircular2d`](/docs/kcl-std/functions/std-sketch-patternCircular2d) * [`patternCircular2d`](/docs/kcl-std/functions/std-sketch-patternCircular2d)
* [`patternLinear2d`](/docs/kcl-std/functions/std-sketch-patternLinear2d) * [`patternLinear2d`](/docs/kcl-std/functions/std-sketch-patternLinear2d)
* [`patternTransform2d`](/docs/kcl-std/functions/std-sketch-patternTransform2d) * [`patternTransform2d`](/docs/kcl-std/functions/std-sketch-patternTransform2d)
* [`planeOf`](/docs/kcl-std/functions/std-sketch-planeOf)
* [`polygon`](/docs/kcl-std/functions/std-sketch-polygon) * [`polygon`](/docs/kcl-std/functions/std-sketch-polygon)
* [`profileStart`](/docs/kcl-std/functions/std-sketch-profileStart) * [`profileStart`](/docs/kcl-std/functions/std-sketch-profileStart)
* [`profileStartX`](/docs/kcl-std/functions/std-sketch-profileStartX) * [`profileStartX`](/docs/kcl-std/functions/std-sketch-profileStartX)
* [`profileStartY`](/docs/kcl-std/functions/std-sketch-profileStartY) * [`profileStartY`](/docs/kcl-std/functions/std-sketch-profileStartY)
* [`rectangle`](/docs/kcl-std/functions/std-sketch-rectangle)
* [`revolve`](/docs/kcl-std/functions/std-sketch-revolve) * [`revolve`](/docs/kcl-std/functions/std-sketch-revolve)
* [`segAng`](/docs/kcl-std/functions/std-sketch-segAng) * [`segAng`](/docs/kcl-std/functions/std-sketch-segAng)
* [`segEnd`](/docs/kcl-std/functions/std-sketch-segEnd) * [`segEnd`](/docs/kcl-std/functions/std-sketch-segEnd)

View File

@ -12,7 +12,7 @@ test.describe('Point and click for boolean workflows', () => {
}, },
{ {
name: 'subtract', name: 'subtract',
code: 'subtract(extrude001, tools = extrude006)', code: 'subtract([extrude001], tools = [extrude006])',
}, },
{ {
name: 'intersect', name: 'intersect',
@ -81,8 +81,6 @@ test.describe('Point and click for boolean workflows', () => {
if (operationName !== 'subtract') { if (operationName !== 'subtract') {
// should down shift key to select multiple objects // should down shift key to select multiple objects
await page.keyboard.down('Shift') await page.keyboard.down('Shift')
} else {
await cmdBar.progressCmdBar()
} }
// Select second object // Select second object
@ -105,8 +103,8 @@ test.describe('Point and click for boolean workflows', () => {
await cmdBar.expectState({ await cmdBar.expectState({
stage: 'review', stage: 'review',
headerArguments: { headerArguments: {
Solids: '1 path', Tool: '1 path',
Tools: '1 path', Target: '1 path',
}, },
commandName, commandName,
}) })

View File

@ -5,7 +5,6 @@ import { uuidv4 } from '@src/lib/utils'
import type { HomePageFixture } from '@e2e/playwright/fixtures/homePageFixture' import type { HomePageFixture } from '@e2e/playwright/fixtures/homePageFixture'
import type { SceneFixture } from '@e2e/playwright/fixtures/sceneFixture' import type { SceneFixture } from '@e2e/playwright/fixtures/sceneFixture'
import type { ToolbarFixture } from '@e2e/playwright/fixtures/toolbarFixture' import type { ToolbarFixture } from '@e2e/playwright/fixtures/toolbarFixture'
import type { CmdBarFixture } from '@e2e/playwright/fixtures/cmdBarFixture'
import { getUtils } from '@e2e/playwright/test-utils' import { getUtils } from '@e2e/playwright/test-utils'
import { expect, test } from '@e2e/playwright/zoo-test' import { expect, test } from '@e2e/playwright/zoo-test'
@ -15,18 +14,13 @@ test.describe('Can create sketches on all planes and their back sides', () => {
homePage: HomePageFixture, homePage: HomePageFixture,
scene: SceneFixture, scene: SceneFixture,
toolbar: ToolbarFixture, toolbar: ToolbarFixture,
cmdBar: CmdBarFixture,
plane: string, plane: string,
clickCoords: { x: number; y: number } clickCoords: { x: number; y: number }
) => { ) => {
const u = await getUtils(page) const u = await getUtils(page)
// await page.addInitScript(() => {
// localStorage.setItem('persistCode', '@settings(defaultLengthUnit = in)')
// })
await page.setBodyDimensions({ width: 1200, height: 500 }) await page.setBodyDimensions({ width: 1200, height: 500 })
await homePage.goToModelingScene() await homePage.goToModelingScene()
// await scene.settled(cmdBar)
const XYPlanRed: [number, number, number] = [98, 50, 51] const XYPlanRed: [number, number, number] = [98, 50, 51]
await scene.expectPixelColor(XYPlanRed, { x: 700, y: 300 }, 15) await scene.expectPixelColor(XYPlanRed, { x: 700, y: 300 }, 15)
@ -125,166 +119,12 @@ test.describe('Can create sketches on all planes and their back sides', () => {
] ]
for (const config of planeConfigs) { for (const config of planeConfigs) {
test(config.plane, async ({ page, homePage, scene, toolbar, cmdBar }) => { test(config.plane, async ({ page, homePage, scene, toolbar }) => {
await sketchOnPlaneAndBackSideTest( await sketchOnPlaneAndBackSideTest(
page, page,
homePage, homePage,
scene, scene,
toolbar, toolbar,
cmdBar,
config.plane,
config.coords
)
})
}
})
test.describe('Can create sketches on offset planes and their back sides', () => {
const sketchOnPlaneAndBackSideTest = async (
page: Page,
homePage: HomePageFixture,
scene: SceneFixture,
toolbar: ToolbarFixture,
cmdbar: CmdBarFixture,
plane: string,
clickCoords: { x: number; y: number }
) => {
const u = await getUtils(page)
await page.addInitScript(() => {
localStorage.setItem(
'persistCode',
`@settings(defaultLengthUnit = in)
xyPlane = offsetPlane(XY, offset = 0.05)
xzPlane = offsetPlane(XZ, offset = 0.05)
yzPlane = offsetPlane(YZ, offset = 0.05)
`
)
})
await page.setBodyDimensions({ width: 1200, height: 500 })
await homePage.goToModelingScene()
// await scene.settled(cmdbar)
const XYPlanRed: [number, number, number] = [74, 74, 74]
await scene.expectPixelColor(XYPlanRed, { x: 700, y: 300 }, 15)
await u.openDebugPanel()
const coord =
plane === '-XY' || plane === '-YZ' || plane === 'XZ' ? -100 : 100
const camCommand: EngineCommand = {
type: 'modeling_cmd_req',
cmd_id: uuidv4(),
cmd: {
type: 'default_camera_look_at',
center: { x: 0, y: 0, z: 0 },
vantage: { x: coord, y: coord, z: coord },
up: { x: 0, y: 0, z: 1 },
},
}
const updateCamCommand: EngineCommand = {
type: 'modeling_cmd_req',
cmd_id: uuidv4(),
cmd: {
type: 'default_camera_get_settings',
},
}
const prefix = plane.length === 3 ? '-' : ''
const planeName = plane
.slice(plane.length === 3 ? 1 : 0)
.toLocaleLowerCase()
const codeLine1 = `sketch001 = startSketchOn(${prefix}${planeName}Plane)`
const codeLine2 = `profile001 = startProfile(sketch001, at = [${0.91 + (plane[0] === '-' ? 0.01 : 0)}, -${1.21 + (plane[0] === '-' ? 0.01 : 0)}])`
await u.openDebugPanel()
await u.clearCommandLogs()
await page.getByRole('button', { name: 'Start Sketch' }).click()
await u.sendCustomCmd(camCommand)
await page.waitForTimeout(100)
await u.sendCustomCmd(updateCamCommand)
await u.closeDebugPanel()
await toolbar.openFeatureTreePane()
await toolbar.getDefaultPlaneVisibilityButton('XY').click()
await toolbar.getDefaultPlaneVisibilityButton('XZ').click()
await toolbar.getDefaultPlaneVisibilityButton('YZ').click()
await expect(
toolbar
.getDefaultPlaneVisibilityButton('YZ')
.locator('[aria-label="eye crossed out"]')
).toBeVisible()
await page.mouse.click(clickCoords.x, clickCoords.y)
await page.waitForTimeout(600) // wait for animation
await toolbar.waitUntilSketchingReady()
await expect(
page.getByRole('button', { name: 'line Line', exact: true })
).toBeVisible()
await u.closeDebugPanel()
await page.mouse.click(707, 393)
await expect(page.locator('.cm-content')).toContainText(codeLine1)
await expect(page.locator('.cm-content')).toContainText(codeLine2)
await page
.getByRole('button', { name: 'line Line', exact: true })
.first()
.click()
await u.openAndClearDebugPanel()
await page.getByRole('button', { name: 'Exit Sketch' }).click()
await u.expectCmdLog('[data-message-type="execution-done"]')
await u.clearCommandLogs()
await u.removeCurrentCode()
}
const planeConfigs = [
{
plane: 'XY',
coords: { x: 600, y: 388 },
description: 'red plane',
},
{
plane: 'YZ',
coords: { x: 700, y: 250 },
description: 'green plane',
},
{
plane: 'XZ',
coords: { x: 684, y: 427 },
description: 'blue plane',
},
{
plane: '-XY',
coords: { x: 600, y: 118 },
description: 'back of red plane',
},
{
plane: '-YZ',
coords: { x: 700, y: 219 },
description: 'back of green plane',
},
{
plane: '-XZ',
coords: { x: 700, y: 80 },
description: 'back of blue plane',
},
]
for (const config of planeConfigs) {
test(config.plane, async ({ page, homePage, scene, toolbar, cmdBar }) => {
await sketchOnPlaneAndBackSideTest(
page,
homePage,
scene,
toolbar,
cmdBar,
config.plane, config.plane,
config.coords config.coords
) )

View File

@ -265,8 +265,6 @@ middle(0)
}) })
await expect( await expect(
page.getByText(`assert failed: Expected 0 to be greater than 0 but it wasn't page.getByText(`assert failed: Expected 0 to be greater than 0 but it wasn't
Backtrace:
assert() assert()
check() check()
middle()`) middle()`)

View File

@ -307,7 +307,7 @@ test.describe('Command bar tests', () => {
) )
const continueButton = page.getByRole('button', { name: 'Continue' }) const continueButton = page.getByRole('button', { name: 'Continue' })
const submitButton = page.getByTestId('command-bar-submit') const submitButton = page.getByRole('button', { name: 'Submit command' })
await continueButton.click() await continueButton.click()
// Review step and argument hotkeys // Review step and argument hotkeys
@ -525,9 +525,7 @@ test.describe('Command bar tests', () => {
const projectName = 'test' const projectName = 'test'
const beforeKclCode = `a = 5 const beforeKclCode = `a = 5
b = a * a b = a * a
c = 3 + a c = 3 + a`
theta = 45deg
`
await context.folderSetupFn(async (dir) => { await context.folderSetupFn(async (dir) => {
const testProject = join(dir, projectName) const testProject = join(dir, projectName)
await fsp.mkdir(testProject, { recursive: true }) await fsp.mkdir(testProject, { recursive: true })
@ -617,45 +615,9 @@ theta = 45deg
stage: 'commandBarClosed', stage: 'commandBarClosed',
}) })
}) })
await test.step(`Edit a parameter with explicit units via command bar`, async () => {
await cmdBar.cmdBarOpenBtn.click()
await cmdBar.chooseCommand('edit parameter')
await cmdBar
.selectOption({
name: 'theta',
})
.click()
await cmdBar.expectState({
stage: 'arguments',
commandName: 'Edit parameter',
currentArgKey: 'value',
currentArgValue: '45deg',
headerArguments: {
Name: 'theta',
Value: '',
},
highlightedHeaderArg: 'value',
})
await cmdBar.argumentInput
.locator('[contenteditable]')
.fill('45deg + 1deg')
await cmdBar.progressCmdBar()
await cmdBar.expectState({
stage: 'review',
commandName: 'Edit parameter',
headerArguments: {
Name: 'theta',
Value: '46deg',
},
})
await cmdBar.progressCmdBar()
await cmdBar.expectState({
stage: 'commandBarClosed',
})
})
await editor.expectEditor.toContain( await editor.expectEditor.toContain(
`a = 5b = a * amyParameter001 = ${newValue}c = 3 + atheta = 45deg + 1deg` `a = 5b = a * amyParameter001 = ${newValue}c = 3 + a`
) )
}) })

View File

@ -54,7 +54,9 @@ test(
await page.keyboard.press('Enter') await page.keyboard.press('Enter')
// Click the checkbox // Click the checkbox
await cmdBar.submit() const submitButton = page.getByText('Confirm Export')
await expect(submitButton).toBeVisible()
await page.keyboard.press('Enter')
// Expect it to succeed // Expect it to succeed
const errorToastMessage = page.getByText(`Error while exporting`) const errorToastMessage = page.getByText(`Error while exporting`)
@ -117,7 +119,9 @@ test(
await page.keyboard.press('Enter') await page.keyboard.press('Enter')
// Click the checkbox // Click the checkbox
await cmdBar.submit() const submitButton = page.getByText('Confirm Export')
await expect(submitButton).toBeVisible()
await page.keyboard.press('Enter')
// Look out for the toast message // Look out for the toast message
const exportingToastMessage = page.getByText(`Exporting...`) const exportingToastMessage = page.getByText(`Exporting...`)

View File

@ -288,7 +288,7 @@ a1 = startSketchOn(offsetPlane(XY, offset = 10))
// error text on hover // error text on hover
await page.hover('.cm-lint-marker-info') await page.hover('.cm-lint-marker-info')
await expect( await expect(
page.getByText('Identifiers should be lowerCamelCase').first() page.getByText('Identifiers must be lowerCamelCase').first()
).toBeVisible() ).toBeVisible()
await page.locator('#code-pane button:first-child').click() await page.locator('#code-pane button:first-child').click()
@ -314,7 +314,7 @@ sketch_001 = startSketchOn(XY)
// error text on hover // error text on hover
await page.hover('.cm-lint-marker-info') await page.hover('.cm-lint-marker-info')
await expect( await expect(
page.getByText('Identifiers should be lowerCamelCase').first() page.getByText('Identifiers must be lowerCamelCase').first()
).toBeVisible() ).toBeVisible()
}) })
@ -511,7 +511,7 @@ sketch_001 = startSketchOn(XY)
// error text on hover // error text on hover
await page.hover('.cm-lint-marker-info') await page.hover('.cm-lint-marker-info')
await expect( await expect(
page.getByText('Identifiers should be lowerCamelCase').first() page.getByText('Identifiers must be lowerCamelCase').first()
).toBeVisible() ).toBeVisible()
// focus the editor // focus the editor
@ -539,7 +539,7 @@ sketch_001 = startSketchOn(XY)
// error text on hover // error text on hover
await page.hover('.cm-lint-marker-info') await page.hover('.cm-lint-marker-info')
await expect( await expect(
page.getByText('Identifiers should be lowerCamelCase').first() page.getByText('Identifiers must be lowerCamelCase').first()
).toBeVisible() ).toBeVisible()
}) })
@ -681,7 +681,7 @@ a1 = startSketchOn(offsetPlane(XY, offset = 10))
// error text on hover // error text on hover
await page.hover('.cm-lint-marker-info') await page.hover('.cm-lint-marker-info')
await expect( await expect(
page.getByText('Identifiers should be lowerCamelCase').first() page.getByText('Identifiers must be lowerCamelCase').first()
).toBeVisible() ).toBeVisible()
// select the line that's causing the error and delete it // select the line that's causing the error and delete it
@ -912,7 +912,7 @@ a1 = startSketchOn(offsetPlane(XY, offset = 10))
|> close() |> close()
|> revolve( |> revolve(
axis = revolveAxis, axis = revolveAxis,
angle = 90 angle = 90deg
) )
` `
) )
@ -1617,33 +1617,4 @@ sketch001 = startSketchOn(XZ)
// Verify error is still visible // Verify error is still visible
await expect(page.locator('.cm-lint-marker-error')).toHaveCount(1) await expect(page.locator('.cm-lint-marker-error')).toHaveCount(1)
}) })
test('Core dump hotkey', async ({ page, scene, cmdBar, homePage }) => {
await page.addInitScript(async () => {
localStorage.setItem(
'persistCode',
`sketch001 = startSketchOn(XZ)
profile001 = circle(sketch001, center = [-100.0, -100.0], radius = 50.0)
`
)
})
const viewportSize = { width: 1200, height: 800 }
await page.setBodyDimensions(viewportSize)
await homePage.goToModelingScene()
await scene.connectionEstablished()
await scene.settled(cmdBar)
const modifier = process.platform === 'darwin' ? 'Meta' : 'Control'
await page.keyboard.press(`${modifier}+Shift+.`)
const toast1 = page.getByText('Starting core dump...')
await expect(toast1).toBeVisible()
const toast2 = page.getByText('Core dump completed')
await expect(toast2).toBeVisible()
})
}) })

View File

@ -229,12 +229,11 @@ test.describe('Feature Tree pane', () => {
const initialCode = `sketch001 = startSketchOn(XZ) const initialCode = `sketch001 = startSketchOn(XZ)
|> circle(center = [0, 0], radius = 5) |> circle(center = [0, 0], radius = 5)
renamedExtrude = extrude(sketch001, length = ${initialInput})` renamedExtrude = extrude(sketch001, length = ${initialInput})`
const newParameterName = 'length001' const newConstantName = 'length001'
const expectedCode = `${newParameterName} = 23 const expectedCode = `${newConstantName} = 23
sketch001 = startSketchOn(XZ) sketch001 = startSketchOn(XZ)
|> circle(center = [0, 0], radius = 5) |> circle(center = [0, 0], radius = 5)
renamedExtrude = extrude(sketch001, length = ${newParameterName})` renamedExtrude = extrude(sketch001, length = ${newConstantName})`
const editedParameterValue = '23 * 2'
await context.folderSetupFn(async (dir) => { await context.folderSetupFn(async (dir) => {
const testDir = join(dir, 'test-sample') const testDir = join(dir, 'test-sample')
@ -280,7 +279,7 @@ test.describe('Feature Tree pane', () => {
}) })
}) })
await test.step('Add a parameter for distance argument and submit', async () => { await test.step('Add a named constant for distance argument and submit', async () => {
await expect(cmdBar.currentArgumentInput).toBeVisible() await expect(cmdBar.currentArgumentInput).toBeVisible()
await cmdBar.variableCheckbox.click() await cmdBar.variableCheckbox.click()
await cmdBar.progressCmdBar() await cmdBar.progressCmdBar()
@ -297,43 +296,13 @@ test.describe('Feature Tree pane', () => {
highlightedCode: '', highlightedCode: '',
diagnostics: [], diagnostics: [],
activeLines: [ activeLines: [
`renamedExtrude = extrude(sketch001, length = ${newParameterName})`, `renamedExtrude = extrude(sketch001, length = ${newConstantName})`,
], ],
}) })
await editor.expectEditor.toContain(expectedCode, { await editor.expectEditor.toContain(expectedCode, {
shouldNormalise: true, shouldNormalise: true,
}) })
}) })
await test.step('Edit the parameter via the feature tree', async () => {
const parameter = await toolbar.getFeatureTreeOperation('Parameter', 0)
await parameter.dblclick()
await cmdBar.expectState({
commandName: 'Edit parameter',
currentArgKey: 'value',
currentArgValue: '23',
headerArguments: {
Name: newParameterName,
Value: '23',
},
stage: 'arguments',
highlightedHeaderArg: 'value',
})
await cmdBar.argumentInput
.locator('[contenteditable]')
.fill(editedParameterValue)
await cmdBar.progressCmdBar()
await cmdBar.expectState({
stage: 'review',
commandName: 'Edit parameter',
headerArguments: {
Name: newParameterName,
Value: '46', // Shows calculated result
},
})
await cmdBar.progressCmdBar()
await editor.expectEditor.toContain(editedParameterValue)
})
}) })
test(`User can edit an offset plane operation from the feature tree`, async ({ test(`User can edit an offset plane operation from the feature tree`, async ({
context, context,

View File

@ -118,11 +118,15 @@ export class CmdBarFixture {
return return
} }
const arrowButton = this.page.getByTestId('command-bar-continue') const arrowButton = this.page.getByRole('button', {
name: 'arrow right Continue',
})
if (await arrowButton.isVisible()) { if (await arrowButton.isVisible()) {
await this.continue() await arrowButton.click()
} else { } else {
await this.submit() await this.page
.getByRole('button', { name: 'checkmark Submit command' })
.click()
} }
} }
@ -187,13 +191,6 @@ export class CmdBarFixture {
return this.page.getByRole('option', options) return this.page.getByRole('option', options)
} }
/**
* Select an optional argument from the command bar during review
*/
clickOptionalArgument = async (argName: string) => {
await this.page.getByTestId(`cmd-bar-add-optional-arg-${argName}`).click()
}
/** /**
* Clicks the Create new variable button for kcl input * Clicks the Create new variable button for kcl input
*/ */

View File

@ -183,15 +183,14 @@ export class EditorFixture {
scrollToText(text: string, placeCursor?: boolean) { scrollToText(text: string, placeCursor?: boolean) {
return this.page.evaluate( return this.page.evaluate(
(args: { text: string; placeCursor?: boolean }) => { (args: { text: string; placeCursor?: boolean }) => {
const editorView = window.editorManager.getEditorView()
// error TS2339: Property 'docView' does not exist on type 'EditorView'. // error TS2339: Property 'docView' does not exist on type 'EditorView'.
// Except it does so :shrug: // Except it does so :shrug:
// @ts-ignore // @ts-ignore
const index = editorView?.docView.view.state.doc let index = window.editorManager._editorView?.docView.view.state.doc
.toString() .toString()
.indexOf(args.text) .indexOf(args.text)
editorView?.focus() window.editorManager._editorView?.focus()
editorView?.dispatch({ window.editorManager._editorView?.dispatch({
selection: window.EditorSelection.create([ selection: window.EditorSelection.create([
window.EditorSelection.cursor(index), window.EditorSelection.cursor(index),
]), ]),

View File

@ -274,13 +274,6 @@ export class ToolbarFixture {
.nth(operationIndex) .nth(operationIndex)
} }
getDefaultPlaneVisibilityButton(plane: 'XY' | 'XZ' | 'YZ' = 'XY') {
const index = plane === 'XZ' ? 0 : plane === 'XY' ? 1 : 2
return this.featureTreePane
.getByTestId('feature-tree-visibility-toggle')
.nth(index)
}
/** /**
* View source on a specific operation in the Feature Tree pane. * View source on a specific operation in the Feature Tree pane.
* @param operationName The name of the operation type * @param operationName The name of the operation type

View File

@ -5,7 +5,7 @@ import type {
FullResult, FullResult,
} from '@playwright/test/reporter' } from '@playwright/test/reporter'
class APIReporter implements Reporter { class MyAPIReporter implements Reporter {
private pendingRequests: Promise<void>[] = [] private pendingRequests: Promise<void>[] = []
private allResults: Record<string, any>[] = [] private allResults: Record<string, any>[] = []
private blockingResults: Record<string, any>[] = [] private blockingResults: Record<string, any>[] = []
@ -32,7 +32,7 @@ class APIReporter implements Reporter {
'X-API-Key': process.env.TAB_API_KEY || '', 'X-API-Key': process.env.TAB_API_KEY || '',
}), }),
body: JSON.stringify({ body: JSON.stringify({
project: `${process.env.GITHUB_SERVER_URL}/${process.env.GITHUB_REPOSITORY}`, project: 'https://github.com/KittyCAD/modeling-app',
branch: branch:
process.env.GITHUB_HEAD_REF || process.env.GITHUB_REF_NAME || '', process.env.GITHUB_HEAD_REF || process.env.GITHUB_REF_NAME || '',
commit: process.env.CI_COMMIT_SHA || process.env.GITHUB_SHA || '', commit: process.env.CI_COMMIT_SHA || process.env.GITHUB_SHA || '',
@ -60,7 +60,7 @@ class APIReporter implements Reporter {
const payload = { const payload = {
// Required information // Required information
project: `${process.env.GITHUB_SERVER_URL}/${process.env.GITHUB_REPOSITORY}`, project: 'https://github.com/KittyCAD/modeling-app',
suite: process.env.CI_SUITE || 'e2e', suite: process.env.CI_SUITE || 'e2e',
branch: process.env.GITHUB_HEAD_REF || process.env.GITHUB_REF_NAME || '', branch: process.env.GITHUB_HEAD_REF || process.env.GITHUB_REF_NAME || '',
commit: process.env.CI_COMMIT_SHA || process.env.GITHUB_SHA || '', commit: process.env.CI_COMMIT_SHA || process.env.GITHUB_SHA || '',
@ -124,4 +124,4 @@ class APIReporter implements Reporter {
} }
} }
export default APIReporter export default MyAPIReporter

File diff suppressed because it is too large Load Diff

View File

@ -475,7 +475,6 @@ test.describe('Can export from electron app', () => {
}, },
tronApp.projectDirName, tronApp.projectDirName,
page, page,
cmdBar,
method method
) )
) )
@ -780,6 +779,9 @@ test.describe(`Project management commands`, () => {
const commandContinueButton = page.getByRole('button', { const commandContinueButton = page.getByRole('button', {
name: 'Continue', name: 'Continue',
}) })
const commandSubmitButton = page.getByRole('button', {
name: 'Submit command',
})
const toastMessage = page.getByText(`Successfully renamed`) const toastMessage = page.getByText(`Successfully renamed`)
await test.step(`Setup`, async () => { await test.step(`Setup`, async () => {
@ -798,7 +800,8 @@ test.describe(`Project management commands`, () => {
await expect(commandContinueButton).toBeVisible() await expect(commandContinueButton).toBeVisible()
await commandContinueButton.click() await commandContinueButton.click()
await cmdBar.submit() await expect(commandSubmitButton).toBeVisible()
await commandSubmitButton.click()
await expect(toastMessage).toBeVisible() await expect(toastMessage).toBeVisible()
}) })
@ -834,6 +837,9 @@ test.describe(`Project management commands`, () => {
}) })
const projectNameOption = page.getByRole('option', { name: projectName }) const projectNameOption = page.getByRole('option', { name: projectName })
const commandWarning = page.getByText('Are you sure you want to delete?') const commandWarning = page.getByText('Are you sure you want to delete?')
const commandSubmitButton = page.getByRole('button', {
name: 'Submit command',
})
const toastMessage = page.getByText(`Successfully deleted`) const toastMessage = page.getByText(`Successfully deleted`)
const noProjectsMessage = page.getByText('No projects found') const noProjectsMessage = page.getByText('No projects found')
@ -853,7 +859,8 @@ test.describe(`Project management commands`, () => {
await projectNameOption.click() await projectNameOption.click()
await expect(commandWarning).toBeVisible() await expect(commandWarning).toBeVisible()
await cmdBar.submit() await expect(commandSubmitButton).toBeVisible()
await commandSubmitButton.click()
await expect(toastMessage).toBeVisible() await expect(toastMessage).toBeVisible()
}) })
@ -887,6 +894,9 @@ test.describe(`Project management commands`, () => {
const commandContinueButton = page.getByRole('button', { const commandContinueButton = page.getByRole('button', {
name: 'Continue', name: 'Continue',
}) })
const commandSubmitButton = page.getByRole('button', {
name: 'Submit command',
})
const toastMessage = page.getByText(`Successfully renamed`) const toastMessage = page.getByText(`Successfully renamed`)
await test.step(`Setup`, async () => { await test.step(`Setup`, async () => {
@ -904,7 +914,8 @@ test.describe(`Project management commands`, () => {
await expect(commandContinueButton).toBeVisible() await expect(commandContinueButton).toBeVisible()
await commandContinueButton.click() await commandContinueButton.click()
await cmdBar.submit() await expect(commandSubmitButton).toBeVisible()
await commandSubmitButton.click()
await expect(toastMessage).toBeVisible() await expect(toastMessage).toBeVisible()
}) })
@ -938,6 +949,9 @@ test.describe(`Project management commands`, () => {
}) })
const projectNameOption = page.getByRole('option', { name: projectName }) const projectNameOption = page.getByRole('option', { name: projectName })
const commandWarning = page.getByText('Are you sure you want to delete?') const commandWarning = page.getByText('Are you sure you want to delete?')
const commandSubmitButton = page.getByRole('button', {
name: 'Submit command',
})
const toastMessage = page.getByText(`Successfully deleted`) const toastMessage = page.getByText(`Successfully deleted`)
const noProjectsMessage = page.getByText('No projects found') const noProjectsMessage = page.getByText('No projects found')
@ -953,7 +967,8 @@ test.describe(`Project management commands`, () => {
await projectNameOption.click() await projectNameOption.click()
await expect(commandWarning).toBeVisible() await expect(commandWarning).toBeVisible()
await cmdBar.submit() await expect(commandSubmitButton).toBeVisible()
await commandSubmitButton.click()
await expect(toastMessage).toBeVisible() await expect(toastMessage).toBeVisible()
}) })

View File

@ -1,7 +1,6 @@
import path from 'path' import path from 'path'
import { bracket } from '@e2e/playwright/fixtures/bracket' import { bracket } from '@e2e/playwright/fixtures/bracket'
import type { Page } from '@playwright/test' import type { Page } from '@playwright/test'
import type { CmdBarFixture } from '@e2e/playwright/fixtures/cmdBarFixture'
import { reportRejection } from '@src/lib/trap' import { reportRejection } from '@src/lib/trap'
import * as fsp from 'fs/promises' import * as fsp from 'fs/promises'
@ -422,7 +421,10 @@ extrude002 = extrude(profile002, length = 150)
await page.keyboard.press('Enter') await page.keyboard.press('Enter')
// Click the checkbox // Click the checkbox
await cmdBar.submit() const submitButton = page.getByText('Confirm Export')
await expect(submitButton).toBeVisible()
await page.keyboard.press('Enter')
// Find the toast. // Find the toast.
// Look out for the toast message // Look out for the toast message
@ -459,7 +461,8 @@ extrude002 = extrude(profile002, length = 150)
await page.keyboard.press('Enter') await page.keyboard.press('Enter')
// Click the checkbox // Click the checkbox
await cmdBar.submit() await expect(submitButton).toBeVisible()
await page.keyboard.press('Enter')
// Find the toast. // Find the toast.
// Look out for the toast message // Look out for the toast message
@ -479,7 +482,6 @@ extrude002 = extrude(profile002, length = 150)
test('ensure you CAN export while an export is already going', async ({ test('ensure you CAN export while an export is already going', async ({
page, page,
homePage, homePage,
cmdBar,
}) => { }) => {
const u = await getUtils(page) const u = await getUtils(page)
await test.step('Set up the code and durations', async () => { await test.step('Set up the code and durations', async () => {
@ -514,11 +516,11 @@ extrude002 = extrude(profile002, length = 150)
const successToastMessage = page.getByText(`Exported successfully`) const successToastMessage = page.getByText(`Exported successfully`)
await test.step('second export', async () => { await test.step('second export', async () => {
await clickExportButton(page, cmdBar) await clickExportButton(page)
await expect(exportingToastMessage).toBeVisible() await expect(exportingToastMessage).toBeVisible()
await clickExportButton(page, cmdBar) await clickExportButton(page)
await test.step('The first export still succeeds', async () => { await test.step('The first export still succeeds', async () => {
await Promise.all([ await Promise.all([
@ -535,7 +537,7 @@ extrude002 = extrude(profile002, length = 150)
await test.step('Successful, unblocked export', async () => { await test.step('Successful, unblocked export', async () => {
// Try exporting again. // Try exporting again.
await clickExportButton(page, cmdBar) await clickExportButton(page)
// Find the toast. // Find the toast.
// Look out for the toast message // Look out for the toast message
@ -878,7 +880,7 @@ s2 = startSketchOn(XY)
}) })
}) })
async function clickExportButton(page: Page, cmdBar: CmdBarFixture) { async function clickExportButton(page: Page) {
await test.step('Running export flow', async () => { await test.step('Running export flow', async () => {
// export the model // export the model
const exportButton = page.getByTestId('export-pane-button') const exportButton = page.getByTestId('export-pane-button')
@ -894,6 +896,9 @@ async function clickExportButton(page: Page, cmdBar: CmdBarFixture) {
await page.keyboard.press('Enter') await page.keyboard.press('Enter')
// Click the checkbox // Click the checkbox
await cmdBar.submit() const submitButton = page.getByText('Confirm Export')
await expect(submitButton).toBeVisible()
await page.keyboard.press('Enter')
}) })
} }

View File

@ -1478,7 +1478,6 @@ sketch001 = startSketchOn(XZ)
await page.mouse.move(1200, 139) await page.mouse.move(1200, 139)
await page.mouse.down() await page.mouse.down()
await page.mouse.move(870, 250) await page.mouse.move(870, 250)
await page.mouse.up()
await page.waitForTimeout(200) await page.waitForTimeout(200)
@ -1488,60 +1487,6 @@ sketch001 = startSketchOn(XZ)
) )
}) })
test('Can undo with closed code pane', async ({
page,
homePage,
editor,
toolbar,
scene,
cmdBar,
}) => {
const u = await getUtils(page)
const viewportSize = { width: 1500, height: 750 }
await page.setBodyDimensions(viewportSize)
await page.addInitScript(async () => {
localStorage.setItem(
'persistCode',
`@settings(defaultLengthUnit=in)
sketch001 = startSketchOn(XZ)
|> startProfile(at = [-10, -10])
|> line(end = [20.0, 10.0])
|> tangentialArc(end = [5.49, 8.37])`
)
})
await homePage.goToModelingScene()
await toolbar.waitForFeatureTreeToBeBuilt()
await scene.settled(cmdBar)
await (await toolbar.getFeatureTreeOperation('Sketch', 0)).dblclick()
await page.waitForTimeout(1000)
await page.mouse.move(1200, 139)
await page.mouse.down()
await page.mouse.move(870, 250)
await page.mouse.up()
await editor.expectEditor.toContain(`tangentialArc(end=[-5.85,4.32])`, {
shouldNormalise: true,
})
await u.closeKclCodePanel()
// Undo the last change
await page.keyboard.down('Control')
await page.keyboard.press('KeyZ')
await page.keyboard.up('Control')
await u.openKclCodePanel()
await editor.expectEditor.toContain(`tangentialArc(end = [5.49, 8.37])`, {
shouldNormalise: true,
})
})
test('Can delete a single segment line with keyboard', async ({ test('Can delete a single segment line with keyboard', async ({
page, page,
scene, scene,
@ -2155,8 +2100,8 @@ profile003 = startProfile(sketch001, at = [206.63, -56.73])
) )
await crnRect1point2() await crnRect1point2()
await editor.expectEditor.toContain( await editor.expectEditor.toContain(
`|> angledLine(angle = 0, length = 2.37, tag = $rectangleSegmentA001) `|> angledLine(angle = 0deg, length = 2.37, tag = $rectangleSegmentA001)
|> angledLine(angle = segAng(rectangleSegmentA001) - 90, length = 7.8) |> angledLine(angle = segAng(rectangleSegmentA001) - 90deg, length = 7.8)
|> angledLine(angle = segAng(rectangleSegmentA001), length = -segLen(rectangleSegmentA001)) |> angledLine(angle = segAng(rectangleSegmentA001), length = -segLen(rectangleSegmentA001))
|> line(endAbsolute = [profileStartX(%), profileStartY(%)]) |> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|> close()`.replaceAll('\n', '') |> close()`.replaceAll('\n', '')
@ -2170,8 +2115,8 @@ profile003 = startProfile(sketch001, at = [206.63, -56.73])
await crnRect2point2() await crnRect2point2()
await page.waitForTimeout(300) await page.waitForTimeout(300)
await editor.expectEditor.toContain( await editor.expectEditor.toContain(
`|> angledLine(angle = 0, length = 5.49, tag = $rectangleSegmentA002) `|> angledLine(angle = 0deg, length = 5.49, tag = $rectangleSegmentA002)
|> angledLine(angle = segAng(rectangleSegmentA002) - 90, length = 4.14) |> angledLine(angle = segAng(rectangleSegmentA002) - 90deg, length = 4.14)
|> angledLine(angle = segAng(rectangleSegmentA002), length = -segLen(rectangleSegmentA002)) |> angledLine(angle = segAng(rectangleSegmentA002), length = -segLen(rectangleSegmentA002))
|> line(endAbsolute = [profileStartX(%), profileStartY(%)]) |> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|> close()`.replaceAll('\n', '') |> close()`.replaceAll('\n', '')
@ -2189,8 +2134,8 @@ profile003 = startProfile(sketch001, at = [206.63, -56.73])
await cntrRect1point2() await cntrRect1point2()
await page.waitForTimeout(300) await page.waitForTimeout(300)
await editor.expectEditor.toContain( await editor.expectEditor.toContain(
`|> angledLine(angle = 0, length = 7.06, tag = $rectangleSegmentA003) `|> angledLine(angle = 0deg, length = 7.06, tag = $rectangleSegmentA003)
|> angledLine(angle = segAng(rectangleSegmentA003) + 90, length = 4.34) |> angledLine(angle = segAng(rectangleSegmentA003) + 90deg, length = 4.34)
|> angledLine(angle = segAng(rectangleSegmentA003), length = -segLen(rectangleSegmentA003)) |> angledLine(angle = segAng(rectangleSegmentA003), length = -segLen(rectangleSegmentA003))
|> line(endAbsolute = [profileStartX(%), profileStartY(%)]) |> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|> close()`.replaceAll('\n', '') |> close()`.replaceAll('\n', '')
@ -2205,8 +2150,8 @@ profile003 = startProfile(sketch001, at = [206.63, -56.73])
await cntrRect2point2() await cntrRect2point2()
await page.waitForTimeout(300) await page.waitForTimeout(300)
await editor.expectEditor.toContain( await editor.expectEditor.toContain(
`|> angledLine(angle = 0, length = 3.12, tag = $rectangleSegmentA004) `|> angledLine(angle = 0deg, length = 3.12, tag = $rectangleSegmentA004)
|> angledLine(angle = segAng(rectangleSegmentA004) + 90, length = 6.24) |> angledLine(angle = segAng(rectangleSegmentA004) + 90deg, length = 6.24)
|> angledLine(angle = segAng(rectangleSegmentA004), length = -segLen(rectangleSegmentA004)) |> angledLine(angle = segAng(rectangleSegmentA004), length = -segLen(rectangleSegmentA004))
|> line(endAbsolute = [profileStartX(%), profileStartY(%)]) |> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|> close()`.replaceAll('\n', '') |> close()`.replaceAll('\n', '')
@ -2355,8 +2300,8 @@ profile001 = startProfile(sketch001, at = [6.24, 4.54])
|> line(end = [8.61, 0.74]) |> line(end = [8.61, 0.74])
|> line(end = [10.99, -5.22]) |> line(end = [10.99, -5.22])
profile002 = startProfile(sketch001, at = [11.19, 5.02]) profile002 = startProfile(sketch001, at = [11.19, 5.02])
|> angledLine(angle = 0, length = 10.78, tag = $rectangleSegmentA001) |> angledLine(angle = 0deg, length = 10.78, tag = $rectangleSegmentA001)
|> angledLine(angle = segAng(rectangleSegmentA001) - 90, length = 4.14) |> angledLine(angle = segAng(rectangleSegmentA001) - 90deg, length = 4.14)
|> angledLine(angle = segAng(rectangleSegmentA001), length = -segLen(rectangleSegmentA001)) |> angledLine(angle = segAng(rectangleSegmentA001), length = -segLen(rectangleSegmentA001))
|> line(endAbsolute = [profileStartX(%), profileStartY(%)]) |> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|> close() |> close()
@ -2428,7 +2373,7 @@ profile004 = circleThreePoint(sketch001, p1 = [13.44, -6.8], p2 = [13.39, -2.07]
await page.mouse.up() await page.mouse.up()
await page.waitForTimeout(200) await page.waitForTimeout(200)
await editor.expectEditor.toContain( await editor.expectEditor.toContain(
`angledLine(angle = -7, length = 10.27, tag = $rectangleSegmentA001)` `angledLine(angle = -7deg, length = 10.27, tag = $rectangleSegmentA001)`
) )
}) })
@ -2470,8 +2415,8 @@ profile004 = circleThreePoint(sketch001, p1 = [13.44, -6.8], p2 = [13.39, -2.07]
await page.waitForTimeout(100) await page.waitForTimeout(100)
await rectEnd() await rectEnd()
await editor.expectEditor.toContain( await editor.expectEditor.toContain(
`|> angledLine(angle = 180, length = 1.97, tag = $rectangleSegmentA002) `|> angledLine(angle = 180deg, length = 1.97, tag = $rectangleSegmentA002)
|> angledLine(angle = segAng(rectangleSegmentA002) + 90, length = 3.89) |> angledLine(angle = segAng(rectangleSegmentA002) + 90deg, length = 3.89)
|> angledLine(angle = segAng(rectangleSegmentA002), length = -segLen(rectangleSegmentA002)) |> angledLine(angle = segAng(rectangleSegmentA002), length = -segLen(rectangleSegmentA002))
|> line(endAbsolute = [profileStartX(%), profileStartY(%)]) |> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|> close()`.replaceAll('\n', '') |> close()`.replaceAll('\n', '')
@ -2496,8 +2441,8 @@ profile001 = startProfile(sketch001, at = [6.24, 4.54])
|> line(end = [8.61, 0.74]) |> line(end = [8.61, 0.74])
|> line(end = [10.99, -5.22]) |> line(end = [10.99, -5.22])
profile002 = startProfile(sketch001, at = [11.19, 5.02]) profile002 = startProfile(sketch001, at = [11.19, 5.02])
|> angledLine(angle = 0, length = 10.78, tag = $rectangleSegmentA001) |> angledLine(angle = 0deg, length = 10.78, tag = $rectangleSegmentA001)
|> angledLine(angle = segAng(rectangleSegmentA001) - 90, length = 4.14) |> angledLine(angle = segAng(rectangleSegmentA001) - 90deg, length = 4.14)
|> angledLine(angle = segAng(rectangleSegmentA001), length = -segLen(rectangleSegmentA001)) |> angledLine(angle = segAng(rectangleSegmentA001), length = -segLen(rectangleSegmentA001))
|> line(endAbsolute = [profileStartX(%), profileStartY(%)]) |> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|> close() |> close()
@ -2826,8 +2771,8 @@ profile002 = startProfile(sketch002, at = [0.75, 13.46])
|> line(end = [4.52, 3.79]) |> line(end = [4.52, 3.79])
|> line(end = [5.98, -2.81]) |> line(end = [5.98, -2.81])
profile003 = startProfile(sketch002, at = [3.19, 13.3]) profile003 = startProfile(sketch002, at = [3.19, 13.3])
|> angledLine(angle = 0, length = 6.64, tag = $rectangleSegmentA001) |> angledLine(angle = 0deg, length = 6.64, tag = $rectangleSegmentA001)
|> angledLine(angle = segAng(rectangleSegmentA001) - 90, length = 2.81) |> angledLine(angle = segAng(rectangleSegmentA001) - 90deg, length = 2.81)
|> angledLine(angle = segAng(rectangleSegmentA001), length = -segLen(rectangleSegmentA001)) |> angledLine(angle = segAng(rectangleSegmentA001), length = -segLen(rectangleSegmentA001))
|> line(endAbsolute = [profileStartX(%), profileStartY(%)]) |> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|> close() |> close()
@ -2844,7 +2789,7 @@ profile006 = startProfile(sketch002, at = [9.65, 3.82])
|> close() |> close()
revolve001 = revolve( revolve001 = revolve(
profile004, profile004,
angle = 45, angle = 45deg,
axis = getNextAdjacentEdge(seg01) axis = getNextAdjacentEdge(seg01)
) )
extrude002 = extrude(profile006, length = 4) extrude002 = extrude(profile006, length = 4)
@ -2868,8 +2813,8 @@ profile010 = circle(
radius = 2.67 radius = 2.67
) )
profile011 = startProfile(sketch003, at = [5.07, -6.39]) profile011 = startProfile(sketch003, at = [5.07, -6.39])
|> angledLine(angle = 0, length = 4.54, tag = $rectangleSegmentA002) |> angledLine(angle = 0deg, length = 4.54, tag = $rectangleSegmentA002)
|> angledLine(angle = segAng(rectangleSegmentA002) - 90, length = 4.17) |> angledLine(angle = segAng(rectangleSegmentA002) - 90deg, length = 4.17)
|> angledLine(angle = segAng(rectangleSegmentA002), length = -segLen(rectangleSegmentA002)) |> angledLine(angle = segAng(rectangleSegmentA002), length = -segLen(rectangleSegmentA002))
|> line(endAbsolute = [profileStartX(%), profileStartY(%)]) |> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|> close() |> close()
@ -3021,7 +2966,7 @@ loft([profile001, profile002])
) )
await rect1Crn2() await rect1Crn2()
await editor.expectEditor.toContain( await editor.expectEditor.toContain(
`angledLine(angle = 0, length = 113.01, tag = $rectangleSegmentA001)` `angledLine(angle = 0deg, length = 113.01, tag = $rectangleSegmentA001)`
) )
}) })
test('Can enter sketch loft edges offsetPlane and continue sketch', async ({ test('Can enter sketch loft edges offsetPlane and continue sketch', async ({
@ -3090,7 +3035,7 @@ loft([profile001, profile002])
) )
await rect1Crn2() await rect1Crn2()
await editor.expectEditor.toContain( await editor.expectEditor.toContain(
`angledLine(angle = 0, length = 106.42], tag = $rectangleSegmentA001)` `angledLine(angle = 0deg, length = 106.42], tag = $rectangleSegmentA001)`
) )
await page.waitForTimeout(100) await page.waitForTimeout(100)
}) })
@ -3312,8 +3257,8 @@ test.describe('manual edits during sketch mode', () => {
extrude001 = extrude(profile001, length = 500) extrude001 = extrude(profile001, length = 500)
sketch002 = startSketchOn(extrude001, face = seg01) sketch002 = startSketchOn(extrude001, face = seg01)
profile002 = startProfile(sketch002, at = [83.39, 329.15]) profile002 = startProfile(sketch002, at = [83.39, 329.15])
|> angledLine(angle = 0, length = 119.61, tag = $rectangleSegmentA001) |> angledLine(angle = 0deg, length = 119.61, tag = $rectangleSegmentA001)
|> angledLine(length = 156.54, angle = -28) |> angledLine(length = 156.54, angle = -28deg)
|> angledLine( |> angledLine(
angle = -151, angle = -151,
length = 116.27, length = 116.27,
@ -3351,7 +3296,9 @@ test.describe('manual edits during sketch mode', () => {
await expect await expect
.poll( .poll(
async () => { async () => {
await editor.expectEditor.toContain('length = 156.54, angle = -28') await editor.expectEditor.toContain(
'length = 156.54, angle = -28deg'
)
await page.mouse.move(handle1Location.x, handle1Location.y) await page.mouse.move(handle1Location.x, handle1Location.y)
await page.mouse.down() await page.mouse.down()
await page.mouse.move( await page.mouse.move(
@ -3362,7 +3309,9 @@ test.describe('manual edits during sketch mode', () => {
} }
) )
await page.mouse.up() await page.mouse.up()
await editor.expectEditor.toContain('length = 231.59, angle = -34') await editor.expectEditor.toContain(
'length = 231.59, angle = -34deg'
)
return true return true
}, },
{ timeout: 10_000 } { timeout: 10_000 }
@ -3380,7 +3329,7 @@ test.describe('manual edits during sketch mode', () => {
const handle2Location = { x: 872, y: 273 } const handle2Location = { x: 872, y: 273 }
await test.step('Edit sketch again', async () => { await test.step('Edit sketch again', async () => {
await editor.expectEditor.toContain('length = 231.59, angle = -34') await editor.expectEditor.toContain('length = 231.59, angle = -34deg')
await page.waitForTimeout(500) await page.waitForTimeout(500)
await expect await expect
.poll( .poll(
@ -3391,7 +3340,9 @@ test.describe('manual edits during sketch mode', () => {
steps: 5, steps: 5,
}) })
await page.mouse.up() await page.mouse.up()
await editor.expectEditor.toContain('length = 167.36, angle = -14') await editor.expectEditor.toContain(
'length = 167.36, angle = -14deg'
)
return true return true
}, },
{ timeout: 10_000 } { timeout: 10_000 }
@ -3416,14 +3367,18 @@ test.describe('manual edits during sketch mode', () => {
await expect await expect
.poll( .poll(
async () => { async () => {
await editor.expectEditor.toContain('length = 167.36, angle = -14') await editor.expectEditor.toContain(
'length = 167.36, angle = -14deg'
)
await page.mouse.move(handle3Location.x, handle3Location.y) await page.mouse.move(handle3Location.x, handle3Location.y)
await page.mouse.down() await page.mouse.down()
await page.mouse.move(handle3Location.x, handle3Location.y + 110, { await page.mouse.move(handle3Location.x, handle3Location.y + 110, {
steps: 5, steps: 5,
}) })
await page.mouse.up() await page.mouse.up()
await editor.expectEditor.toContain('length = 219.2, angle = -56') await editor.expectEditor.toContain(
'length = 219.2, angle = -56deg'
)
return true return true
}, },
{ timeout: 10_000 } { timeout: 10_000 }
@ -3448,9 +3403,9 @@ profile001 = startProfile(sketch001, at = [106.68, 89.77])
extrude001 = extrude(profile001, length = 500) extrude001 = extrude(profile001, length = 500)
sketch002 = startSketchOn(extrude001, face = seg01) sketch002 = startSketchOn(extrude001, face = seg01)
profile002 = startProfile(sketch002, at = [83.39, 329.15]) profile002 = startProfile(sketch002, at = [83.39, 329.15])
|> angledLine(angle = 0, length = 119.61, tag = $rectangleSegmentA001) |> angledLine(angle = 0deg, length = 119.61, tag = $rectangleSegmentA001)
|> angledLine(length = 219.2, angle = -56) |> angledLine(length = 219.2, angle = -56deg)
|> angledLine(angle = -151, length = 116.27) |> angledLine(angle = -151deg, length = 116.27)
|> line(endAbsolute = [profileStartX(%), profileStartY(%)]) |> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|> close() |> close()
profile003 = startProfile(sketch002, at = [-201.08, 254.17]) profile003 = startProfile(sketch002, at = [-201.08, 254.17])
@ -3487,10 +3442,10 @@ profile003 = startProfile(sketch002, at = [-201.08, 254.17])
extrude001 = extrude(profile001, length = 500) extrude001 = extrude(profile001, length = 500)
sketch002 = startSketchOn(extrude001, face = seg01) sketch002 = startSketchOn(extrude001, face = seg01)
profile002 = startProfile(sketch002, at = [83.39, 329.15]) profile002 = startProfile(sketch002, at = [83.39, 329.15])
|> angledLine(angle = 0, length = 119.61, tag = $rectangleSegmentA001) |> angledLine(angle = 0deg, length = 119.61, tag = $rectangleSegmentA001)
|> angledLine(length = 156.54, angle = -28) |> angledLine(length = 156.54, angle = -28deg)
|> angledLine( |> angledLine(
angle = -151, angle = -151deg,
length = 116.27, length = 116.27,
) )
|> line(endAbsolute = [profileStartX(%), profileStartY(%)]) |> line(endAbsolute = [profileStartX(%), profileStartY(%)])
@ -3551,10 +3506,10 @@ profile003 = startProfile(sketch002, at = [-201.08, 254.17])
extrude001 = extrude(profile001, length = 500) extrude001 = extrude(profile001, length = 500)
sketch002 = startSketchOn(extrude001, face = seg01) sketch002 = startSketchOn(extrude001, face = seg01)
profile002 = startProfile(sketch002, at = [83.39, 329.15]) profile002 = startProfile(sketch002, at = [83.39, 329.15])
|> angledLine(angle = 0, length = 119.61, tag = $rectangleSegmentA001) |> angledLine(angle = 0deg, length = 119.61, tag = $rectangleSegmentA001)
|> angledLine(length = 156.54, angle = -28) |> angledLine(length = 156.54, angle = -28deg)
|> angledLine( |> angledLine(
angle = -151, angle = -151deg,
length = 116.27, length = 116.27,
) )
|> line(endAbsolute = [profileStartX(%), profileStartY(%)]) |> line(endAbsolute = [profileStartX(%), profileStartY(%)])

View File

@ -845,14 +845,14 @@ test.describe('code color goober', { tag: '@snapshot' }, () => {
sweepPath = startSketchOn(XZ) sweepPath = startSketchOn(XZ)
|> startProfile(at = [0.05, 0.05]) |> startProfile(at = [0.05, 0.05])
|> line(end = [0, 7]) |> line(end = [0, 7])
|> tangentialArc(angle = 90, radius = 5) |> tangentialArc(angle = 90deg, radius = 5)
|> line(end = [-3, 0]) |> line(end = [-3, 0])
|> tangentialArc(angle = -90, radius = 5) |> tangentialArc(angle = -90deg, radius = 5)
|> line(end = [0, 7]) |> line(end = [0, 7])
sweepSketch = startSketchOn(XY) sweepSketch = startSketchOn(XY)
|> startProfile(at = [2, 0]) |> startProfile(at = [2, 0])
|> arc(angleStart = 0, angleEnd = 360, radius = 2) |> arc(angleStart = 0, angleEnd = 360deg, radius = 2)
|> sweep(path = sweepPath) |> sweep(path = sweepPath)
|> appearance( |> appearance(
color = "#bb00ff", color = "#bb00ff",
@ -889,14 +889,14 @@ sweepSketch = startSketchOn(XY)
sweepPath = startSketchOn(XZ) sweepPath = startSketchOn(XZ)
|> startProfile(at = [0.05, 0.05]) |> startProfile(at = [0.05, 0.05])
|> line(end = [0, 7]) |> line(end = [0, 7])
|> tangentialArc(angle = 90, radius = 5) |> tangentialArc(angle = 90deg, radius = 5)
|> line(end = [-3, 0]) |> line(end = [-3, 0])
|> tangentialArc(angle = -90, radius = 5) |> tangentialArc(angle = -90deg, radius = 5)
|> line(end = [0, 7]) |> line(end = [0, 7])
sweepSketch = startSketchOn(XY) sweepSketch = startSketchOn(XY)
|> startProfile(at = [2, 0]) |> startProfile(at = [2, 0])
|> arc(angleStart = 0, angleEnd = 360, radius = 2) |> arc(angleStart = 0deg, angleEnd = 360deg, radius = 2)
|> sweep(path = sweepPath) |> sweep(path = sweepPath)
|> appearance( |> appearance(
color = '#bb00ff', color = '#bb00ff',
@ -934,14 +934,14 @@ sweepSketch = startSketchOn(XY)
sweepPath = startSketchOn(XZ) sweepPath = startSketchOn(XZ)
|> startProfile(at = [0.05, 0.05]) |> startProfile(at = [0.05, 0.05])
|> line(end = [0, 7]) |> line(end = [0, 7])
|> tangentialArc(angle = 90, radius = 5) |> tangentialArc(angle = 90deg, radius = 5)
|> line(end = [-3, 0]) |> line(end = [-3, 0])
|> tangentialArc(angle = -90, radius = 5) |> tangentialArc(angle = -90deg, radius = 5)
|> line(end = [0, 7]) |> line(end = [0, 7])
sweepSketch = startSketchOn(XY) sweepSketch = startSketchOn(XY)
|> startProfile(at = [2, 0]) |> startProfile(at = [2, 0])
|> arc(angleStart = 0, angleEnd = 360, radius = 2) |> arc(angleStart = 0deg, angleEnd = 360deg, radius = 2)
|> sweep(path = sweepPath) |> sweep(path = sweepPath)
|> appearance( |> appearance(
color = "#bb00ff", color = "#bb00ff",

Binary file not shown.

Before

Width:  |  Height:  |  Size: 56 KiB

After

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 51 KiB

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 58 KiB

After

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 51 KiB

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 50 KiB

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 48 KiB

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 48 KiB

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 52 KiB

After

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 58 KiB

After

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 51 KiB

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 49 KiB

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 75 KiB

After

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 48 KiB

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 61 KiB

After

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 135 KiB

After

Width:  |  Height:  |  Size: 135 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 118 KiB

After

Width:  |  Height:  |  Size: 119 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 135 KiB

After

Width:  |  Height:  |  Size: 135 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 68 KiB

After

Width:  |  Height:  |  Size: 67 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 71 KiB

After

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 67 KiB

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 69 KiB

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 69 KiB

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 65 KiB

After

Width:  |  Height:  |  Size: 64 KiB

View File

@ -12,7 +12,6 @@ export const TEST_SETTINGS: DeepPartial<Settings> = {
}, },
onboarding_status: 'dismissed', onboarding_status: 'dismissed',
show_debug_panel: true, show_debug_panel: true,
fixed_size_grid: false,
}, },
modeling: { modeling: {
enable_ssao: false, enable_ssao: false,

View File

@ -22,7 +22,6 @@ export const token = process.env.token || ''
import type { ProjectConfiguration } from '@rust/kcl-lib/bindings/ProjectConfiguration' import type { ProjectConfiguration } from '@rust/kcl-lib/bindings/ProjectConfiguration'
import type { ElectronZoo } from '@e2e/playwright/fixtures/fixtureSetup' import type { ElectronZoo } from '@e2e/playwright/fixtures/fixtureSetup'
import type { CmdBarFixture } from '@e2e/playwright/fixtures/cmdBarFixture'
import { isErrorWhitelisted } from '@e2e/playwright/lib/console-error-whitelist' import { isErrorWhitelisted } from '@e2e/playwright/lib/console-error-whitelist'
import { TEST_SETTINGS, TEST_SETTINGS_KEY } from '@e2e/playwright/storageStates' import { TEST_SETTINGS, TEST_SETTINGS_KEY } from '@e2e/playwright/storageStates'
import { test } from '@e2e/playwright/zoo-test' import { test } from '@e2e/playwright/zoo-test'
@ -159,10 +158,10 @@ async function openKclCodePanel(page: Page) {
await page.evaluate(() => { await page.evaluate(() => {
// editorManager is available on the window object. // editorManager is available on the window object.
//@ts-ignore this is in an entirely different context that tsc can't see. //@ts-ignore this is in an entirely different context that tsc can't see.
editorManager.getEditorView().dispatch({ editorManager._editorView.dispatch({
selection: { selection: {
//@ts-ignore this is in an entirely different context that tsc can't see. //@ts-ignore this is in an entirely different context that tsc can't see.
anchor: editorManager.getEditorView().docView.length, anchor: editorManager._editorView.docView.length,
}, },
scrollIntoView: true, scrollIntoView: true,
}) })
@ -738,7 +737,6 @@ export const doExport = async (
output: Models['OutputFormat3d_type'], output: Models['OutputFormat3d_type'],
rootDir: string, rootDir: string,
page: Page, page: Page,
cmdBar: CmdBarFixture,
exportFrom: 'dropdown' | 'sidebarButton' | 'commandBar' = 'dropdown' exportFrom: 'dropdown' | 'sidebarButton' | 'commandBar' = 'dropdown'
): Promise<Paths> => { ): Promise<Paths> => {
if (exportFrom === 'dropdown') { if (exportFrom === 'dropdown') {
@ -782,7 +780,9 @@ export const doExport = async (
.click() .click()
await page.locator('#arg-form').waitFor({ state: 'detached' }) await page.locator('#arg-form').waitFor({ state: 'detached' })
} }
await cmdBar.submit() await expect(page.getByText('Confirm Export')).toBeVisible()
await page.getByRole('button', { name: 'Submit command' }).click()
await expect(page.getByText('Exported successfully')).toBeVisible() await expect(page.getByText('Exported successfully')).toBeVisible()
@ -880,10 +880,6 @@ export async function setup(
}, },
...TEST_SETTINGS.project, ...TEST_SETTINGS.project,
onboarding_status: 'dismissed', onboarding_status: 'dismissed',
// Tests were written before this setting existed.
// It's true by default because it's a good user experience, but
// these tests require it to be false.
fixed_size_grid: false,
}, },
project: { project: {
...TEST_SETTINGS.project, ...TEST_SETTINGS.project,

View File

@ -10,7 +10,7 @@ import {
import { expect, test } from '@e2e/playwright/zoo-test' import { expect, test } from '@e2e/playwright/zoo-test'
test.describe('Testing constraints', () => { test.describe('Testing constraints', () => {
test('Can constrain line length', async ({ page, homePage, cmdBar }) => { test('Can constrain line length', async ({ page, homePage }) => {
await page.addInitScript(async () => { await page.addInitScript(async () => {
localStorage.setItem( localStorage.setItem(
'persistCode', 'persistCode',
@ -50,10 +50,14 @@ test.describe('Testing constraints', () => {
await page.waitForTimeout(100) await page.waitForTimeout(100)
await page.getByTestId('constraint-length').click() await page.getByTestId('constraint-length').click()
await page.getByTestId('cmd-bar-arg-value').getByRole('textbox').fill('20') await page.getByTestId('cmd-bar-arg-value').getByRole('textbox').fill('20')
await cmdBar.continue() await page
.getByRole('button', {
name: 'arrow right Continue',
})
.click()
await expect(page.locator('.cm-content')).toHaveText( await expect(page.locator('.cm-content')).toHaveText(
`length001 = 20sketch001 = startSketchOn(XY) |> startProfile(at = [-10, -10]) |> line(end = [20, 0]) |> angledLine(angle = 90, length = length001) |> xLine(length = -20)` `length001 = 20sketch001 = startSketchOn(XY) |> startProfile(at = [-10, -10]) |> line(end = [20, 0]) |> angledLine(angle = 90deg, length = length001) |> xLine(length = -20)`
) )
// Make sure we didn't pop out of sketch mode. // Make sure we didn't pop out of sketch mode.
@ -204,7 +208,9 @@ test.describe('Testing constraints', () => {
.click() .click()
// Wait for the codemod to take effect // Wait for the codemod to take effect
await expect(page.locator('.cm-content')).toContainText(`angle = -57,`) await expect(page.locator('.cm-content')).toContainText(
`angle = -57deg,`
)
await expect(page.locator('.cm-content')).toContainText( await expect(page.locator('.cm-content')).toContainText(
`offset = ${offset},` `offset = ${offset},`
) )
@ -215,7 +221,7 @@ test.describe('Testing constraints', () => {
`|> line(end = [74.36, 130.4], tag = $seg01)` `|> line(end = [74.36, 130.4], tag = $seg01)`
) )
await expect(activeLinesContent[1]).toHaveText( await expect(activeLinesContent[1]).toHaveText(
` |> angledLineThatIntersects(angle = -57, offset = ${offset}, intersectTag = seg01)` ` |> angledLineThatIntersects(angle = -57deg, offset = ${offset}, intersectTag = seg01)`
) )
// checking the count of the overlays is a good proxy check that the client sketch scene is in a good state // checking the count of the overlays is a good proxy check that the client sketch scene is in a good state
@ -466,7 +472,7 @@ test.describe('Testing constraints', () => {
testName: 'No variable', testName: 'No variable',
addVariable: false, addVariable: false,
axisSelect: false, axisSelect: false,
value: 'segAng(seg01) + 22.69', value: 'segAng(seg01) + 22.69deg',
}, },
{ {
testName: 'Add variable, selecting axis', testName: 'Add variable, selecting axis',
@ -478,7 +484,7 @@ test.describe('Testing constraints', () => {
testName: 'No variable, selecting axis', testName: 'No variable, selecting axis',
addVariable: false, addVariable: false,
axisSelect: true, axisSelect: true,
value: 'turns::QUARTER_TURN - 7', value: 'turns::QUARTER_TURN - 7deg',
}, },
] as const ] as const
for (const { testName, addVariable, value, axisSelect } of cases) { for (const { testName, addVariable, value, axisSelect } of cases) {
@ -581,7 +587,7 @@ test.describe('Testing constraints', () => {
testName: 'Angle - No variable', testName: 'Angle - No variable',
addVariable: false, addVariable: false,
constraint: 'angle', constraint: 'angle',
value: '83, 78.33', value: '83deg, 78.33',
}, },
] as const ] as const
for (const { testName, addVariable, value, constraint } of cases) { for (const { testName, addVariable, value, constraint } of cases) {
@ -653,13 +659,13 @@ test.describe('Testing constraints', () => {
testName: 'Length - Add variable', testName: 'Length - Add variable',
addVariable: true, addVariable: true,
constraint: 'length', constraint: 'length',
value: '83, length001', value: '83deg, length001',
}, },
{ {
testName: 'Length - No variable', testName: 'Length - No variable',
addVariable: false, addVariable: false,
constraint: 'length', constraint: 'length',
value: '83, 78.33', value: '83deg, 78.33',
}, },
] as const ] as const
for (const { testName, addVariable, value, constraint } of cases) { for (const { testName, addVariable, value, constraint } of cases) {
@ -677,6 +683,9 @@ test.describe('Testing constraints', () => {
.getByRole('textbox') .getByRole('textbox')
const cmdBarKclVariableNameInput = const cmdBarKclVariableNameInput =
page.getByPlaceholder('Variable name') page.getByPlaceholder('Variable name')
const cmdBarSubmitButton = page.getByRole('button', {
name: 'arrow right Continue',
})
await page.addInitScript(async () => { await page.addInitScript(async () => {
localStorage.setItem( localStorage.setItem(
@ -729,7 +738,7 @@ part002 = startSketchOn(XZ)
await page.waitForTimeout(500) await page.waitForTimeout(500)
const [ang, len] = value.split(', ') const [ang, len] = value.split(', ')
const changedCode = `|> angledLine(angle = ${ang}, length = ${len})` const changedCode = `|> angledLine(angle = ${ang}, length = ${len})`
await cmdBar.continue() await cmdBarSubmitButton.click()
await expect(page.locator('.cm-content')).toContainText(changedCode) await expect(page.locator('.cm-content')).toContainText(changedCode)
// checking active assures the cursor is where it should be // checking active assures the cursor is where it should be
@ -845,7 +854,7 @@ part002 = startSketchOn(XZ)
test.describe('Two segment - no modal constraints', () => { test.describe('Two segment - no modal constraints', () => {
const cases = [ const cases = [
{ {
codeAfter: `|> angledLine(angle = 83, length = segLen(seg01))`, codeAfter: `|> angledLine(angle = 83deg, length = segLen(seg01))`,
constraintName: 'Equal Length', constraintName: 'Equal Length',
}, },
{ {
@ -1094,7 +1103,11 @@ part002 = startSketchOn(XZ)
await page.waitForTimeout(500) await page.waitForTimeout(500)
await page.getByTestId('cmd-bar-arg-value').getByRole('textbox').fill('10') await page.getByTestId('cmd-bar-arg-value').getByRole('textbox').fill('10')
await cmdBar.continue() await page
.getByRole('button', {
name: 'arrow right Continue',
})
.click()
await pollEditorLinesSelectedLength(page, 1) await pollEditorLinesSelectedLength(page, 1)
activeLinesContent = await page.locator('.cm-activeLine').all() activeLinesContent = await page.locator('.cm-activeLine').all()
@ -1163,7 +1176,7 @@ test.describe('Electron constraint tests', () => {
await cmdBar.progressCmdBar() await cmdBar.progressCmdBar()
await editor.expectEditor.toContain('length001 = 15.3') await editor.expectEditor.toContain('length001 = 15.3')
await editor.expectEditor.toContain( await editor.expectEditor.toContain(
'|> angledLine(angle = 9, length = length001)' '|> angledLine(angle = 9deg, length = length001)'
) )
}) })

View File

@ -21,7 +21,7 @@ test.describe('Testing loading external models', () => {
// We have no more web tests // We have no more web tests
test.fail( test.fail(
'Web: should overwrite current code, cannot create new file', 'Web: should overwrite current code, cannot create new file',
async ({ editor, context, page, homePage, cmdBar }) => { async ({ editor, context, page, homePage }) => {
const u = await getUtils(page) const u = await getUtils(page)
await test.step(`Test setup`, async () => { await test.step(`Test setup`, async () => {
await context.addInitScript((code) => { await context.addInitScript((code) => {
@ -52,6 +52,9 @@ test.describe('Testing loading external models', () => {
name, name,
}) })
const warningText = page.getByText('Overwrite current file with sample?') const warningText = page.getByText('Overwrite current file with sample?')
const confirmButton = page.getByRole('button', {
name: 'Submit command',
})
await test.step(`Precondition: check the initial code`, async () => { await test.step(`Precondition: check the initial code`, async () => {
await u.openKclCodePanel() await u.openKclCodePanel()
@ -67,7 +70,7 @@ test.describe('Testing loading external models', () => {
await expect(commandMethodOption('Create new file')).not.toBeVisible() await expect(commandMethodOption('Create new file')).not.toBeVisible()
await commandMethodOption('Overwrite').click() await commandMethodOption('Overwrite').click()
await expect(warningText).toBeVisible() await expect(warningText).toBeVisible()
await cmdBar.submit() await confirmButton.click()
await editor.expectEditor.toContain('// ' + newSample.title) await editor.expectEditor.toContain('// ' + newSample.title)
}) })

View File

@ -3,7 +3,6 @@ import type { LineInputsType } from '@src/lang/std/sketchcombos'
import { uuidv4 } from '@src/lib/utils' import { uuidv4 } from '@src/lib/utils'
import type { EditorFixture } from '@e2e/playwright/fixtures/editorFixture' import type { EditorFixture } from '@e2e/playwright/fixtures/editorFixture'
import type { CmdBarFixture } from '@e2e/playwright/fixtures/cmdBarFixture'
import { deg, getUtils, wiggleMove } from '@e2e/playwright/test-utils' import { deg, getUtils, wiggleMove } from '@e2e/playwright/test-utils'
import { expect, test } from '@e2e/playwright/zoo-test' import { expect, test } from '@e2e/playwright/zoo-test'
@ -19,7 +18,7 @@ test.describe('Testing segment overlays', () => {
* @param {number} options.steps - The number of steps to perform * @param {number} options.steps - The number of steps to perform
*/ */
const _clickConstrained = const _clickConstrained =
(page: Page, editor: EditorFixture, cmdBar: CmdBarFixture) => (page: Page, editor: EditorFixture) =>
async ({ async ({
hoverPos, hoverPos,
constraintType, constraintType,
@ -94,7 +93,11 @@ test.describe('Testing segment overlays', () => {
page.getByTestId('cmd-bar-arg-value').getByRole('textbox') page.getByTestId('cmd-bar-arg-value').getByRole('textbox')
).toBeFocused() ).toBeFocused()
await page.waitForTimeout(500) await page.waitForTimeout(500)
await cmdBar.continue() await page
.getByRole('button', {
name: 'arrow right Continue',
})
.click()
await editor.expectEditor.toContain(expectFinal, { await editor.expectEditor.toContain(expectFinal, {
shouldNormalise: true, shouldNormalise: true,
}) })
@ -110,7 +113,7 @@ test.describe('Testing segment overlays', () => {
* @param {number} options.steps - The number of steps to perform * @param {number} options.steps - The number of steps to perform
*/ */
const _clickUnconstrained = const _clickUnconstrained =
(page: Page, editor: EditorFixture, cmdBar: CmdBarFixture) => (page: Page, editor: EditorFixture) =>
async ({ async ({
hoverPos, hoverPos,
constraintType, constraintType,
@ -160,7 +163,11 @@ test.describe('Testing segment overlays', () => {
page.getByTestId('cmd-bar-arg-value').getByRole('textbox') page.getByTestId('cmd-bar-arg-value').getByRole('textbox')
).toBeFocused() ).toBeFocused()
await page.waitForTimeout(500) await page.waitForTimeout(500)
await cmdBar.continue() await page
.getByRole('button', {
name: 'arrow right Continue',
})
.click()
await editor.expectEditor.toContain(expectAfterUnconstrained, { await editor.expectEditor.toContain(expectAfterUnconstrained, {
shouldNormalise: true, shouldNormalise: true,
}) })
@ -232,8 +239,8 @@ test.describe('Testing segment overlays', () => {
await expect(page.getByTestId('segment-overlay')).toHaveCount(14) await expect(page.getByTestId('segment-overlay')).toHaveCount(14)
const clickUnconstrained = _clickUnconstrained(page, editor, cmdBar) const clickUnconstrained = _clickUnconstrained(page, editor)
const clickConstrained = _clickConstrained(page, editor, cmdBar) const clickConstrained = _clickConstrained(page, editor)
await u.openAndClearDebugPanel() await u.openAndClearDebugPanel()
await u.sendCustomCmd({ await u.sendCustomCmd({
@ -657,7 +664,7 @@ profile002 = circle(sketch001, center = [345, 0], radius = 238.38)
await expect( await expect(
page.getByTestId('cmd-bar-arg-value').getByRole('textbox') page.getByTestId('cmd-bar-arg-value').getByRole('textbox')
).toBeFocused() ).toBeFocused()
await cmdBar.continue() await page.getByRole('button', { name: 'arrow right Continue' }).click()
// Verify the X constraint was added // Verify the X constraint was added
await editor.expectEditor.toContain('center = [xAbs001, 0]', { await editor.expectEditor.toContain('center = [xAbs001, 0]', {
@ -675,7 +682,7 @@ profile002 = circle(sketch001, center = [345, 0], radius = 238.38)
await expect( await expect(
page.getByTestId('cmd-bar-arg-value').getByRole('textbox') page.getByTestId('cmd-bar-arg-value').getByRole('textbox')
).toBeFocused() ).toBeFocused()
await cmdBar.continue() await page.getByRole('button', { name: 'arrow right Continue' }).click()
// Verify the Y constraint was added // Verify the Y constraint was added
await editor.expectEditor.toContain('center = [xAbs001, yAbs001]', { await editor.expectEditor.toContain('center = [xAbs001, yAbs001]', {
@ -693,7 +700,7 @@ profile002 = circle(sketch001, center = [345, 0], radius = 238.38)
await expect( await expect(
page.getByTestId('cmd-bar-arg-value').getByRole('textbox') page.getByTestId('cmd-bar-arg-value').getByRole('textbox')
).toBeFocused() ).toBeFocused()
await cmdBar.continue() await page.getByRole('button', { name: 'arrow right Continue' }).click()
// Verify all constraints were added // Verify all constraints were added
await editor.expectEditor.toContain( await editor.expectEditor.toContain(
@ -880,7 +887,7 @@ profile003 = startProfile(sketch001, at = [64.39, 35.16])
await expect( await expect(
page.getByTestId('cmd-bar-arg-value').getByRole('textbox') page.getByTestId('cmd-bar-arg-value').getByRole('textbox')
).toBeFocused() ).toBeFocused()
await cmdBar.continue() await page.getByRole('button', { name: 'arrow right Continue' }).click()
// Verify the constraint was added // Verify the constraint was added
await editor.expectEditor.toContain( await editor.expectEditor.toContain(
@ -903,7 +910,7 @@ profile003 = startProfile(sketch001, at = [64.39, 35.16])
await expect( await expect(
page.getByTestId('cmd-bar-arg-value').getByRole('textbox') page.getByTestId('cmd-bar-arg-value').getByRole('textbox')
).toBeFocused() ).toBeFocused()
await cmdBar.continue() await page.getByRole('button', { name: 'arrow right Continue' }).click()
// Verify both constraints were added // Verify both constraints were added
await editor.expectEditor.toContain( await editor.expectEditor.toContain(
@ -928,7 +935,7 @@ profile003 = startProfile(sketch001, at = [64.39, 35.16])
await expect( await expect(
page.getByTestId('cmd-bar-arg-value').getByRole('textbox') page.getByTestId('cmd-bar-arg-value').getByRole('textbox')
).toBeFocused() ).toBeFocused()
await cmdBar.continue() await page.getByRole('button', { name: 'arrow right Continue' }).click()
// Verify the constraint was added // Verify the constraint was added
await editor.expectEditor.toContain('endAbsolute = [xAbs002, 84.07]', { await editor.expectEditor.toContain('endAbsolute = [xAbs002, 84.07]', {
@ -948,7 +955,7 @@ profile003 = startProfile(sketch001, at = [64.39, 35.16])
await expect( await expect(
page.getByTestId('cmd-bar-arg-value').getByRole('textbox') page.getByTestId('cmd-bar-arg-value').getByRole('textbox')
).toBeFocused() ).toBeFocused()
await cmdBar.continue() await page.getByRole('button', { name: 'arrow right Continue' }).click()
// Verify all constraints were added // Verify all constraints were added
await editor.expectEditor.toContain( await editor.expectEditor.toContain(

View File

@ -32,7 +32,7 @@ test('Units menu', async ({ page, homePage }) => {
test( test(
'Successful export shows a success toast', 'Successful export shows a success toast',
{ tag: '@skipLocalEngine' }, { tag: '@skipLocalEngine' },
async ({ page, homePage, cmdBar, tronApp }) => { async ({ page, homePage, tronApp }) => {
// FYI this test doesn't work with only engine running locally // FYI this test doesn't work with only engine running locally
// And you will need to have the KittyCAD CLI installed // And you will need to have the KittyCAD CLI installed
const u = await getUtils(page) const u = await getUtils(page)
@ -94,8 +94,7 @@ part001 = startSketchOn(-XZ)
presentation: 'pretty', presentation: 'pretty',
}, },
tronApp?.projectDirName, tronApp?.projectDirName,
page, page
cmdBar
) )
} }
) )
@ -255,7 +254,6 @@ test('First escape in tool pops you out of tool, second exits sketch mode', asyn
test('Basic default modeling and sketch hotkeys work', async ({ test('Basic default modeling and sketch hotkeys work', async ({
page, page,
homePage, homePage,
cmdBar,
}) => { }) => {
const u = await getUtils(page) const u = await getUtils(page)
await test.step(`Set up test`, async () => { await test.step(`Set up test`, async () => {
@ -399,8 +397,11 @@ test('Basic default modeling and sketch hotkeys work', async ({
await expect(page.getByRole('button', { name: 'Continue' })).toBeVisible({ await expect(page.getByRole('button', { name: 'Continue' })).toBeVisible({
timeout: 20_000, timeout: 20_000,
}) })
await cmdBar.continue() await page.getByRole('button', { name: 'Continue' }).click()
await cmdBar.submit() await expect(
page.getByRole('button', { name: 'Submit command' })
).toBeVisible()
await page.getByRole('button', { name: 'Submit command' }).click()
await expect(page.locator('.cm-content')).toContainText('extrude(') await expect(page.locator('.cm-content')).toContainText('extrude(')
}) })
@ -574,7 +575,8 @@ profile001 = startProfile(sketch002, at = [-12.34, 12.34])
await cmdBar.progressCmdBar() await cmdBar.progressCmdBar()
await cmdBar.progressCmdBar() await cmdBar.progressCmdBar()
await cmdBar.submit() await expect(page.getByText('Confirm Extrude')).toBeVisible()
await cmdBar.progressCmdBar()
const result2 = result.genNext` const result2 = result.genNext`
const sketch002 = extrude(sketch002, length = ${[5, 5]} + 7)` const sketch002 = extrude(sketch002, length = ${[5, 5]} + 7)`

18
flake.lock generated
View File

@ -20,11 +20,11 @@
}, },
"nixpkgs": { "nixpkgs": {
"locked": { "locked": {
"lastModified": 1750865895, "lastModified": 1745998881,
"narHash": "sha256-p2dWAQcLVzquy9LxYCZPwyUdugw78Qv3ChvnX755qHA=", "narHash": "sha256-vonyYAKJSlsX4n9GCsS0pHxR6yCrfqBIuGvANlkwG6U=",
"owner": "NixOS", "owner": "NixOS",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "61c0f513911459945e2cb8bf333dc849f1b976ff", "rev": "423d2df5b04b4ee7688c3d71396e872afa236a89",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -36,11 +36,11 @@
}, },
"nixpkgs_2": { "nixpkgs_2": {
"locked": { "locked": {
"lastModified": 1750865895, "lastModified": 1745998881,
"narHash": "sha256-p2dWAQcLVzquy9LxYCZPwyUdugw78Qv3ChvnX755qHA=", "narHash": "sha256-vonyYAKJSlsX4n9GCsS0pHxR6yCrfqBIuGvANlkwG6U=",
"owner": "NixOS", "owner": "NixOS",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "61c0f513911459945e2cb8bf333dc849f1b976ff", "rev": "423d2df5b04b4ee7688c3d71396e872afa236a89",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -78,11 +78,11 @@
"nixpkgs": "nixpkgs_3" "nixpkgs": "nixpkgs_3"
}, },
"locked": { "locked": {
"lastModified": 1750964660, "lastModified": 1745980514,
"narHash": "sha256-YQ6EyFetjH1uy5JhdhRdPe6cuNXlYpMAQePFfZj4W7M=", "narHash": "sha256-CITAeiuXGjDvT5iZBXr6vKVWQwsUQLJUMFO91bfJFC4=",
"owner": "oxalica", "owner": "oxalica",
"repo": "rust-overlay", "repo": "rust-overlay",
"rev": "04f0fcfb1a50c63529805a798b4b5c21610ff390", "rev": "7fbdae44b0f40ea432e46fd152ad8be0f8f41ad6",
"type": "github" "type": "github"
}, },
"original": { "original": {

View File

@ -125,57 +125,18 @@ test('Shows a loading spinner when uninitialized credit count', async () => {
await expect(queryByTestId('spinner')).toBeVisible() await expect(queryByTestId('spinner')).toBeVisible()
}) })
const unKnownTierData = {
balance: {
monthlyApiCreditsRemaining: 10,
stableApiCreditsRemaining: 25,
},
subscriptions: {
monthlyPayAsYouGoApiCreditsTotal: 20,
name: "unknown",
}
}
const freeTierData = {
balance: {
monthlyApiCreditsRemaining: 10,
stableApiCreditsRemaining: 0,
},
subscriptions: {
monthlyPayAsYouGoApiCreditsTotal: 20,
name: "free",
}
}
const proTierData = {
// These are all ignored
balance: {
monthlyApiCreditsRemaining: 10,
stableApiCreditsRemaining: 0,
},
subscriptions: {
// This should be ignored because it's Pro tier.
monthlyPayAsYouGoApiCreditsTotal: 20,
name: "pro",
}
}
const enterpriseTierData = {
// These are all ignored, user is part of an org.
balance: {
monthlyApiCreditsRemaining: 10,
stableApiCreditsRemaining: 0,
},
subscriptions: {
// This should be ignored because it's Pro tier.
monthlyPayAsYouGoApiCreditsTotal: 20,
// This should be ignored because the user is part of an Org.
name: "free",
}
}
test('Shows the total credits for Unknown subscription', async () => { test('Shows the total credits for Unknown subscription', async () => {
const data = unKnownTierData const data = {
balance: {
monthlyApiCreditsRemaining: 10,
stableApiCreditsRemaining: 25,
},
subscriptions: {
monthlyPayAsYouGoApiCreditsTotal: 20,
name: "unknown",
}
}
server.use( server.use(
http.get('*/user/payment/balance', (req, res, ctx) => { http.get('*/user/payment/balance', (req, res, ctx) => {
return HttpResponse.json(createUserPaymentBalanceResponse(data.balance)) return HttpResponse.json(createUserPaymentBalanceResponse(data.balance))
@ -205,7 +166,17 @@ test('Shows the total credits for Unknown subscription', async () => {
}) })
test('Progress bar reflects ratio left of Free subscription', async () => { test('Progress bar reflects ratio left of Free subscription', async () => {
const data = freeTierData const data = {
balance: {
monthlyApiCreditsRemaining: 10,
stableApiCreditsRemaining: 0,
},
subscriptions: {
monthlyPayAsYouGoApiCreditsTotal: 20,
name: "free",
}
}
server.use( server.use(
http.get('*/user/payment/balance', (req, res, ctx) => { http.get('*/user/payment/balance', (req, res, ctx) => {
return HttpResponse.json(createUserPaymentBalanceResponse(data.balance)) return HttpResponse.json(createUserPaymentBalanceResponse(data.balance))
@ -241,7 +212,19 @@ test('Progress bar reflects ratio left of Free subscription', async () => {
}) })
}) })
test('Shows infinite credits for Pro subscription', async () => { test('Shows infinite credits for Pro subscription', async () => {
const data = proTierData const data = {
// These are all ignored
balance: {
monthlyApiCreditsRemaining: 10,
stableApiCreditsRemaining: 0,
},
subscriptions: {
// This should be ignored because it's Pro tier.
monthlyPayAsYouGoApiCreditsTotal: 20,
name: "pro",
}
}
server.use( server.use(
http.get('*/user/payment/balance', (req, res, ctx) => { http.get('*/user/payment/balance', (req, res, ctx) => {
return HttpResponse.json(createUserPaymentBalanceResponse(data.balance)) return HttpResponse.json(createUserPaymentBalanceResponse(data.balance))
@ -272,7 +255,19 @@ test('Shows infinite credits for Pro subscription', async () => {
await expect(queryByTestId('billing-remaining-progress-bar-inline')).toBe(null) await expect(queryByTestId('billing-remaining-progress-bar-inline')).toBe(null)
}) })
test('Shows infinite credits for Enterprise subscription', async () => { test('Shows infinite credits for Enterprise subscription', async () => {
const data = enterpriseTierData const data = {
// These are all ignored, user is part of an org.
balance: {
monthlyApiCreditsRemaining: 10,
stableApiCreditsRemaining: 0,
},
subscriptions: {
// This should be ignored because it's Pro tier.
monthlyPayAsYouGoApiCreditsTotal: 20,
// This should be ignored because the user is part of an Org.
name: "free",
}
}
server.use( server.use(
http.get('*/user/payment/balance', (req, res, ctx) => { http.get('*/user/payment/balance', (req, res, ctx) => {
@ -302,58 +297,3 @@ test('Shows infinite credits for Enterprise subscription', async () => {
await expect(queryByTestId('infinity')).toBeVisible() await expect(queryByTestId('infinity')).toBeVisible()
await expect(queryByTestId('billing-remaining-progress-bar-inline')).toBe(null) await expect(queryByTestId('billing-remaining-progress-bar-inline')).toBe(null)
}) })
test('Show upgrade button if credits are not infinite', async () => {
const data = freeTierData
server.use(
http.get('*/user/payment/balance', (req, res, ctx) => {
return HttpResponse.json(createUserPaymentBalanceResponse(data.balance))
}),
http.get('*/user/payment/subscriptions', (req, res, ctx) => {
return HttpResponse.json(createUserPaymentSubscriptionsResponse(data.subscriptions))
}),
http.get('*/org', (req, res, ctx) => {
return new HttpResponse(403)
}),
)
const billingActor = createActor(billingMachine, { input: BILLING_CONTEXT_DEFAULTS }).start()
const { queryByTestId } = render(<BillingDialog
billingActor={billingActor}
/>)
await act(() => {
billingActor.send({ type: BillingTransition.Update, apiToken: "it doesn't matter wtf this is :)" })
})
await expect(queryByTestId('billing-upgrade-button')).toBeVisible()
})
test('Hide upgrade button if credits are infinite', async () => {
const data = enterpriseTierData
server.use(
http.get('*/user/payment/balance', (req, res, ctx) => {
return HttpResponse.json(createUserPaymentBalanceResponse(data.balance))
}),
http.get('*/user/payment/subscriptions', (req, res, ctx) => {
return HttpResponse.json(createUserPaymentSubscriptionsResponse(data.subscriptions))
}),
// Ok finally the first use of an org lol
http.get('*/org', (req, res, ctx) => {
return HttpResponse.json(createOrgResponse())
}),
)
const billingActor = createActor(billingMachine, { input: BILLING_CONTEXT_DEFAULTS }).start()
const { queryByTestId } = render(<BillingDialog
billingActor={billingActor}
/>)
await act(() => {
billingActor.send({ type: BillingTransition.Update, apiToken: "it doesn't matter wtf this is :)" })
})
await expect(queryByTestId('billing-upgrade-button')).toBe(null)
})

1119
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -110,11 +110,8 @@
"remove-importmeta": "sed -i 's/import.meta.url/window.location.origin/g' \"./rust/kcl-wasm-lib/pkg/kcl_wasm_lib.js\"; sed -i '' 's/import.meta.url/window.location.origin/g' \"./rust/kcl-wasm-lib/pkg/kcl_wasm_lib.js\" || echo \"sed for both mac and linux\"", "remove-importmeta": "sed -i 's/import.meta.url/window.location.origin/g' \"./rust/kcl-wasm-lib/pkg/kcl_wasm_lib.js\"; sed -i '' 's/import.meta.url/window.location.origin/g' \"./rust/kcl-wasm-lib/pkg/kcl_wasm_lib.js\" || echo \"sed for both mac and linux\"",
"lint-fix": "eslint --fix --ext .ts --ext .tsx src e2e packages/codemirror-lsp-client/src rust/kcl-language-server/client/src", "lint-fix": "eslint --fix --ext .ts --ext .tsx src e2e packages/codemirror-lsp-client/src rust/kcl-language-server/client/src",
"lint": "eslint --max-warnings 0 --ext .ts --ext .tsx src e2e packages/codemirror-lsp-client/src rust/kcl-language-server/client/src", "lint": "eslint --max-warnings 0 --ext .ts --ext .tsx src e2e packages/codemirror-lsp-client/src rust/kcl-language-server/client/src",
"url-checker":"./scripts/url-checker.sh",
"url-checker:overwrite":"npm run url-checker > scripts/known/urls.txt",
"url-checker:diff":"./scripts/diff-url-checker.sh",
"circular-deps": "dpdm --no-warning --no-tree -T --skip-dynamic-imports=circular src/index.tsx", "circular-deps": "dpdm --no-warning --no-tree -T --skip-dynamic-imports=circular src/index.tsx",
"circular-deps:overwrite": "npm run circular-deps | sed '$d' | grep -v '^npm run' > scripts/known/circular.txt", "circular-deps:overwrite": "npm run circular-deps | sed '$d' | grep -v '^npm run' > known-circular.txt",
"circular-deps:diff": "./scripts/diff-circular-deps.sh", "circular-deps:diff": "./scripts/diff-circular-deps.sh",
"circular-deps:diff:nodejs": "npm run circular-deps:diff || node ./scripts/diff.js", "circular-deps:diff:nodejs": "npm run circular-deps:diff || node ./scripts/diff.js",
"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",

View File

@ -111,8 +111,7 @@ commaSep1NoTrailingComma<term> { term ("," term)* }
PipeSubstitution { "%" } PipeSubstitution { "%" }
// Includes non-whitespace unicode characters. identifier { (@asciiLetter | "_") (@asciiLetter | @digit | "_")* }
identifier { $[a-zA-Z_\u{a1}-\u{167f}\u{1681}-\u{1fff}\u{200e}-\u{2027}\u{202a}-\u{202e}\u{2030}-\u{205e}\u{2061}-\u{2fff}\u{3001}-\u{fefe}\u{ff00}-\u{10ffff}] $[a-zA-Z0-9_\u{a1}-\u{167f}\u{1681}-\u{1fff}\u{200e}-\u{2027}\u{202a}-\u{202e}\u{2030}-\u{205e}\u{2061}-\u{2fff}\u{3001}-\u{fefe}\u{ff00}-\u{10ffff}]* }
AnnotationName { "@" identifier? } AnnotationName { "@" identifier? }
PropertyName { identifier } PropertyName { identifier }
TagDeclarator { "$" identifier } TagDeclarator { "$" identifier }

View File

@ -29,7 +29,7 @@
"vscode-uri": "^3.1.0" "vscode-uri": "^3.1.0"
}, },
"devDependencies": { "devDependencies": {
"@types/node": "^24.0.7", "@types/node": "^22.14.1",
"ts-node": "^10.9.2" "ts-node": "^10.9.2"
} }
} }

View File

@ -27,7 +27,7 @@ insideWall = extrude(insideWallSketch, length = overallThickness)
// Create the sketch of one of the balls // Create the sketch of one of the balls
ballsSketch = startSketchOn(XY) ballsSketch = startSketchOn(XY)
|> startProfile(at = [shaftDia / 2 + wallThickness, 0.001]) |> startProfile(at = [shaftDia / 2 + wallThickness, 0.001])
|> arc(angleStart = 180, angleEnd = 0, radius = sphereDia / 2) |> arc(angleStart = 180deg, angleEnd = 0, radius = sphereDia / 2)
|> close() |> close()
// Revolve the ball to make a sphere and pattern around the inside wall // Revolve the ball to make a sphere and pattern around the inside wall
@ -44,9 +44,9 @@ balls = revolve(ballsSketch, axis = X)
chainSketch = startSketchOn(XY) chainSketch = startSketchOn(XY)
|> startProfile(at = [ |> startProfile(at = [
shaftDia / 2 + wallThickness + sphereDia / 2 - (chainWidth / 2), shaftDia / 2 + wallThickness + sphereDia / 2 - (chainWidth / 2),
0.125 * sin(60) 0.125 * sin(60deg)
]) ])
|> arc(angleStart = 120, angleEnd = 60, radius = sphereDia / 2) |> arc(angleStart = 120deg, angleEnd = 60deg, radius = sphereDia / 2)
|> line(end = [0, chainThickness]) |> line(end = [0, chainThickness])
|> line(end = [-chainWidth, 0]) |> line(end = [-chainWidth, 0])
|> close() |> close()
@ -54,7 +54,7 @@ chainSketch = startSketchOn(XY)
// Revolve the chain sketch // Revolve the chain sketch
chainHead = revolve(chainSketch, axis = X) chainHead = revolve(chainSketch, axis = X)
|> patternCircular3d( |> patternCircular3d(
arcDegrees = 360, arcDegrees = 360deg,
axis = [0, 0, 1], axis = [0, 0, 1],
center = [0, 0, 0], center = [0, 0, 0],
instances = nBalls, instances = nBalls,
@ -72,9 +72,9 @@ linkSketch = startSketchOn(XZ)
) )
// Revolve the link sketch // Revolve the link sketch
linkRevolve = revolve(linkSketch, axis = Y, angle = 360 / nBalls) linkRevolve = revolve(linkSketch, axis = Y, angle = 360deg / nBalls)
|> patternCircular3d( |> patternCircular3d(
arcDegrees = 360, arcDegrees = 360deg,
axis = [0, 0, 1], axis = [0, 0, 1],
center = [0, 0, 0], center = [0, 0, 0],
instances = nBalls, instances = nBalls,

View File

@ -23,12 +23,12 @@ nVentBosses = 36
// Drilling parameters. // Drilling parameters.
dDrillDia = 6 dDrillDia = 6
aBase = 90 aBase = 90deg
aSweep = 30 aSweep = 30deg
nArcs = 12 nArcs = 12
// Bell parameters. // Bell parameters.
aDraftBell = 5 aDraftBell = 5deg
tBell = 5 // Wall thickness. tBell = 5 // Wall thickness.
hBellAboveDiscFace = 40 hBellAboveDiscFace = 40
hBellSubflush = 4 hBellSubflush = 4
@ -98,7 +98,7 @@ planeVent = offsetPlane(XY, offset = tDiscHalf)
sketchVent = startSketchOn(planeVent) sketchVent = startSketchOn(planeVent)
profileVent = startProfile(sketchVent, at = [-wVent, dDisc / 2]) profileVent = startProfile(sketchVent, at = [-wVent, dDisc / 2])
|> angledLine(angle = 0, length = wVent, tag = $rectangleSegmentA001) |> angledLine(angle = 0, length = wVent, tag = $rectangleSegmentA001)
|> angledLine(angle = segAng(rectangleSegmentA001) - 90, length = hFrictionSurface, tag = $seg02) |> angledLine(angle = segAng(rectangleSegmentA001) - 90deg, length = hFrictionSurface, tag = $seg02)
|> angledLine(angle = segAng(rectangleSegmentA001), length = -segLen(rectangleSegmentA001), tag = $seg03) |> angledLine(angle = segAng(rectangleSegmentA001), length = -segLen(rectangleSegmentA001), tag = $seg03)
|> line(endAbsolute = [profileStartX(%), profileStartY(%)], tag = $seg01) |> line(endAbsolute = [profileStartX(%), profileStartY(%)], tag = $seg01)
|> close() |> close()
@ -118,7 +118,7 @@ ventSet = patternCircular3d(
instances = nVentBosses, instances = nVentBosses,
axis = [0, 0, 1], axis = [0, 0, 1],
center = [0, 0, tDiscHalf], center = [0, 0, tDiscHalf],
arcDegrees = 360, arcDegrees = 360deg,
rotateDuplicates = true, rotateDuplicates = true,
) )
@ -134,8 +134,8 @@ discOutboard = createDiscHalf(
// Now create bell. // Now create bell.
rCenter = dDisc / 2 - hFrictionSurface - wUndercut rCenter = dDisc / 2 - hFrictionSurface - wUndercut
rBore = dBore / 2 rBore = dBore / 2
lDraftExterior = hBellAboveDiscFace / tan(90 - aDraftBell) lDraftExterior = hBellAboveDiscFace / tan(90deg - aDraftBell)
lDraftInterior = (hBellAboveDiscFace - tBell) / tan(90 - aDraftBell) lDraftInterior = (hBellAboveDiscFace - tBell) / tan(90deg - aDraftBell)
// Inner and outer radius of outboard face of disc bell. // Inner and outer radius of outboard face of disc bell.
rOuter = rCenter - lDraftExterior - rBore rOuter = rCenter - lDraftExterior - rBore
@ -151,7 +151,7 @@ bodyDiscBell = startProfile(
) )
|> arc( |> arc(
%, %,
angleStart = -180, angleStart = -180deg,
angleEnd = 0, angleEnd = 0,
radius = wUndercut / 2, radius = wUndercut / 2,
) )
@ -172,7 +172,7 @@ profileStud = circle(sketchLugs, center = [0, dPitchCircle / 2], radius = dStudD
%, %,
instances = nStuds, instances = nStuds,
center = [0, 0], center = [0, 0],
arcDegrees = 360, arcDegrees = 360deg,
rotateDuplicates = true, rotateDuplicates = true,
) )

View File

@ -28,8 +28,8 @@ hour = 9
minute = 29 minute = 29
// Calculate hand angles // Calculate hand angles
hourHandAngle = 90 - (hour * 30) hourHandAngle = 90deg - (hour * 30deg)
minuteHandAngle = 90 - (minute * 6) minuteHandAngle = 90deg - (minute * 6deg)
// Create the clock body // Create the clock body
clockBodySketch = startSketchOn(XY) clockBodySketch = startSketchOn(XY)
@ -72,18 +72,18 @@ numberObject = {
// one = { i = [90, 160] }, // one = { i = [90, 160] },
one = { one = {
i = [ i = [
clockDiameter / 2 * 3 / 4 * cos(60), clockDiameter / 2 * 3 / 4 * cos(60deg),
clockDiameter / 2 * 3 / 4 * sin(60) clockDiameter / 2 * 3 / 4 * sin(60deg)
] ]
}, },
two = { two = {
i = [ i = [
clockDiameter / 2 * 3 / 4 * cos(30) - 10, clockDiameter / 2 * 3 / 4 * cos(30deg) - 10,
clockDiameter / 2 * 3 / 4 * sin(30) clockDiameter / 2 * 3 / 4 * sin(30deg)
], ],
i2 = [ i2 = [
clockDiameter / 2 * 3 / 4 * cos(30) + 5, clockDiameter / 2 * 3 / 4 * cos(30deg) + 5,
clockDiameter / 2 * 3 / 4 * sin(30) clockDiameter / 2 * 3 / 4 * sin(30deg)
] ]
}, },
three = { three = {
@ -102,100 +102,100 @@ numberObject = {
}, },
four = { four = {
i = [ i = [
clockDiameter / 2 * 3 / 4 * cos(-30) - 10, clockDiameter / 2 * 3 / 4 * cos(-30deg) - 10,
clockDiameter / 2 * 3 / 4 * sin(-30) clockDiameter / 2 * 3 / 4 * sin(-30deg)
], ],
v = [ v = [
clockDiameter / 2 * 3 / 4 * cos(-30) + 13, clockDiameter / 2 * 3 / 4 * cos(-30deg) + 13,
clockDiameter / 2 * 3 / 4 * sin(-30) clockDiameter / 2 * 3 / 4 * sin(-30deg)
] ]
}, },
five = { five = {
v = [ v = [
clockDiameter / 2 * 3 / 4 * cos(-60), clockDiameter / 2 * 3 / 4 * cos(-60deg),
clockDiameter / 2 * 3 / 4 * sin(-60) clockDiameter / 2 * 3 / 4 * sin(-60deg)
] ]
}, },
six = { six = {
v = [ v = [
clockDiameter / 2 * 3 / 4 * cos(-90) - 10, clockDiameter / 2 * 3 / 4 * cos(-90deg) - 10,
clockDiameter / 2 * 3 / 4 * sin(-90) clockDiameter / 2 * 3 / 4 * sin(-90deg)
], ],
i = [ i = [
clockDiameter / 2 * 3 / 4 * cos(-90) + 12, clockDiameter / 2 * 3 / 4 * cos(-90deg) + 12,
clockDiameter / 2 * 3 / 4 * sin(-90) clockDiameter / 2 * 3 / 4 * sin(-90deg)
] ]
}, },
seven = { seven = {
v = [ v = [
clockDiameter / 2 * 3 / 4 * cos(-120) - 15, clockDiameter / 2 * 3 / 4 * cos(-120deg) - 15,
clockDiameter / 2 * 3 / 4 * sin(-120) clockDiameter / 2 * 3 / 4 * sin(-120deg)
], ],
i = [ i = [
clockDiameter / 2 * 3 / 4 * cos(-120) + 5, clockDiameter / 2 * 3 / 4 * cos(-120deg) + 5,
clockDiameter / 2 * 3 / 4 * sin(-120) clockDiameter / 2 * 3 / 4 * sin(-120deg)
], ],
i2 = [ i2 = [
clockDiameter / 2 * 3 / 4 * cos(-120) + 20, clockDiameter / 2 * 3 / 4 * cos(-120deg) + 20,
clockDiameter / 2 * 3 / 4 * sin(-120) clockDiameter / 2 * 3 / 4 * sin(-120deg)
] ]
}, },
eight = { eight = {
v = [ v = [
clockDiameter / 2 * 3 / 4 * cos(-150) - 10, clockDiameter / 2 * 3 / 4 * cos(-150deg) - 10,
clockDiameter / 2 * 3 / 4 * sin(-150) clockDiameter / 2 * 3 / 4 * sin(-150deg)
], ],
i = [ i = [
clockDiameter / 2 * 3 / 4 * cos(-150) + 10, clockDiameter / 2 * 3 / 4 * cos(-150deg) + 10,
clockDiameter / 2 * 3 / 4 * sin(-150) clockDiameter / 2 * 3 / 4 * sin(-150deg)
], ],
i2 = [ i2 = [
clockDiameter / 2 * 3 / 4 * cos(-150) + 25, clockDiameter / 2 * 3 / 4 * cos(-150deg) + 25,
clockDiameter / 2 * 3 / 4 * sin(-150) clockDiameter / 2 * 3 / 4 * sin(-150deg)
], ],
i3 = [ i3 = [
clockDiameter / 2 * 3 / 4 * cos(-150) + 40, clockDiameter / 2 * 3 / 4 * cos(-150deg) + 40,
clockDiameter / 2 * 3 / 4 * sin(-150) clockDiameter / 2 * 3 / 4 * sin(-150deg)
] ]
}, },
nine = { nine = {
i = [ i = [
clockDiameter / 2 * 3 / 4 * cos(180) - 15, clockDiameter / 2 * 3 / 4 * cos(180deg) - 15,
clockDiameter / 2 * 3 / 4 * sin(180) clockDiameter / 2 * 3 / 4 * sin(180deg)
], ],
x = [ x = [
clockDiameter / 2 * 3 / 4 * cos(180) + 15, clockDiameter / 2 * 3 / 4 * cos(180deg) + 15,
clockDiameter / 2 * 3 / 4 * sin(180) clockDiameter / 2 * 3 / 4 * sin(180deg)
] ]
}, },
ten = { ten = {
x = [ x = [
clockDiameter / 2 * 3 / 4 * cos(150) + 5, clockDiameter / 2 * 3 / 4 * cos(150deg) + 5,
clockDiameter / 2 * 3 / 4 * sin(150) clockDiameter / 2 * 3 / 4 * sin(150deg)
] ]
}, },
eleven = { eleven = {
x = [ x = [
clockDiameter / 2 * 3 / 4 * cos(120), clockDiameter / 2 * 3 / 4 * cos(120deg),
clockDiameter / 2 * 3 / 4 * sin(120) clockDiameter / 2 * 3 / 4 * sin(120deg)
], ],
i = [ i = [
clockDiameter / 2 * 3 / 4 * cos(120) + 10, clockDiameter / 2 * 3 / 4 * cos(120deg) + 10,
clockDiameter / 2 * 3 / 4 * sin(120) clockDiameter / 2 * 3 / 4 * sin(120deg)
] ]
}, },
twelve = { twelve = {
x = [ x = [
clockDiameter / 2 * 3 / 4 * cos(90) - 10, clockDiameter / 2 * 3 / 4 * cos(90deg) - 10,
clockDiameter / 2 * 3 / 4 * sin(90) clockDiameter / 2 * 3 / 4 * sin(90deg)
], ],
i = [ i = [
clockDiameter / 2 * 3 / 4 * cos(90) + 5, clockDiameter / 2 * 3 / 4 * cos(90deg) + 5,
clockDiameter / 2 * 3 / 4 * sin(90) clockDiameter / 2 * 3 / 4 * sin(90deg)
], ],
i2 = [ i2 = [
clockDiameter / 2 * 3 / 4 * cos(90) + 20, clockDiameter / 2 * 3 / 4 * cos(90deg) + 20,
clockDiameter / 2 * 3 / 4 * sin(90) clockDiameter / 2 * 3 / 4 * sin(90deg)
] ]
} }
} }
@ -234,16 +234,16 @@ fn letterX(startX, startY) {
], ],
) )
|> xLine(%, length = xWidth / 6) |> xLine(%, length = xWidth / 6)
|> angledLine(%, angle = -70, lengthY = xLength * 1 / 3) |> angledLine(%, angle = -70deg, lengthY = xLength * 1 / 3)
|> angledLine(%, angle = 70, lengthY = xLength * 1 / 3) |> angledLine(%, angle = 70deg, lengthY = xLength * 1 / 3)
|> xLine(%, length = xWidth / 6) |> xLine(%, length = xWidth / 6)
|> angledLine(%, angle = 70 + 180, lengthY = xLength * 1 / 2) |> angledLine(%, angle = 70deg + 180deg, lengthY = xLength * 1 / 2)
|> angledLine(%, angle = -70, lengthY = xLength * 1 / 2) |> angledLine(%, angle = -70deg, lengthY = xLength * 1 / 2)
|> xLine(%, length = -xWidth / 6) |> xLine(%, length = -xWidth / 6)
|> angledLine(%, angle = -70 - 180, lengthY = xLength * 1 / 3) |> angledLine(%, angle = -70deg - 180deg, lengthY = xLength * 1 / 3)
|> angledLine(%, angle = 70 + 180, lengthY = xLength * 1 / 3) |> angledLine(%, angle = 70deg + 180deg, lengthY = xLength * 1 / 3)
|> xLine(%, length = -xWidth / 6) |> xLine(%, length = -xWidth / 6)
|> angledLine(%, angle = 70, lengthY = xLength * 1 / 2) |> angledLine(%, angle = 70deg, lengthY = xLength * 1 / 2)
|> close(%) |> close(%)
|> extrude(%, length = numberThickness) |> extrude(%, length = numberThickness)
|> appearance(%, color = "#140f0f") |> appearance(%, color = "#140f0f")
@ -333,25 +333,25 @@ sketch005 = startSketchOn(offsetPlane(XY, offset = 55))
profile007 = startProfile( profile007 = startProfile(
sketch005, sketch005,
at = [ at = [
nubDiameter / 2 * 1.375 * cos(hourHandAngle + 20), nubDiameter / 2 * 1.375 * cos(hourHandAngle + 20deg),
nubDiameter / 2 * 1.375 * sin(hourHandAngle + 20) nubDiameter / 2 * 1.375 * sin(hourHandAngle + 20deg)
], ],
) )
|> arc( |> arc(
%, %,
interiorAbsolute = [ interiorAbsolute = [
nubDiameter / 2 * 1.375 * cos(hourHandAngle + 180), nubDiameter / 2 * 1.375 * cos(hourHandAngle + 180deg),
nubDiameter / 2 * 1.375 * sin(hourHandAngle + 180) nubDiameter / 2 * 1.375 * sin(hourHandAngle + 180deg)
], ],
endAbsolute = [ endAbsolute = [
nubDiameter / 2 * 1.375 * cos(hourHandAngle + 340), nubDiameter / 2 * 1.375 * cos(hourHandAngle + 340deg),
nubDiameter / 2 * 1.375 * sin(hourHandAngle + 340) nubDiameter / 2 * 1.375 * sin(hourHandAngle + 340deg)
], ],
) )
|> angledLine(%, angle = hourHandAngle, length = hourHandArmLength) |> angledLine(%, angle = hourHandAngle, length = hourHandArmLength)
|> angledLine( |> angledLine(
%, %,
angle = hourHandAngle - 90, angle = hourHandAngle - 90deg,
length = hourHandWidth / 2, length = hourHandWidth / 2,
tag = $seg004, tag = $seg004,
) )
@ -363,9 +363,9 @@ profile007 = startProfile(
], ],
tag = $seg002, tag = $seg002,
) )
|> angledLine(%, angle = segAng(seg002) + 120, length = segLen(seg002)) |> angledLine(%, angle = segAng(seg002) + 120deg, length = segLen(seg002))
// |> angledLineThatIntersects(%, angle = segAng(seg002) + hourHandAngle - 90, intersectTag = seg004) // |> angledLineThatIntersects(%, angle = segAng(seg002) + hourHandAngle - 90deg, intersectTag = seg004)
|> angledLine(%, angle = hourHandAngle - 90, length = segLen(seg004)) |> angledLine(%, angle = hourHandAngle - 90deg, length = segLen(seg004))
|> line(%, endAbsolute = [profileStartX(%), profileStartY(%)]) |> line(%, endAbsolute = [profileStartX(%), profileStartY(%)])
|> close(%) |> close(%)
profile008 = circle(sketch005, center = [0, 0], diameter = nubDiameter) profile008 = circle(sketch005, center = [0, 0], diameter = nubDiameter)
@ -378,25 +378,25 @@ sketch006 = startSketchOn(offsetPlane(XY, offset = 50))
profile009 = startProfile( profile009 = startProfile(
sketch006, sketch006,
at = [ at = [
nubDiameter / 2 * 1.375 * cos(minuteHandAngle + 20), nubDiameter / 2 * 1.375 * cos(minuteHandAngle + 20deg),
nubDiameter / 2 * 1.375 * sin(minuteHandAngle + 20) nubDiameter / 2 * 1.375 * sin(minuteHandAngle + 20deg)
], ],
) )
|> arc( |> arc(
%, %,
interiorAbsolute = [ interiorAbsolute = [
nubDiameter / 2 * 1.375 * cos(minuteHandAngle + 180), nubDiameter / 2 * 1.375 * cos(minuteHandAngle + 180deg),
nubDiameter / 2 * 1.375 * sin(minuteHandAngle + 180) nubDiameter / 2 * 1.375 * sin(minuteHandAngle + 180deg)
], ],
endAbsolute = [ endAbsolute = [
nubDiameter / 2 * 1.375 * cos(minuteHandAngle + 340), nubDiameter / 2 * 1.375 * cos(minuteHandAngle + 340deg),
nubDiameter / 2 * 1.375 * sin(minuteHandAngle + 340) nubDiameter / 2 * 1.375 * sin(minuteHandAngle + 340deg)
], ],
) )
|> angledLine(%, angle = minuteHandAngle, length = minuteHandArmLength) |> angledLine(%, angle = minuteHandAngle, length = minuteHandArmLength)
|> angledLine( |> angledLine(
%, %,
angle = minuteHandAngle - 90, angle = minuteHandAngle - 90deg,
length = minuteHandWidth / 2, length = minuteHandWidth / 2,
tag = $seg003, tag = $seg003,
) )
@ -408,8 +408,8 @@ profile009 = startProfile(
], ],
tag = $seg005, tag = $seg005,
) )
|> angledLine(%, angle = segAng(seg005) + 120, length = segLen(seg005)) |> angledLine(%, angle = segAng(seg005) + 120deg, length = segLen(seg005))
|> angledLine(%, angle = minuteHandAngle - 90, length = segLen(seg003)) |> angledLine(%, angle = minuteHandAngle - 90deg, length = segLen(seg003))
|> line(%, endAbsolute = [profileStartX(%), profileStartY(%)]) |> line(%, endAbsolute = [profileStartX(%), profileStartY(%)])
|> close(%) |> close(%)
profile010 = circle(sketch006, center = [0, 0], diameter = 30) profile010 = circle(sketch006, center = [0, 0], diameter = 30)
@ -430,8 +430,8 @@ profile004 = startProfile(sketch003, at = [-slotWidth / 2, 200])
|> arc( |> arc(
%, %,
radius = screwHeadDiameter / 2 + screwTolerance, radius = screwHeadDiameter / 2 + screwTolerance,
angleStart = 120, angleStart = 120deg,
angleEnd = 420, angleEnd = 420deg,
) )
|> yLine(%, length = slotLength) |> yLine(%, length = slotLength)
|> tangentialArc(endAbsolute = [profileStartX(%), profileStartY(%)]) |> tangentialArc(endAbsolute = [profileStartX(%), profileStartY(%)])

View File

@ -14,15 +14,15 @@ coldPlate = startSketchOn(YZ)
|> startProfile(at = [0, tubeDiameter * 2]) |> startProfile(at = [0, tubeDiameter * 2])
|> xLine(length = bendRadius - (tubeDiameter / 2)) |> xLine(length = bendRadius - (tubeDiameter / 2))
|> yLine(length = -tubeDiameter) |> yLine(length = -tubeDiameter)
|> tangentialArc(angle = 180, radius = tubeDiameter / 2) |> tangentialArc(angle = 180deg, radius = tubeDiameter / 2)
|> yLine(length = tubeDiameter) |> yLine(length = tubeDiameter)
|> xLine(length = bendRadius * 2 - tubeDiameter, tag = $seg07) |> xLine(length = bendRadius * 2 - tubeDiameter, tag = $seg07)
|> yLine(length = -tubeDiameter, tag = $seg09) |> yLine(length = -tubeDiameter, tag = $seg09)
|> tangentialArc(angle = 180, radius = tubeDiameter / 2) |> tangentialArc(angle = 180deg, radius = tubeDiameter / 2)
|> yLine(length = tubeDiameter, tag = $seg08) |> yLine(length = tubeDiameter, tag = $seg08)
|> xLine(length = bendRadius - (tubeDiameter / 2)) |> xLine(length = bendRadius - (tubeDiameter / 2))
|> angledLine(angle = -77, length = tubeDiameter / 3) |> angledLine(angle = -77deg, length = tubeDiameter / 3)
|> tangentialArc(angle = 77, radius = tubeDiameter, tag = $seg01) |> tangentialArc(angle = 77deg, radius = tubeDiameter, tag = $seg01)
|> angledLine(angle = tangentToEnd(seg01), length = 1) |> angledLine(angle = tangentToEnd(seg01), length = 1)
|> yLine(endAbsolute = 0) |> yLine(endAbsolute = 0)
|> xLine(endAbsolute = 0) |> xLine(endAbsolute = 0)
@ -34,11 +34,11 @@ coldPlate = startSketchOn(YZ)
copperTubePath = startSketchOn(offsetPlane(XY, offset = tubeDiameter)) copperTubePath = startSketchOn(offsetPlane(XY, offset = tubeDiameter))
|> startProfile(at = [-7.35, -bendRadius * 3]) |> startProfile(at = [-7.35, -bendRadius * 3])
|> xLine(length = 14.13, tag = $seg05) |> xLine(length = 14.13, tag = $seg05)
|> tangentialArc(angle = 180, radius = bendRadius, tag = $seg02) |> tangentialArc(angle = 180deg, radius = bendRadius, tag = $seg02)
|> angledLine(angle = tangentToEnd(seg02), length = 13.02, tag = $seg06) |> angledLine(angle = tangentToEnd(seg02), length = 13.02, tag = $seg06)
|> tangentialArc(angle = -180, radius = bendRadius, tag = $seg03) |> tangentialArc(angle = -180deg, radius = bendRadius, tag = $seg03)
|> angledLine(angle = tangentToEnd(seg03), length = segLen(seg06)) |> angledLine(angle = tangentToEnd(seg03), length = segLen(seg06))
|> tangentialArc(angle = 180, radius = bendRadius, tag = $seg04) |> tangentialArc(angle = 180deg, radius = bendRadius, tag = $seg04)
|> angledLine(angle = tangentToEnd(seg04), length = segLen(seg05)) |> angledLine(angle = tangentToEnd(seg04), length = segLen(seg05))
// Create the profile for the inner and outer diameter of the hollow copper tube // Create the profile for the inner and outer diameter of the hollow copper tube

View File

@ -22,13 +22,13 @@ tangentLength = (r1 - r2) / tan(tangentAngle)
plateBody = startSketchOn(XY) plateBody = startSketchOn(XY)
// Use polar coordinates to start the sketch at the tangent point of the larger radius // Use polar coordinates to start the sketch at the tangent point of the larger radius
|> startProfile(at = polar(angle = 90 - tangentAngle, length = r1)) |> startProfile(at = polar(angle = 90deg - tangentAngle, length = r1))
|> angledLine(angle = -tangentAngle, length = tangentLength) |> angledLine(angle = -tangentAngle, length = tangentLength)
|> tangentialArc(radius = r2, angle = (tangentAngle - 90) * 2) |> tangentialArc(radius = r2, angle = (tangentAngle - 90deg) * 2)
|> angledLine(angle = tangentAngle, length = -tangentLength) |> angledLine(angle = tangentAngle, length = -tangentLength)
|> tangentialArc(radius = r1, angle = -tangentAngle * 2) |> tangentialArc(radius = r1, angle = -tangentAngle * 2)
|> angledLine(angle = -tangentAngle, length = -tangentLength) |> angledLine(angle = -tangentAngle, length = -tangentLength)
|> tangentialArc(radius = r2, angle = (tangentAngle - 90) * 2) |> tangentialArc(radius = r2, angle = (tangentAngle - 90deg) * 2)
|> angledLine(angle = tangentAngle, length = tangentLength) |> angledLine(angle = tangentAngle, length = tangentLength)
|> tangentialArc(endAbsolute = [profileStartX(%), profileStartY(%)]) |> tangentialArc(endAbsolute = [profileStartX(%), profileStartY(%)])
|> close() |> close()

View File

@ -12,7 +12,7 @@ import * from "parameters.kcl"
bottomFaceSketch = startSketchOn(YZ) bottomFaceSketch = startSketchOn(YZ)
|> startProfile(at = [-fanSize / 2, -fanSize / 2]) |> startProfile(at = [-fanSize / 2, -fanSize / 2])
|> angledLine(angle = 0, length = fanSize, tag = $rectangleSegmentA001) |> angledLine(angle = 0, length = fanSize, tag = $rectangleSegmentA001)
|> angledLine(angle = segAng(rectangleSegmentA001) + 90, length = fanSize, tag = $rectangleSegmentB001) |> angledLine(angle = segAng(rectangleSegmentA001) + 90deg, length = fanSize, tag = $rectangleSegmentB001)
|> angledLine(angle = segAng(rectangleSegmentA001), length = -segLen(rectangleSegmentA001), tag = $rectangleSegmentC001) |> angledLine(angle = segAng(rectangleSegmentA001), length = -segLen(rectangleSegmentA001), tag = $rectangleSegmentC001)
|> line(endAbsolute = [profileStartX(%), profileStartY(%)], tag = $rectangleSegmentD001) |> line(endAbsolute = [profileStartX(%), profileStartY(%)], tag = $rectangleSegmentD001)
|> close() |> close()
@ -50,12 +50,12 @@ bottomFaceSketch = startSketchOn(YZ)
// Add large openings to the bottom face to allow airflow through the fan // Add large openings to the bottom face to allow airflow through the fan
airflowPattern = startSketchOn(bottomFaceSketch, face = END) airflowPattern = startSketchOn(bottomFaceSketch, face = END)
|> startProfile(at = [fanSize * 7 / 25, -fanSize * 9 / 25]) |> startProfile(at = [fanSize * 7 / 25, -fanSize * 9 / 25])
|> angledLine(angle = 140, length = fanSize * 12 / 25, tag = $seg01) |> angledLine(angle = 140deg, length = fanSize * 12 / 25, tag = $seg01)
|> tangentialArc(radius = fanSize * 1 / 50, angle = 90) |> tangentialArc(radius = fanSize * 1 / 50, angle = 90deg)
|> angledLine(angle = -130, length = fanSize * 8 / 25) |> angledLine(angle = -130deg, length = fanSize * 8 / 25)
|> tangentialArc(radius = fanSize * 1 / 50, angle = 90) |> tangentialArc(radius = fanSize * 1 / 50, angle = 90deg)
|> angledLine(angle = segAng(seg01) + 180, length = fanSize * 2 / 25) |> angledLine(angle = segAng(seg01) + 180deg, length = fanSize * 2 / 25)
|> tangentialArc(radius = fanSize * 8 / 25, angle = 40) |> tangentialArc(radius = fanSize * 8 / 25, angle = 40deg)
|> xLine(length = fanSize * 3 / 25) |> xLine(length = fanSize * 3 / 25)
|> tangentialArc(endAbsolute = [profileStartX(%), profileStartY(%)]) |> tangentialArc(endAbsolute = [profileStartX(%), profileStartY(%)])
|> close() |> close()
@ -75,13 +75,13 @@ bodyMiddle = startSketchOn(bottomFaceSketch, face = END)
housingMiddleLength / 2, housingMiddleLength / 2,
-housingMiddleLength / 2 - housingMiddleRadius -housingMiddleLength / 2 - housingMiddleRadius
]) ])
|> tangentialArc(radius = housingMiddleRadius, angle = 90) |> tangentialArc(radius = housingMiddleRadius, angle = 90deg)
|> yLine(length = housingMiddleLength) |> yLine(length = housingMiddleLength)
|> tangentialArc(radius = housingMiddleRadius, angle = 90) |> tangentialArc(radius = housingMiddleRadius, angle = 90deg)
|> xLine(length = -housingMiddleLength) |> xLine(length = -housingMiddleLength)
|> tangentialArc(radius = housingMiddleRadius, angle = 90) |> tangentialArc(radius = housingMiddleRadius, angle = 90deg)
|> yLine(length = -housingMiddleLength) |> yLine(length = -housingMiddleLength)
|> tangentialArc(radius = housingMiddleRadius, angle = 90) |> tangentialArc(radius = housingMiddleRadius, angle = 90deg)
|> line(endAbsolute = [profileStartX(%), profileStartY(%)]) |> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|> extrude(length = fanHeight - 4 - 4) |> extrude(length = fanHeight - 4 - 4)

View File

@ -20,32 +20,32 @@ fn fanBlade(offsetHeight, startAngle) {
15 * cos(startAngle), 15 * cos(startAngle),
15 * sin(startAngle) 15 * sin(startAngle)
]) ])
|> arc(angleStart = startAngle, angleEnd = startAngle + 14, radius = 15) |> arc(angleStart = startAngle, angleEnd = startAngle + 14deg, radius = 15)
|> arc( |> arc(
endAbsolute = [ endAbsolute = [
fanSize * 22 / 50 * cos(startAngle - 20), fanSize * 22 / 50 * cos(startAngle - 20deg),
fanSize * 22 / 50 * sin(startAngle - 20) fanSize * 22 / 50 * sin(startAngle - 20deg)
], ],
interiorAbsolute = [ interiorAbsolute = [
fanSize * 11 / 50 * cos(startAngle + 3), fanSize * 11 / 50 * cos(startAngle + 3deg),
fanSize * 11 / 50 * sin(startAngle + 3) fanSize * 11 / 50 * sin(startAngle + 3deg)
], ],
) )
|> arc( |> arc(
endAbsolute = [ endAbsolute = [
fanSize * 22 / 50 * cos(startAngle - 24), fanSize * 22 / 50 * cos(startAngle - 24deg),
fanSize * 22 / 50 * sin(startAngle - 24) fanSize * 22 / 50 * sin(startAngle - 24deg)
], ],
interiorAbsolute = [ interiorAbsolute = [
fanSize * 22 / 50 * cos(startAngle - 22), fanSize * 22 / 50 * cos(startAngle - 22deg),
fanSize * 22 / 50 * sin(startAngle - 22) fanSize * 22 / 50 * sin(startAngle - 22deg)
], ],
) )
|> arc( |> arc(
endAbsolute = [profileStartX(%), profileStartY(%)], endAbsolute = [profileStartX(%), profileStartY(%)],
interiorAbsolute = [ interiorAbsolute = [
fanSize * 11 / 50 * cos(startAngle - 5), fanSize * 11 / 50 * cos(startAngle - 5deg),
fanSize * 11 / 50 * sin(startAngle - 5) fanSize * 11 / 50 * sin(startAngle - 5deg)
], ],
) )
|> close() |> close()
@ -54,8 +54,8 @@ fn fanBlade(offsetHeight, startAngle) {
// Loft the fan blade cross sections into a single blade, then pattern them about the fan center // Loft the fan blade cross sections into a single blade, then pattern them about the fan center
crossSections = [ crossSections = [
fanBlade(offsetHeight = 4.5, startAngle = 50), fanBlade(offsetHeight = 4.5, startAngle = 50deg),
fanBlade(offsetHeight = (fanHeight - 2 - 4) / 2, startAngle = 30), fanBlade(offsetHeight = (fanHeight - 2 - 4) / 2, startAngle = 30deg),
fanBlade(offsetHeight = fanHeight - 2, startAngle = 0) fanBlade(offsetHeight = fanHeight - 2, startAngle = 0)
] ]
bladeLoft = loft(crossSections) bladeLoft = loft(crossSections)
@ -63,7 +63,7 @@ bladeLoft = loft(crossSections)
instances = 9, instances = 9,
axis = [1, 0, 0], axis = [1, 0, 0],
center = [0, 0, 0], center = [0, 0, 0],
arcDegrees = 360, arcDegrees = 360deg,
rotateDuplicates = true, rotateDuplicates = true,
) )

View File

@ -11,10 +11,10 @@ import * from "parameters.kcl"
endTubePath = startSketchOn(offsetPlane(YZ, offset = -20)) endTubePath = startSketchOn(offsetPlane(YZ, offset = -20))
|> startProfile(at = [fanSize / 4, fanSize + 38]) |> startProfile(at = [fanSize / 4, fanSize + 38])
|> yLine(endAbsolute = bendRadius + 10, tag = $seg01) |> yLine(endAbsolute = bendRadius + 10, tag = $seg01)
|> tangentialArc(radius = bendRadius, angle = -90) |> tangentialArc(radius = bendRadius, angle = -90deg)
|> xLine(endAbsolute = 0, tag = $seg02) |> xLine(endAbsolute = 0, tag = $seg02)
|> xLine(length = -segLen(seg02)) |> xLine(length = -segLen(seg02))
|> tangentialArc(radius = bendRadius, angle = -90) |> tangentialArc(radius = bendRadius, angle = -90deg)
|> yLine(length = segLen(seg01)) |> yLine(length = segLen(seg01))
// Sweep and translate the outermost tube on each end // Sweep and translate the outermost tube on each end
@ -35,13 +35,13 @@ endTube = startSketchOn(offsetPlane(XY, offset = fanSize + 38))
centerTubePath = startSketchOn(offsetPlane(YZ, offset = -4)) centerTubePath = startSketchOn(offsetPlane(YZ, offset = -4))
|> startProfile(at = [fanSize / 2.67, fanSize + 38]) |> startProfile(at = [fanSize / 2.67, fanSize + 38])
|> yLine(endAbsolute = bendRadius + 15 + 10) |> yLine(endAbsolute = bendRadius + 15 + 10)
|> tangentialArc(radius = bendRadius, angle = -45) |> tangentialArc(radius = bendRadius, angle = -45deg)
|> angledLine(angle = -135, lengthY = 15) |> angledLine(angle = -135deg, lengthY = 15)
|> tangentialArc(radius = bendRadius, angle = -45) |> tangentialArc(radius = bendRadius, angle = -45deg)
|> xLine(endAbsolute = 0, tag = $seg03) |> xLine(endAbsolute = 0, tag = $seg03)
|> xLine(length = -segLen(seg03)) |> xLine(length = -segLen(seg03))
|> tangentialArc(radius = bendRadius, angle = -155) |> tangentialArc(radius = bendRadius, angle = -155deg)
|> tangentialArc(radius = bendRadius, angle = 65) |> tangentialArc(radius = bendRadius, angle = 65deg)
|> yLine(endAbsolute = fanSize + 38) |> yLine(endAbsolute = fanSize + 38)
// Draw the profile and sweep the 4 interior tubes // Draw the profile and sweep the 4 interior tubes
@ -68,13 +68,13 @@ centerTube = startSketchOn(offsetPlane(XY, offset = fanSize + 38))
heatFins = startSketchOn(offsetPlane(XY, offset = 45)) heatFins = startSketchOn(offsetPlane(XY, offset = 45))
|> startProfile(at = [0, -fanSize / 2]) |> startProfile(at = [0, -fanSize / 2])
|> xLine(length = 9) |> xLine(length = 9)
|> angledLine(angle = -60, length = 2.5, tag = $seg04) |> angledLine(angle = -60deg, length = 2.5, tag = $seg04)
|> xLine(length = 0.75) |> xLine(length = 0.75)
|> arc(interiorAbsolute = [lastSegX(%) + 1, lastSegY(%) + 1.2], endAbsolute = [lastSegX(%) + 2, lastSegY(%)]) |> arc(interiorAbsolute = [lastSegX(%) + 1, lastSegY(%) + 1.2], endAbsolute = [lastSegX(%) + 2, lastSegY(%)])
|> xLine(length = 0.75) |> xLine(length = 0.75)
|> angledLine(angle = 60, length = segLen(seg04)) |> angledLine(angle = 60deg, length = segLen(seg04))
|> xLine(endAbsolute = heatSinkDepth / 2 - 3) |> xLine(endAbsolute = heatSinkDepth / 2 - 3)
|> tangentialArc(angle = 90, radius = 3) |> tangentialArc(angle = 90deg, radius = 3)
|> yLine(endAbsolute = 0) |> yLine(endAbsolute = 0)
|> mirror2d(axis = X) |> mirror2d(axis = X)
|> mirror2d(axis = Y) |> mirror2d(axis = Y)
@ -91,11 +91,11 @@ heatFins = startSketchOn(offsetPlane(XY, offset = 45))
coolerBase = startSketchOn(-XZ) coolerBase = startSketchOn(-XZ)
baseLower = startProfile(coolerBase, at = [0, 10]) baseLower = startProfile(coolerBase, at = [0, 10])
|> xLine(length = -0.9) |> xLine(length = -0.9)
|> arc(angleStart = 0, angleEnd = -180, radius = 3.1) |> arc(angleStart = 0, angleEnd = -180deg, radius = 3.1)
|> xLine(length = -1.8) |> xLine(length = -1.8)
|> arc(angleStart = 0, angleEnd = -180, radius = 3) |> arc(angleStart = 0, angleEnd = -180deg, radius = 3)
|> xLine(length = -1.8) |> xLine(length = -1.8)
|> arc(angleStart = 0, angleEnd = -180, radius = 3) |> arc(angleStart = 0, angleEnd = -180deg, radius = 3)
|> xLine(length = -1.8) |> xLine(length = -1.8)
|> xLine(length = -2) |> xLine(length = -2)
|> yLine(length = -10) |> yLine(length = -10)
@ -105,15 +105,15 @@ baseLower = startProfile(coolerBase, at = [0, 10])
baseUpper = startProfile(coolerBase, at = [0, 10]) baseUpper = startProfile(coolerBase, at = [0, 10])
|> xLine(length = -0.9) |> xLine(length = -0.9)
|> arc(angleStart = 0, angleEnd = 180, radius = 3.1) |> arc(angleStart = 0, angleEnd = 180deg, radius = 3.1)
|> xLine(length = -1.8) |> xLine(length = -1.8)
|> arc(angleStart = 0, angleEnd = 180, radius = 3) |> arc(angleStart = 0, angleEnd = 180deg, radius = 3)
|> xLine(length = -1.8) |> xLine(length = -1.8)
|> arc(angleStart = 0, angleEnd = 180, radius = 3) |> arc(angleStart = 0, angleEnd = 180deg, radius = 3)
|> xLine(length = -1.8) |> xLine(length = -1.8)
|> xLine(length = -1) |> xLine(length = -1)
|> yLine(length = 4) |> yLine(length = 4)
|> tangentialArc(angle = -90, radius = 2) |> tangentialArc(angle = -90deg, radius = 2)
|> xLine(endAbsolute = 0) |> xLine(endAbsolute = 0)
|> mirror2d(axis = Y) |> mirror2d(axis = Y)
|> extrude(length = 2 * segLen(seg02) * 3 / 4, symmetric = true) |> extrude(length = 2 * segLen(seg02) * 3 / 4, symmetric = true)
@ -122,15 +122,15 @@ baseUpper = startProfile(coolerBase, at = [0, 10])
mountingBracket = startSketchOn(XZ) mountingBracket = startSketchOn(XZ)
|> startProfile(at = [-10, 16]) |> startProfile(at = [-10, 16])
|> xLine(length = -20) |> xLine(length = -20)
|> tangentialArc(angle = 20, radius = bendRadius) |> tangentialArc(angle = 20deg, radius = bendRadius)
|> angledLine(angle = -160, length = 14, tag = $seg09) |> angledLine(angle = -160deg, length = 14, tag = $seg09)
|> tangentialArc(angle = -30, radius = bendRadius + sheetThickness) |> tangentialArc(angle = -30deg, radius = bendRadius + sheetThickness)
|> angledLine(angle = 170, length = 21.5, tag = $seg04Q) |> angledLine(angle = 170deg, length = 21.5, tag = $seg04Q)
|> angledLine(angle = 170 - 90, length = sheetThickness, tag = $seg08) |> angledLine(angle = 170deg - 90, length = sheetThickness, tag = $seg08)
|> angledLine(angle = segAng(seg04Q) + 180, length = segLen(seg04Q), tag = $seg05E) |> angledLine(angle = segAng(seg04Q) + 180deg, length = segLen(seg04Q), tag = $seg05E)
|> tangentialArc(angle = 30, radius = bendRadius) |> tangentialArc(angle = 30deg, radius = bendRadius)
|> angledLine(angle = segAng(seg09) + 180, length = segLen(seg09)) |> angledLine(angle = segAng(seg09) + 180deg, length = segLen(seg09))
|> tangentialArc(angle = -20, radius = bendRadius + sheetThickness) |> tangentialArc(angle = -20deg, radius = bendRadius + sheetThickness)
|> xLine(endAbsolute = profileStartX(%)) |> xLine(endAbsolute = profileStartX(%))
|> line(endAbsolute = [profileStartX(%), profileStartY(%)], tag = $seg07) |> line(endAbsolute = [profileStartX(%), profileStartY(%)], tag = $seg07)
|> close() |> close()
@ -166,6 +166,6 @@ thruHole = startSketchOn(mountingBracket, face = seg05E)
instances = 2, instances = 2,
axis = [0, 0, 1], axis = [0, 0, 1],
center = [0, 0, 0], center = [0, 0, 0],
arcDegrees = 360, arcDegrees = 360deg,
rotateDuplicates = true, rotateDuplicates = true,
) )

View File

@ -37,7 +37,7 @@ mountingWire
instances = 2, instances = 2,
axis = [0, 1, 0], axis = [0, 1, 0],
center = [0, 0, 40 + fanSize / 2], center = [0, 0, 40 + fanSize / 2],
arcDegrees = 360, arcDegrees = 360deg,
rotateDuplicates = true, rotateDuplicates = true,
) )
|> patternCircular3d( |> patternCircular3d(
@ -45,7 +45,7 @@ mountingWire
instances = 2, instances = 2,
axis = [0, 0, 1], axis = [0, 0, 1],
center = [0, 0, 0], center = [0, 0, 0],
arcDegrees = 360, arcDegrees = 360deg,
rotateDuplicates = true, rotateDuplicates = true,
) )
removableSticker removableSticker

Some files were not shown because too many files have changed in this diff Show More