Compare commits
32 Commits
kurt-camer
...
pierremtb/
Author | SHA1 | Date | |
---|---|---|---|
2a415012d7 | |||
d39dd5697c | |||
c9f7275bc4 | |||
4297dc43ae | |||
935b4ee7f5 | |||
2523242bb1 | |||
309e4fadf0 | |||
842054de09 | |||
af146284b6 | |||
5ac40e6849 | |||
d3e0625e8a | |||
484da28374 | |||
fa9e6ccea9 | |||
77fbb68419 | |||
253a7910b0 | |||
88c6be810f | |||
2860926571 | |||
fb81c02877 | |||
d78648f0f8 | |||
fe31769be5 | |||
388371b05d | |||
9db69007e5 | |||
f2a6492ab7 | |||
3ddce116e5 | |||
57f7d022ca | |||
cbc12559c7 | |||
b3cd5ef3ba | |||
a094995730 | |||
46b4b01d23 | |||
4d1eaf9381 | |||
df3668adc8 | |||
270781d5cd |
2
.github/ci-cd-scripts/playwright-electron.sh
vendored
@ -21,7 +21,7 @@ if [[ ! -f "test-results/.last-run.json" ]]; then
|
||||
fi
|
||||
|
||||
retry=1
|
||||
max_retrys=5
|
||||
max_retrys=1
|
||||
|
||||
# retry failed tests, doing our own retries because using inbuilt playwright retries causes connection issues
|
||||
while [[ $retry -le $max_retrys ]]; do
|
||||
|
3
.github/workflows/build-and-store-wasm.yml
vendored
@ -22,6 +22,9 @@ jobs:
|
||||
uses: Swatinem/rust-cache@v2
|
||||
with:
|
||||
workspaces: './src/wasm-lib'
|
||||
- uses: taiki-e/install-action@2dbeb927f58939d3aa13bf06ba0c0a34b76b9bfb
|
||||
with:
|
||||
tool: wasm-pack
|
||||
- name: build wasm
|
||||
run: yarn build:wasm
|
||||
|
||||
|
15
.github/workflows/build-apps.yml
vendored
@ -32,7 +32,6 @@ jobs:
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: '.nvmrc'
|
||||
cache: 'yarn'
|
||||
|
||||
- run: yarn install
|
||||
|
||||
@ -44,6 +43,10 @@ jobs:
|
||||
workspaces: './src/wasm-lib'
|
||||
|
||||
# TODO: see if we can fetch from main instead if no diff at src/wasm-lib
|
||||
- uses: taiki-e/install-action@2dbeb927f58939d3aa13bf06ba0c0a34b76b9bfb
|
||||
with:
|
||||
tool: wasm-pack
|
||||
|
||||
- name: Run build:wasm
|
||||
run: "yarn build:wasm"
|
||||
|
||||
@ -120,15 +123,13 @@ jobs:
|
||||
cp prepared-files/assets/icon.ico assets/icon.ico
|
||||
cp prepared-files/assets/icon.png assets/icon.png
|
||||
|
||||
- name: Sync node version and setup cache
|
||||
uses: actions/setup-node@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: '.nvmrc'
|
||||
cache: 'yarn' # Set this to npm, yarn or pnpm.
|
||||
|
||||
- name: yarn install
|
||||
# Windows is picky sometimes and fails on fetch. Step takes about ~30s
|
||||
uses: nick-fields/retry@v3.0.0
|
||||
uses: nick-fields/retry@v3.0.1
|
||||
with:
|
||||
timeout_minutes: 2
|
||||
max_attempts: 3
|
||||
@ -179,7 +180,7 @@ jobs:
|
||||
WINDOWS_CERTIFICATE_THUMBPRINT: ${{ secrets.WINDOWS_CERTIFICATE_THUMBPRINT }}
|
||||
DEBUG: "electron-notarize*"
|
||||
# TODO: Fix electron-notarize flakes. The logs above should help gather more data on failures
|
||||
uses: nick-fields/retry@v3.0.0
|
||||
uses: nick-fields/retry@v3.0.1
|
||||
with:
|
||||
timeout_minutes: 10
|
||||
max_attempts: 3
|
||||
@ -240,7 +241,7 @@ jobs:
|
||||
WINDOWS_CERTIFICATE_THUMBPRINT: ${{ secrets.WINDOWS_CERTIFICATE_THUMBPRINT }}
|
||||
DEBUG: "electron-notarize*"
|
||||
# TODO: Fix electron-notarize flakes. The logs above should help gather more data on failures
|
||||
uses: nick-fields/retry@v3.0.0
|
||||
uses: nick-fields/retry@v3.0.1
|
||||
with:
|
||||
timeout_minutes: 10
|
||||
max_attempts: 3
|
||||
|
2
.github/workflows/cargo-test.yml
vendored
@ -46,7 +46,7 @@ jobs:
|
||||
shell: bash
|
||||
run: |-
|
||||
cd "${{ matrix.dir }}"
|
||||
cargo llvm-cov nextest --workspace --lcov --output-path lcov.info --test-threads=1 --no-fail-fast -P ci 2>&1 | tee /tmp/github-actions.log
|
||||
cargo llvm-cov nextest --workspace --lcov --output-path lcov.info --test-threads=1 --retries=2 --no-fail-fast -P ci 2>&1 | tee /tmp/github-actions.log
|
||||
env:
|
||||
KITTYCAD_API_TOKEN: ${{secrets.KITTYCAD_API_TOKEN}}
|
||||
RUST_MIN_STACK: 10485760000
|
||||
|
10
.github/workflows/check-exampleKcl.yml
vendored
@ -27,6 +27,15 @@ jobs:
|
||||
const owner = context.repo.owner;
|
||||
const repo = context.repo.repo;
|
||||
|
||||
const { data: comments } = await github.rest.issues.listComments({
|
||||
owner,
|
||||
repo,
|
||||
issue_number
|
||||
});
|
||||
|
||||
const commentExists = comments.some(comment => comment.body === message);
|
||||
|
||||
if (!commentExists) {
|
||||
// Post a comment on the PR
|
||||
await github.rest.issues.createComment({
|
||||
owner,
|
||||
@ -34,3 +43,4 @@ jobs:
|
||||
issue_number,
|
||||
body: message,
|
||||
});
|
||||
}
|
11
.github/workflows/e2e-tests.yml
vendored
@ -89,6 +89,9 @@ jobs:
|
||||
continue-on-error: true
|
||||
- name: Setup Rust
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
- uses: taiki-e/install-action@2dbeb927f58939d3aa13bf06ba0c0a34b76b9bfb
|
||||
with:
|
||||
tool: wasm-pack
|
||||
- name: Cache Wasm (because rust diff)
|
||||
if: needs.check-rust-changes.outputs.rust-changed == 'true'
|
||||
uses: Swatinem/rust-cache@v2
|
||||
@ -200,9 +203,11 @@ jobs:
|
||||
- name: Run playwright/electron flow (with retries)
|
||||
id: retry
|
||||
if: ${{ !cancelled() && (success() || failure()) }}
|
||||
shell: bash
|
||||
run: |
|
||||
.github/ci-cd-scripts/playwright-electron.sh ${{matrix.shardIndex}} ${{matrix.shardTotal}} ${{matrix.os}}
|
||||
uses: nick-fields/retry@v3.0.1
|
||||
with:
|
||||
command: .github/ci-cd-scripts/playwright-electron.sh ${{matrix.shardIndex}} ${{matrix.shardTotal}} ${{matrix.os}}
|
||||
timeout_minutes: 30
|
||||
max_attempts: 25
|
||||
env:
|
||||
CI: true
|
||||
FAIL_ON_CONSOLE_ERRORS: true
|
||||
|
12
.github/workflows/static-analysis.yml
vendored
@ -37,6 +37,9 @@ jobs:
|
||||
node-version-file: '.nvmrc'
|
||||
cache: 'yarn'
|
||||
- run: yarn install
|
||||
- uses: taiki-e/install-action@2dbeb927f58939d3aa13bf06ba0c0a34b76b9bfb
|
||||
with:
|
||||
tool: wasm-pack
|
||||
- run: yarn build:wasm
|
||||
|
||||
yarn-tsc:
|
||||
@ -53,6 +56,9 @@ jobs:
|
||||
with:
|
||||
workspaces: './src/wasm-lib'
|
||||
|
||||
- uses: taiki-e/install-action@2dbeb927f58939d3aa13bf06ba0c0a34b76b9bfb
|
||||
with:
|
||||
tool: wasm-pack
|
||||
- run: yarn build:wasm
|
||||
- run: yarn tsc
|
||||
|
||||
@ -92,6 +98,9 @@ jobs:
|
||||
cache: 'yarn'
|
||||
|
||||
- run: yarn install
|
||||
- uses: taiki-e/install-action@2dbeb927f58939d3aa13bf06ba0c0a34b76b9bfb
|
||||
with:
|
||||
tool: wasm-pack
|
||||
- run: yarn build:wasm
|
||||
|
||||
- run: yarn simpleserver:bg
|
||||
@ -118,6 +127,9 @@ jobs:
|
||||
cache: 'yarn'
|
||||
|
||||
- run: yarn install
|
||||
- uses: taiki-e/install-action@2dbeb927f58939d3aa13bf06ba0c0a34b76b9bfb
|
||||
with:
|
||||
tool: wasm-pack
|
||||
- run: yarn build:wasm
|
||||
|
||||
- run: yarn simpleserver:bg
|
||||
|
5
.gitignore
vendored
@ -24,7 +24,7 @@ yarn-debug.log*
|
||||
yarn-error.log*
|
||||
|
||||
.idea
|
||||
.vscode
|
||||
# .vscode
|
||||
.helix
|
||||
src/wasm-lib/.idea
|
||||
src/wasm-lib/.vscode
|
||||
@ -41,9 +41,12 @@ e2e/playwright/playwright-secrets.env
|
||||
e2e/playwright/temp1.png
|
||||
e2e/playwright/temp2.png
|
||||
e2e/playwright/temp3.png
|
||||
# this will be overridden for specific directories
|
||||
e2e/playwright/**/*.png
|
||||
# exports from snapshot-tests.spec.ts "exports of each format should work"
|
||||
e2e/playwright/export-snapshots/*
|
||||
!e2e/playwright/export-snapshots/*.png
|
||||
!e2e/playwright/snapshot-tests.spec.ts-snapshots/*.png
|
||||
|
||||
/kcl-samples
|
||||
/test-results/
|
||||
|
5
.vscode/settings.json
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
{
|
||||
"rust-analyzer.linkedProjects": [
|
||||
"src/wasm-lib/Cargo.toml"
|
||||
]
|
||||
}
|
41
README.md
@ -48,22 +48,49 @@ We recommend downloading the latest application binary from [our Releases page](
|
||||
|
||||
## Running a development build
|
||||
|
||||
First, [install Rust via `rustup`](https://www.rust-lang.org/tools/install). This project uses a lot of Rust compiled to [WASM](https://webassembly.org/) within it. We always use the latest stable version of Rust, so you may need to run `rustup update stable`. Then, run:
|
||||
Install a node version manager such as [fnm](https://github.com/Schniz/fnm?tab=readme-ov-#installation).
|
||||
|
||||
On Windows, it's also recommended to [upgrade your PowerShell version](https://learn.microsoft.com/en-us/powershell/scripting/install/installing-powershell-on-windows?view=powershell-7.5#winget), we're using 7.
|
||||
|
||||
Then in the repo run the following to install and use the node version specified in `.nvmrc`. You might need to specify your processor architecture with `--arch arm64` or `--arch x64` if it's not autodetected.
|
||||
```
|
||||
fnm install --corepack-enabled
|
||||
fnm use
|
||||
```
|
||||
|
||||
Install the NPM dependencies with:
|
||||
```
|
||||
yarn install
|
||||
```
|
||||
|
||||
followed by:
|
||||
|
||||
This project uses a lot of Rust compiled to [WASM](https://webassembly.org/) within it. We have package scripts to run rustup, see `package.json` for reference:
|
||||
```
|
||||
# macOS/Linux
|
||||
yarn install:rust
|
||||
yarn install:wasm-pack:sh
|
||||
|
||||
# Windows
|
||||
yarn install:rust:windows
|
||||
yarn install:wasm-pack:cargo
|
||||
```
|
||||
|
||||
Then to build the WASM layer, run:
|
||||
```
|
||||
# macOS/Linux
|
||||
yarn build:wasm
|
||||
|
||||
# Windows
|
||||
yarn build:wasm:windows
|
||||
```
|
||||
|
||||
or if you have the gh cli installed
|
||||
Or if you have the `gh` cli installed and want to download the latest main wasm bundle. Note that on Windows, you need to associate .ps1 files with PowerShell, which can be done via the right click menu, selecting `C:\Program Files\PowerShell\7\pwsh.exe`, and you can install tools like `gh` via `yarn install:tools:windows`.
|
||||
|
||||
```
|
||||
./get-latest-wasm-bundle.sh # this will download the latest main wasm bundle
|
||||
# macOS/Linux
|
||||
yarn fetch:wasm
|
||||
|
||||
# Windows
|
||||
yarn fetch:wasm:windows
|
||||
```
|
||||
|
||||
That will build the WASM binary and put in the `public` dir (though gitignored).
|
||||
@ -74,7 +101,7 @@ Finally, to run the web app only, run:
|
||||
yarn start
|
||||
```
|
||||
|
||||
If you're not an KittyCAD employee you won't be able to access the dev environment, you should copy everything from `.env.production` to `.env.development` to make it point to production instead, then when you navigate to `localhost:3000` the easiest way to sign in is to paste `localStorage.setItem('TOKEN_PERSIST_KEY', "your-token-from-https://zoo.dev/account/api-tokens")` replacing the with a real token from https://zoo.dev/account/api-tokens of course, then navigate to localhost:3000 again. Note that navigating to `localhost:3000/signin` removes your token so you will need to set the token again.
|
||||
If you're not a Zoo employee you won't be able to access the dev environment, you should copy everything from `.env.production` to `.env.development` to make it point to production instead, then when you navigate to `localhost:3000` the easiest way to sign in is to paste `localStorage.setItem('TOKEN_PERSIST_KEY', "your-token-from-https://zoo.dev/account/api-tokens")` replacing the with a real token from https://zoo.dev/account/api-tokens of course, then navigate to localhost:3000 again. Note that navigating to `localhost:3000/signin` removes your token so you will need to set the token again.
|
||||
|
||||
### Development environment variables
|
||||
|
||||
@ -101,7 +128,7 @@ This will start the application and hot-reload on changes.
|
||||
|
||||
Devtools can be opened with the usual Cmd-Opt-I (Mac) or Ctrl-Shift-I (Linux and Windows).
|
||||
|
||||
To build with electron-builder, run `yarn tronb:package:dev` (or `yarn tronb:package:prod` to point to the .env.production variables)
|
||||
To package the app for your platform with electron-builder, run `yarn tronb:package:dev` (or `yarn tronb:package:prod` to point to the .env.production variables)
|
||||
|
||||
## Checking out commits / Bisecting
|
||||
|
||||
|
@ -9,7 +9,7 @@ Set the appearance of a solid. This only works on solids, not sketches or indivi
|
||||
This will work on any solid, including extruded solids, revolved solids, and shelled solids.
|
||||
|
||||
```js
|
||||
appearance(solid_set: SolidSet, color: String, metalness?: number, roughness?: number) -> SolidSet
|
||||
appearance(solidSet: SolidSet, color: String, metalness?: number, roughness?: number) -> SolidSet
|
||||
```
|
||||
|
||||
|
||||
@ -17,7 +17,7 @@ appearance(solid_set: SolidSet, color: String, metalness?: number, roughness?: n
|
||||
|
||||
| Name | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `solid_set` | [`SolidSet`](/docs/kcl/types/SolidSet) | The solid(s) whose appearance is being set | Yes |
|
||||
| `solidSet` | [`SolidSet`](/docs/kcl/types/SolidSet) | The solid(s) whose appearance is being set | Yes |
|
||||
| `color` | `String` | Color of the new material, a hex string like '#ff0000' | Yes |
|
||||
| `metalness` | `number` | Metalness of the new material, a percentage like 95.7. | No |
|
||||
| `roughness` | `number` | Roughness of the new material, a percentage like 95.7. | No |
|
||||
|
@ -9,7 +9,7 @@ Construct a 2-dimensional circle, of the specified radius, centered at
|
||||
the provided (x, y) origin point.
|
||||
|
||||
```js
|
||||
circle(data: CircleData, sketch_surface_or_group: SketchOrSurface, tag?: TagDeclarator) -> Sketch
|
||||
circle(data: CircleData, sketchSurfaceOrGroup: SketchOrSurface, tag?: TagDeclarator) -> Sketch
|
||||
```
|
||||
|
||||
|
||||
@ -18,7 +18,7 @@ circle(data: CircleData, sketch_surface_or_group: SketchOrSurface, tag?: TagDecl
|
||||
| Name | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `data` | [`CircleData`](/docs/kcl/types/CircleData) | Data for drawing an circle | Yes |
|
||||
| `sketch_surface_or_group` | [`SketchOrSurface`](/docs/kcl/types/SketchOrSurface) | A sketch surface or a sketch. | Yes |
|
||||
| `sketchSurfaceOrGroup` | [`SketchOrSurface`](/docs/kcl/types/SketchOrSurface) | A sketch surface or a sketch. | Yes |
|
||||
| `tag` | [`TagDeclarator`](/docs/kcl/types#tag-declaration) | | No |
|
||||
|
||||
### Returns
|
||||
|
@ -9,7 +9,7 @@ Construct a circle derived from 3 points.
|
||||
|
||||
|
||||
```js
|
||||
circleThreePoint(p1: [number], p2: [number], p3: [number], sketch_surface_or_group: SketchOrSurface, tag?: TagDeclarator) -> Sketch
|
||||
circleThreePoint(p1: [number], p2: [number], p3: [number], sketchSurfaceOrGroup: SketchOrSurface, tag?: TagDeclarator) -> Sketch
|
||||
```
|
||||
|
||||
|
||||
@ -20,7 +20,7 @@ circleThreePoint(p1: [number], p2: [number], p3: [number], sketch_surface_or_gro
|
||||
| `p1` | `[number]` | 1st point to derive the circle. | Yes |
|
||||
| `p2` | `[number]` | 2nd point to derive the circle. | Yes |
|
||||
| `p3` | `[number]` | 3rd point to derive the circle. | Yes |
|
||||
| `sketch_surface_or_group` | [`SketchOrSurface`](/docs/kcl/types/SketchOrSurface) | Plane or surface to sketch on. | Yes |
|
||||
| `sketchSurfaceOrGroup` | [`SketchOrSurface`](/docs/kcl/types/SketchOrSurface) | Plane or surface to sketch on. | Yes |
|
||||
| `tag` | [`TagDeclarator`](/docs/kcl/types#tag-declaration) | Identifier for the circle to reference elsewhere. | No |
|
||||
|
||||
### Returns
|
||||
|
@ -1,15 +0,0 @@
|
||||
---
|
||||
title: "std::prelude::HALF_TURN"
|
||||
excerpt: ""
|
||||
layout: manual
|
||||
---
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
```js
|
||||
std::prelude::HALF_TURN: number(deg) = 180deg
|
||||
```
|
||||
|
||||
|
@ -1,15 +0,0 @@
|
||||
---
|
||||
title: "std::prelude::QUARTER_TURN"
|
||||
excerpt: ""
|
||||
layout: manual
|
||||
---
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
```js
|
||||
std::prelude::QUARTER_TURN: number(deg) = 90deg
|
||||
```
|
||||
|
||||
|
@ -1,15 +0,0 @@
|
||||
---
|
||||
title: "std::prelude::THREE_QUARTER_TURN"
|
||||
excerpt: ""
|
||||
layout: manual
|
||||
---
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
```js
|
||||
std::prelude::THREE_QUARTER_TURN: number(deg) = 270deg
|
||||
```
|
||||
|
||||
|
@ -1,15 +0,0 @@
|
||||
---
|
||||
title: "std::prelude::ZERO"
|
||||
excerpt: ""
|
||||
layout: manual
|
||||
---
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
```js
|
||||
std::prelude::ZERO: number = 0
|
||||
```
|
||||
|
||||
|
15
docs/kcl/const_std-HALF_TURN.md
Normal file
@ -0,0 +1,15 @@
|
||||
---
|
||||
title: "std::HALF_TURN"
|
||||
excerpt: ""
|
||||
layout: manual
|
||||
---
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
```js
|
||||
std::HALF_TURN: number(deg) = 180deg
|
||||
```
|
||||
|
||||
|
15
docs/kcl/const_std-QUARTER_TURN.md
Normal file
@ -0,0 +1,15 @@
|
||||
---
|
||||
title: "std::QUARTER_TURN"
|
||||
excerpt: ""
|
||||
layout: manual
|
||||
---
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
```js
|
||||
std::QUARTER_TURN: number(deg) = 90deg
|
||||
```
|
||||
|
||||
|
15
docs/kcl/const_std-THREE_QUARTER_TURN.md
Normal file
@ -0,0 +1,15 @@
|
||||
---
|
||||
title: "std::THREE_QUARTER_TURN"
|
||||
excerpt: ""
|
||||
layout: manual
|
||||
---
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
```js
|
||||
std::THREE_QUARTER_TURN: number(deg) = 270deg
|
||||
```
|
||||
|
||||
|
15
docs/kcl/const_std-ZERO.md
Normal file
@ -0,0 +1,15 @@
|
||||
---
|
||||
title: "std::ZERO"
|
||||
excerpt: ""
|
||||
layout: manual
|
||||
---
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
```js
|
||||
std::ZERO: number = 0
|
||||
```
|
||||
|
||||
|
@ -9,7 +9,7 @@ Extend a 2-dimensional sketch through a third dimension in order to
|
||||
create new 3-dimensional volume, or if extruded into an existing volume, cut into an existing solid.
|
||||
|
||||
```js
|
||||
extrude(sketch_set: SketchSet, length: number) -> SolidSet
|
||||
extrude(sketchSet: SketchSet, length: number) -> SolidSet
|
||||
```
|
||||
|
||||
|
||||
@ -17,7 +17,7 @@ extrude(sketch_set: SketchSet, length: number) -> SolidSet
|
||||
|
||||
| Name | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `sketch_set` | [`SketchSet`](/docs/kcl/types/SketchSet) | Which sketches should be extruded | Yes |
|
||||
| `sketchSet` | [`SketchSet`](/docs/kcl/types/SketchSet) | Which sketches should be extruded | Yes |
|
||||
| `length` | `number` | How far to extrude the given sketches | Yes |
|
||||
|
||||
### Returns
|
||||
|
@ -9,7 +9,7 @@ Create a helix.
|
||||
|
||||
|
||||
```js
|
||||
helix(revolutions: number, angle_start: number, ccw?: bool, radius: number, axis: Axis3dOrEdgeReference, length?: number) -> HelixValue
|
||||
helix(revolutions: number, angleStart: number, ccw?: bool, radius: number, axis: Axis3dOrEdgeReference, length?: number) -> HelixValue
|
||||
```
|
||||
|
||||
|
||||
@ -18,7 +18,7 @@ helix(revolutions: number, angle_start: number, ccw?: bool, radius: number, axis
|
||||
| Name | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `revolutions` | `number` | Number of revolutions. | Yes |
|
||||
| `angle_start` | `number` | Start angle (in degrees). | Yes |
|
||||
| `angleStart` | `number` | Start angle (in degrees). | Yes |
|
||||
| `ccw` | `bool` | Is the helix rotation counter clockwise? The default is `false`. | No |
|
||||
| `radius` | `number` | Radius of the helix. | Yes |
|
||||
| `axis` | [`Axis3dOrEdgeReference`](/docs/kcl/types/Axis3dOrEdgeReference) | Axis to use for the helix. | Yes |
|
||||
|
@ -9,7 +9,7 @@ Use a 2-dimensional sketch to cut a hole in another 2-dimensional sketch.
|
||||
|
||||
|
||||
```js
|
||||
hole(hole_sketch: SketchSet, sketch: Sketch) -> Sketch
|
||||
hole(holeSketch: SketchSet, sketch: Sketch) -> Sketch
|
||||
```
|
||||
|
||||
|
||||
@ -17,7 +17,7 @@ hole(hole_sketch: SketchSet, sketch: Sketch) -> Sketch
|
||||
|
||||
| Name | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `hole_sketch` | [`SketchSet`](/docs/kcl/types/SketchSet) | A sketch or a group of sketches. | Yes |
|
||||
| `holeSketch` | [`SketchSet`](/docs/kcl/types/SketchSet) | A sketch or a group of sketches. | Yes |
|
||||
| `sketch` | [`Sketch`](/docs/kcl/types/Sketch) | A sketch is a collection of paths. | Yes |
|
||||
|
||||
### Returns
|
||||
|
@ -15,7 +15,7 @@ For formats lacking unit data (such as STL, OBJ, or PLY files), the default unit
|
||||
Note: The import command currently only works when using the native Modeling App.
|
||||
|
||||
```js
|
||||
import(file_path: String, options?: ImportFormat) -> ImportedGeometry
|
||||
import(filePath: String, options?: ImportFormat) -> ImportedGeometry
|
||||
```
|
||||
|
||||
|
||||
@ -23,7 +23,7 @@ import(file_path: String, options?: ImportFormat) -> ImportedGeometry
|
||||
|
||||
| Name | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `file_path` | `String` | | Yes |
|
||||
| `filePath` | `String` | | Yes |
|
||||
| `options` | [`ImportFormat`](/docs/kcl/types/ImportFormat) | Import format specifier | No |
|
||||
|
||||
### Returns
|
||||
|
@ -10,6 +10,10 @@ layout: manual
|
||||
* [Modules](kcl/modules)
|
||||
* [Known Issues](kcl/KNOWN-ISSUES)
|
||||
* **`std`**
|
||||
* [`HALF_TURN`](kcl/const_std-HALF_TURN)
|
||||
* [`QUARTER_TURN`](kcl/const_std-QUARTER_TURN)
|
||||
* [`THREE_QUARTER_TURN`](kcl/const_std-THREE_QUARTER_TURN)
|
||||
* [`ZERO`](kcl/const_std-ZERO)
|
||||
* [`abs`](kcl/abs)
|
||||
* [`acos`](kcl/acos)
|
||||
* [`angleToMatchLengthX`](kcl/angleToMatchLengthX)
|
||||
@ -118,8 +122,3 @@ layout: manual
|
||||
* [`cos`](kcl/std-math-cos)
|
||||
* [`sin`](kcl/std-math-sin)
|
||||
* [`tan`](kcl/std-math-tan)
|
||||
* **`std::prelude`**
|
||||
* [`HALF_TURN`](kcl/const_std-prelude-HALF_TURN)
|
||||
* [`QUARTER_TURN`](kcl/const_std-prelude-QUARTER_TURN)
|
||||
* [`THREE_QUARTER_TURN`](kcl/const_std-prelude-THREE_QUARTER_TURN)
|
||||
* [`ZERO`](kcl/const_std-prelude-ZERO)
|
||||
|
@ -9,7 +9,7 @@ Extend the current sketch with a new straight line.
|
||||
|
||||
|
||||
```js
|
||||
line(sketch: Sketch, end_absolute?: [number], end?: [number], tag?: TagDeclarator) -> Sketch
|
||||
line(sketch: Sketch, endAbsolute?: [number], end?: [number], tag?: TagDeclarator) -> Sketch
|
||||
```
|
||||
|
||||
|
||||
@ -18,7 +18,7 @@ line(sketch: Sketch, end_absolute?: [number], end?: [number], tag?: TagDeclarato
|
||||
| Name | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `sketch` | [`Sketch`](/docs/kcl/types/Sketch) | Which sketch should this path be added to? | Yes |
|
||||
| `end_absolute` | `[number]` | Which absolute point should this line go to? Incompatible with `end`. | No |
|
||||
| `endAbsolute` | `[number]` | Which absolute point should this line go to? Incompatible with `end`. | No |
|
||||
| `end` | `[number]` | How far away (along the X and Y axes) should this line go? Incompatible with `endAbsolute`. | No |
|
||||
| `tag` | [`TagDeclarator`](/docs/kcl/types#tag-declaration) | Create a new tag which refers to this line | No |
|
||||
|
||||
|
@ -9,7 +9,7 @@ Create a 3D surface or solid by interpolating between two or more sketches.
|
||||
The sketches need to closed and on the same plane.
|
||||
|
||||
```js
|
||||
loft(sketches: [Sketch], v_degree: NonZeroU32, bez_approximate_rational: bool, base_curve_index?: integer, tolerance?: number) -> Solid
|
||||
loft(sketches: [Sketch], vDegree: NonZeroU32, bezApproximateRational: bool, baseCurveIndex?: integer, tolerance?: number) -> Solid
|
||||
```
|
||||
|
||||
|
||||
@ -18,9 +18,9 @@ loft(sketches: [Sketch], v_degree: NonZeroU32, bez_approximate_rational: bool, b
|
||||
| Name | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `sketches` | [`[Sketch]`](/docs/kcl/types/Sketch) | Which sketches to loft. Must include at least 2 sketches. | Yes |
|
||||
| `v_degree` | `NonZeroU32` | Degree of the interpolation. Must be greater than zero. For example, use 2 for quadratic, or 3 for cubic interpolation in the V direction. This defaults to 2, if not specified. | Yes |
|
||||
| `bez_approximate_rational` | `bool` | Attempt to approximate rational curves (such as arcs) using a bezier. This will remove banding around interpolations between arcs and non-arcs. It may produce errors in other scenarios Over time, this field won't be necessary. | Yes |
|
||||
| `base_curve_index` | `integer` | This can be set to override the automatically determined topological base curve, which is usually the first section encountered. | No |
|
||||
| `vDegree` | `NonZeroU32` | Degree of the interpolation. Must be greater than zero. For example, use 2 for quadratic, or 3 for cubic interpolation in the V direction. This defaults to 2, if not specified. | Yes |
|
||||
| `bezApproximateRational` | `bool` | Attempt to approximate rational curves (such as arcs) using a bezier. This will remove banding around interpolations between arcs and non-arcs. It may produce errors in other scenarios Over time, this field won't be necessary. | Yes |
|
||||
| `baseCurveIndex` | `integer` | This can be set to override the automatically determined topological base curve, which is usually the first section encountered. | No |
|
||||
| `tolerance` | `number` | Tolerance for the loft operation. | No |
|
||||
|
||||
### Returns
|
||||
|
@ -9,7 +9,7 @@ Apply a function to every element of a list.
|
||||
Given a list like `[a, b, c]`, and a function like `f`, returns `[f(a), f(b), f(c)]`
|
||||
|
||||
```js
|
||||
map(array: [KclValue], map_fn: FunctionParam) -> [KclValue]
|
||||
map(array: [KclValue], mapFn: FunctionSource) -> [KclValue]
|
||||
```
|
||||
|
||||
|
||||
@ -18,7 +18,7 @@ map(array: [KclValue], map_fn: FunctionParam) -> [KclValue]
|
||||
| Name | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `array` | [`[KclValue]`](/docs/kcl/types/KclValue) | | Yes |
|
||||
| `map_fn` | `FunctionParam` | | Yes |
|
||||
| `mapFn` | `FunctionSource` | | Yes |
|
||||
|
||||
### Returns
|
||||
|
||||
|
@ -11,7 +11,7 @@ Only works on unclosed sketches for now.
|
||||
Mirror occurs around a local sketch axis rather than a global axis.
|
||||
|
||||
```js
|
||||
mirror2d(data: Mirror2dData, sketch_set: SketchSet) -> [Sketch]
|
||||
mirror2d(data: Mirror2dData, sketchSet: SketchSet) -> [Sketch]
|
||||
```
|
||||
|
||||
|
||||
@ -20,7 +20,7 @@ mirror2d(data: Mirror2dData, sketch_set: SketchSet) -> [Sketch]
|
||||
| Name | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `data` | [`Mirror2dData`](/docs/kcl/types/Mirror2dData) | Data for a mirror. | Yes |
|
||||
| `sketch_set` | [`SketchSet`](/docs/kcl/types/SketchSet) | A sketch or a group of sketches. | Yes |
|
||||
| `sketchSet` | [`SketchSet`](/docs/kcl/types/SketchSet) | A sketch or a group of sketches. | Yes |
|
||||
|
||||
### Returns
|
||||
|
||||
|
@ -9,7 +9,7 @@ Repeat a 2-dimensional sketch some number of times along a partial or
|
||||
complete circle some specified number of times. Each object may additionally be rotated along the circle, ensuring orentation of the solid with respect to the center of the circle is maintained.
|
||||
|
||||
```js
|
||||
patternCircular2d(sketch_set: SketchSet, instances: integer, center: [number], arc_degrees: number, rotate_duplicates: bool, use_original?: bool) -> [Sketch]
|
||||
patternCircular2d(sketchSet: SketchSet, instances: integer, center: [number], arcDegrees: number, rotateDuplicates: bool, useOriginal?: bool) -> [Sketch]
|
||||
```
|
||||
|
||||
|
||||
@ -17,12 +17,12 @@ patternCircular2d(sketch_set: SketchSet, instances: integer, center: [number], a
|
||||
|
||||
| Name | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `sketch_set` | [`SketchSet`](/docs/kcl/types/SketchSet) | Which sketch(es) to pattern | Yes |
|
||||
| `sketchSet` | [`SketchSet`](/docs/kcl/types/SketchSet) | Which sketch(es) to pattern | Yes |
|
||||
| `instances` | `integer` | The number of total instances. Must be greater than or equal to 1. This includes the original entity. For example, if instances is 2, there will be two copies -- the original, and one new copy. If instances is 1, this has no effect. | Yes |
|
||||
| `center` | `[number]` | The center about which to make the pattern. This is a 2D vector. | Yes |
|
||||
| `arc_degrees` | `number` | The arc angle (in degrees) to place the repetitions. Must be greater than 0. | Yes |
|
||||
| `rotate_duplicates` | `bool` | Whether or not to rotate the duplicates as they are copied. | Yes |
|
||||
| `use_original` | `bool` | If the target was sketched on an extrusion, setting this will use the original sketch as the target, not the entire joined solid. Defaults to false. | No |
|
||||
| `arcDegrees` | `number` | The arc angle (in degrees) to place the repetitions. Must be greater than 0. | Yes |
|
||||
| `rotateDuplicates` | `bool` | Whether or not to rotate the duplicates as they are copied. | Yes |
|
||||
| `useOriginal` | `bool` | If the target was sketched on an extrusion, setting this will use the original sketch as the target, not the entire joined solid. Defaults to false. | No |
|
||||
|
||||
### Returns
|
||||
|
||||
|
@ -9,7 +9,7 @@ Repeat a 3-dimensional solid some number of times along a partial or
|
||||
complete circle some specified number of times. Each object may additionally be rotated along the circle, ensuring orentation of the solid with respect to the center of the circle is maintained.
|
||||
|
||||
```js
|
||||
patternCircular3d(solid_set: SolidSet, instances: integer, axis: [number], center: [number], arc_degrees: number, rotate_duplicates: bool, use_original?: bool) -> [Solid]
|
||||
patternCircular3d(solidSet: SolidSet, instances: integer, axis: [number], center: [number], arcDegrees: number, rotateDuplicates: bool, useOriginal?: bool) -> [Solid]
|
||||
```
|
||||
|
||||
|
||||
@ -17,13 +17,13 @@ patternCircular3d(solid_set: SolidSet, instances: integer, axis: [number], cente
|
||||
|
||||
| Name | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `solid_set` | [`SolidSet`](/docs/kcl/types/SolidSet) | Which solid(s) to pattern | Yes |
|
||||
| `solidSet` | [`SolidSet`](/docs/kcl/types/SolidSet) | Which solid(s) to pattern | Yes |
|
||||
| `instances` | `integer` | The number of total instances. Must be greater than or equal to 1. This includes the original entity. For example, if instances is 2, there will be two copies -- the original, and one new copy. If instances is 1, this has no effect. | Yes |
|
||||
| `axis` | `[number]` | The axis around which to make the pattern. This is a 3D vector | Yes |
|
||||
| `center` | `[number]` | The center about which to make the pattern. This is a 3D vector. | Yes |
|
||||
| `arc_degrees` | `number` | The arc angle (in degrees) to place the repetitions. Must be greater than 0. | Yes |
|
||||
| `rotate_duplicates` | `bool` | Whether or not to rotate the duplicates as they are copied. | Yes |
|
||||
| `use_original` | `bool` | If the target was sketched on an extrusion, setting this will use the original sketch as the target, not the entire joined solid. Defaults to false. | No |
|
||||
| `arcDegrees` | `number` | The arc angle (in degrees) to place the repetitions. Must be greater than 0. | Yes |
|
||||
| `rotateDuplicates` | `bool` | Whether or not to rotate the duplicates as they are copied. | Yes |
|
||||
| `useOriginal` | `bool` | If the target was sketched on an extrusion, setting this will use the original sketch as the target, not the entire joined solid. Defaults to false. | No |
|
||||
|
||||
### Returns
|
||||
|
||||
|
@ -9,7 +9,7 @@ Repeat a 2-dimensional sketch along some dimension, with a dynamic amount
|
||||
of distance between each repetition, some specified number of times.
|
||||
|
||||
```js
|
||||
patternLinear2d(sketch_set: SketchSet, instances: integer, distance: number, axis: [number], use_original?: bool) -> [Sketch]
|
||||
patternLinear2d(sketchSet: SketchSet, instances: integer, distance: number, axis: [number], useOriginal?: bool) -> [Sketch]
|
||||
```
|
||||
|
||||
|
||||
@ -17,11 +17,11 @@ patternLinear2d(sketch_set: SketchSet, instances: integer, distance: number, axi
|
||||
|
||||
| Name | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `sketch_set` | [`SketchSet`](/docs/kcl/types/SketchSet) | The sketch(es) to duplicate | Yes |
|
||||
| `sketchSet` | [`SketchSet`](/docs/kcl/types/SketchSet) | The sketch(es) to duplicate | Yes |
|
||||
| `instances` | `integer` | The number of total instances. Must be greater than or equal to 1. This includes the original entity. For example, if instances is 2, there will be two copies -- the original, and one new copy. If instances is 1, this has no effect. | Yes |
|
||||
| `distance` | `number` | Distance between each repetition. Also known as 'spacing'. | Yes |
|
||||
| `axis` | `[number]` | The axis of the pattern. A 2D vector. | Yes |
|
||||
| `use_original` | `bool` | If the target was sketched on an extrusion, setting this will use the original sketch as the target, not the entire joined solid. Defaults to false. | No |
|
||||
| `useOriginal` | `bool` | If the target was sketched on an extrusion, setting this will use the original sketch as the target, not the entire joined solid. Defaults to false. | No |
|
||||
|
||||
### Returns
|
||||
|
||||
|
@ -9,7 +9,7 @@ Repeat a 3-dimensional solid along a linear path, with a dynamic amount
|
||||
of distance between each repetition, some specified number of times.
|
||||
|
||||
```js
|
||||
patternLinear3d(solid_set: SolidSet, instances: integer, distance: number, axis: [number], use_original?: bool) -> [Solid]
|
||||
patternLinear3d(solidSet: SolidSet, instances: integer, distance: number, axis: [number], useOriginal?: bool) -> [Solid]
|
||||
```
|
||||
|
||||
|
||||
@ -17,11 +17,11 @@ patternLinear3d(solid_set: SolidSet, instances: integer, distance: number, axis:
|
||||
|
||||
| Name | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `solid_set` | [`SolidSet`](/docs/kcl/types/SolidSet) | The solid(s) to duplicate | Yes |
|
||||
| `solidSet` | [`SolidSet`](/docs/kcl/types/SolidSet) | The solid(s) to duplicate | Yes |
|
||||
| `instances` | `integer` | The number of total instances. Must be greater than or equal to 1. This includes the original entity. For example, if instances is 2, there will be two copies -- the original, and one new copy. If instances is 1, this has no effect. | Yes |
|
||||
| `distance` | `number` | Distance between each repetition. Also known as 'spacing'. | Yes |
|
||||
| `axis` | `[number]` | The axis of the pattern. A 2D vector. | Yes |
|
||||
| `use_original` | `bool` | If the target was sketched on an extrusion, setting this will use the original sketch as the target, not the entire joined solid. Defaults to false. | No |
|
||||
| `useOriginal` | `bool` | If the target was sketched on an extrusion, setting this will use the original sketch as the target, not the entire joined solid. Defaults to false. | No |
|
||||
|
||||
### Returns
|
||||
|
||||
|
@ -35,7 +35,7 @@ The transform function returns a transform object. All properties of the object
|
||||
- `rotation.origin` (either "local" i.e. rotate around its own center, "global" i.e. rotate around the scene's center, or a 3D point, defaults to "local")
|
||||
|
||||
```js
|
||||
patternTransform(solid_set: SolidSet, instances: integer, transform: FunctionParam, use_original?: bool) -> [Solid]
|
||||
patternTransform(solidSet: SolidSet, instances: integer, transform: FunctionSource, useOriginal?: bool) -> [Solid]
|
||||
```
|
||||
|
||||
|
||||
@ -43,10 +43,10 @@ patternTransform(solid_set: SolidSet, instances: integer, transform: FunctionPar
|
||||
|
||||
| Name | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `solid_set` | [`SolidSet`](/docs/kcl/types/SolidSet) | The solid(s) to duplicate | Yes |
|
||||
| `solidSet` | [`SolidSet`](/docs/kcl/types/SolidSet) | The solid(s) to duplicate | Yes |
|
||||
| `instances` | `integer` | The number of total instances. Must be greater than or equal to 1. This includes the original entity. For example, if instances is 2, there will be two copies -- the original, and one new copy. If instances is 1, this has no effect. | Yes |
|
||||
| `transform` | `FunctionParam` | How each replica should be transformed. The transform function takes a single parameter: an integer representing which number replication the transform is for. E.g. the first replica to be transformed will be passed the argument `1`. This simplifies your math: the transform function can rely on id `0` being the original instance passed into the `patternTransform`. See the examples. | Yes |
|
||||
| `use_original` | `bool` | If the target was sketched on an extrusion, setting this will use the original sketch as the target, not the entire joined solid. Defaults to false. | No |
|
||||
| `transform` | `FunctionSource` | How each replica should be transformed. The transform function takes a single parameter: an integer representing which number replication the transform is for. E.g. the first replica to be transformed will be passed the argument `1`. This simplifies your math: the transform function can rely on id `0` being the original instance passed into the `patternTransform`. See the examples. | Yes |
|
||||
| `useOriginal` | `bool` | If the target was sketched on an extrusion, setting this will use the original sketch as the target, not the entire joined solid. Defaults to false. | No |
|
||||
|
||||
### Returns
|
||||
|
||||
|
@ -9,7 +9,7 @@ Just like patternTransform, but works on 2D sketches not 3D solids.
|
||||
|
||||
|
||||
```js
|
||||
patternTransform2d(sketch_set: SketchSet, instances: integer, transform: FunctionParam, use_original?: bool) -> [Sketch]
|
||||
patternTransform2d(sketchSet: SketchSet, instances: integer, transform: FunctionSource, useOriginal?: bool) -> [Sketch]
|
||||
```
|
||||
|
||||
|
||||
@ -17,10 +17,10 @@ patternTransform2d(sketch_set: SketchSet, instances: integer, transform: Functio
|
||||
|
||||
| Name | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `sketch_set` | [`SketchSet`](/docs/kcl/types/SketchSet) | The sketch(es) to duplicate | Yes |
|
||||
| `sketchSet` | [`SketchSet`](/docs/kcl/types/SketchSet) | The sketch(es) to duplicate | Yes |
|
||||
| `instances` | `integer` | The number of total instances. Must be greater than or equal to 1. This includes the original entity. For example, if instances is 2, there will be two copies -- the original, and one new copy. If instances is 1, this has no effect. | Yes |
|
||||
| `transform` | `FunctionParam` | How each replica should be transformed. The transform function takes a single parameter: an integer representing which number replication the transform is for. E.g. the first replica to be transformed will be passed the argument `1`. This simplifies your math: the transform function can rely on id `0` being the original instance passed into the `patternTransform`. See the examples. | Yes |
|
||||
| `use_original` | `bool` | If the target was sketched on an extrusion, setting this will use the original sketch as the target, not the entire joined solid. Defaults to false. | No |
|
||||
| `transform` | `FunctionSource` | How each replica should be transformed. The transform function takes a single parameter: an integer representing which number replication the transform is for. E.g. the first replica to be transformed will be passed the argument `1`. This simplifies your math: the transform function can rely on id `0` being the original instance passed into the `patternTransform`. See the examples. | Yes |
|
||||
| `useOriginal` | `bool` | If the target was sketched on an extrusion, setting this will use the original sketch as the target, not the entire joined solid. Defaults to false. | No |
|
||||
|
||||
### Returns
|
||||
|
||||
|
@ -9,7 +9,7 @@ Create a regular polygon with the specified number of sides that is either inscr
|
||||
|
||||
|
||||
```js
|
||||
polygon(data: PolygonData, sketch_surface_or_group: SketchOrSurface, tag?: TagDeclarator) -> Sketch
|
||||
polygon(data: PolygonData, sketchSurfaceOrGroup: SketchOrSurface, tag?: TagDeclarator) -> Sketch
|
||||
```
|
||||
|
||||
|
||||
@ -18,7 +18,7 @@ polygon(data: PolygonData, sketch_surface_or_group: SketchOrSurface, tag?: TagDe
|
||||
| Name | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `data` | [`PolygonData`](/docs/kcl/types/PolygonData) | Data for drawing a polygon | Yes |
|
||||
| `sketch_surface_or_group` | [`SketchOrSurface`](/docs/kcl/types/SketchOrSurface) | A sketch surface or a sketch. | Yes |
|
||||
| `sketchSurfaceOrGroup` | [`SketchOrSurface`](/docs/kcl/types/SketchOrSurface) | A sketch surface or a sketch. | Yes |
|
||||
| `tag` | [`TagDeclarator`](/docs/kcl/types#tag-declaration) | | No |
|
||||
|
||||
### Returns
|
||||
|
@ -9,7 +9,7 @@ Take a starting value. Then, for each element of an array, calculate the next va
|
||||
using the previous value and the element.
|
||||
|
||||
```js
|
||||
reduce(array: [KclValue], start: KclValue, reduce_fn: FunctionParam) -> KclValue
|
||||
reduce(array: [KclValue], start: KclValue, reduceFn: FunctionSource) -> KclValue
|
||||
```
|
||||
|
||||
|
||||
@ -19,7 +19,7 @@ reduce(array: [KclValue], start: KclValue, reduce_fn: FunctionParam) -> KclValue
|
||||
|----------|------|-------------|----------|
|
||||
| `array` | [`[KclValue]`](/docs/kcl/types/KclValue) | | Yes |
|
||||
| `start` | [`KclValue`](/docs/kcl/types/KclValue) | Any KCL value. | Yes |
|
||||
| `reduce_fn` | `FunctionParam` | | Yes |
|
||||
| `reduceFn` | `FunctionSource` | | Yes |
|
||||
|
||||
### Returns
|
||||
|
||||
|
@ -9,7 +9,7 @@ Remove volume from a 3-dimensional shape such that a wall of the
|
||||
provided thickness remains, taking volume starting at the provided face, leaving it open in that direction.
|
||||
|
||||
```js
|
||||
shell(solid_set: SolidSet, thickness: number, faces: [FaceTag]) -> SolidSet
|
||||
shell(solidSet: SolidSet, thickness: number, faces: [FaceTag]) -> SolidSet
|
||||
```
|
||||
|
||||
|
||||
@ -17,7 +17,7 @@ shell(solid_set: SolidSet, thickness: number, faces: [FaceTag]) -> SolidSet
|
||||
|
||||
| Name | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `solid_set` | [`SolidSet`](/docs/kcl/types/SolidSet) | Which solid (or solids) to shell out | Yes |
|
||||
| `solidSet` | [`SolidSet`](/docs/kcl/types/SolidSet) | Which solid (or solids) to shell out | Yes |
|
||||
| `thickness` | `number` | The thickness of the shell | Yes |
|
||||
| `faces` | [`[FaceTag]`](/docs/kcl/types/FaceTag) | The faces you want removed | Yes |
|
||||
|
||||
|
@ -9,7 +9,7 @@ Start a new profile at a given point.
|
||||
|
||||
|
||||
```js
|
||||
startProfileAt(to: [number], sketch_surface: SketchSurface, tag?: TagDeclarator) -> Sketch
|
||||
startProfileAt(to: [number], sketchSurface: SketchSurface, tag?: TagDeclarator) -> Sketch
|
||||
```
|
||||
|
||||
|
||||
@ -18,7 +18,7 @@ startProfileAt(to: [number], sketch_surface: SketchSurface, tag?: TagDeclarator)
|
||||
| Name | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `to` | `[number]` | | Yes |
|
||||
| `sketch_surface` | [`SketchSurface`](/docs/kcl/types/SketchSurface) | A sketch type. | Yes |
|
||||
| `sketchSurface` | [`SketchSurface`](/docs/kcl/types/SketchSurface) | A sketch type. | Yes |
|
||||
| `tag` | [`TagDeclarator`](/docs/kcl/types#tag-declaration) | | No |
|
||||
|
||||
### Returns
|
||||
|
13810
docs/kcl/std.json
@ -266,10 +266,10 @@ myRect = rect([20, 0])
|
||||
|
||||
myRect
|
||||
|> extrude(10, %)
|
||||
|> fillet({
|
||||
|> fillet(
|
||||
radius = 0.5,
|
||||
tags = [myRect.tags.rectangleSegmentA001]
|
||||
}, %)
|
||||
)
|
||||
```
|
||||
|
||||
See how we use the tag `rectangleSegmentA001` in the `fillet` function outside
|
||||
|
@ -295,7 +295,6 @@ Data for an imported geometry.
|
||||
| Property | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `type` |enum: `Function`| | No |
|
||||
| `memory` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
|
||||
| `__meta` |`[` [`Metadata`](/docs/kcl/types/Metadata) `]`| | No |
|
||||
|
||||
|
||||
|
@ -82,7 +82,7 @@ test.describe('Command bar tests', { tag: ['@skipWin'] }, () => {
|
||||
await page.keyboard.press('Enter') // submit
|
||||
await page.waitForTimeout(100)
|
||||
await expect(page.locator('.cm-activeLine')).toContainText(
|
||||
`fillet({ radius = ${KCL_DEFAULT_LENGTH}, tags = [seg01] }, %)`
|
||||
`fillet( radius = ${KCL_DEFAULT_LENGTH}, tags = [seg01] )`
|
||||
)
|
||||
})
|
||||
|
||||
@ -219,7 +219,11 @@ test.describe('Command bar tests', { tag: ['@skipWin'] }, () => {
|
||||
}
|
||||
)
|
||||
|
||||
test('Can extrude from the command bar', async ({ page, homePage }) => {
|
||||
test('Can extrude from the command bar', async ({
|
||||
page,
|
||||
homePage,
|
||||
cmdBar,
|
||||
}) => {
|
||||
await page.addInitScript(async () => {
|
||||
localStorage.setItem(
|
||||
'persistCode',
|
||||
@ -254,7 +258,7 @@ test.describe('Command bar tests', { tag: ['@skipWin'] }, () => {
|
||||
await expect(cmdSearchBar).toBeVisible()
|
||||
|
||||
// Search for extrude command and choose it
|
||||
await page.getByRole('option', { name: 'Extrude' }).click()
|
||||
await cmdBar.cmdOptions.getByText('Extrude').click()
|
||||
|
||||
// Assert that we're on the selection step
|
||||
await expect(page.getByRole('button', { name: 'selection' })).toBeDisabled()
|
||||
|
@ -59,18 +59,25 @@ export interface Fixtures {
|
||||
homePage: HomePageFixture
|
||||
}
|
||||
export class AuthenticatedTronApp {
|
||||
public readonly _page: Page
|
||||
public originalPage: Page
|
||||
public page: Page
|
||||
public browserContext: BrowserContext
|
||||
public context: BrowserContext
|
||||
public readonly testInfo: TestInfo
|
||||
public electronApp: ElectronApplication | undefined
|
||||
public readonly viewPortSize = { width: 1200, height: 500 }
|
||||
public dir: string = ''
|
||||
|
||||
constructor(context: BrowserContext, page: Page, testInfo: TestInfo) {
|
||||
this._page = page
|
||||
this.page = page
|
||||
this.context = context
|
||||
constructor(
|
||||
browserContext: BrowserContext,
|
||||
originalPage: Page,
|
||||
testInfo: TestInfo
|
||||
) {
|
||||
this.page = originalPage
|
||||
this.originalPage = originalPage
|
||||
this.browserContext = browserContext
|
||||
// Will be overwritten in the initializer
|
||||
this.context = browserContext
|
||||
this.testInfo = testInfo
|
||||
}
|
||||
async initialise(
|
||||
@ -86,9 +93,17 @@ export class AuthenticatedTronApp {
|
||||
folderSetupFn: arg.folderSetupFn,
|
||||
cleanProjectDir: arg.cleanProjectDir,
|
||||
appSettings: arg.appSettings,
|
||||
viewport: this.viewPortSize,
|
||||
})
|
||||
this.page = page
|
||||
|
||||
// These assignments "fix" some brokenness in the Playwright Workbench when
|
||||
// running against electron applications.
|
||||
// The timeline is still broken but failure screenshots work again.
|
||||
this.context = context
|
||||
// TODO: try to get this to work again for screenshots, but it messed with test ends when enabled
|
||||
// Object.assign(this.browserContext, this.context)
|
||||
|
||||
this.electronApp = electronApp
|
||||
this.dir = dir
|
||||
|
||||
|
@ -13,8 +13,8 @@ import {
|
||||
import * as TOML from '@iarna/toml'
|
||||
import { expectPixelColor } from './fixtures/sceneFixture'
|
||||
|
||||
// Because onboarding relies on an app setting we need to set it as incompletel
|
||||
// for all these tests.
|
||||
// Because our default test settings have the onboardingStatus set to 'dismissed',
|
||||
// we must set it to empty for the tests where we want to see the onboarding immediately.
|
||||
|
||||
test.describe('Onboarding tests', () => {
|
||||
test(
|
||||
@ -22,7 +22,7 @@ test.describe('Onboarding tests', () => {
|
||||
{
|
||||
appSettings: {
|
||||
app: {
|
||||
onboardingStatus: 'incomplete',
|
||||
onboardingStatus: '',
|
||||
},
|
||||
},
|
||||
cleanProjectDir: true,
|
||||
@ -63,7 +63,7 @@ test.describe('Onboarding tests', () => {
|
||||
tag: '@electron',
|
||||
appSettings: {
|
||||
app: {
|
||||
onboardingStatus: 'incomplete',
|
||||
onboardingStatus: '',
|
||||
},
|
||||
},
|
||||
cleanProjectDir: true,
|
||||
@ -106,11 +106,6 @@ test.describe('Onboarding tests', () => {
|
||||
test(
|
||||
'Code resets after confirmation',
|
||||
{
|
||||
appSettings: {
|
||||
app: {
|
||||
onboardingStatus: 'incomplete',
|
||||
},
|
||||
},
|
||||
cleanProjectDir: true,
|
||||
},
|
||||
async ({ context, page, homePage }) => {
|
||||
@ -158,7 +153,7 @@ test.describe('Onboarding tests', () => {
|
||||
{
|
||||
appSettings: {
|
||||
app: {
|
||||
onboardingStatus: 'incomplete',
|
||||
onboardingStatus: '',
|
||||
},
|
||||
},
|
||||
},
|
||||
@ -319,7 +314,7 @@ test.describe('Onboarding tests', () => {
|
||||
{
|
||||
appSettings: {
|
||||
app: {
|
||||
onboardingStatus: 'incomplete',
|
||||
onboardingStatus: '',
|
||||
},
|
||||
},
|
||||
cleanProjectDir: true,
|
||||
@ -392,7 +387,7 @@ test.describe('Onboarding tests', () => {
|
||||
{
|
||||
appSettings: {
|
||||
app: {
|
||||
onboardingStatus: 'incomplete',
|
||||
onboardingStatus: '',
|
||||
},
|
||||
},
|
||||
cleanProjectDir: true,
|
||||
|
@ -211,12 +211,13 @@ test.describe('Point-and-click tests', { tag: ['@skipWin'] }, () => {
|
||||
cameraPos: { x: 16020, y: -2000, z: 10500 },
|
||||
cameraTarget: { x: -150, y: -4500, z: -80 },
|
||||
beforeChamferSnippet: `angledLine([segAng(rectangleSegmentA001)-90,217.26],%,$seg01)
|
||||
chamfer({length = 30,tags = [
|
||||
chamfer(length = 30,tags = [
|
||||
seg01,
|
||||
getNextAdjacentEdge(yo),
|
||||
getNextAdjacentEdge(seg02),
|
||||
getOppositeEdge(seg01)
|
||||
]}, %)`,
|
||||
],
|
||||
)`,
|
||||
|
||||
afterChamferSelectSnippet:
|
||||
'sketch002 = startSketchOn(extrude001, seg03)',
|
||||
@ -236,14 +237,14 @@ test.describe('Point-and-click tests', { tag: ['@skipWin'] }, () => {
|
||||
beforeChamferSnippet: `angledLine([
|
||||
segAng(rectangleSegmentA001) - 90,
|
||||
217.26
|
||||
], %, $seg01)chamfer({
|
||||
], %, $seg01)chamfer(
|
||||
length = 30,
|
||||
tags = [
|
||||
seg01,
|
||||
getNextAdjacentEdge(yo),
|
||||
getNextAdjacentEdge(seg02)
|
||||
]
|
||||
}, %)`,
|
||||
)`,
|
||||
|
||||
afterChamferSelectSnippet:
|
||||
'sketch003 = startSketchOn(extrude001, seg04)',
|
||||
@ -260,13 +261,13 @@ test.describe('Point-and-click tests', { tag: ['@skipWin'] }, () => {
|
||||
clickCoords: { x: 677, y: 87 },
|
||||
cameraPos: { x: -6200, y: 1500, z: 6200 },
|
||||
cameraTarget: { x: 8300, y: 1100, z: 4800 },
|
||||
beforeChamferSnippet: `angledLine([0, 268.43], %, $rectangleSegmentA001)chamfer({
|
||||
beforeChamferSnippet: `angledLine([0, 268.43], %, $rectangleSegmentA001)chamfer(
|
||||
length = 30,
|
||||
tags = [
|
||||
getNextAdjacentEdge(yo),
|
||||
getNextAdjacentEdge(seg02)
|
||||
]
|
||||
}, %)`,
|
||||
)`,
|
||||
afterChamferSelectSnippet:
|
||||
'sketch004 = startSketchOn(extrude001, seg05)',
|
||||
afterRectangle1stClickSnippet:
|
||||
@ -282,10 +283,9 @@ test.describe('Point-and-click tests', { tag: ['@skipWin'] }, () => {
|
||||
clickCoords: { x: 620, y: 300 },
|
||||
cameraPos: { x: -1100, y: -7700, z: 1600 },
|
||||
cameraTarget: { x: 1450, y: 670, z: 4000 },
|
||||
beforeChamferSnippet: `chamfer({
|
||||
length = 30,
|
||||
tags = [getNextAdjacentEdge(yo)]
|
||||
}, %)`,
|
||||
beforeChamferSnippet: `chamfer(length = 30, tags = [getNextAdjacentEdge(yo)])`,
|
||||
beforeChamferSnippetEnd:
|
||||
'|> chamfer(length = 30, tags = [getNextAdjacentEdge(yo)])',
|
||||
afterChamferSelectSnippet:
|
||||
'sketch005 = startSketchOn(extrude001, seg06)',
|
||||
afterRectangle1stClickSnippet:
|
||||
@ -313,30 +313,15 @@ test.describe('Point-and-click tests', { tag: ['@skipWin'] }, () => {
|
||||
|> line(endAbsolute = [profileStartX(%), profileStartY(%)], tag = $seg02)
|
||||
|> close()
|
||||
extrude001 = extrude(sketch001, length = 100)
|
||||
|> chamfer({
|
||||
length = 30,
|
||||
tags = [getOppositeEdge(seg01)]
|
||||
}, %, $seg03)
|
||||
|> chamfer({ length = 30, tags = [seg01] }, %, $seg04)
|
||||
|> chamfer({
|
||||
length = 30,
|
||||
tags = [getNextAdjacentEdge(seg02)]
|
||||
}, %, $seg05)
|
||||
|> chamfer({
|
||||
length = 30,
|
||||
tags = [getNextAdjacentEdge(yo)]
|
||||
}, %, $seg06)
|
||||
|> chamfer(length = 30, tags = [getOppositeEdge(seg01)], tag = $seg03)
|
||||
|> chamfer(length = 30, tags = [seg01], tag = $seg04)
|
||||
|> chamfer(length = 30, tags = [getNextAdjacentEdge(seg02)], tag = $seg05)
|
||||
|> chamfer(length = 30, tags = [getNextAdjacentEdge(yo)], tag = $seg06)
|
||||
sketch005 = startSketchOn(extrude001, seg06)
|
||||
profile004=startProfileAt([-23.43,19.69], sketch005)
|
||||
|> angledLine([0, 9.1], %, $rectangleSegmentA005)
|
||||
|> angledLine([
|
||||
segAng(rectangleSegmentA005) - 90,
|
||||
84.07
|
||||
], %)
|
||||
|> angledLine([
|
||||
segAng(rectangleSegmentA005),
|
||||
-segLen(rectangleSegmentA005)
|
||||
], %)
|
||||
|> angledLine([segAng(rectangleSegmentA005) - 90, 84.07], %)
|
||||
|> angledLine([segAng(rectangleSegmentA005), -segLen(rectangleSegmentA005)], %)
|
||||
|> line(endAbsolute=[profileStartX(%), profileStartY(%)])
|
||||
|> close()
|
||||
sketch004 = startSketchOn(extrude001, seg05)
|
||||
@ -378,7 +363,6 @@ profile001 = startProfileAt([205.96, 254.59], sketch002)
|
||||
], %)
|
||||
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|
||||
|> close()
|
||||
|
||||
`,
|
||||
{ shouldNormalise: true }
|
||||
)
|
||||
@ -415,13 +399,13 @@ profile001 = startProfileAt([205.96, 254.59], sketch002)
|
||||
cameraPos: { x: 16020, y: -2000, z: 10500 },
|
||||
cameraTarget: { x: -150, y: -4500, z: -80 },
|
||||
beforeChamferSnippet: `angledLine([segAng(rectangleSegmentA001)-90,217.26],%,$seg01)
|
||||
chamfer({length=30,tags=[
|
||||
chamfer(extrude001,length=30,tags=[
|
||||
seg01,
|
||||
getNextAdjacentEdge(yo),
|
||||
getNextAdjacentEdge(seg02),
|
||||
getOppositeEdge(seg01)
|
||||
]}, extrude001)`,
|
||||
beforeChamferSnippetEnd: '}, extrude001)',
|
||||
getOppositeEdge(seg01),
|
||||
])`,
|
||||
beforeChamferSnippetEnd: ')',
|
||||
afterChamferSelectSnippet:
|
||||
'sketch002 = startSketchOn(extrude001, seg03)',
|
||||
afterRectangle1stClickSnippet:
|
||||
@ -447,18 +431,20 @@ profile001 = startProfileAt([205.96, 254.59], sketch002)
|
||||
|> line(endAbsolute = [profileStartX(%), profileStartY(%)], tag = $seg02)
|
||||
|> close()
|
||||
extrude001 = extrude(sketch001, length = 100)
|
||||
chamf = chamfer({
|
||||
chamf = chamfer(
|
||||
extrude001,
|
||||
length = 30,
|
||||
tags = [getOppositeEdge(seg01)]
|
||||
}, extrude001, $seg03)
|
||||
|> chamfer({
|
||||
tags = [getOppositeEdge(seg01)],
|
||||
tag = $seg03,
|
||||
)
|
||||
|> chamfer(
|
||||
length = 30,
|
||||
tags = [
|
||||
seg01,
|
||||
getNextAdjacentEdge(yo),
|
||||
getNextAdjacentEdge(seg02)
|
||||
]
|
||||
}, %)
|
||||
],
|
||||
)
|
||||
sketch002 = startSketchOn(extrude001, seg03)
|
||||
profile001 = startProfileAt([205.96, 254.59], sketch002)
|
||||
|> angledLine([0, 11.39], %, $rectangleSegmentA002)
|
||||
@ -1078,7 +1064,7 @@ openSketch = startSketchOn('XY')
|
||||
0
|
||||
)
|
||||
await operationButton.click({ button: 'left' })
|
||||
await page.keyboard.press('Backspace')
|
||||
await page.keyboard.press('Delete')
|
||||
await scene.expectPixelColor([50, 51, 96], testPoint, 15)
|
||||
})
|
||||
})
|
||||
@ -1139,11 +1125,53 @@ openSketch = startSketchOn('XY')
|
||||
await scene.expectPixelColor([250, 250, 250], testPoint, 15)
|
||||
})
|
||||
|
||||
await test.step('Delete offset plane via feature tree selection', async () => {
|
||||
await test.step(`Edit helix through the feature tree`, async () => {
|
||||
await editor.closePane()
|
||||
const operationButton = await toolbar.getFeatureTreeOperation('Helix', 0)
|
||||
await operationButton.dblclick()
|
||||
const initialInput = '5'
|
||||
const newInput = '50'
|
||||
await cmdBar.expectState({
|
||||
commandName: 'Helix',
|
||||
stage: 'arguments',
|
||||
currentArgKey: 'length',
|
||||
currentArgValue: initialInput,
|
||||
headerArguments: {
|
||||
AngleStart: '360',
|
||||
Axis: 'X',
|
||||
CounterClockWise: '',
|
||||
Length: initialInput,
|
||||
Radius: '5',
|
||||
Revolutions: '1',
|
||||
},
|
||||
highlightedHeaderArg: 'length',
|
||||
})
|
||||
await expect(cmdBar.currentArgumentInput).toBeVisible()
|
||||
await cmdBar.currentArgumentInput.locator('.cm-content').fill(newInput)
|
||||
await cmdBar.progressCmdBar()
|
||||
await cmdBar.expectState({
|
||||
stage: 'review',
|
||||
headerArguments: {
|
||||
AngleStart: '360',
|
||||
Axis: 'X',
|
||||
CounterClockWise: '',
|
||||
Length: newInput,
|
||||
Radius: '5',
|
||||
Revolutions: '1',
|
||||
},
|
||||
commandName: 'Helix',
|
||||
})
|
||||
await cmdBar.progressCmdBar()
|
||||
await toolbar.closeFeatureTreePane()
|
||||
await editor.openPane()
|
||||
await editor.expectEditor.toContain('length = ' + newInput)
|
||||
})
|
||||
|
||||
await test.step('Delete helix via feature tree selection', async () => {
|
||||
await editor.closePane()
|
||||
const operationButton = await toolbar.getFeatureTreeOperation('Helix', 0)
|
||||
await operationButton.click({ button: 'left' })
|
||||
await page.keyboard.press('Backspace')
|
||||
await page.keyboard.press('Delete')
|
||||
// Red plane is back
|
||||
await scene.expectPixelColor([96, 52, 52], testPoint, 15)
|
||||
})
|
||||
@ -1235,7 +1263,7 @@ openSketch = startSketchOn('XY')
|
||||
await editor.closePane()
|
||||
const operationButton = await toolbar.getFeatureTreeOperation('Loft', 0)
|
||||
await operationButton.click({ button: 'left' })
|
||||
await page.keyboard.press('Backspace')
|
||||
await page.keyboard.press('Delete')
|
||||
await scene.expectPixelColor([254, 254, 254], testPoint, 15)
|
||||
})
|
||||
})
|
||||
@ -1278,7 +1306,7 @@ loft001 = loft([sketch001, sketch002])
|
||||
await expect(page.locator('.cm-activeLine')).toHaveText(`
|
||||
|> circle({ center = [0, 0], radius = 30 }, %)
|
||||
`)
|
||||
await page.keyboard.press('Backspace')
|
||||
await page.keyboard.press('Delete')
|
||||
// Check for sketch 1
|
||||
await scene.expectPixelColor([254, 254, 254], testPoint, 15)
|
||||
})
|
||||
@ -1289,7 +1317,7 @@ loft001 = loft([sketch001, sketch002])
|
||||
await expect(page.locator('.cm-activeLine')).toHaveText(`
|
||||
|> circle({ center = [0, 0], radius = 20 }, %)
|
||||
`)
|
||||
await page.keyboard.press('Backspace')
|
||||
await page.keyboard.press('Delete')
|
||||
// Check for plane001
|
||||
await scene.expectPixelColor([228, 228, 228], testPoint, 15)
|
||||
})
|
||||
@ -1300,7 +1328,7 @@ loft001 = loft([sketch001, sketch002])
|
||||
await expect(page.locator('.cm-activeLine')).toHaveText(`
|
||||
plane001 = offsetPlane('XZ', offset = 50)
|
||||
`)
|
||||
await page.keyboard.press('Backspace')
|
||||
await page.keyboard.press('Delete')
|
||||
// Check for sketch 1
|
||||
await scene.expectPixelColor([254, 254, 254], testPoint, 15)
|
||||
})
|
||||
@ -1394,7 +1422,7 @@ sketch002 = startSketchOn('XZ')
|
||||
await page.waitForTimeout(500)
|
||||
const operationButton = await toolbar.getFeatureTreeOperation('Sweep', 0)
|
||||
await operationButton.click({ button: 'left' })
|
||||
await page.keyboard.press('Backspace')
|
||||
await page.keyboard.press('Delete')
|
||||
await page.waitForTimeout(500)
|
||||
await toolbar.closePane('feature-tree')
|
||||
await scene.expectPixelColor([53, 53, 53], testPoint, 15)
|
||||
@ -1492,9 +1520,9 @@ sketch002 = startSketchOn('XZ')
|
||||
|> close()
|
||||
extrude001 = extrude(sketch001, length = -12)
|
||||
`
|
||||
const firstFilletDeclaration = 'fillet({ radius = 5, tags = [seg01] }, %)'
|
||||
const firstFilletDeclaration = 'fillet(radius = 5, tags = [seg01])'
|
||||
const secondFilletDeclaration =
|
||||
'fillet({ radius = 5, tags = [getOppositeEdge(seg01)] }, %)'
|
||||
'fillet(radius = 5, tags = [getOppositeEdge(seg01)])'
|
||||
|
||||
// Locators
|
||||
const firstEdgeLocation = { x: 600, y: 193 }
|
||||
@ -1594,7 +1622,7 @@ extrude001 = extrude(sketch001, length = -12)
|
||||
await editor.expectEditor.toContain(firstFilletDeclaration)
|
||||
await editor.expectState({
|
||||
diagnostics: [],
|
||||
activeLines: ['|>fillet({radius=5,tags=[seg01]},%)'],
|
||||
activeLines: ['|> fillet(radius = 5, tags = [seg01])'],
|
||||
highlightedCode: '',
|
||||
})
|
||||
})
|
||||
@ -1674,7 +1702,7 @@ extrude001 = extrude(sketch001, length = -12)
|
||||
await editor.expectEditor.toContain(secondFilletDeclaration)
|
||||
await editor.expectState({
|
||||
diagnostics: [],
|
||||
activeLines: ['radius=5,'],
|
||||
activeLines: ['|>fillet(radius=5,tags=[getOppositeEdge(seg01)])'],
|
||||
highlightedCode: '',
|
||||
})
|
||||
})
|
||||
@ -1700,7 +1728,7 @@ extrude001 = extrude(sketch001, length = -12)
|
||||
1
|
||||
)
|
||||
await operationButton.click({ button: 'left' })
|
||||
await page.keyboard.press('Backspace')
|
||||
await page.keyboard.press('Delete')
|
||||
await page.waitForTimeout(500)
|
||||
await scene.expectPixelColor(edgeColorWhite, secondEdgeLocation, 15) // deleted
|
||||
await editor.expectEditor.not.toContain(secondFilletDeclaration)
|
||||
@ -1726,18 +1754,17 @@ extrude001 = extrude(sketch001, length = -12)
|
||||
|> line(endAbsolute = [profileStartX(%), profileStartY(%)], tag = $seg01)
|
||||
|> close()
|
||||
extrude001 = extrude(sketch001, length = -12)
|
||||
|> fillet({ radius = 5, tags = [seg01] }, %) // fillet01
|
||||
|> fillet({ radius = 5, tags = [seg02] }, %) // fillet02
|
||||
fillet03 = fillet({ radius = 5, tags = [getOppositeEdge(seg01)]}, extrude001)
|
||||
fillet04 = fillet({ radius = 5, tags = [getOppositeEdge(seg02)]}, extrude001)
|
||||
|> fillet(radius = 5, tags = [seg01]) // fillet01
|
||||
|> fillet(radius = 5, tags = [seg02]) // fillet02
|
||||
fillet03 = fillet(extrude001, radius = 5, tags = [getOppositeEdge(seg01)])
|
||||
fillet04 = fillet(extrude001, radius = 5, tags = [getOppositeEdge(seg02)])
|
||||
`
|
||||
const pipedFilletDeclaration = 'fillet({ radius = 5, tags = [seg01] }, %)'
|
||||
const secondPipedFilletDeclaration =
|
||||
'fillet({ radius = 5, tags = [seg02] }, %)'
|
||||
const pipedFilletDeclaration = 'fillet(radius = 5, tags = [seg01])'
|
||||
const secondPipedFilletDeclaration = 'fillet(radius = 5, tags = [seg02])'
|
||||
const standaloneFilletDeclaration =
|
||||
'fillet03 = fillet({ radius = 5, tags = [getOppositeEdge(seg01)]}, extrude001)'
|
||||
'fillet03 = fillet(extrude001, radius = 5, tags = [getOppositeEdge(seg01)])'
|
||||
const secondStandaloneFilletDeclaration =
|
||||
'fillet04 = fillet({ radius = 5, tags = [getOppositeEdge(seg02)]}, extrude001)'
|
||||
'fillet04 = fillet(extrude001, radius = 5, tags = [getOppositeEdge(seg02)])'
|
||||
|
||||
// Locators
|
||||
const pipedFilletEdgeLocation = { x: 600, y: 193 }
|
||||
@ -1803,7 +1830,7 @@ fillet04 = fillet({ radius = 5, tags = [getOppositeEdge(seg02)]}, extrude001)
|
||||
0
|
||||
)
|
||||
await operationButton.click({ button: 'left' })
|
||||
await page.keyboard.press('Backspace')
|
||||
await page.keyboard.press('Delete')
|
||||
await page.waitForTimeout(500)
|
||||
})
|
||||
await test.step('Verify piped fillet is deleted but other fillets are not (in the editor)', async () => {
|
||||
@ -1833,7 +1860,7 @@ fillet04 = fillet({ radius = 5, tags = [getOppositeEdge(seg02)]}, extrude001)
|
||||
1
|
||||
)
|
||||
await operationButton.click({ button: 'left' })
|
||||
await page.keyboard.press('Backspace')
|
||||
await page.keyboard.press('Delete')
|
||||
await page.waitForTimeout(500)
|
||||
})
|
||||
await test.step('Verify non-piped fillet is deleted but other two fillets are not (in the editor)', async () => {
|
||||
@ -1871,9 +1898,9 @@ fillet04 = fillet({ radius = 5, tags = [getOppositeEdge(seg02)]}, extrude001)
|
||||
|> close()
|
||||
extrude001 = extrude(sketch001, length = -12)
|
||||
`
|
||||
const firstChamferDeclaration = 'chamfer({ length = 5, tags = [seg01] }, %)'
|
||||
const firstChamferDeclaration = 'chamfer(length = 5, tags = [seg01])'
|
||||
const secondChamferDeclaration =
|
||||
'chamfer({ length = 5, tags = [getOppositeEdge(seg01)] }, %)'
|
||||
'chamfer(length = 5, tags = [getOppositeEdge(seg01)])'
|
||||
|
||||
// Locators
|
||||
const firstEdgeLocation = { x: 600, y: 193 }
|
||||
@ -1964,7 +1991,7 @@ extrude001 = extrude(sketch001, length = -12)
|
||||
await editor.expectEditor.toContain(firstChamferDeclaration)
|
||||
await editor.expectState({
|
||||
diagnostics: [],
|
||||
activeLines: ['|>chamfer({length=5,tags=[seg01]},%)'],
|
||||
activeLines: ['|>chamfer(length=5,tags=[seg01])'],
|
||||
highlightedCode: '',
|
||||
})
|
||||
})
|
||||
@ -2048,7 +2075,7 @@ extrude001 = extrude(sketch001, length = -12)
|
||||
await editor.expectEditor.toContain(secondChamferDeclaration)
|
||||
await editor.expectState({
|
||||
diagnostics: [],
|
||||
activeLines: ['length=5,'],
|
||||
activeLines: ['|>chamfer(length=5,tags=[getOppositeEdge(seg01)])'],
|
||||
highlightedCode: '',
|
||||
})
|
||||
})
|
||||
@ -2072,7 +2099,7 @@ extrude001 = extrude(sketch001, length = -12)
|
||||
1
|
||||
)
|
||||
await operationButton.click({ button: 'left' })
|
||||
await page.keyboard.press('Backspace')
|
||||
await page.keyboard.press('Delete')
|
||||
await page.waitForTimeout(500)
|
||||
await scene.expectPixelColor(edgeColorWhite, secondEdgeLocation, 15) // deleted
|
||||
await scene.expectPixelColor(chamferColor, firstEdgeLocation, 15) // stayed
|
||||
@ -2096,18 +2123,17 @@ extrude001 = extrude(sketch001, length = -12)
|
||||
|> line(endAbsolute = [profileStartX(%), profileStartY(%)], tag = $seg01)
|
||||
|> close()
|
||||
extrude001 = extrude(sketch001, length = -12)
|
||||
|> chamfer({ length = 5, tags = [seg01] }, %) // chamfer01
|
||||
|> chamfer({ length = 5, tags = [seg02] }, %) // chamfer02
|
||||
chamfer03 = chamfer({ length = 5, tags = [getOppositeEdge(seg01)]}, extrude001)
|
||||
chamfer04 = chamfer({ length = 5, tags = [getOppositeEdge(seg02)]}, extrude001)
|
||||
|> chamfer(length = 5, tags = [seg01]) // chamfer01
|
||||
|> chamfer(length = 5, tags = [seg02]) // chamfer02
|
||||
chamfer03 = chamfer(extrude001, length = 5, tags = [getOppositeEdge(seg01)])
|
||||
chamfer04 = chamfer(extrude001, length = 5, tags = [getOppositeEdge(seg02)])
|
||||
`
|
||||
const pipedChamferDeclaration = 'chamfer({ length = 5, tags = [seg01] }, %)'
|
||||
const secondPipedChamferDeclaration =
|
||||
'chamfer({ length = 5, tags = [seg02] }, %)'
|
||||
const pipedChamferDeclaration = 'chamfer(length = 5, tags = [seg01])'
|
||||
const secondPipedChamferDeclaration = 'chamfer(length = 5, tags = [seg02])'
|
||||
const standaloneChamferDeclaration =
|
||||
'chamfer03 = chamfer({ length = 5, tags = [getOppositeEdge(seg01)]}, extrude001)'
|
||||
'chamfer03 = chamfer(extrude001, length = 5, tags = [getOppositeEdge(seg01)])'
|
||||
const secondStandaloneChamferDeclaration =
|
||||
'chamfer04 = chamfer({ length = 5, tags = [getOppositeEdge(seg02)]}, extrude001)'
|
||||
'chamfer04 = chamfer(extrude001, length = 5, tags = [getOppositeEdge(seg02)])'
|
||||
|
||||
// Locators
|
||||
const pipedChamferEdgeLocation = { x: 600, y: 193 }
|
||||
@ -2176,7 +2202,7 @@ chamfer04 = chamfer({ length = 5, tags = [getOppositeEdge(seg02)]}, extrude001
|
||||
0
|
||||
)
|
||||
await operationButton.click({ button: 'left' })
|
||||
await page.keyboard.press('Backspace')
|
||||
await page.keyboard.press('Delete')
|
||||
await page.waitForTimeout(500)
|
||||
})
|
||||
await test.step('Verify piped chamfer is deleted but other chamfers are not (in the editor)', async () => {
|
||||
@ -2208,7 +2234,7 @@ chamfer04 = chamfer({ length = 5, tags = [getOppositeEdge(seg02)]}, extrude001
|
||||
1
|
||||
)
|
||||
await operationButton.click({ button: 'left' })
|
||||
await page.keyboard.press('Backspace')
|
||||
await page.keyboard.press('Delete')
|
||||
await page.waitForTimeout(500)
|
||||
})
|
||||
await test.step('Verify non-piped chamfer is deleted but other two chamfers are not (in the editor)', async () => {
|
||||
@ -2412,7 +2438,7 @@ extrude001 = extrude(sketch001, length = 40)
|
||||
await editor.closePane()
|
||||
const operationButton = await toolbar.getFeatureTreeOperation('Shell', 0)
|
||||
await operationButton.click({ button: 'left' })
|
||||
await page.keyboard.press('Backspace')
|
||||
await page.keyboard.press('Delete')
|
||||
await scene.expectPixelColor([99, 99, 99], testPoint, 15)
|
||||
})
|
||||
})
|
||||
@ -2551,7 +2577,7 @@ profile001 = startProfileAt([-20, 20], sketch001)
|
||||
const deleteOperation = async (operationButton: Locator) => {
|
||||
if (shouldUseKeyboard) {
|
||||
await operationButton.click({ button: 'left' })
|
||||
await page.keyboard.press('Backspace')
|
||||
await page.keyboard.press('Delete')
|
||||
} else {
|
||||
await operationButton.click({ button: 'right' })
|
||||
const editButton = page.getByTestId('context-menu-delete')
|
||||
@ -2812,4 +2838,107 @@ radius = 8.69
|
||||
expect(editor.expectEditor.toContain(newCodeToFind)).toBeTruthy()
|
||||
})
|
||||
})
|
||||
|
||||
test(`Set appearance`, async ({
|
||||
context,
|
||||
page,
|
||||
homePage,
|
||||
scene,
|
||||
editor,
|
||||
toolbar,
|
||||
cmdBar,
|
||||
}) => {
|
||||
const initialCode = `sketch001 = startSketchOn('XZ')
|
||||
profile001 = circle({
|
||||
center = [0, 0],
|
||||
radius = 100
|
||||
}, sketch001)
|
||||
extrude001 = extrude(profile001, length = 100)
|
||||
`
|
||||
await context.addInitScript((initialCode) => {
|
||||
localStorage.setItem('persistCode', initialCode)
|
||||
}, initialCode)
|
||||
await page.setBodyDimensions({ width: 1000, height: 500 })
|
||||
await homePage.goToModelingScene()
|
||||
await scene.waitForExecutionDone()
|
||||
|
||||
// One dumb hardcoded screen pixel value
|
||||
const testPoint = { x: 500, y: 250 }
|
||||
const initialColor: [number, number, number] = [135, 135, 135]
|
||||
|
||||
await test.step(`Confirm extrude exists with default appearance`, async () => {
|
||||
await toolbar.closePane('code')
|
||||
await scene.expectPixelColor(initialColor, testPoint, 15)
|
||||
})
|
||||
|
||||
async function setApperanceAndCheck(
|
||||
option: string,
|
||||
hex: string,
|
||||
shapeColor: [number, number, number]
|
||||
) {
|
||||
await toolbar.openPane('feature-tree')
|
||||
const operationButton = await toolbar.getFeatureTreeOperation(
|
||||
'Extrude',
|
||||
0
|
||||
)
|
||||
await operationButton.click({ button: 'right' })
|
||||
const menuButton = page.getByTestId('context-menu-set-appearance')
|
||||
await menuButton.click()
|
||||
await cmdBar.expectState({
|
||||
commandName: 'Appearance',
|
||||
currentArgKey: 'color',
|
||||
currentArgValue: '',
|
||||
headerArguments: {
|
||||
Color: '',
|
||||
},
|
||||
highlightedHeaderArg: 'color',
|
||||
stage: 'arguments',
|
||||
})
|
||||
const item = page.getByText(option, { exact: true })
|
||||
await item.click()
|
||||
await cmdBar.expectState({
|
||||
commandName: 'Appearance',
|
||||
headerArguments: {
|
||||
Color: hex,
|
||||
},
|
||||
stage: 'review',
|
||||
})
|
||||
await cmdBar.progressCmdBar()
|
||||
await toolbar.closePane('feature-tree')
|
||||
await scene.expectPixelColor(shapeColor, testPoint, 40)
|
||||
await toolbar.openPane('code')
|
||||
if (hex === 'default') {
|
||||
const anyAppearanceDeclaration = `|> appearance(`
|
||||
await editor.expectEditor.not.toContain(anyAppearanceDeclaration)
|
||||
} else {
|
||||
const declaration = `|> appearance(%, color = '${hex}')`
|
||||
await editor.expectEditor.toContain(declaration)
|
||||
// TODO: fix selection range after appearance update
|
||||
// await editor.expectState({
|
||||
// diagnostics: [],
|
||||
// activeLines: [declaration],
|
||||
// highlightedCode: '',
|
||||
// })
|
||||
}
|
||||
await toolbar.closePane('code')
|
||||
}
|
||||
|
||||
await test.step(`Go through the Set Appearance flow for all options`, async () => {
|
||||
await setApperanceAndCheck('Red', '#FF0000', [180, 0, 0])
|
||||
await setApperanceAndCheck('Green', '#00FF00', [0, 180, 0])
|
||||
await setApperanceAndCheck('Blue', '#0000FF', [0, 0, 180])
|
||||
await setApperanceAndCheck('Turquoise', '#00FFFF', [0, 180, 180])
|
||||
await setApperanceAndCheck('Purple', '#FF00FF', [180, 0, 180])
|
||||
await setApperanceAndCheck('Yellow', '#FFFF00', [180, 180, 0])
|
||||
await setApperanceAndCheck('Black', '#000000', [0, 0, 0])
|
||||
await setApperanceAndCheck('Dark Grey', '#080808', [10, 10, 10])
|
||||
await setApperanceAndCheck('Light Grey', '#D3D3D3', [190, 190, 190])
|
||||
await setApperanceAndCheck('White', '#FFFFFF', [200, 200, 200])
|
||||
await setApperanceAndCheck(
|
||||
'Default (clear appearance)',
|
||||
'default',
|
||||
initialColor
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
@ -424,7 +424,7 @@ test(
|
||||
test(
|
||||
'when code with error first loads you get errors in console',
|
||||
{ tag: '@electron' },
|
||||
async ({ context, page }, testInfo) => {
|
||||
async ({ context, page, editor }, testInfo) => {
|
||||
await context.folderSetupFn(async (dir) => {
|
||||
await fsp.mkdir(path.join(dir, 'broken-code'), { recursive: true })
|
||||
await fsp.copyFile(
|
||||
@ -434,16 +434,19 @@ test(
|
||||
})
|
||||
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
|
||||
await expect(page.getByText('broken-code')).toBeVisible()
|
||||
|
||||
await page.getByText('broken-code').click()
|
||||
|
||||
// Gotcha: You can not use scene.waitForExecutionDone() since the KCL code is going to fail
|
||||
await expect(page.getByTestId('loading')).toBeAttached()
|
||||
await expect(page.getByTestId('loading')).not.toBeAttached({
|
||||
timeout: 20_000,
|
||||
})
|
||||
|
||||
// Gotcha: Scroll to the text content in code mirror because CodeMirror lazy loads DOM content
|
||||
await editor.scrollToText(
|
||||
"|> line(end = [0, wallMountL], tag = 'outerEdge')"
|
||||
)
|
||||
// error in guter
|
||||
await expect(page.locator('.cm-lint-marker-error')).toBeVisible()
|
||||
|
||||
|
Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 34 KiB |
Before Width: | Height: | Size: 54 KiB After Width: | Height: | Size: 54 KiB |
Before Width: | Height: | Size: 76 KiB After Width: | Height: | Size: 76 KiB |
Before Width: | Height: | Size: 51 KiB After Width: | Height: | Size: 51 KiB |
Before Width: | Height: | Size: 67 KiB After Width: | Height: | Size: 66 KiB |
Before Width: | Height: | Size: 143 KiB After Width: | Height: | Size: 145 KiB |
Before Width: | Height: | Size: 127 KiB After Width: | Height: | Size: 129 KiB |
Before Width: | Height: | Size: 49 KiB After Width: | Height: | Size: 49 KiB |
Before Width: | Height: | Size: 55 KiB After Width: | Height: | Size: 55 KiB |
Before Width: | Height: | Size: 49 KiB After Width: | Height: | Size: 49 KiB |
Before Width: | Height: | Size: 54 KiB After Width: | Height: | Size: 54 KiB |
Before Width: | Height: | Size: 54 KiB After Width: | Height: | Size: 54 KiB |
Before Width: | Height: | Size: 49 KiB After Width: | Height: | Size: 49 KiB |
@ -937,11 +937,16 @@ export async function setupElectron({
|
||||
testInfo,
|
||||
cleanProjectDir = true,
|
||||
appSettings,
|
||||
viewport,
|
||||
}: {
|
||||
testInfo: TestInfo
|
||||
folderSetupFn?: (projectDirName: string) => Promise<void>
|
||||
cleanProjectDir?: boolean
|
||||
appSettings?: Partial<SaveSettingsPayload>
|
||||
viewport: {
|
||||
width: number
|
||||
height: number
|
||||
}
|
||||
}): Promise<{
|
||||
electronApp: ElectronApplication
|
||||
context: BrowserContext
|
||||
@ -972,6 +977,14 @@ export async function setupElectron({
|
||||
...(process.env.ELECTRON_OVERRIDE_DIST_PATH
|
||||
? { executablePath: process.env.ELECTRON_OVERRIDE_DIST_PATH + 'electron' }
|
||||
: {}),
|
||||
...(process.env.PLAYWRIGHT_RECORD_VIDEO
|
||||
? {
|
||||
recordVideo: {
|
||||
dir: testInfo.snapshotPath(),
|
||||
size: viewport,
|
||||
},
|
||||
}
|
||||
: {}),
|
||||
}
|
||||
|
||||
// Do this once and then reuse window on subsequent calls.
|
||||
|
@ -94,6 +94,8 @@ test.describe('Testing Camera Movement', { tag: ['@skipWin'] }, () => {
|
||||
await bakeInRetries(async () => {
|
||||
await page.mouse.move(700, 200)
|
||||
await page.mouse.down({ button: 'right' })
|
||||
await page.waitForTimeout(100)
|
||||
|
||||
const appLogoBBox = await page.getByTestId('app-logo').boundingBox()
|
||||
expect(appLogoBBox).not.toBeNull()
|
||||
if (!appLogoBBox) throw new Error('app logo not found')
|
||||
@ -101,7 +103,9 @@ test.describe('Testing Camera Movement', { tag: ['@skipWin'] }, () => {
|
||||
appLogoBBox.x + appLogoBBox.width / 2,
|
||||
appLogoBBox.y + appLogoBBox.height / 2
|
||||
)
|
||||
await page.waitForTimeout(100)
|
||||
await page.mouse.move(600, 303)
|
||||
await page.waitForTimeout(100)
|
||||
await page.mouse.up({ button: 'right' })
|
||||
}, [4, -10.5, -120])
|
||||
|
||||
@ -358,9 +362,7 @@ test.describe('Testing Camera Movement', { tag: ['@skipWin'] }, () => {
|
||||
exact: true,
|
||||
})
|
||||
const userSettingsTab = page.getByRole('radio', { name: 'User' })
|
||||
const mouseControlsSetting = page
|
||||
.locator('#mouseControls')
|
||||
.getByRole('combobox')
|
||||
const mouseControlsSetting = () => page.locator('#camera-controls').first()
|
||||
const mouseControlSuccesToast = page.getByText(
|
||||
'Set mouse controls to "Solidworks"'
|
||||
)
|
||||
@ -390,7 +392,14 @@ test.describe('Testing Camera Movement', { tag: ['@skipWin'] }, () => {
|
||||
await settingsLink.click()
|
||||
await expect(settingsDialogHeading).toBeVisible()
|
||||
await userSettingsTab.click()
|
||||
await mouseControlsSetting.selectOption({ label: 'Solidworks' })
|
||||
const setting = mouseControlsSetting()
|
||||
await expect(setting).toBeAttached()
|
||||
await setting.scrollIntoViewIfNeeded()
|
||||
await setting.selectOption({ label: 'Solidworks' })
|
||||
await expect(setting, 'Setting value did not change').toHaveValue(
|
||||
'Solidworks',
|
||||
{ timeout: 120_000 }
|
||||
)
|
||||
await expect(mouseControlSuccesToast).toBeVisible()
|
||||
await settingsCloseButton.click()
|
||||
})
|
||||
|
@ -382,7 +382,7 @@ profile003 = startProfileAt([40.16, -120.48], sketch006)
|
||||
'|> line(end = [0, -pipeLength])'
|
||||
)
|
||||
await u.clearCommandLogs()
|
||||
await page.keyboard.press('Backspace')
|
||||
await page.keyboard.press('Delete')
|
||||
await u.expectCmdLog('[data-message-type="execution-done"]', 10_000)
|
||||
await page.waitForTimeout(200)
|
||||
|
||||
@ -439,7 +439,7 @@ profile003 = startProfileAt([40.16, -120.48], sketch006)
|
||||
'|> startProfileAt([23.24, 136.52], %)'
|
||||
)
|
||||
await u.clearCommandLogs()
|
||||
await page.keyboard.press('Backspace')
|
||||
await page.keyboard.press('Delete')
|
||||
await u.expectCmdLog('[data-message-type="execution-done"]', 10_000)
|
||||
await page.waitForTimeout(200)
|
||||
await expect(u.codeLocator).not.toContainText(`sketch005 = startSketchOn({`)
|
||||
@ -453,7 +453,7 @@ profile003 = startProfileAt([40.16, -120.48], sketch006)
|
||||
' |> line(end = [20.91, -28.61])'
|
||||
)
|
||||
await u.clearCommandLogs()
|
||||
await page.keyboard.press('Backspace')
|
||||
await page.keyboard.press('Delete')
|
||||
await u.expectCmdLog('[data-message-type="execution-done"]', 10_000)
|
||||
await page.waitForTimeout(200)
|
||||
await expect(u.codeLocator).not.toContainText(codeToBeDeletedSnippet)
|
||||
@ -518,7 +518,7 @@ profile003 = startProfileAt([40.16, -120.48], sketch006)
|
||||
'|> line(end = [170.36, -121.61], tag = $seg01)'
|
||||
)
|
||||
await u.clearCommandLogs()
|
||||
await page.keyboard.press('Backspace')
|
||||
await page.keyboard.press('Delete')
|
||||
|
||||
await expect(page.getByText('Unable to delete selection')).toBeVisible()
|
||||
}
|
||||
@ -764,15 +764,15 @@ profile003 = startProfileAt([40.16, -120.48], sketch006)
|
||||
|> line(endAbsolute = [profileStartX(%), profileStartY(%)], tag = $seg02)
|
||||
|> close()
|
||||
extrude001 = extrude(sketch001, length = 100)
|
||||
|> chamfer({
|
||||
|> chamfer(
|
||||
length = 30,
|
||||
tags = [
|
||||
seg01,
|
||||
getNextAdjacentEdge(yo),
|
||||
getNextAdjacentEdge(seg02),
|
||||
getOppositeEdge(seg01)
|
||||
]
|
||||
}, %)
|
||||
],
|
||||
)
|
||||
`)
|
||||
await expect(
|
||||
page.getByTestId('model-state-indicator-execution-done')
|
||||
@ -810,15 +810,15 @@ profile003 = startProfileAt([40.16, -120.48], sketch006)
|
||||
await checkCodeAtHoverPosition(
|
||||
'oppositeChamfer',
|
||||
oppositeChamfer,
|
||||
`angledLine([segAng(rectangleSegmentA001)-90,217.26],%,$seg01)chamfer({length=30,tags=[seg01,getNextAdjacentEdge(yo),getNextAdjacentEdge(seg02),getOppositeEdge(seg01)]},%)`,
|
||||
'}, %)'
|
||||
`angledLine([segAng(rectangleSegmentA001)-90,217.26],%,$seg01)chamfer(length=30,tags=[seg01,getNextAdjacentEdge(yo),getNextAdjacentEdge(seg02),getOppositeEdge(seg01)],)`,
|
||||
' )'
|
||||
)
|
||||
|
||||
await checkCodeAtHoverPosition(
|
||||
'baseChamfer',
|
||||
baseChamfer,
|
||||
`angledLine([segAng(rectangleSegmentA001)-90,217.26],%,$seg01)chamfer({length=30,tags=[seg01,getNextAdjacentEdge(yo),getNextAdjacentEdge(seg02),getOppositeEdge(seg01)]},%)`,
|
||||
'}, %)'
|
||||
`angledLine([segAng(rectangleSegmentA001)-90,217.26],%,$seg01)chamfer(length=30,tags=[seg01,getNextAdjacentEdge(yo),getNextAdjacentEdge(seg02),getOppositeEdge(seg01)],)`,
|
||||
' )'
|
||||
)
|
||||
|
||||
await u.openAndClearDebugPanel()
|
||||
@ -848,15 +848,15 @@ profile003 = startProfileAt([40.16, -120.48], sketch006)
|
||||
await checkCodeAtHoverPosition(
|
||||
'adjacentChamfer1',
|
||||
adjacentChamfer1,
|
||||
`line(endAbsolute=[profileStartX(%),profileStartY(%)],tag=$seg02)chamfer({length=30,tags=[seg01,getNextAdjacentEdge(yo),getNextAdjacentEdge(seg02),getOppositeEdge(seg01)]},%)`,
|
||||
'}, %)'
|
||||
`line(endAbsolute=[profileStartX(%),profileStartY(%)],tag=$seg02)chamfer(length=30,tags=[seg01,getNextAdjacentEdge(yo),getNextAdjacentEdge(seg02),getOppositeEdge(seg01)],)`,
|
||||
' )'
|
||||
)
|
||||
|
||||
await checkCodeAtHoverPosition(
|
||||
'adjacentChamfer2',
|
||||
adjacentChamfer2,
|
||||
`angledLine([segAng(rectangleSegmentA001),-segLen(rectangleSegmentA001)],%,$yo)chamfer({length=30,tags=[seg01,getNextAdjacentEdge(yo),getNextAdjacentEdge(seg02),getOppositeEdge(seg01)]},%)`,
|
||||
'}, %)'
|
||||
`angledLine([segAng(rectangleSegmentA001),-segLen(rectangleSegmentA001)],%,$yo)chamfer(length=30,tags=[seg01,getNextAdjacentEdge(yo),getNextAdjacentEdge(seg02),getOppositeEdge(seg01)],)`,
|
||||
' )'
|
||||
)
|
||||
})
|
||||
test("Extrude button should be disabled if there's no extrudable geometry when nothing is selected", async ({
|
||||
|
@ -633,6 +633,7 @@ test.describe('Testing settings', () => {
|
||||
`Set default unit to "${unitOfMeasure}" as a user default`
|
||||
)
|
||||
await expect(toastMessage).toBeVisible()
|
||||
await expect(toastMessage).not.toBeVisible()
|
||||
})
|
||||
}
|
||||
await changeUnitOfMeasureInUserTab('in')
|
||||
@ -945,4 +946,76 @@ fn cube`
|
||||
).toBeVisible()
|
||||
})
|
||||
})
|
||||
|
||||
/**
|
||||
* This test assumes that the default value of the "highlight edges" setting is "on".
|
||||
*/
|
||||
test(`Toggle stream settings multiple times`, async ({
|
||||
page,
|
||||
scene,
|
||||
homePage,
|
||||
context,
|
||||
toolbar,
|
||||
cmdBar,
|
||||
}, testInfo) => {
|
||||
await context.folderSetupFn(async (dir) => {
|
||||
const projectDir = join(dir, 'project-000')
|
||||
await fsp.mkdir(projectDir, { recursive: true })
|
||||
await fsp.copyFile(
|
||||
executorInputPath('cube.kcl'),
|
||||
join(projectDir, 'main.kcl')
|
||||
)
|
||||
})
|
||||
|
||||
await test.step(`First snapshot`, async () => {
|
||||
await homePage.openProject('project-000')
|
||||
await toolbar.closePane('code')
|
||||
await expect(toolbar.startSketchBtn).toBeEnabled({ timeout: 20_000 })
|
||||
await scene.clickNoWhere()
|
||||
})
|
||||
|
||||
const toast = (value: boolean) =>
|
||||
page.getByText(
|
||||
`Set highlight edges to "${String(value)}" as a user default`
|
||||
)
|
||||
const initialPath = testInfo.snapshotPath('toggle-settings-initial.png')
|
||||
const initialScreenshot = await scene.streamWrapper.screenshot({
|
||||
path: initialPath,
|
||||
mask: [page.getByTestId('model-state-indicator')],
|
||||
})
|
||||
|
||||
await test.step(`Toggle highlightEdges off`, async () => {
|
||||
await cmdBar.openCmdBar()
|
||||
await cmdBar.chooseCommand('Settings · modeling · highlight edges')
|
||||
await cmdBar.selectOption({ name: 'off' }).click()
|
||||
const falseToast = toast(false)
|
||||
await expect(falseToast).toBeVisible()
|
||||
await falseToast.waitFor({ state: 'detached' })
|
||||
})
|
||||
|
||||
await expect(scene.streamWrapper).not.toHaveScreenshot(
|
||||
'toggle-settings-initial.png',
|
||||
{
|
||||
maxDiffPixels: 15,
|
||||
mask: [page.getByTestId('model-state-indicator')],
|
||||
}
|
||||
)
|
||||
|
||||
await test.step(`Toggle highlightEdges on`, async () => {
|
||||
await cmdBar.openCmdBar()
|
||||
await cmdBar.chooseCommand('Settings · modeling · highlight edges')
|
||||
await cmdBar.selectOption({ name: 'on' }).click()
|
||||
const trueToast = toast(true)
|
||||
await expect(trueToast).toBeVisible()
|
||||
await trueToast.waitFor({ state: 'detached' })
|
||||
})
|
||||
|
||||
await expect(scene.streamWrapper).toHaveScreenshot(
|
||||
'toggle-settings-initial.png',
|
||||
{
|
||||
maxDiffPixels: 15,
|
||||
mask: [page.getByTestId('model-state-indicator')],
|
||||
}
|
||||
)
|
||||
})
|
||||
})
|
||||
|
@ -68,13 +68,6 @@ type PWFunction = (
|
||||
|
||||
let firstUrl = ''
|
||||
|
||||
// The below error is due to the extreme type spaghetti going on. playwright/
|
||||
// types/test.d.ts does not export 2 functions (below is one of them) but tsc
|
||||
// is trying to use a interface name it can't see.
|
||||
// e2e/playwright/zoo-test.ts:64:14 - error TS4023: Exported variable 'test' has
|
||||
// or is using name 'TestFunction' from external module
|
||||
// "/home/lee/Code/Zoo/modeling-app/dirty2/node_modules/playwright/types/test"
|
||||
// but cannot be named.
|
||||
export const test = (
|
||||
desc: string,
|
||||
objOrFn: PWFunction | TestDetails,
|
||||
|
1
exp
Normal file
@ -0,0 +1 @@
|
||||
sketch001=startSketchOn('XZ')|>startProfileAt([75.8,317.2],%)//[$startCapTag,$EndCapTag]|>angledLine([0,268.43],%,$rectangleSegmentA001)|>angledLine([segAng(rectangleSegmentA001)-90,217.26],%,$seg01)|>angledLine([segAng(rectangleSegmentA001),-segLen(rectangleSegmentA001)],%,$yo)|>line(endAbsolute=[profileStartX(%),profileStartY(%)],tag=$seg02)|>close()extrude001=extrude(sketch001,length=100)|>chamfer(length=30,tags=[getOppositeEdge(seg01)],tag=$seg03)|>chamfer(length=30,tags=[seg01],tag=$seg04)|>chamfer(length=30,tags=[getNextAdjacentEdge(seg02)],tag=$seg05)|>chamfer(length=30,tags=[getNextAdjacentEdge(yo)],tag=$seg06)sketch004=startSketchOn(extrude001,seg05)profile003=startProfileAt([82.57,322.96],sketch004)|>angledLine([0,11.16],%,$rectangleSegmentA004)|>angledLine([segAng(rectangleSegmentA004)-90,103.07],%)|>angledLine([segAng(rectangleSegmentA004),-segLen(rectangleSegmentA004)],%)|>line(endAbsolute=[profileStartX(%),profileStartY(%)])|>close()sketch003=startSketchOn(extrude001,seg04)profile002=startProfileAt([-209.64,255.28],sketch003)|>angledLine([0,11.56],%,$rectangleSegmentA003)|>angledLine([segAng(rectangleSegmentA003)-90,106.84],%)|>angledLine([segAng(rectangleSegmentA003),-segLen(rectangleSegmentA003)],%)|>line(endAbsolute=[profileStartX(%),profileStartY(%)])|>close()sketch002=startSketchOn(extrude001,seg03)profile001=startProfileAt([205.96,254.59],sketch002)|>angledLine([0,11.39],%,$rectangleSegmentA002)|>angledLine([segAng(rectangleSegmentA002)-90,105.26],%)|>angledLine([segAng(rectangleSegmentA002),-segLen(rectangleSegmentA002)],%)|>line(endAbsolute=[profileStartX(%),profileStartY(%)])|>close()
|
1
got
Normal file
@ -0,0 +1 @@
|
||||
sketch001=startSketchOn('XZ')|>startProfileAt([75.8,317.2],%)//[$startCapTag,$EndCapTag]|>angledLine([0,268.43],%,$rectangleSegmentA001)|>angledLine([segAng(rectangleSegmentA001)-90,217.26],%,$seg01)|>angledLine([segAng(rectangleSegmentA001),-segLen(rectangleSegmentA001)],%,$yo)|>line(endAbsolute=[profileStartX(%),profileStartY(%)],tag=$seg02)|>close()extrude001=extrude(sketch001,length=100)|>chamfer(length=30,tags=[getOppositeEdge(seg01)],tag=$seg03)|>chamfer(length=30,tags=[seg01],tag=$seg04)|>chamfer(length=30,tags=[getNextAdjacentEdge(seg02)],tag=$seg05)|>chamfer(length=30,tags=[getNextAdjacentEdge(yo)],tag=$seg06)sketch005=startSketchOn(extrude001,seg06)profile004=startProfileAt([-23.43,19.69],sketch005)|>angledLine([0,9.1],%,$rectangleSegmentA005)|>angledLine([segAng(rectangleSegmentA005)-90,84.07],%)|>angledLine([segAng(rectangleSegmentA005),-segLen(rectangleSegmentA005)],%)|>line(endAbsolute=[profileStartX(%),profileStartY(%)])|>close()sketch004=startSketchOn(extrude001,seg05)profile003=startProfileAt([82.57,322.96],sketch004)|>angledLine([0,11.16],%,$rectangleSegmentA004)|>angledLine([segAng(rectangleSegmentA004)-90,103.07],%)|>angledLine([segAng(rectangleSegmentA004),-segLen(rectangleSegmentA004)],%)|>line(endAbsolute=[profileStartX(%),profileStartY(%)])|>close()sketch003=startSketchOn(extrude001,seg04)profile002=startProfileAt([-209.64,255.28],sketch003)|>angledLine([0,11.56],%,$rectangleSegmentA003)|>angledLine([segAng(rectangleSegmentA003)-90,106.84],%)|>angledLine([segAng(rectangleSegmentA003),-segLen(rectangleSegmentA003)],%)|>line(endAbsolute=[profileStartX(%),profileStartY(%)])|>close()sketch002=startSketchOn(extrude001,seg03)profile001=startProfileAt([205.96,254.59],sketch002)|>angledLine([0,11.39],%,$rectangleSegmentA002)|>angledLine([segAng(rectangleSegmentA002)-90,105.26],%)|>angledLine([segAng(rectangleSegmentA002),-segLen(rectangleSegmentA002)],%)|>line(endAbsolute=[profileStartX(%),profileStartY(%)])|>close()
|
27
package.json
@ -8,13 +8,13 @@
|
||||
"email": "info@zoo.dev",
|
||||
"url": "https://zoo.dev"
|
||||
},
|
||||
"description": "Edit CAD visually or with code",
|
||||
"description": "Zoo Modeling App",
|
||||
"main": ".vite/build/main.js",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@codemirror/autocomplete": "^6.17.0",
|
||||
"@codemirror/commands": "^6.6.0",
|
||||
"@codemirror/language": "^6.10.3",
|
||||
"@codemirror/language": "^6.10.8",
|
||||
"@codemirror/lint": "^6.8.4",
|
||||
"@codemirror/search": "^6.5.6",
|
||||
"@codemirror/state": "^6.4.1",
|
||||
@ -40,7 +40,7 @@
|
||||
"codemirror": "^6.0.1",
|
||||
"decamelize": "^6.0.0",
|
||||
"diff": "^7.0.0",
|
||||
"electron-updater": "^6.5.0",
|
||||
"electron-updater": "^6.6.0",
|
||||
"fuse.js": "^7.0.0",
|
||||
"html2canvas-pro": "^1.5.8",
|
||||
"isomorphic-fetch": "^3.0.0",
|
||||
@ -69,10 +69,15 @@
|
||||
"yargs": "^17.7.2"
|
||||
},
|
||||
"scripts": {
|
||||
"install:rust": "curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y",
|
||||
"install:rust:windows": "winget install Microsoft.VisualStudio.2022.Community --silent --override \"--wait --quiet --add ProductLang En-us --add Microsoft.VisualStudio.Workload.NativeDesktop --includeRecommended\" && winget install Rustlang.Rustup",
|
||||
"install:wasm-pack:sh": ". $HOME/.cargo/env && curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh -s -- -y",
|
||||
"install:wasm-pack:cargo": "cargo install wasm-pack",
|
||||
"install:tools:windows": "winget install jqlang.jq MikeFarah.yq GitHub.cli",
|
||||
"start": "vite --port=3000 --host=0.0.0.0",
|
||||
"start:prod": "vite preview --port=3000",
|
||||
"serve": "vite serve --port=3000",
|
||||
"build": "curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y && source \"$HOME/.cargo/env\" && curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh -s -- -y && yarn build:wasm && vite build",
|
||||
"build": "yarn install:rust && . $HOME/.cargo/env && yarn install:wasm-pack:sh && yarn build:wasm && vite build",
|
||||
"build:local": "vite build",
|
||||
"build:both": "vite build",
|
||||
"build:both:local": "yarn build:wasm && vite build",
|
||||
@ -84,11 +89,13 @@
|
||||
"simpleserver:stop": "kill-port 3000",
|
||||
"fmt": "prettier --write ./src *.ts *.json *.js ./e2e ./packages",
|
||||
"fmt-check": "prettier --check ./src *.ts *.json *.js ./e2e ./packages",
|
||||
"fetch:wasm": "./get-latest-wasm-bundle.sh",
|
||||
"fetch:samples": "echo \"Fetching latest KCL samples...\" && curl -o public/kcl-samples-manifest-fallback.json https://raw.githubusercontent.com/KittyCAD/kcl-samples/next/manifest.json",
|
||||
"isomorphic-copy-wasm": "(copy src/wasm-lib/pkg/wasm_lib_bg.wasm public || cp src/wasm-lib/pkg/wasm_lib_bg.wasm public)",
|
||||
"fetch:wasm": "./scripts/get-latest-wasm-bundle.sh",
|
||||
"fetch:wasm:windows": "./scripts/get-latest-wasm-bundle.ps1",
|
||||
"fetch:samples": "echo \"Fetching latest KCL samples...\" && curl -o public/kcl-samples-manifest-fallback.json https://raw.githubusercontent.com/KittyCAD/next/manifest.json",
|
||||
"build:wasm-dev": "yarn wasm-prep && (cd src/wasm-lib && wasm-pack build --dev --target web --out-dir pkg && cargo test -p kcl-lib export_bindings) && yarn isomorphic-copy-wasm && yarn fmt",
|
||||
"build:wasm": "yarn wasm-prep && cd src/wasm-lib && wasm-pack build --release --target web --out-dir pkg && cargo test -p kcl-lib export_bindings && cd ../.. && yarn isomorphic-copy-wasm && yarn fmt",
|
||||
"build:wasm:nocopy": "yarn wasm-prep && cd src/wasm-lib && wasm-pack build --release --target web --out-dir pkg && cargo test -p kcl-lib export_bindings",
|
||||
"build:wasm": "yarn build:wasm:nocopy && cp src/wasm-lib/pkg/wasm_lib_bg.wasm public && yarn fmt",
|
||||
"build:wasm:windows": "yarn install:wasm-pack:cargo && yarn build:wasm:nocopy && copy src\\wasm-lib\\pkg\\wasm_lib_bg.wasm public && yarn fmt",
|
||||
"remove-importmeta": "sed -i 's/import.meta.url/window.location.origin/g' \"./src/wasm-lib/pkg/wasm_lib.js\"; sed -i '' 's/import.meta.url/window.location.origin/g' \"./src/wasm-lib/pkg/wasm_lib.js\" || echo \"sed for both mac and linux\"",
|
||||
"wasm-prep": "rimraf src/wasm-lib/pkg && mkdirp src/wasm-lib/pkg && rimraf src/wasm-lib/kcl/bindings",
|
||||
"lint-fix": "eslint --fix --ext .ts --ext .tsx src e2e packages/codemirror-lsp-client/src",
|
||||
@ -96,6 +103,7 @@
|
||||
"files:set-version": "echo \"$(jq --arg v \"$VERSION\" '.version=$v' package.json --indent 2)\" > package.json",
|
||||
"files:set-notes": "./scripts/set-files-notes.sh",
|
||||
"files:flip-to-nightly": "./scripts/flip-files-to-nightly.sh",
|
||||
"files:flip-to-nightly:windows": "./scripts/flip-files-to-nightly.ps1",
|
||||
"files:invalidate-bucket": "./scripts/invalidate-files-bucket.sh",
|
||||
"files:invalidate-bucket:nightly": "./scripts/invalidate-files-bucket.sh --nightly",
|
||||
"postinstall": "yarn fetch:samples && yarn xstate:typegen && ./node_modules/.bin/electron-rebuild",
|
||||
@ -198,7 +206,7 @@
|
||||
"postinstall-postinstall": "^2.1.0",
|
||||
"prettier": "^2.8.8",
|
||||
"setimmediate": "^1.0.5",
|
||||
"tailwindcss": "^3.4.1",
|
||||
"tailwindcss": "^3.4.17",
|
||||
"ts-node": "^10.0.0",
|
||||
"typescript": "^5.7.3",
|
||||
"typescript-eslint": "^8.23.0",
|
||||
@ -207,7 +215,6 @@
|
||||
"vite-tsconfig-paths": "^4.3.2",
|
||||
"vitest": "^1.6.1",
|
||||
"vitest-webgl-canvas-mock": "^1.1.0",
|
||||
"wasm-pack": "^0.13.1",
|
||||
"ws": "^8.17.0",
|
||||
"yarn": "^1.22.22"
|
||||
},
|
||||
|
20
scripts/flip-files-to-nightly.ps1
Normal file
@ -0,0 +1,20 @@
|
||||
$VERSION=$(Get-Date -Format "yy.M.d")
|
||||
$COMMIT=$(git rev-parse --short HEAD)
|
||||
$PRODUCT_NAME="Zoo Modeling App (Nightly)"
|
||||
|
||||
# package.json
|
||||
yq -i '.version = env(VERSION)' -p=json -o=json package.json
|
||||
yq -i '.productName = env(PRODUCT_NAME)' -p=json -o=json package.json
|
||||
yq -i '.name = "zoo-modeling-app-nightly"' -p=json -o=json package.json
|
||||
|
||||
# electron-builder.yml
|
||||
yq -i '.publish[0].url = "https://dl.zoo.dev/releases/modeling-app/nightly"' electron-builder.yml
|
||||
yq -i '.appId = "dev.zoo.modeling-app-nightly"' electron-builder.yml
|
||||
yq -i '.nsis.include = "./scripts/installer-nightly.nsh"' electron-builder.yml
|
||||
|
||||
# Release notes
|
||||
echo "Nightly build $VERSION (commit $COMMIT)" > release-notes.md
|
||||
|
||||
# icons
|
||||
cp assets/icon-nightly.png assets/icon.png
|
||||
cp assets/icon-nightly.ico assets/icon.ico
|
13
scripts/get-latest-wasm-bundle.ps1
Normal file
@ -0,0 +1,13 @@
|
||||
$REPO_OWNER = "KittyCAD"
|
||||
$REPO_NAME = "modeling-app"
|
||||
$WORKFLOW_NAME = "build-and-store-wasm.yml"
|
||||
$ARTIFACT_NAME = "wasm-bundle"
|
||||
|
||||
# Fetch the latest completed workflow run ID for the specified workflow
|
||||
$RUN_ID = (gh run list -w $WORKFLOW_NAME --repo $REPO_OWNER/$REPO_NAME --limit 1 --json databaseId -s completed --jq 'first | .databaseId') -join [Environment]::NewLine
|
||||
|
||||
$PKG_PATH="./src/wasm-lib/pkg/"
|
||||
rm -r $PKG_PATH/*
|
||||
gh run download $RUN_ID --repo $REPO_OWNER/$REPO_NAME --name $ARTIFACT_NAME --dir $PKG_PATH
|
||||
cp $PKG_PATH/wasm_lib_bg.wasm public
|
||||
echo "latest wasm copied to public folder"
|
0
get-latest-wasm-bundle.sh → scripts/get-latest-wasm-bundle.sh
Executable file → Normal file
@ -6,14 +6,12 @@ import { useHotkeys } from 'react-hotkeys-hook'
|
||||
import { useLoaderData, useNavigate } from 'react-router-dom'
|
||||
import { type IndexLoaderData } from 'lib/types'
|
||||
import { PATHS } from 'lib/paths'
|
||||
import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext'
|
||||
import { onboardingPaths } from 'routes/Onboarding/paths'
|
||||
import { useEngineConnectionSubscriptions } from 'hooks/useEngineConnectionSubscriptions'
|
||||
import { codeManager, engineCommandManager } from 'lib/singletons'
|
||||
import { useAbsoluteFilePath } from 'hooks/useAbsoluteFilePath'
|
||||
import { isDesktop } from 'lib/isDesktop'
|
||||
import { useLspContext } from 'components/LspProvider'
|
||||
import { useRefreshSettings } from 'hooks/useRefreshSettings'
|
||||
import { ModelingSidebar } from 'components/ModelingSidebar/ModelingSidebar'
|
||||
import { LowerRightControls } from 'components/LowerRightControls'
|
||||
import ModalContainer from 'react-modal-promise'
|
||||
@ -30,6 +28,7 @@ import { useRouteLoaderData } from 'react-router-dom'
|
||||
import { useEngineCommands } from 'components/EngineCommands'
|
||||
import { commandBarActor } from 'machines/commandBarMachine'
|
||||
import { useToken } from 'machines/appMachine'
|
||||
import { useSettings } from 'machines/appMachine'
|
||||
maybeWriteToDisk()
|
||||
.then(() => {})
|
||||
.catch(() => {})
|
||||
@ -49,7 +48,6 @@ export function App() {
|
||||
})
|
||||
})
|
||||
|
||||
useRefreshSettings(PATHS.FILE + 'SETTINGS')
|
||||
const navigate = useNavigate()
|
||||
const filePath = useAbsoluteFilePath()
|
||||
const { onProjectOpen } = useLspContext()
|
||||
@ -71,7 +69,7 @@ export function App() {
|
||||
|
||||
useHotKeyListener()
|
||||
|
||||
const { settings } = useSettingsAuthContext()
|
||||
const settings = useSettings()
|
||||
const token = useToken()
|
||||
|
||||
const coreDumpManager = useMemo(
|
||||
@ -81,7 +79,7 @@ export function App() {
|
||||
|
||||
const {
|
||||
app: { onboardingStatus },
|
||||
} = settings.context
|
||||
} = settings
|
||||
|
||||
useHotkeys('backspace', (e) => {
|
||||
e.preventDefault()
|
||||
|
@ -28,10 +28,8 @@ import {
|
||||
fileLoader,
|
||||
homeLoader,
|
||||
onboardingRedirectLoader,
|
||||
settingsLoader,
|
||||
telemetryLoader,
|
||||
} from 'lib/routeLoaders'
|
||||
import SettingsAuthProvider from 'components/SettingsAuthProvider'
|
||||
import LspProvider from 'components/LspProvider'
|
||||
import { KclContextProvider } from 'lang/KclProvider'
|
||||
import { ASK_TO_OPEN_QUERY_PARAM, BROWSER_PROJECT_NAME } from 'lib/constants'
|
||||
@ -45,22 +43,17 @@ import { AppStateProvider } from 'AppState'
|
||||
import { reportRejection } from 'lib/trap'
|
||||
import { RouteProvider } from 'components/RouteProvider'
|
||||
import { ProjectsContextProvider } from 'components/ProjectsContextProvider'
|
||||
import { OpenInDesktopAppHandler } from 'components/OpenInDesktopAppHandler'
|
||||
import { useToken } from 'machines/appMachine'
|
||||
import { OpenInDesktopAppHandler } from 'components/OpenInDesktopAppHandler'
|
||||
|
||||
const createRouter = isDesktop() ? createHashRouter : createBrowserRouter
|
||||
|
||||
const router = createRouter([
|
||||
{
|
||||
loader: settingsLoader,
|
||||
id: PATHS.INDEX,
|
||||
// TODO: Re-evaluate if this is true
|
||||
/* Make sure auth is the outermost provider or else we will have
|
||||
* inefficient re-renders, use the react profiler to see. */
|
||||
element: (
|
||||
<OpenInDesktopAppHandler>
|
||||
<RouteProvider>
|
||||
<SettingsAuthProvider>
|
||||
<LspProvider>
|
||||
<ProjectsContextProvider>
|
||||
<KclContextProvider>
|
||||
@ -72,7 +65,6 @@ const router = createRouter([
|
||||
</KclContextProvider>
|
||||
</ProjectsContextProvider>
|
||||
</LspProvider>
|
||||
</SettingsAuthProvider>
|
||||
</RouteProvider>
|
||||
</OpenInDesktopAppHandler>
|
||||
),
|
||||
@ -120,7 +112,6 @@ const router = createRouter([
|
||||
children: [
|
||||
{
|
||||
id: PATHS.FILE + 'SETTINGS',
|
||||
loader: settingsLoader,
|
||||
children: [
|
||||
{
|
||||
loader: onboardingRedirectLoader,
|
||||
@ -166,11 +157,9 @@ const router = createRouter([
|
||||
index: true,
|
||||
element: <></>,
|
||||
id: PATHS.HOME + 'SETTINGS',
|
||||
loader: settingsLoader,
|
||||
},
|
||||
{
|
||||
path: makeUrlPathRelative(PATHS.SETTINGS),
|
||||
loader: settingsLoader,
|
||||
element: <Settings />,
|
||||
},
|
||||
{
|
||||
|
@ -2,7 +2,6 @@ import { useRef, useEffect, useState, useMemo, Fragment } from 'react'
|
||||
import { useModelingContext } from 'hooks/useModelingContext'
|
||||
|
||||
import { cameraMouseDragGuards } from 'lib/cameraControls'
|
||||
import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext'
|
||||
import { ARROWHEAD, DEBUG_SHOW_BOTH_SCENES } from './sceneInfra'
|
||||
import { ReactCameraProperties } from './CameraControls'
|
||||
import { throttle, toSync } from 'lib/utils'
|
||||
@ -48,6 +47,7 @@ import { ActionButton } from 'components/ActionButton'
|
||||
import { err, reportRejection, trap } from 'lib/trap'
|
||||
import { Node } from 'wasm-lib/kcl/bindings/Node'
|
||||
import { commandBarActor } from 'machines/commandBarMachine'
|
||||
import { useSettings } from 'machines/appMachine'
|
||||
|
||||
function useShouldHideScene(): { hideClient: boolean; hideServer: boolean } {
|
||||
const [isCamMoving, setIsCamMoving] = useState(false)
|
||||
@ -76,8 +76,8 @@ export const ClientSideScene = ({
|
||||
cameraControls,
|
||||
}: {
|
||||
cameraControls: ReturnType<
|
||||
typeof useSettingsAuthContext
|
||||
>['settings']['context']['modeling']['mouseControls']['current']
|
||||
typeof useSettings
|
||||
>['modeling']['mouseControls']['current']
|
||||
}) => {
|
||||
const canvasRef = useRef<HTMLDivElement>(null)
|
||||
const { state, send, context } = useModelingContext()
|
||||
|
@ -133,9 +133,11 @@ function DisplayObj({
|
||||
}}
|
||||
onClick={(e) => {
|
||||
const range = topLevelRange(obj?.start || 0, obj.end || 0)
|
||||
const idInfo = codeToIdSelections([
|
||||
{ codeRef: codeRefFromRange(range, kclManager.ast) },
|
||||
])[0]
|
||||
const idInfo = codeToIdSelections(
|
||||
[{ codeRef: codeRefFromRange(range, kclManager.ast) }],
|
||||
engineCommandManager.artifactGraph,
|
||||
engineCommandManager.artifactIndex
|
||||
)[0]
|
||||
const artifact = engineCommandManager.artifactGraph.get(
|
||||
idInfo?.id || ''
|
||||
)
|
||||
|
@ -1,24 +1,22 @@
|
||||
import { Switch } from '@headlessui/react'
|
||||
import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext'
|
||||
import { settingsActor, useSettings } from 'machines/appMachine'
|
||||
import { useEffect, useState } from 'react'
|
||||
|
||||
export function CameraProjectionToggle() {
|
||||
const { settings } = useSettingsAuthContext()
|
||||
const settings = useSettings()
|
||||
const isCameraProjectionPerspective =
|
||||
settings.context.modeling.cameraProjection.current === 'perspective'
|
||||
settings.modeling.cameraProjection.current === 'perspective'
|
||||
const [checked, setChecked] = useState(isCameraProjectionPerspective)
|
||||
|
||||
useEffect(() => {
|
||||
setChecked(
|
||||
settings.context.modeling.cameraProjection.current === 'perspective'
|
||||
)
|
||||
}, [settings.context.modeling.cameraProjection.current])
|
||||
setChecked(settings.modeling.cameraProjection.current === 'perspective')
|
||||
}, [settings.modeling.cameraProjection.current])
|
||||
|
||||
return (
|
||||
<Switch
|
||||
checked={checked}
|
||||
onChange={(newValue) => {
|
||||
settings.send({
|
||||
settingsActor.send({
|
||||
type: 'set.modeling.cameraProjection',
|
||||
data: {
|
||||
level: 'user',
|
||||
|
@ -7,7 +7,6 @@ import {
|
||||
} from '@codemirror/autocomplete'
|
||||
import { EditorView, keymap, ViewUpdate } from '@codemirror/view'
|
||||
import { CustomIcon } from 'components/CustomIcon'
|
||||
import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext'
|
||||
import { CommandArgument, KclCommandValue } from 'lib/commandTypes'
|
||||
import { getSystemTheme } from 'lib/theme'
|
||||
import { useCalculateKclExpression } from 'lib/useCalculateKclExpression'
|
||||
@ -20,6 +19,7 @@ import { createIdentifier, createVariableDeclaration } from 'lang/modifyAst'
|
||||
import { useCodeMirror } from 'components/ModelingSidebar/ModelingPanes/CodeEditor'
|
||||
import { useSelector } from '@xstate/react'
|
||||
import { commandBarActor, useCommandBarState } from 'machines/commandBarMachine'
|
||||
import { useSettings } from 'machines/appMachine'
|
||||
import toast from 'react-hot-toast'
|
||||
|
||||
const machineContextSelector = (snapshot?: {
|
||||
@ -42,7 +42,7 @@ function CommandBarKclInput({
|
||||
const previouslySetValue = commandBarState.context.argumentsToSubmit[
|
||||
arg.name
|
||||
] as KclCommandValue | undefined
|
||||
const { settings } = useSettingsAuthContext()
|
||||
const settings = useSettings()
|
||||
const argMachineContext = useSelector(
|
||||
arg.machineActor,
|
||||
machineContextSelector
|
||||
@ -117,9 +117,9 @@ function CommandBarKclInput({
|
||||
: defaultValue.length,
|
||||
},
|
||||
theme:
|
||||
settings.context.app.theme.current === 'system'
|
||||
settings.app.theme.current === 'system'
|
||||
? getSystemTheme()
|
||||
: settings.context.app.theme.current,
|
||||
: settings.app.theme.current,
|
||||
extensions: [
|
||||
varMentionsExtension,
|
||||
EditorView.updateListener.of((vu: ViewUpdate) => {
|
||||
|
@ -1,16 +1,16 @@
|
||||
import { Dialog } from '@headlessui/react'
|
||||
import { ActionButton } from './ActionButton'
|
||||
import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext'
|
||||
import { useState } from 'react'
|
||||
import { useSearchParams } from 'react-router-dom'
|
||||
import { CREATE_FILE_URL_PARAM } from 'lib/constants'
|
||||
import { useSettings } from 'machines/appMachine'
|
||||
|
||||
const DownloadAppBanner = () => {
|
||||
const [searchParams] = useSearchParams()
|
||||
const hasCreateFileParam = searchParams.has(CREATE_FILE_URL_PARAM)
|
||||
const { settings } = useSettingsAuthContext()
|
||||
const settings = useSettings()
|
||||
const [isBannerDismissed, setIsBannerDismissed] = useState(
|
||||
settings.context.app.dismissWebBanner.current || hasCreateFileParam
|
||||
settings.app.dismissWebBanner.current
|
||||
)
|
||||
|
||||
return (
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { useMachine } from '@xstate/react'
|
||||
import { useNavigate, useRouteLoaderData } from 'react-router-dom'
|
||||
import { useLocation, useNavigate, useRouteLoaderData } from 'react-router-dom'
|
||||
import { type IndexLoaderData } from 'lib/types'
|
||||
import { PATHS } from 'lib/paths'
|
||||
import { BROWSER_PATH, PATHS } from 'lib/paths'
|
||||
import React, { createContext, useEffect, useMemo } from 'react'
|
||||
import { toast } from 'react-hot-toast'
|
||||
import {
|
||||
@ -27,9 +27,10 @@ import {
|
||||
getKclSamplesManifest,
|
||||
KclSamplesManifestItem,
|
||||
} from 'lib/getKclSamplesManifest'
|
||||
import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext'
|
||||
import { markOnce } from 'lib/performance'
|
||||
import { commandBarActor } from 'machines/commandBarMachine'
|
||||
import { settingsActor, useSettings } from 'machines/appMachine'
|
||||
import { createRouteCommands } from 'lib/commandBarConfigs/routeCommandConfig'
|
||||
import { useToken } from 'machines/appMachine'
|
||||
|
||||
type MachineContext<T extends AnyStateMachine> = {
|
||||
@ -48,14 +49,51 @@ export const FileMachineProvider = ({
|
||||
children: React.ReactNode
|
||||
}) => {
|
||||
const navigate = useNavigate()
|
||||
const { settings } = useSettingsAuthContext()
|
||||
const location = useLocation()
|
||||
const token = useToken()
|
||||
const settings = useSettings()
|
||||
const projectData = useRouteLoaderData(PATHS.FILE) as IndexLoaderData
|
||||
const { project, file } = projectData
|
||||
const [kclSamples, setKclSamples] = React.useState<KclSamplesManifestItem[]>(
|
||||
[]
|
||||
)
|
||||
|
||||
// Due to the route provider, i've moved this to the FileMachineProvider instead of CommandBarProvider
|
||||
// This will register the commands to route to Telemetry, Home, and Settings.
|
||||
useEffect(() => {
|
||||
const filePath =
|
||||
PATHS.FILE + '/' + encodeURIComponent(file?.path || BROWSER_PATH)
|
||||
const { RouteTelemetryCommand, RouteHomeCommand, RouteSettingsCommand } =
|
||||
createRouteCommands(navigate, location, filePath)
|
||||
commandBarActor.send({
|
||||
type: 'Remove commands',
|
||||
data: {
|
||||
commands: [
|
||||
RouteTelemetryCommand,
|
||||
RouteHomeCommand,
|
||||
RouteSettingsCommand,
|
||||
],
|
||||
},
|
||||
})
|
||||
if (location.pathname === PATHS.HOME) {
|
||||
commandBarActor.send({
|
||||
type: 'Add commands',
|
||||
data: { commands: [RouteTelemetryCommand, RouteSettingsCommand] },
|
||||
})
|
||||
} else if (location.pathname.includes(PATHS.FILE)) {
|
||||
commandBarActor.send({
|
||||
type: 'Add commands',
|
||||
data: {
|
||||
commands: [
|
||||
RouteTelemetryCommand,
|
||||
RouteSettingsCommand,
|
||||
RouteHomeCommand,
|
||||
],
|
||||
},
|
||||
})
|
||||
}
|
||||
}, [location])
|
||||
|
||||
useEffect(() => {
|
||||
markOnce('code/didLoadFile')
|
||||
async function fetchKclSamples() {
|
||||
@ -323,7 +361,7 @@ export const FileMachineProvider = ({
|
||||
authToken: token ?? '',
|
||||
projectData,
|
||||
settings: {
|
||||
defaultUnit: settings?.context?.modeling.defaultUnit.current ?? 'mm',
|
||||
defaultUnit: settings.modeling.defaultUnit.current ?? 'mm',
|
||||
},
|
||||
specialPropsForSampleCommand: {
|
||||
onSubmit: async (data) => {
|
||||
@ -345,7 +383,7 @@ export const FileMachineProvider = ({
|
||||
// Either way, we want to overwrite the defaultUnit project setting
|
||||
// with the sample's setting.
|
||||
if (data.sampleUnits) {
|
||||
settings.send({
|
||||
settingsActor.send({
|
||||
type: 'set.modeling.defaultUnit',
|
||||
data: {
|
||||
level: 'project',
|
||||
|
@ -1,6 +1,5 @@
|
||||
import { Popover } from '@headlessui/react'
|
||||
import Tooltip from './Tooltip'
|
||||
import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext'
|
||||
import { CustomIcon } from './CustomIcon'
|
||||
import { useLocation, useNavigate } from 'react-router-dom'
|
||||
import { PATHS } from 'lib/paths'
|
||||
@ -9,6 +8,7 @@ import { useAbsoluteFilePath } from 'hooks/useAbsoluteFilePath'
|
||||
import { useLspContext } from './LspProvider'
|
||||
import { openExternalBrowserIfDesktop } from 'lib/openWindow'
|
||||
import { reportRejection } from 'lib/trap'
|
||||
import { settingsActor } from 'machines/appMachine'
|
||||
|
||||
const HelpMenuDivider = () => (
|
||||
<div className="h-[1px] bg-chalkboard-110 dark:bg-chalkboard-80" />
|
||||
@ -20,7 +20,6 @@ export function HelpMenu(props: React.PropsWithChildren) {
|
||||
const filePath = useAbsoluteFilePath()
|
||||
const isInProject = location.pathname.includes(PATHS.FILE)
|
||||
const navigate = useNavigate()
|
||||
const { settings } = useSettingsAuthContext()
|
||||
|
||||
return (
|
||||
<Popover className="relative">
|
||||
@ -106,7 +105,7 @@ export function HelpMenu(props: React.PropsWithChildren) {
|
||||
<HelpMenuItem
|
||||
as="button"
|
||||
onClick={() => {
|
||||
settings.send({
|
||||
settingsActor.send({
|
||||
type: 'set.app.onboardingStatus',
|
||||
data: {
|
||||
value: '',
|
||||
|
@ -10,7 +10,6 @@ import {
|
||||
import { TEST, VITE_KC_API_BASE_URL } from 'env'
|
||||
import { kcl } from 'editor/plugins/lsp/kcl/language'
|
||||
import { copilotPlugin } from 'editor/plugins/lsp/copilot'
|
||||
import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext'
|
||||
import { Extension } from '@codemirror/state'
|
||||
import { LanguageSupport } from '@codemirror/language'
|
||||
import { useNavigate } from 'react-router-dom'
|
||||
|
@ -22,7 +22,6 @@ import {
|
||||
modelingMachineDefaultContext,
|
||||
} from 'machines/modelingMachine'
|
||||
import { useSetupEngineManager } from 'hooks/useSetupEngineManager'
|
||||
import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext'
|
||||
import {
|
||||
isCursorInSketchCommandRange,
|
||||
updateSketchDetailsNodePaths,
|
||||
@ -110,6 +109,8 @@ import { kclEditorActor } from 'machines/kclEditorMachine'
|
||||
import { commandBarActor } from 'machines/commandBarMachine'
|
||||
import { useToken } from 'machines/appMachine'
|
||||
import { getNodePathFromSourceRange } from 'lang/queryAstNodePathUtils'
|
||||
import { useSettings } from 'machines/appMachine'
|
||||
import { isDesktop } from 'lib/isDesktop'
|
||||
|
||||
type MachineContext<T extends AnyStateMachine> = {
|
||||
state: StateFrom<T>
|
||||
@ -131,8 +132,6 @@ export const ModelingMachineProvider = ({
|
||||
children: React.ReactNode
|
||||
}) => {
|
||||
const {
|
||||
settings: {
|
||||
context: {
|
||||
app: { theme, enableSSAO, allowOrbitInSketchMode },
|
||||
modeling: {
|
||||
defaultUnit,
|
||||
@ -141,9 +140,7 @@ export const ModelingMachineProvider = ({
|
||||
showScaleGrid,
|
||||
cameraOrbit,
|
||||
},
|
||||
},
|
||||
},
|
||||
} = useSettingsAuthContext()
|
||||
} = useSettings()
|
||||
const previousAllowOrbitInSketchMode = useRef(allowOrbitInSketchMode.current)
|
||||
const navigate = useNavigate()
|
||||
const { context, send: fileMachineSend } = useFileContext()
|
||||
@ -806,7 +803,7 @@ export const ModelingMachineProvider = ({
|
||||
engineCommandManager.artifactGraph
|
||||
)
|
||||
if (err(plane)) return Promise.reject(plane)
|
||||
// if the user selected a segment, make sure we enter the right sketch as there can be multiple on a plan
|
||||
// if the user selected a segment, make sure we enter the right sketch as there can be multiple on a plane
|
||||
// but still works if the user selected a plane/face by defaulting to the first path
|
||||
const mainPath =
|
||||
artifact?.type === 'segment' || artifact?.type === 'solid2d'
|
||||
@ -1721,8 +1718,12 @@ export const ModelingMachineProvider = ({
|
||||
previousAllowOrbitInSketchMode.current = allowOrbitInSketchMode.current
|
||||
}, [allowOrbitInSketchMode])
|
||||
|
||||
// Allow using the delete key to delete solids
|
||||
useHotkeys(['backspace', 'delete', 'del'], () => {
|
||||
// Allow using the delete key to delete solids. Backspace only on macOS as Windows and Linux have dedicated Delete
|
||||
const deleteKeys =
|
||||
isDesktop() && window.electron.os.isMac
|
||||
? ['backspace', 'delete', 'del']
|
||||
: ['delete', 'del']
|
||||
useHotkeys(deleteKeys, () => {
|
||||
modelingSend({ type: 'Delete selection' })
|
||||
})
|
||||
|
||||
|
@ -1,12 +1,12 @@
|
||||
import { ReactNode } from 'react'
|
||||
import styles from './ModelingPane.module.css'
|
||||
import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext'
|
||||
import { ActionButton } from 'components/ActionButton'
|
||||
import Tooltip from 'components/Tooltip'
|
||||
import { CustomIconName } from 'components/CustomIcon'
|
||||
import { IconDefinition } from '@fortawesome/free-solid-svg-icons'
|
||||
import { ActionIcon } from 'components/ActionIcon'
|
||||
import { onboardingPaths } from 'routes/Onboarding/paths'
|
||||
import { useSettings } from 'machines/appMachine'
|
||||
|
||||
export interface ModelingPaneProps {
|
||||
id: string
|
||||
@ -68,8 +68,8 @@ export const ModelingPane = ({
|
||||
title,
|
||||
...props
|
||||
}: ModelingPaneProps) => {
|
||||
const { settings } = useSettingsAuthContext()
|
||||
const onboardingStatus = settings.context.app.onboardingStatus
|
||||
const settings = useSettings()
|
||||
const onboardingStatus = settings.app.onboardingStatus
|
||||
const pointerEventsCssClass =
|
||||
onboardingStatus.current === onboardingPaths.CAMERA
|
||||
? 'pointer-events-none '
|
||||
|
@ -324,6 +324,18 @@ const OperationItem = (props: {
|
||||
}
|
||||
}
|
||||
|
||||
function enterAppearanceFlow() {
|
||||
if (props.item.type === 'StdLibCall') {
|
||||
props.send({
|
||||
type: 'enterAppearanceFlow',
|
||||
data: {
|
||||
targetSourceRange: sourceRangeFromRust(props.item.sourceRange),
|
||||
currentOperation: props.item,
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
function deleteOperation() {
|
||||
if (
|
||||
props.item.type === 'StdLibCall' ||
|
||||
@ -380,6 +392,13 @@ const OperationItem = (props: {
|
||||
: []),
|
||||
...(props.item.type === 'StdLibCall'
|
||||
? [
|
||||
<ContextMenuItem
|
||||
disabled={!stdLibMap[props.item.name]?.supportsAppearance}
|
||||
onClick={enterAppearanceFlow}
|
||||
data-testid="context-menu-set-appearance"
|
||||
>
|
||||
Set appearance
|
||||
</ContextMenuItem>,
|
||||
<ContextMenuItem
|
||||
disabled={!stdLibMap[props.item.name]?.prepareToEdit}
|
||||
onClick={enterEditFlow}
|
||||
|
@ -1,5 +1,4 @@
|
||||
import { TEST } from 'env'
|
||||
import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext'
|
||||
import { Themes, getSystemTheme } from 'lib/theme'
|
||||
import { useEffect, useMemo, useRef } from 'react'
|
||||
import { highlightSelectionMatches, searchKeymap } from '@codemirror/search'
|
||||
@ -51,6 +50,7 @@ import {
|
||||
} from 'machines/kclEditorMachine'
|
||||
import { useSelector } from '@xstate/react'
|
||||
import { modelingMachineEvent } from 'editor/manager'
|
||||
import { useSettings } from 'machines/appMachine'
|
||||
|
||||
export const editorShortcutMeta = {
|
||||
formatCode: {
|
||||
@ -63,9 +63,7 @@ export const editorShortcutMeta = {
|
||||
}
|
||||
|
||||
export const KclEditorPane = () => {
|
||||
const {
|
||||
settings: { context },
|
||||
} = useSettingsAuthContext()
|
||||
const context = useSettings()
|
||||
const lastSelectionEvent = useSelector(kclEditorActor, selectionEventSelector)
|
||||
const editorIsMounted = useSelector(kclEditorActor, editorIsMountedSelector)
|
||||
const theme =
|
||||
|
@ -33,7 +33,7 @@ describe('processMemory', () => {
|
||||
const output = processMemory(execState.variables)
|
||||
expect(output.myVar).toEqual(5)
|
||||
expect(output.otherVar).toEqual(3)
|
||||
expect(output.myFn).toEqual('__function(a)__')
|
||||
expect(output.myFn).toEqual('__function__')
|
||||
expect(output.theExtrude).toEqual([
|
||||
{
|
||||
type: 'extrudePlane',
|
||||
|
@ -107,9 +107,7 @@ export const processMemory = (variables: VariableMap) => {
|
||||
}
|
||||
//@ts-ignore
|
||||
} else if (val.type === 'Function') {
|
||||
processedMemory[key] = `__function(${(val as any)?.expression?.params
|
||||
?.map?.(({ identifier }: any) => identifier?.name || '')
|
||||
.join(', ')})__`
|
||||
processedMemory[key] = `__function__`
|
||||
}
|
||||
}
|
||||
return processedMemory
|
||||
|
@ -1,4 +1,3 @@
|
||||
import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext'
|
||||
import { Resizable } from 're-resizable'
|
||||
import {
|
||||
MouseEventHandler,
|
||||
@ -21,6 +20,7 @@ import { MachineManagerContext } from 'components/MachineManagerProvider'
|
||||
import { onboardingPaths } from 'routes/Onboarding/paths'
|
||||
import { SIDEBAR_BUTTON_SUFFIX } from 'lib/constants'
|
||||
import { commandBarActor } from 'machines/commandBarMachine'
|
||||
import { useSettings } from 'machines/appMachine'
|
||||
|
||||
interface ModelingSidebarProps {
|
||||
paneOpacity: '' | 'opacity-20' | 'opacity-40'
|
||||
@ -38,23 +38,23 @@ function getPlatformString(): 'web' | 'desktop' {
|
||||
export function ModelingSidebar({ paneOpacity }: ModelingSidebarProps) {
|
||||
const machineManager = useContext(MachineManagerContext)
|
||||
const kclContext = useKclContext()
|
||||
const { settings } = useSettingsAuthContext()
|
||||
const onboardingStatus = settings.context.app.onboardingStatus
|
||||
const settings = useSettings()
|
||||
const onboardingStatus = settings.app.onboardingStatus
|
||||
const { send, context } = useModelingContext()
|
||||
const pointerEventsCssClass =
|
||||
onboardingStatus.current === onboardingPaths.CAMERA ||
|
||||
context.store?.openPanes.length === 0
|
||||
? 'pointer-events-none '
|
||||
: 'pointer-events-auto '
|
||||
const showDebugPanel = settings.context.modeling.showDebugPanel
|
||||
const showDebugPanel = settings.modeling.showDebugPanel
|
||||
|
||||
const paneCallbackProps = useMemo(
|
||||
() => ({
|
||||
kclContext,
|
||||
settings: settings.context,
|
||||
settings,
|
||||
platform: getPlatformString(),
|
||||
}),
|
||||
[kclContext.diagnostics, settings.context]
|
||||
[kclContext.diagnostics, settings]
|
||||
)
|
||||
|
||||
const sidebarActions: SidebarAction[] = [
|
||||
@ -144,7 +144,7 @@ export function ModelingSidebar({ paneOpacity }: ModelingSidebarProps) {
|
||||
},
|
||||
})
|
||||
}
|
||||
}, [settings.context])
|
||||
}, [settings.modeling.showDebugPanel])
|
||||
|
||||
const togglePane = useCallback(
|
||||
(newPane: SidebarType) => {
|
||||
|
@ -1,6 +1,5 @@
|
||||
import { fireEvent, render, screen } from '@testing-library/react'
|
||||
import { BrowserRouter } from 'react-router-dom'
|
||||
import { SettingsAuthProviderJest } from './SettingsAuthProvider'
|
||||
import {
|
||||
NETWORK_HEALTH_TEXT,
|
||||
NetworkHealthIndicator,
|
||||
@ -9,11 +8,7 @@ import { NetworkHealthState } from 'hooks/useNetworkStatus'
|
||||
|
||||
function TestWrap({ children }: { children: React.ReactNode }) {
|
||||
// wrap in router and xState context
|
||||
return (
|
||||
<BrowserRouter>
|
||||
<SettingsAuthProviderJest>{children}</SettingsAuthProviderJest>
|
||||
</BrowserRouter>
|
||||
)
|
||||
return <BrowserRouter>{children}</BrowserRouter>
|
||||
}
|
||||
|
||||
// Our Playwright tests for this are much more comprehensive.
|
||||
|
@ -1,7 +1,6 @@
|
||||
import { render, screen } from '@testing-library/react'
|
||||
import { BrowserRouter } from 'react-router-dom'
|
||||
import ProjectSidebarMenu from './ProjectSidebarMenu'
|
||||
import { SettingsAuthProviderJest } from './SettingsAuthProvider'
|
||||
import { Project } from 'lib/project'
|
||||
|
||||
const now = new Date()
|
||||
@ -32,9 +31,7 @@ describe('ProjectSidebarMenu tests', () => {
|
||||
test('Disables popover menu by default', () => {
|
||||
render(
|
||||
<BrowserRouter>
|
||||
<SettingsAuthProviderJest>
|
||||
<ProjectSidebarMenu project={projectWellFormed} />
|
||||
</SettingsAuthProviderJest>
|
||||
</BrowserRouter>
|
||||
)
|
||||
|
||||
|