Compare commits

..

2 Commits

Author SHA1 Message Date
8b20f898fe fix fmt 2025-02-21 14:48:38 +01:00
d7c5c3cf9d fix 2025-02-21 14:46:47 +01:00
380 changed files with 33765 additions and 23261 deletions

View File

@ -21,7 +21,7 @@ if [[ ! -f "test-results/.last-run.json" ]]; then
fi fi
retry=1 retry=1
max_retrys=1 max_retrys=5
# retry failed tests, doing our own retries because using inbuilt playwright retries causes connection issues # retry failed tests, doing our own retries because using inbuilt playwright retries causes connection issues
while [[ $retry -le $max_retrys ]]; do while [[ $retry -le $max_retrys ]]; do

View File

@ -22,9 +22,6 @@ jobs:
uses: Swatinem/rust-cache@v2 uses: Swatinem/rust-cache@v2
with: with:
workspaces: './src/wasm-lib' workspaces: './src/wasm-lib'
- uses: taiki-e/install-action@2dbeb927f58939d3aa13bf06ba0c0a34b76b9bfb
with:
tool: wasm-pack
- name: build wasm - name: build wasm
run: yarn build:wasm run: yarn build:wasm

View File

@ -32,6 +32,7 @@ jobs:
- uses: actions/setup-node@v4 - uses: actions/setup-node@v4
with: with:
node-version-file: '.nvmrc' node-version-file: '.nvmrc'
cache: 'yarn'
- run: yarn install - run: yarn install
@ -43,10 +44,6 @@ jobs:
workspaces: './src/wasm-lib' workspaces: './src/wasm-lib'
# TODO: see if we can fetch from main instead if no diff at 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 - name: Run build:wasm
run: "yarn build:wasm" run: "yarn build:wasm"
@ -123,13 +120,15 @@ jobs:
cp prepared-files/assets/icon.ico assets/icon.ico cp prepared-files/assets/icon.ico assets/icon.ico
cp prepared-files/assets/icon.png assets/icon.png cp prepared-files/assets/icon.png assets/icon.png
- uses: actions/setup-node@v4 - name: Sync node version and setup cache
uses: actions/setup-node@v4
with: with:
node-version-file: '.nvmrc' node-version-file: '.nvmrc'
cache: 'yarn' # Set this to npm, yarn or pnpm.
- name: yarn install - name: yarn install
# Windows is picky sometimes and fails on fetch. Step takes about ~30s # Windows is picky sometimes and fails on fetch. Step takes about ~30s
uses: nick-fields/retry@v3.0.1 uses: nick-fields/retry@v3.0.0
with: with:
timeout_minutes: 2 timeout_minutes: 2
max_attempts: 3 max_attempts: 3
@ -180,7 +179,7 @@ jobs:
WINDOWS_CERTIFICATE_THUMBPRINT: ${{ secrets.WINDOWS_CERTIFICATE_THUMBPRINT }} WINDOWS_CERTIFICATE_THUMBPRINT: ${{ secrets.WINDOWS_CERTIFICATE_THUMBPRINT }}
DEBUG: "electron-notarize*" DEBUG: "electron-notarize*"
# TODO: Fix electron-notarize flakes. The logs above should help gather more data on failures # TODO: Fix electron-notarize flakes. The logs above should help gather more data on failures
uses: nick-fields/retry@v3.0.1 uses: nick-fields/retry@v3.0.0
with: with:
timeout_minutes: 10 timeout_minutes: 10
max_attempts: 3 max_attempts: 3
@ -241,7 +240,7 @@ jobs:
WINDOWS_CERTIFICATE_THUMBPRINT: ${{ secrets.WINDOWS_CERTIFICATE_THUMBPRINT }} WINDOWS_CERTIFICATE_THUMBPRINT: ${{ secrets.WINDOWS_CERTIFICATE_THUMBPRINT }}
DEBUG: "electron-notarize*" DEBUG: "electron-notarize*"
# TODO: Fix electron-notarize flakes. The logs above should help gather more data on failures # TODO: Fix electron-notarize flakes. The logs above should help gather more data on failures
uses: nick-fields/retry@v3.0.1 uses: nick-fields/retry@v3.0.0
with: with:
timeout_minutes: 10 timeout_minutes: 10
max_attempts: 3 max_attempts: 3

View File

@ -46,7 +46,7 @@ jobs:
shell: bash shell: bash
run: |- run: |-
cd "${{ matrix.dir }}" cd "${{ matrix.dir }}"
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 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
env: env:
KITTYCAD_API_TOKEN: ${{secrets.KITTYCAD_API_TOKEN}} KITTYCAD_API_TOKEN: ${{secrets.KITTYCAD_API_TOKEN}}
RUST_MIN_STACK: 10485760000 RUST_MIN_STACK: 10485760000

View File

@ -26,21 +26,11 @@ jobs:
const issue_number = context.payload.pull_request.number; const issue_number = context.payload.pull_request.number;
const owner = context.repo.owner; const owner = context.repo.owner;
const repo = context.repo.repo; const repo = context.repo.repo;
const { data: comments } = await github.rest.issues.listComments({ // Post a comment on the PR
await github.rest.issues.createComment({
owner, owner,
repo, repo,
issue_number issue_number,
}); body: message,
});
const commentExists = comments.some(comment => comment.body === message);
if (!commentExists) {
// Post a comment on the PR
await github.rest.issues.createComment({
owner,
repo,
issue_number,
body: message,
});
}

View File

@ -89,9 +89,6 @@ jobs:
continue-on-error: true continue-on-error: true
- name: Setup Rust - name: Setup Rust
uses: dtolnay/rust-toolchain@stable uses: dtolnay/rust-toolchain@stable
- uses: taiki-e/install-action@2dbeb927f58939d3aa13bf06ba0c0a34b76b9bfb
with:
tool: wasm-pack
- name: Cache Wasm (because rust diff) - name: Cache Wasm (because rust diff)
if: needs.check-rust-changes.outputs.rust-changed == 'true' if: needs.check-rust-changes.outputs.rust-changed == 'true'
uses: Swatinem/rust-cache@v2 uses: Swatinem/rust-cache@v2
@ -203,11 +200,9 @@ jobs:
- name: Run playwright/electron flow (with retries) - name: Run playwright/electron flow (with retries)
id: retry id: retry
if: ${{ !cancelled() && (success() || failure()) }} if: ${{ !cancelled() && (success() || failure()) }}
uses: nick-fields/retry@v3.0.1 shell: bash
with: run: |
command: .github/ci-cd-scripts/playwright-electron.sh ${{matrix.shardIndex}} ${{matrix.shardTotal}} ${{matrix.os}} .github/ci-cd-scripts/playwright-electron.sh ${{matrix.shardIndex}} ${{matrix.shardTotal}} ${{matrix.os}}
timeout_minutes: 30
max_attempts: 25
env: env:
CI: true CI: true
FAIL_ON_CONSOLE_ERRORS: true FAIL_ON_CONSOLE_ERRORS: true

View File

@ -37,9 +37,6 @@ jobs:
node-version-file: '.nvmrc' node-version-file: '.nvmrc'
cache: 'yarn' cache: 'yarn'
- run: yarn install - run: yarn install
- uses: taiki-e/install-action@2dbeb927f58939d3aa13bf06ba0c0a34b76b9bfb
with:
tool: wasm-pack
- run: yarn build:wasm - run: yarn build:wasm
yarn-tsc: yarn-tsc:
@ -56,9 +53,6 @@ jobs:
with: with:
workspaces: './src/wasm-lib' workspaces: './src/wasm-lib'
- uses: taiki-e/install-action@2dbeb927f58939d3aa13bf06ba0c0a34b76b9bfb
with:
tool: wasm-pack
- run: yarn build:wasm - run: yarn build:wasm
- run: yarn tsc - run: yarn tsc
@ -98,9 +92,6 @@ jobs:
cache: 'yarn' cache: 'yarn'
- run: yarn install - run: yarn install
- uses: taiki-e/install-action@2dbeb927f58939d3aa13bf06ba0c0a34b76b9bfb
with:
tool: wasm-pack
- run: yarn build:wasm - run: yarn build:wasm
- run: yarn simpleserver:bg - run: yarn simpleserver:bg
@ -127,9 +118,6 @@ jobs:
cache: 'yarn' cache: 'yarn'
- run: yarn install - run: yarn install
- uses: taiki-e/install-action@2dbeb927f58939d3aa13bf06ba0c0a34b76b9bfb
with:
tool: wasm-pack
- run: yarn build:wasm - run: yarn build:wasm
- run: yarn simpleserver:bg - run: yarn simpleserver:bg

2
.gitignore vendored
View File

@ -24,7 +24,7 @@ yarn-debug.log*
yarn-error.log* yarn-error.log*
.idea .idea
# .vscode .vscode
.helix .helix
src/wasm-lib/.idea src/wasm-lib/.idea
src/wasm-lib/.vscode src/wasm-lib/.vscode

View File

@ -1,5 +0,0 @@
{
"rust-analyzer.linkedProjects": [
"src/wasm-lib/Cargo.toml"
]
}

View File

@ -48,49 +48,22 @@ We recommend downloading the latest application binary from [our Releases page](
## Running a development build ## Running a development build
Install a node version manager such as [fnm](https://github.com/Schniz/fnm?tab=readme-ov-#installation). 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:
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 yarn install
``` ```
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: followed by:
```
# 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 yarn build:wasm
# Windows
yarn build:wasm:windows
``` ```
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`. or if you have the gh cli installed
``` ```
# macOS/Linux ./get-latest-wasm-bundle.sh # this will download the latest main wasm bundle
yarn fetch:wasm
# Windows
yarn fetch:wasm:windows
``` ```
That will build the WASM binary and put in the `public` dir (though gitignored). That will build the WASM binary and put in the `public` dir (though gitignored).
@ -101,7 +74,7 @@ Finally, to run the web app only, run:
yarn start yarn start
``` ```
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. 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.
### Development environment variables ### Development environment variables
@ -128,7 +101,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). Devtools can be opened with the usual Cmd-Opt-I (Mac) or Ctrl-Shift-I (Linux and Windows).
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) To build with electron-builder, run `yarn tronb:package:dev` (or `yarn tronb:package:prod` to point to the .env.production variables)
## Checking out commits / Bisecting ## Checking out commits / Bisecting

View File

@ -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. This will work on any solid, including extruded solids, revolved solids, and shelled solids.
```js ```js
appearance(solidSet: SolidSet, color: String, metalness?: number, roughness?: number) -> SolidSet appearance(solid_set: SolidSet, color: String, metalness?: number, roughness?: number) -> SolidSet
``` ```
@ -17,7 +17,7 @@ appearance(solidSet: SolidSet, color: String, metalness?: number, roughness?: nu
| Name | Type | Description | Required | | Name | Type | Description | Required |
|----------|------|-------------|----------| |----------|------|-------------|----------|
| `solidSet` | [`SolidSet`](/docs/kcl/types/SolidSet) | The solid(s) whose appearance is being set | Yes | | `solid_set` | [`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 | | `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 | | `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 | | `roughness` | `number` | Roughness of the new material, a percentage like 95.7. | No |

File diff suppressed because one or more lines are too long

View File

@ -9,7 +9,7 @@ Construct a 2-dimensional circle, of the specified radius, centered at
the provided (x, y) origin point. the provided (x, y) origin point.
```js ```js
circle(data: CircleData, sketchSurfaceOrGroup: SketchOrSurface, tag?: TagDeclarator) -> Sketch circle(data: CircleData, sketch_surface_or_group: SketchOrSurface, tag?: TagDeclarator) -> Sketch
``` ```
@ -18,7 +18,7 @@ circle(data: CircleData, sketchSurfaceOrGroup: SketchOrSurface, tag?: TagDeclara
| Name | Type | Description | Required | | Name | Type | Description | Required |
|----------|------|-------------|----------| |----------|------|-------------|----------|
| `data` | [`CircleData`](/docs/kcl/types/CircleData) | Data for drawing an circle | Yes | | `data` | [`CircleData`](/docs/kcl/types/CircleData) | Data for drawing an circle | Yes |
| `sketchSurfaceOrGroup` | [`SketchOrSurface`](/docs/kcl/types/SketchOrSurface) | A sketch surface or a sketch. | Yes | | `sketch_surface_or_group` | [`SketchOrSurface`](/docs/kcl/types/SketchOrSurface) | A sketch surface or a sketch. | Yes |
| `tag` | [`TagDeclarator`](/docs/kcl/types#tag-declaration) | | No | | `tag` | [`TagDeclarator`](/docs/kcl/types#tag-declaration) | | No |
### Returns ### Returns

View File

@ -9,7 +9,7 @@ Construct a circle derived from 3 points.
```js ```js
circleThreePoint(p1: [number], p2: [number], p3: [number], sketchSurfaceOrGroup: SketchOrSurface, tag?: TagDeclarator) -> Sketch circleThreePoint(p1: [number], p2: [number], p3: [number], sketch_surface_or_group: SketchOrSurface, tag?: TagDeclarator) -> Sketch
``` ```
@ -20,7 +20,7 @@ circleThreePoint(p1: [number], p2: [number], p3: [number], sketchSurfaceOrGroup:
| `p1` | `[number]` | 1st point to derive the circle. | Yes | | `p1` | `[number]` | 1st point to derive the circle. | Yes |
| `p2` | `[number]` | 2nd point to derive the circle. | Yes | | `p2` | `[number]` | 2nd point to derive the circle. | Yes |
| `p3` | `[number]` | 3rd point to derive the circle. | Yes | | `p3` | `[number]` | 3rd point to derive the circle. | Yes |
| `sketchSurfaceOrGroup` | [`SketchOrSurface`](/docs/kcl/types/SketchOrSurface) | Plane or surface to sketch on. | Yes | | `sketch_surface_or_group` | [`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 | | `tag` | [`TagDeclarator`](/docs/kcl/types#tag-declaration) | Identifier for the circle to reference elsewhere. | No |
### Returns ### Returns

View File

@ -0,0 +1,15 @@
---
title: "std::prelude::HALF_TURN"
excerpt: ""
layout: manual
---
```js
std::prelude::HALF_TURN: number(deg) = 180deg
```

View File

@ -0,0 +1,15 @@
---
title: "std::prelude::QUARTER_TURN"
excerpt: ""
layout: manual
---
```js
std::prelude::QUARTER_TURN: number(deg) = 90deg
```

View File

@ -0,0 +1,15 @@
---
title: "std::prelude::THREE_QUARTER_TURN"
excerpt: ""
layout: manual
---
```js
std::prelude::THREE_QUARTER_TURN: number(deg) = 270deg
```

View File

@ -0,0 +1,15 @@
---
title: "std::prelude::ZERO"
excerpt: ""
layout: manual
---
```js
std::prelude::ZERO: number = 0
```

View File

@ -1,15 +0,0 @@
---
title: "std::HALF_TURN"
excerpt: ""
layout: manual
---
```js
std::HALF_TURN: number(deg) = 180deg
```

View File

@ -1,15 +0,0 @@
---
title: "std::QUARTER_TURN"
excerpt: ""
layout: manual
---
```js
std::QUARTER_TURN: number(deg) = 90deg
```

View File

@ -1,15 +0,0 @@
---
title: "std::THREE_QUARTER_TURN"
excerpt: ""
layout: manual
---
```js
std::THREE_QUARTER_TURN: number(deg) = 270deg
```

View File

@ -1,15 +0,0 @@
---
title: "std::ZERO"
excerpt: ""
layout: manual
---
```js
std::ZERO: number = 0
```

View File

@ -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. create new 3-dimensional volume, or if extruded into an existing volume, cut into an existing solid.
```js ```js
extrude(sketchSet: SketchSet, length: number) -> SolidSet extrude(sketch_set: SketchSet, length: number) -> SolidSet
``` ```
@ -17,7 +17,7 @@ extrude(sketchSet: SketchSet, length: number) -> SolidSet
| Name | Type | Description | Required | | Name | Type | Description | Required |
|----------|------|-------------|----------| |----------|------|-------------|----------|
| `sketchSet` | [`SketchSet`](/docs/kcl/types/SketchSet) | Which sketches should be extruded | Yes | | `sketch_set` | [`SketchSet`](/docs/kcl/types/SketchSet) | Which sketches should be extruded | Yes |
| `length` | `number` | How far to extrude the given sketches | Yes | | `length` | `number` | How far to extrude the given sketches | Yes |
### Returns ### Returns

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -9,7 +9,7 @@ Create a helix.
```js ```js
helix(revolutions: number, angleStart: number, ccw?: bool, radius: number, axis: Axis3dOrEdgeReference, length?: number) -> HelixValue helix(revolutions: number, angle_start: number, ccw?: bool, radius: number, axis: Axis3dOrEdgeReference, length?: number) -> HelixValue
``` ```
@ -18,7 +18,7 @@ helix(revolutions: number, angleStart: number, ccw?: bool, radius: number, axis:
| Name | Type | Description | Required | | Name | Type | Description | Required |
|----------|------|-------------|----------| |----------|------|-------------|----------|
| `revolutions` | `number` | Number of revolutions. | Yes | | `revolutions` | `number` | Number of revolutions. | Yes |
| `angleStart` | `number` | Start angle (in degrees). | Yes | | `angle_start` | `number` | Start angle (in degrees). | Yes |
| `ccw` | `bool` | Is the helix rotation counter clockwise? The default is `false`. | No | | `ccw` | `bool` | Is the helix rotation counter clockwise? The default is `false`. | No |
| `radius` | `number` | Radius of the helix. | Yes | | `radius` | `number` | Radius of the helix. | Yes |
| `axis` | [`Axis3dOrEdgeReference`](/docs/kcl/types/Axis3dOrEdgeReference) | Axis to use for the helix. | Yes | | `axis` | [`Axis3dOrEdgeReference`](/docs/kcl/types/Axis3dOrEdgeReference) | Axis to use for the helix. | Yes |
@ -82,11 +82,11 @@ helixPath = helix(
length = 10, length = 10,
radius = 5, radius = 5,
axis = { axis = {
custom = { custom = {
axis = [0, 0, 1.0], axis = [0, 0, 1.0],
origin = [0, 0.25, 0] origin = [0, 0.25, 0]
} }
}, },
) )
// Create a spring by sweeping around the helix path. // Create a spring by sweeping around the helix path.

View File

@ -9,7 +9,7 @@ Use a 2-dimensional sketch to cut a hole in another 2-dimensional sketch.
```js ```js
hole(holeSketch: SketchSet, sketch: Sketch) -> Sketch hole(hole_sketch: SketchSet, sketch: Sketch) -> Sketch
``` ```
@ -17,7 +17,7 @@ hole(holeSketch: SketchSet, sketch: Sketch) -> Sketch
| Name | Type | Description | Required | | Name | Type | Description | Required |
|----------|------|-------------|----------| |----------|------|-------------|----------|
| `holeSketch` | [`SketchSet`](/docs/kcl/types/SketchSet) | A sketch or a group of sketches. | Yes | | `hole_sketch` | [`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 | | `sketch` | [`Sketch`](/docs/kcl/types/Sketch) | A sketch is a collection of paths. | Yes |
### Returns ### Returns

View File

@ -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. Note: The import command currently only works when using the native Modeling App.
```js ```js
import(filePath: String, options?: ImportFormat) -> ImportedGeometry import(file_path: String, options?: ImportFormat) -> ImportedGeometry
``` ```
@ -23,7 +23,7 @@ import(filePath: String, options?: ImportFormat) -> ImportedGeometry
| Name | Type | Description | Required | | Name | Type | Description | Required |
|----------|------|-------------|----------| |----------|------|-------------|----------|
| `filePath` | `String` | | Yes | | `file_path` | `String` | | Yes |
| `options` | [`ImportFormat`](/docs/kcl/types/ImportFormat) | Import format specifier | No | | `options` | [`ImportFormat`](/docs/kcl/types/ImportFormat) | Import format specifier | No |
### Returns ### Returns

View File

@ -10,10 +10,6 @@ layout: manual
* [Modules](kcl/modules) * [Modules](kcl/modules)
* [Known Issues](kcl/KNOWN-ISSUES) * [Known Issues](kcl/KNOWN-ISSUES)
* **`std`** * **`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) * [`abs`](kcl/abs)
* [`acos`](kcl/acos) * [`acos`](kcl/acos)
* [`angleToMatchLengthX`](kcl/angleToMatchLengthX) * [`angleToMatchLengthX`](kcl/angleToMatchLengthX)
@ -122,3 +118,8 @@ layout: manual
* [`cos`](kcl/std-math-cos) * [`cos`](kcl/std-math-cos)
* [`sin`](kcl/std-math-sin) * [`sin`](kcl/std-math-sin)
* [`tan`](kcl/std-math-tan) * [`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)

File diff suppressed because one or more lines are too long

View File

@ -9,7 +9,7 @@ Extend the current sketch with a new straight line.
```js ```js
line(sketch: Sketch, endAbsolute?: [number], end?: [number], tag?: TagDeclarator) -> Sketch line(sketch: Sketch, end_absolute?: [number], end?: [number], tag?: TagDeclarator) -> Sketch
``` ```
@ -18,7 +18,7 @@ line(sketch: Sketch, endAbsolute?: [number], end?: [number], tag?: TagDeclarator
| Name | Type | Description | Required | | Name | Type | Description | Required |
|----------|------|-------------|----------| |----------|------|-------------|----------|
| `sketch` | [`Sketch`](/docs/kcl/types/Sketch) | Which sketch should this path be added to? | Yes | | `sketch` | [`Sketch`](/docs/kcl/types/Sketch) | Which sketch should this path be added to? | Yes |
| `endAbsolute` | `[number]` | Which absolute point should this line go to? Incompatible with `end`. | No | | `end_absolute` | `[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 | | `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 | | `tag` | [`TagDeclarator`](/docs/kcl/types#tag-declaration) | Create a new tag which refers to this line | No |

View File

@ -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. The sketches need to closed and on the same plane.
```js ```js
loft(sketches: [Sketch], vDegree: NonZeroU32, bezApproximateRational: bool, baseCurveIndex?: integer, tolerance?: number) -> Solid loft(sketches: [Sketch], v_degree: NonZeroU32, bez_approximate_rational: bool, base_curve_index?: integer, tolerance?: number) -> Solid
``` ```
@ -18,9 +18,9 @@ loft(sketches: [Sketch], vDegree: NonZeroU32, bezApproximateRational: bool, base
| Name | Type | Description | Required | | Name | Type | Description | Required |
|----------|------|-------------|----------| |----------|------|-------------|----------|
| `sketches` | [`[Sketch]`](/docs/kcl/types/Sketch) | Which sketches to loft. Must include at least 2 sketches. | Yes | | `sketches` | [`[Sketch]`](/docs/kcl/types/Sketch) | Which sketches to loft. Must include at least 2 sketches. | Yes |
| `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 | | `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 |
| `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 | | `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 |
| `baseCurveIndex` | `integer` | This can be set to override the automatically determined topological base curve, which is usually the first section encountered. | No | | `base_curve_index` | `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 | | `tolerance` | `number` | Tolerance for the loft operation. | No |
### Returns ### Returns
@ -95,10 +95,10 @@ circleSketch1 = startSketchOn(offsetPlane('XY', offset = 150))
loft( loft(
[ [
squareSketch, squareSketch,
circleSketch0, circleSketch0,
circleSketch1 circleSketch1
], ],
baseCurveIndex = 0, baseCurveIndex = 0,
bezApproximateRational = false, bezApproximateRational = false,
tolerance = 0.000001, tolerance = 0.000001,

View File

@ -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)]` Given a list like `[a, b, c]`, and a function like `f`, returns `[f(a), f(b), f(c)]`
```js ```js
map(array: [KclValue], mapFn: FunctionSource) -> [KclValue] map(array: [KclValue], map_fn: FunctionParam) -> [KclValue]
``` ```
@ -18,7 +18,7 @@ map(array: [KclValue], mapFn: FunctionSource) -> [KclValue]
| Name | Type | Description | Required | | Name | Type | Description | Required |
|----------|------|-------------|----------| |----------|------|-------------|----------|
| `array` | [`[KclValue]`](/docs/kcl/types/KclValue) | | Yes | | `array` | [`[KclValue]`](/docs/kcl/types/KclValue) | | Yes |
| `mapFn` | `FunctionSource` | | Yes | | `map_fn` | `FunctionParam` | | Yes |
### Returns ### Returns

View File

@ -11,7 +11,7 @@ Only works on unclosed sketches for now.
Mirror occurs around a local sketch axis rather than a global axis. Mirror occurs around a local sketch axis rather than a global axis.
```js ```js
mirror2d(data: Mirror2dData, sketchSet: SketchSet) -> [Sketch] mirror2d(data: Mirror2dData, sketch_set: SketchSet) -> [Sketch]
``` ```
@ -20,7 +20,7 @@ mirror2d(data: Mirror2dData, sketchSet: SketchSet) -> [Sketch]
| Name | Type | Description | Required | | Name | Type | Description | Required |
|----------|------|-------------|----------| |----------|------|-------------|----------|
| `data` | [`Mirror2dData`](/docs/kcl/types/Mirror2dData) | Data for a mirror. | Yes | | `data` | [`Mirror2dData`](/docs/kcl/types/Mirror2dData) | Data for a mirror. | Yes |
| `sketchSet` | [`SketchSet`](/docs/kcl/types/SketchSet) | A sketch or a group of sketches. | Yes | | `sketch_set` | [`SketchSet`](/docs/kcl/types/SketchSet) | A sketch or a group of sketches. | Yes |
### Returns ### Returns

View File

@ -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. 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 ```js
patternCircular2d(sketchSet: SketchSet, instances: integer, center: [number], arcDegrees: number, rotateDuplicates: bool, useOriginal?: bool) -> [Sketch] patternCircular2d(sketch_set: SketchSet, instances: integer, center: [number], arc_degrees: number, rotate_duplicates: bool, use_original?: bool) -> [Sketch]
``` ```
@ -17,12 +17,12 @@ patternCircular2d(sketchSet: SketchSet, instances: integer, center: [number], ar
| Name | Type | Description | Required | | Name | Type | Description | Required |
|----------|------|-------------|----------| |----------|------|-------------|----------|
| `sketchSet` | [`SketchSet`](/docs/kcl/types/SketchSet) | Which sketch(es) to pattern | Yes | | `sketch_set` | [`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 | | `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 | | `center` | `[number]` | The center about which to make the pattern. This is a 2D vector. | Yes |
| `arcDegrees` | `number` | The arc angle (in degrees) to place the repetitions. Must be greater than 0. | Yes | | `arc_degrees` | `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 | | `rotate_duplicates` | `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 | | `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 |
### Returns ### Returns

View File

@ -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. 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 ```js
patternCircular3d(solidSet: SolidSet, instances: integer, axis: [number], center: [number], arcDegrees: number, rotateDuplicates: bool, useOriginal?: bool) -> [Solid] patternCircular3d(solid_set: SolidSet, instances: integer, axis: [number], center: [number], arc_degrees: number, rotate_duplicates: bool, use_original?: bool) -> [Solid]
``` ```
@ -17,13 +17,13 @@ patternCircular3d(solidSet: SolidSet, instances: integer, axis: [number], center
| Name | Type | Description | Required | | Name | Type | Description | Required |
|----------|------|-------------|----------| |----------|------|-------------|----------|
| `solidSet` | [`SolidSet`](/docs/kcl/types/SolidSet) | Which solid(s) to pattern | Yes | | `solid_set` | [`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 | | `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 | | `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 | | `center` | `[number]` | The center about which to make the pattern. This is a 3D vector. | Yes |
| `arcDegrees` | `number` | The arc angle (in degrees) to place the repetitions. Must be greater than 0. | Yes | | `arc_degrees` | `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 | | `rotate_duplicates` | `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 | | `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 |
### Returns ### Returns

View File

@ -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. of distance between each repetition, some specified number of times.
```js ```js
patternLinear2d(sketchSet: SketchSet, instances: integer, distance: number, axis: [number], useOriginal?: bool) -> [Sketch] patternLinear2d(sketch_set: SketchSet, instances: integer, distance: number, axis: [number], use_original?: bool) -> [Sketch]
``` ```
@ -17,11 +17,11 @@ patternLinear2d(sketchSet: SketchSet, instances: integer, distance: number, axis
| Name | Type | Description | Required | | Name | Type | Description | Required |
|----------|------|-------------|----------| |----------|------|-------------|----------|
| `sketchSet` | [`SketchSet`](/docs/kcl/types/SketchSet) | The sketch(es) to duplicate | Yes | | `sketch_set` | [`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 | | `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 | | `distance` | `number` | Distance between each repetition. Also known as 'spacing'. | Yes |
| `axis` | `[number]` | The axis of the pattern. A 2D vector. | Yes | | `axis` | `[number]` | The axis of the pattern. A 2D vector. | 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 | | `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 |
### Returns ### Returns

View File

@ -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. of distance between each repetition, some specified number of times.
```js ```js
patternLinear3d(solidSet: SolidSet, instances: integer, distance: number, axis: [number], useOriginal?: bool) -> [Solid] patternLinear3d(solid_set: SolidSet, instances: integer, distance: number, axis: [number], use_original?: bool) -> [Solid]
``` ```
@ -17,11 +17,11 @@ patternLinear3d(solidSet: SolidSet, instances: integer, distance: number, axis:
| Name | Type | Description | Required | | Name | Type | Description | Required |
|----------|------|-------------|----------| |----------|------|-------------|----------|
| `solidSet` | [`SolidSet`](/docs/kcl/types/SolidSet) | The solid(s) to duplicate | Yes | | `solid_set` | [`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 | | `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 | | `distance` | `number` | Distance between each repetition. Also known as 'spacing'. | Yes |
| `axis` | `[number]` | The axis of the pattern. A 2D vector. | Yes | | `axis` | `[number]` | The axis of the pattern. A 2D vector. | 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 | | `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 |
### Returns ### Returns

View File

@ -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") - `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 ```js
patternTransform(solidSet: SolidSet, instances: integer, transform: FunctionSource, useOriginal?: bool) -> [Solid] patternTransform(solid_set: SolidSet, instances: integer, transform: FunctionParam, use_original?: bool) -> [Solid]
``` ```
@ -43,10 +43,10 @@ patternTransform(solidSet: SolidSet, instances: integer, transform: FunctionSour
| Name | Type | Description | Required | | Name | Type | Description | Required |
|----------|------|-------------|----------| |----------|------|-------------|----------|
| `solidSet` | [`SolidSet`](/docs/kcl/types/SolidSet) | The solid(s) to duplicate | Yes | | `solid_set` | [`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 | | `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` | `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 | | `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 |
| `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 | | `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 |
### Returns ### Returns

View File

@ -9,7 +9,7 @@ Just like patternTransform, but works on 2D sketches not 3D solids.
```js ```js
patternTransform2d(sketchSet: SketchSet, instances: integer, transform: FunctionSource, useOriginal?: bool) -> [Sketch] patternTransform2d(sketch_set: SketchSet, instances: integer, transform: FunctionParam, use_original?: bool) -> [Sketch]
``` ```
@ -17,10 +17,10 @@ patternTransform2d(sketchSet: SketchSet, instances: integer, transform: Function
| Name | Type | Description | Required | | Name | Type | Description | Required |
|----------|------|-------------|----------| |----------|------|-------------|----------|
| `sketchSet` | [`SketchSet`](/docs/kcl/types/SketchSet) | The sketch(es) to duplicate | Yes | | `sketch_set` | [`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 | | `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` | `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 | | `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 |
| `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 | | `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 |
### Returns ### Returns

View File

@ -9,7 +9,7 @@ Create a regular polygon with the specified number of sides that is either inscr
```js ```js
polygon(data: PolygonData, sketchSurfaceOrGroup: SketchOrSurface, tag?: TagDeclarator) -> Sketch polygon(data: PolygonData, sketch_surface_or_group: SketchOrSurface, tag?: TagDeclarator) -> Sketch
``` ```
@ -18,7 +18,7 @@ polygon(data: PolygonData, sketchSurfaceOrGroup: SketchOrSurface, tag?: TagDecla
| Name | Type | Description | Required | | Name | Type | Description | Required |
|----------|------|-------------|----------| |----------|------|-------------|----------|
| `data` | [`PolygonData`](/docs/kcl/types/PolygonData) | Data for drawing a polygon | Yes | | `data` | [`PolygonData`](/docs/kcl/types/PolygonData) | Data for drawing a polygon | Yes |
| `sketchSurfaceOrGroup` | [`SketchOrSurface`](/docs/kcl/types/SketchOrSurface) | A sketch surface or a sketch. | Yes | | `sketch_surface_or_group` | [`SketchOrSurface`](/docs/kcl/types/SketchOrSurface) | A sketch surface or a sketch. | Yes |
| `tag` | [`TagDeclarator`](/docs/kcl/types#tag-declaration) | | No | | `tag` | [`TagDeclarator`](/docs/kcl/types#tag-declaration) | | No |
### Returns ### Returns

View File

@ -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. using the previous value and the element.
```js ```js
reduce(array: [KclValue], start: KclValue, reduceFn: FunctionSource) -> KclValue reduce(array: [KclValue], start: KclValue, reduce_fn: FunctionParam) -> KclValue
``` ```
@ -19,7 +19,7 @@ reduce(array: [KclValue], start: KclValue, reduceFn: FunctionSource) -> KclValue
|----------|------|-------------|----------| |----------|------|-------------|----------|
| `array` | [`[KclValue]`](/docs/kcl/types/KclValue) | | Yes | | `array` | [`[KclValue]`](/docs/kcl/types/KclValue) | | Yes |
| `start` | [`KclValue`](/docs/kcl/types/KclValue) | Any KCL value. | Yes | | `start` | [`KclValue`](/docs/kcl/types/KclValue) | Any KCL value. | Yes |
| `reduceFn` | `FunctionSource` | | Yes | | `reduce_fn` | `FunctionParam` | | Yes |
### Returns ### Returns

View File

@ -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. provided thickness remains, taking volume starting at the provided face, leaving it open in that direction.
```js ```js
shell(solidSet: SolidSet, thickness: number, faces: [FaceTag]) -> SolidSet shell(solid_set: SolidSet, thickness: number, faces: [FaceTag]) -> SolidSet
``` ```
@ -17,7 +17,7 @@ shell(solidSet: SolidSet, thickness: number, faces: [FaceTag]) -> SolidSet
| Name | Type | Description | Required | | Name | Type | Description | Required |
|----------|------|-------------|----------| |----------|------|-------------|----------|
| `solidSet` | [`SolidSet`](/docs/kcl/types/SolidSet) | Which solid (or solids) to shell out | Yes | | `solid_set` | [`SolidSet`](/docs/kcl/types/SolidSet) | Which solid (or solids) to shell out | Yes |
| `thickness` | `number` | The thickness of the shell | Yes | | `thickness` | `number` | The thickness of the shell | Yes |
| `faces` | [`[FaceTag]`](/docs/kcl/types/FaceTag) | The faces you want removed | Yes | | `faces` | [`[FaceTag]`](/docs/kcl/types/FaceTag) | The faces you want removed | Yes |

View File

@ -9,7 +9,7 @@ Start a new profile at a given point.
```js ```js
startProfileAt(to: [number], sketchSurface: SketchSurface, tag?: TagDeclarator) -> Sketch startProfileAt(to: [number], sketch_surface: SketchSurface, tag?: TagDeclarator) -> Sketch
``` ```
@ -18,7 +18,7 @@ startProfileAt(to: [number], sketchSurface: SketchSurface, tag?: TagDeclarator)
| Name | Type | Description | Required | | Name | Type | Description | Required |
|----------|------|-------------|----------| |----------|------|-------------|----------|
| `to` | `[number]` | | Yes | | `to` | `[number]` | | Yes |
| `sketchSurface` | [`SketchSurface`](/docs/kcl/types/SketchSurface) | A sketch type. | Yes | | `sketch_surface` | [`SketchSurface`](/docs/kcl/types/SketchSurface) | A sketch type. | Yes |
| `tag` | [`TagDeclarator`](/docs/kcl/types#tag-declaration) | | No | | `tag` | [`TagDeclarator`](/docs/kcl/types#tag-declaration) | | No |
### Returns ### Returns

File diff suppressed because it is too large Load Diff

View File

@ -266,10 +266,10 @@ myRect = rect([20, 0])
myRect myRect
|> extrude(10, %) |> extrude(10, %)
|> fillet( |> fillet({
radius = 0.5, radius = 0.5,
tags = [myRect.tags.rectangleSegmentA001] tags = [myRect.tags.rectangleSegmentA001]
) }, %)
``` ```
See how we use the tag `rectangleSegmentA001` in the `fillet` function outside See how we use the tag `rectangleSegmentA001` in the `fillet` function outside

View File

@ -295,6 +295,7 @@ Data for an imported geometry.
| Property | Type | Description | Required | | Property | Type | Description | Required |
|----------|------|-------------|----------| |----------|------|-------------|----------|
| `type` |enum: `Function`| | No | | `type` |enum: `Function`| | No |
| `memory` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
| `__meta` |`[` [`Metadata`](/docs/kcl/types/Metadata) `]`| | No | | `__meta` |`[` [`Metadata`](/docs/kcl/types/Metadata) `]`| | No |

View File

@ -82,7 +82,7 @@ test.describe('Command bar tests', { tag: ['@skipWin'] }, () => {
await page.keyboard.press('Enter') // submit await page.keyboard.press('Enter') // submit
await page.waitForTimeout(100) await page.waitForTimeout(100)
await expect(page.locator('.cm-activeLine')).toContainText( await expect(page.locator('.cm-activeLine')).toContainText(
`fillet( radius = ${KCL_DEFAULT_LENGTH}, tags = [seg01] )` `fillet({ radius = ${KCL_DEFAULT_LENGTH}, tags = [seg01] }, %)`
) )
}) })
@ -219,11 +219,7 @@ test.describe('Command bar tests', { tag: ['@skipWin'] }, () => {
} }
) )
test('Can extrude from the command bar', async ({ test('Can extrude from the command bar', async ({ page, homePage }) => {
page,
homePage,
cmdBar,
}) => {
await page.addInitScript(async () => { await page.addInitScript(async () => {
localStorage.setItem( localStorage.setItem(
'persistCode', 'persistCode',
@ -258,7 +254,7 @@ test.describe('Command bar tests', { tag: ['@skipWin'] }, () => {
await expect(cmdSearchBar).toBeVisible() await expect(cmdSearchBar).toBeVisible()
// Search for extrude command and choose it // Search for extrude command and choose it
await cmdBar.cmdOptions.getByText('Extrude').click() await page.getByRole('option', { name: 'Extrude' }).click()
// Assert that we're on the selection step // Assert that we're on the selection step
await expect(page.getByRole('button', { name: 'selection' })).toBeDisabled() await expect(page.getByRole('button', { name: 'selection' })).toBeDisabled()

View File

@ -59,25 +59,18 @@ export interface Fixtures {
homePage: HomePageFixture homePage: HomePageFixture
} }
export class AuthenticatedTronApp { export class AuthenticatedTronApp {
public originalPage: Page public readonly _page: Page
public page: Page public page: Page
public browserContext: BrowserContext
public context: BrowserContext public context: BrowserContext
public readonly testInfo: TestInfo public readonly testInfo: TestInfo
public electronApp: ElectronApplication | undefined public electronApp: ElectronApplication | undefined
public readonly viewPortSize = { width: 1200, height: 500 } public readonly viewPortSize = { width: 1200, height: 500 }
public dir: string = '' public dir: string = ''
constructor( constructor(context: BrowserContext, page: Page, testInfo: TestInfo) {
browserContext: BrowserContext, this._page = page
originalPage: Page, this.page = page
testInfo: TestInfo this.context = context
) {
this.page = originalPage
this.originalPage = originalPage
this.browserContext = browserContext
// Will be overwritten in the initializer
this.context = browserContext
this.testInfo = testInfo this.testInfo = testInfo
} }
async initialise( async initialise(
@ -93,17 +86,9 @@ export class AuthenticatedTronApp {
folderSetupFn: arg.folderSetupFn, folderSetupFn: arg.folderSetupFn,
cleanProjectDir: arg.cleanProjectDir, cleanProjectDir: arg.cleanProjectDir,
appSettings: arg.appSettings, appSettings: arg.appSettings,
viewport: this.viewPortSize,
}) })
this.page = page 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 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.electronApp = electronApp
this.dir = dir this.dir = dir

View File

@ -13,8 +13,8 @@ import {
import * as TOML from '@iarna/toml' import * as TOML from '@iarna/toml'
import { expectPixelColor } from './fixtures/sceneFixture' import { expectPixelColor } from './fixtures/sceneFixture'
// Because our default test settings have the onboardingStatus set to 'dismissed', // Because onboarding relies on an app setting we need to set it as incompletel
// we must set it to empty for the tests where we want to see the onboarding immediately. // for all these tests.
test.describe('Onboarding tests', () => { test.describe('Onboarding tests', () => {
test( test(
@ -22,7 +22,7 @@ test.describe('Onboarding tests', () => {
{ {
appSettings: { appSettings: {
app: { app: {
onboardingStatus: '', onboardingStatus: 'incomplete',
}, },
}, },
cleanProjectDir: true, cleanProjectDir: true,
@ -63,7 +63,7 @@ test.describe('Onboarding tests', () => {
tag: '@electron', tag: '@electron',
appSettings: { appSettings: {
app: { app: {
onboardingStatus: '', onboardingStatus: 'incomplete',
}, },
}, },
cleanProjectDir: true, cleanProjectDir: true,
@ -106,6 +106,11 @@ test.describe('Onboarding tests', () => {
test( test(
'Code resets after confirmation', 'Code resets after confirmation',
{ {
appSettings: {
app: {
onboardingStatus: 'incomplete',
},
},
cleanProjectDir: true, cleanProjectDir: true,
}, },
async ({ context, page, homePage }) => { async ({ context, page, homePage }) => {
@ -153,7 +158,7 @@ test.describe('Onboarding tests', () => {
{ {
appSettings: { appSettings: {
app: { app: {
onboardingStatus: '', onboardingStatus: 'incomplete',
}, },
}, },
}, },
@ -314,7 +319,7 @@ test.describe('Onboarding tests', () => {
{ {
appSettings: { appSettings: {
app: { app: {
onboardingStatus: '', onboardingStatus: 'incomplete',
}, },
}, },
cleanProjectDir: true, cleanProjectDir: true,
@ -387,7 +392,7 @@ test.describe('Onboarding tests', () => {
{ {
appSettings: { appSettings: {
app: { app: {
onboardingStatus: '', onboardingStatus: 'incomplete',
}, },
}, },
cleanProjectDir: true, cleanProjectDir: true,

View File

@ -211,13 +211,12 @@ test.describe('Point-and-click tests', { tag: ['@skipWin'] }, () => {
cameraPos: { x: 16020, y: -2000, z: 10500 }, cameraPos: { x: 16020, y: -2000, z: 10500 },
cameraTarget: { x: -150, y: -4500, z: -80 }, cameraTarget: { x: -150, y: -4500, z: -80 },
beforeChamferSnippet: `angledLine([segAng(rectangleSegmentA001)-90,217.26],%,$seg01) beforeChamferSnippet: `angledLine([segAng(rectangleSegmentA001)-90,217.26],%,$seg01)
chamfer(length = 30,tags = [ chamfer({length = 30,tags = [
seg01, seg01,
getNextAdjacentEdge(yo), getNextAdjacentEdge(yo),
getNextAdjacentEdge(seg02), getNextAdjacentEdge(seg02),
getOppositeEdge(seg01) getOppositeEdge(seg01)
], ]}, %)`,
)`,
afterChamferSelectSnippet: afterChamferSelectSnippet:
'sketch002 = startSketchOn(extrude001, seg03)', 'sketch002 = startSketchOn(extrude001, seg03)',
@ -237,14 +236,14 @@ test.describe('Point-and-click tests', { tag: ['@skipWin'] }, () => {
beforeChamferSnippet: `angledLine([ beforeChamferSnippet: `angledLine([
segAng(rectangleSegmentA001) - 90, segAng(rectangleSegmentA001) - 90,
217.26 217.26
], %, $seg01)chamfer( ], %, $seg01)chamfer({
length = 30, length = 30,
tags = [ tags = [
seg01, seg01,
getNextAdjacentEdge(yo), getNextAdjacentEdge(yo),
getNextAdjacentEdge(seg02) getNextAdjacentEdge(seg02)
] ]
)`, }, %)`,
afterChamferSelectSnippet: afterChamferSelectSnippet:
'sketch003 = startSketchOn(extrude001, seg04)', 'sketch003 = startSketchOn(extrude001, seg04)',
@ -261,13 +260,13 @@ test.describe('Point-and-click tests', { tag: ['@skipWin'] }, () => {
clickCoords: { x: 677, y: 87 }, clickCoords: { x: 677, y: 87 },
cameraPos: { x: -6200, y: 1500, z: 6200 }, cameraPos: { x: -6200, y: 1500, z: 6200 },
cameraTarget: { x: 8300, y: 1100, z: 4800 }, cameraTarget: { x: 8300, y: 1100, z: 4800 },
beforeChamferSnippet: `angledLine([0, 268.43], %, $rectangleSegmentA001)chamfer( beforeChamferSnippet: `angledLine([0, 268.43], %, $rectangleSegmentA001)chamfer({
length = 30, length = 30,
tags = [ tags = [
getNextAdjacentEdge(yo), getNextAdjacentEdge(yo),
getNextAdjacentEdge(seg02) getNextAdjacentEdge(seg02)
] ]
)`, }, %)`,
afterChamferSelectSnippet: afterChamferSelectSnippet:
'sketch004 = startSketchOn(extrude001, seg05)', 'sketch004 = startSketchOn(extrude001, seg05)',
afterRectangle1stClickSnippet: afterRectangle1stClickSnippet:
@ -283,9 +282,10 @@ test.describe('Point-and-click tests', { tag: ['@skipWin'] }, () => {
clickCoords: { x: 620, y: 300 }, clickCoords: { x: 620, y: 300 },
cameraPos: { x: -1100, y: -7700, z: 1600 }, cameraPos: { x: -1100, y: -7700, z: 1600 },
cameraTarget: { x: 1450, y: 670, z: 4000 }, cameraTarget: { x: 1450, y: 670, z: 4000 },
beforeChamferSnippet: `chamfer(length = 30, tags = [getNextAdjacentEdge(yo)])`, beforeChamferSnippet: `chamfer({
beforeChamferSnippetEnd: length = 30,
'|> chamfer(length = 30, tags = [getNextAdjacentEdge(yo)])', tags = [getNextAdjacentEdge(yo)]
}, %)`,
afterChamferSelectSnippet: afterChamferSelectSnippet:
'sketch005 = startSketchOn(extrude001, seg06)', 'sketch005 = startSketchOn(extrude001, seg06)',
afterRectangle1stClickSnippet: afterRectangle1stClickSnippet:
@ -313,16 +313,31 @@ test.describe('Point-and-click tests', { tag: ['@skipWin'] }, () => {
|> line(endAbsolute = [profileStartX(%), profileStartY(%)], tag = $seg02) |> line(endAbsolute = [profileStartX(%), profileStartY(%)], tag = $seg02)
|> close() |> close()
extrude001 = extrude(sketch001, length = 100) extrude001 = extrude(sketch001, length = 100)
|> chamfer(length = 30, tags = [getOppositeEdge(seg01)], tag = $seg03) |> chamfer({
|> chamfer(length = 30, tags = [seg01], tag = $seg04) length = 30,
|> chamfer(length = 30, tags = [getNextAdjacentEdge(seg02)], tag = $seg05) tags = [getOppositeEdge(seg01)]
|> chamfer(length = 30, tags = [getNextAdjacentEdge(yo)], tag = $seg06) }, %, $seg03)
|> chamfer({ length = 30, tags = [seg01] }, %, $seg04)
|> chamfer({
length = 30,
tags = [getNextAdjacentEdge(seg02)]
}, %, $seg05)
|> chamfer({
length = 30,
tags = [getNextAdjacentEdge(yo)]
}, %, $seg06)
sketch005 = startSketchOn(extrude001, seg06) sketch005 = startSketchOn(extrude001, seg06)
profile004=startProfileAt([-23.43,19.69], sketch005) profile004 = startProfileAt([-23.43, 19.69], sketch005)
|> angledLine([0, 9.1], %, $rectangleSegmentA005) |> angledLine([0, 9.1], %, $rectangleSegmentA005)
|> angledLine([segAng(rectangleSegmentA005) - 90, 84.07], %) |> angledLine([
|> angledLine([segAng(rectangleSegmentA005), -segLen(rectangleSegmentA005)], %) segAng(rectangleSegmentA005) - 90,
|> line(endAbsolute=[profileStartX(%), profileStartY(%)]) 84.07
], %)
|> angledLine([
segAng(rectangleSegmentA005),
-segLen(rectangleSegmentA005)
], %)
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|> close() |> close()
sketch004 = startSketchOn(extrude001, seg05) sketch004 = startSketchOn(extrude001, seg05)
profile003 = startProfileAt([82.57, 322.96], sketch004) profile003 = startProfileAt([82.57, 322.96], sketch004)
@ -363,6 +378,7 @@ profile001 = startProfileAt([205.96, 254.59], sketch002)
], %) ], %)
|> line(endAbsolute = [profileStartX(%), profileStartY(%)]) |> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|> close() |> close()
`, `,
{ shouldNormalise: true } { shouldNormalise: true }
) )
@ -399,13 +415,13 @@ profile001 = startProfileAt([205.96, 254.59], sketch002)
cameraPos: { x: 16020, y: -2000, z: 10500 }, cameraPos: { x: 16020, y: -2000, z: 10500 },
cameraTarget: { x: -150, y: -4500, z: -80 }, cameraTarget: { x: -150, y: -4500, z: -80 },
beforeChamferSnippet: `angledLine([segAng(rectangleSegmentA001)-90,217.26],%,$seg01) beforeChamferSnippet: `angledLine([segAng(rectangleSegmentA001)-90,217.26],%,$seg01)
chamfer(extrude001,length=30,tags=[ chamfer({length=30,tags=[
seg01, seg01,
getNextAdjacentEdge(yo), getNextAdjacentEdge(yo),
getNextAdjacentEdge(seg02), getNextAdjacentEdge(seg02),
getOppositeEdge(seg01), getOppositeEdge(seg01)
])`, ]}, extrude001)`,
beforeChamferSnippetEnd: ')', beforeChamferSnippetEnd: '}, extrude001)',
afterChamferSelectSnippet: afterChamferSelectSnippet:
'sketch002 = startSketchOn(extrude001, seg03)', 'sketch002 = startSketchOn(extrude001, seg03)',
afterRectangle1stClickSnippet: afterRectangle1stClickSnippet:
@ -431,20 +447,18 @@ profile001 = startProfileAt([205.96, 254.59], sketch002)
|> line(endAbsolute = [profileStartX(%), profileStartY(%)], tag = $seg02) |> line(endAbsolute = [profileStartX(%), profileStartY(%)], tag = $seg02)
|> close() |> close()
extrude001 = extrude(sketch001, length = 100) extrude001 = extrude(sketch001, length = 100)
chamf = chamfer( chamf = chamfer({
extrude001,
length = 30, length = 30,
tags = [getOppositeEdge(seg01)], tags = [getOppositeEdge(seg01)]
tag = $seg03, }, extrude001, $seg03)
) |> chamfer({
|> chamfer(
length = 30, length = 30,
tags = [ tags = [
seg01, seg01,
getNextAdjacentEdge(yo), getNextAdjacentEdge(yo),
getNextAdjacentEdge(seg02) getNextAdjacentEdge(seg02)
], ]
) }, %)
sketch002 = startSketchOn(extrude001, seg03) sketch002 = startSketchOn(extrude001, seg03)
profile001 = startProfileAt([205.96, 254.59], sketch002) profile001 = startProfileAt([205.96, 254.59], sketch002)
|> angledLine([0, 11.39], %, $rectangleSegmentA002) |> angledLine([0, 11.39], %, $rectangleSegmentA002)
@ -456,7 +470,7 @@ profile001 = startProfileAt([205.96, 254.59], sketch002)
segAng(rectangleSegmentA002), segAng(rectangleSegmentA002),
-segLen(rectangleSegmentA002) -segLen(rectangleSegmentA002)
], %) ], %)
|> line(endAbsolute = [profileStartX(%), profileStartY(%)]) |> line(endAbsolute=[profileStartX(%), profileStartY(%)])
|> close() |> close()
`, `,
{ shouldNormalise: true } { shouldNormalise: true }
@ -1064,7 +1078,7 @@ openSketch = startSketchOn('XY')
0 0
) )
await operationButton.click({ button: 'left' }) await operationButton.click({ button: 'left' })
await page.keyboard.press('Delete') await page.keyboard.press('Backspace')
await scene.expectPixelColor([50, 51, 96], testPoint, 15) await scene.expectPixelColor([50, 51, 96], testPoint, 15)
}) })
}) })
@ -1125,53 +1139,11 @@ openSketch = startSketchOn('XY')
await scene.expectPixelColor([250, 250, 250], testPoint, 15) await scene.expectPixelColor([250, 250, 250], testPoint, 15)
}) })
await test.step(`Edit helix through the feature tree`, async () => { await test.step('Delete offset plane via feature tree selection', 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() await editor.closePane()
const operationButton = await toolbar.getFeatureTreeOperation('Helix', 0) const operationButton = await toolbar.getFeatureTreeOperation('Helix', 0)
await operationButton.click({ button: 'left' }) await operationButton.click({ button: 'left' })
await page.keyboard.press('Delete') await page.keyboard.press('Backspace')
// Red plane is back // Red plane is back
await scene.expectPixelColor([96, 52, 52], testPoint, 15) await scene.expectPixelColor([96, 52, 52], testPoint, 15)
}) })
@ -1263,7 +1235,7 @@ openSketch = startSketchOn('XY')
await editor.closePane() await editor.closePane()
const operationButton = await toolbar.getFeatureTreeOperation('Loft', 0) const operationButton = await toolbar.getFeatureTreeOperation('Loft', 0)
await operationButton.click({ button: 'left' }) await operationButton.click({ button: 'left' })
await page.keyboard.press('Delete') await page.keyboard.press('Backspace')
await scene.expectPixelColor([254, 254, 254], testPoint, 15) await scene.expectPixelColor([254, 254, 254], testPoint, 15)
}) })
}) })
@ -1306,7 +1278,7 @@ loft001 = loft([sketch001, sketch002])
await expect(page.locator('.cm-activeLine')).toHaveText(` await expect(page.locator('.cm-activeLine')).toHaveText(`
|> circle({ center = [0, 0], radius = 30 }, %) |> circle({ center = [0, 0], radius = 30 }, %)
`) `)
await page.keyboard.press('Delete') await page.keyboard.press('Backspace')
// Check for sketch 1 // Check for sketch 1
await scene.expectPixelColor([254, 254, 254], testPoint, 15) await scene.expectPixelColor([254, 254, 254], testPoint, 15)
}) })
@ -1317,7 +1289,7 @@ loft001 = loft([sketch001, sketch002])
await expect(page.locator('.cm-activeLine')).toHaveText(` await expect(page.locator('.cm-activeLine')).toHaveText(`
|> circle({ center = [0, 0], radius = 20 }, %) |> circle({ center = [0, 0], radius = 20 }, %)
`) `)
await page.keyboard.press('Delete') await page.keyboard.press('Backspace')
// Check for plane001 // Check for plane001
await scene.expectPixelColor([228, 228, 228], testPoint, 15) await scene.expectPixelColor([228, 228, 228], testPoint, 15)
}) })
@ -1328,7 +1300,7 @@ loft001 = loft([sketch001, sketch002])
await expect(page.locator('.cm-activeLine')).toHaveText(` await expect(page.locator('.cm-activeLine')).toHaveText(`
plane001 = offsetPlane('XZ', offset = 50) plane001 = offsetPlane('XZ', offset = 50)
`) `)
await page.keyboard.press('Delete') await page.keyboard.press('Backspace')
// Check for sketch 1 // Check for sketch 1
await scene.expectPixelColor([254, 254, 254], testPoint, 15) await scene.expectPixelColor([254, 254, 254], testPoint, 15)
}) })
@ -1422,7 +1394,7 @@ sketch002 = startSketchOn('XZ')
await page.waitForTimeout(500) await page.waitForTimeout(500)
const operationButton = await toolbar.getFeatureTreeOperation('Sweep', 0) const operationButton = await toolbar.getFeatureTreeOperation('Sweep', 0)
await operationButton.click({ button: 'left' }) await operationButton.click({ button: 'left' })
await page.keyboard.press('Delete') await page.keyboard.press('Backspace')
await page.waitForTimeout(500) await page.waitForTimeout(500)
await toolbar.closePane('feature-tree') await toolbar.closePane('feature-tree')
await scene.expectPixelColor([53, 53, 53], testPoint, 15) await scene.expectPixelColor([53, 53, 53], testPoint, 15)
@ -1520,9 +1492,9 @@ sketch002 = startSketchOn('XZ')
|> close() |> close()
extrude001 = extrude(sketch001, length = -12) extrude001 = extrude(sketch001, length = -12)
` `
const firstFilletDeclaration = 'fillet(radius = 5, tags = [seg01])' const firstFilletDeclaration = 'fillet({ radius = 5, tags = [seg01] }, %)'
const secondFilletDeclaration = const secondFilletDeclaration =
'fillet(radius = 5, tags = [getOppositeEdge(seg01)])' 'fillet({ radius = 5, tags = [getOppositeEdge(seg01)] }, %)'
// Locators // Locators
const firstEdgeLocation = { x: 600, y: 193 } const firstEdgeLocation = { x: 600, y: 193 }
@ -1622,7 +1594,7 @@ extrude001 = extrude(sketch001, length = -12)
await editor.expectEditor.toContain(firstFilletDeclaration) await editor.expectEditor.toContain(firstFilletDeclaration)
await editor.expectState({ await editor.expectState({
diagnostics: [], diagnostics: [],
activeLines: ['|> fillet(radius = 5, tags = [seg01])'], activeLines: ['|>fillet({radius=5,tags=[seg01]},%)'],
highlightedCode: '', highlightedCode: '',
}) })
}) })
@ -1702,7 +1674,7 @@ extrude001 = extrude(sketch001, length = -12)
await editor.expectEditor.toContain(secondFilletDeclaration) await editor.expectEditor.toContain(secondFilletDeclaration)
await editor.expectState({ await editor.expectState({
diagnostics: [], diagnostics: [],
activeLines: ['|>fillet(radius=5,tags=[getOppositeEdge(seg01)])'], activeLines: ['radius=5,'],
highlightedCode: '', highlightedCode: '',
}) })
}) })
@ -1728,7 +1700,7 @@ extrude001 = extrude(sketch001, length = -12)
1 1
) )
await operationButton.click({ button: 'left' }) await operationButton.click({ button: 'left' })
await page.keyboard.press('Delete') await page.keyboard.press('Backspace')
await page.waitForTimeout(500) await page.waitForTimeout(500)
await scene.expectPixelColor(edgeColorWhite, secondEdgeLocation, 15) // deleted await scene.expectPixelColor(edgeColorWhite, secondEdgeLocation, 15) // deleted
await editor.expectEditor.not.toContain(secondFilletDeclaration) await editor.expectEditor.not.toContain(secondFilletDeclaration)
@ -1754,17 +1726,18 @@ extrude001 = extrude(sketch001, length = -12)
|> line(endAbsolute = [profileStartX(%), profileStartY(%)], tag = $seg01) |> line(endAbsolute = [profileStartX(%), profileStartY(%)], tag = $seg01)
|> close() |> close()
extrude001 = extrude(sketch001, length = -12) extrude001 = extrude(sketch001, length = -12)
|> fillet(radius = 5, tags = [seg01]) // fillet01 |> fillet({ radius = 5, tags = [seg01] }, %) // fillet01
|> fillet(radius = 5, tags = [seg02]) // fillet02 |> fillet({ radius = 5, tags = [seg02] }, %) // fillet02
fillet03 = fillet(extrude001, radius = 5, tags = [getOppositeEdge(seg01)]) fillet03 = fillet({ radius = 5, tags = [getOppositeEdge(seg01)]}, extrude001)
fillet04 = fillet(extrude001, radius = 5, tags = [getOppositeEdge(seg02)]) fillet04 = fillet({ radius = 5, tags = [getOppositeEdge(seg02)]}, extrude001)
` `
const pipedFilletDeclaration = 'fillet(radius = 5, tags = [seg01])' const pipedFilletDeclaration = 'fillet({ radius = 5, tags = [seg01] }, %)'
const secondPipedFilletDeclaration = 'fillet(radius = 5, tags = [seg02])' const secondPipedFilletDeclaration =
'fillet({ radius = 5, tags = [seg02] }, %)'
const standaloneFilletDeclaration = const standaloneFilletDeclaration =
'fillet03 = fillet(extrude001, radius = 5, tags = [getOppositeEdge(seg01)])' 'fillet03 = fillet({ radius = 5, tags = [getOppositeEdge(seg01)]}, extrude001)'
const secondStandaloneFilletDeclaration = const secondStandaloneFilletDeclaration =
'fillet04 = fillet(extrude001, radius = 5, tags = [getOppositeEdge(seg02)])' 'fillet04 = fillet({ radius = 5, tags = [getOppositeEdge(seg02)]}, extrude001)'
// Locators // Locators
const pipedFilletEdgeLocation = { x: 600, y: 193 } const pipedFilletEdgeLocation = { x: 600, y: 193 }
@ -1830,7 +1803,7 @@ fillet04 = fillet(extrude001, radius = 5, tags = [getOppositeEdge(seg02)])
0 0
) )
await operationButton.click({ button: 'left' }) await operationButton.click({ button: 'left' })
await page.keyboard.press('Delete') await page.keyboard.press('Backspace')
await page.waitForTimeout(500) await page.waitForTimeout(500)
}) })
await test.step('Verify piped fillet is deleted but other fillets are not (in the editor)', async () => { await test.step('Verify piped fillet is deleted but other fillets are not (in the editor)', async () => {
@ -1860,7 +1833,7 @@ fillet04 = fillet(extrude001, radius = 5, tags = [getOppositeEdge(seg02)])
1 1
) )
await operationButton.click({ button: 'left' }) await operationButton.click({ button: 'left' })
await page.keyboard.press('Delete') await page.keyboard.press('Backspace')
await page.waitForTimeout(500) await page.waitForTimeout(500)
}) })
await test.step('Verify non-piped fillet is deleted but other two fillets are not (in the editor)', async () => { await test.step('Verify non-piped fillet is deleted but other two fillets are not (in the editor)', async () => {
@ -1898,9 +1871,9 @@ fillet04 = fillet(extrude001, radius = 5, tags = [getOppositeEdge(seg02)])
|> close() |> close()
extrude001 = extrude(sketch001, length = -12) extrude001 = extrude(sketch001, length = -12)
` `
const firstChamferDeclaration = 'chamfer(length = 5, tags = [seg01])' const firstChamferDeclaration = 'chamfer({ length = 5, tags = [seg01] }, %)'
const secondChamferDeclaration = const secondChamferDeclaration =
'chamfer(length = 5, tags = [getOppositeEdge(seg01)])' 'chamfer({ length = 5, tags = [getOppositeEdge(seg01)] }, %)'
// Locators // Locators
const firstEdgeLocation = { x: 600, y: 193 } const firstEdgeLocation = { x: 600, y: 193 }
@ -1991,7 +1964,7 @@ extrude001 = extrude(sketch001, length = -12)
await editor.expectEditor.toContain(firstChamferDeclaration) await editor.expectEditor.toContain(firstChamferDeclaration)
await editor.expectState({ await editor.expectState({
diagnostics: [], diagnostics: [],
activeLines: ['|>chamfer(length=5,tags=[seg01])'], activeLines: ['|>chamfer({length=5,tags=[seg01]},%)'],
highlightedCode: '', highlightedCode: '',
}) })
}) })
@ -2075,7 +2048,7 @@ extrude001 = extrude(sketch001, length = -12)
await editor.expectEditor.toContain(secondChamferDeclaration) await editor.expectEditor.toContain(secondChamferDeclaration)
await editor.expectState({ await editor.expectState({
diagnostics: [], diagnostics: [],
activeLines: ['|>chamfer(length=5,tags=[getOppositeEdge(seg01)])'], activeLines: ['length=5,'],
highlightedCode: '', highlightedCode: '',
}) })
}) })
@ -2099,7 +2072,7 @@ extrude001 = extrude(sketch001, length = -12)
1 1
) )
await operationButton.click({ button: 'left' }) await operationButton.click({ button: 'left' })
await page.keyboard.press('Delete') await page.keyboard.press('Backspace')
await page.waitForTimeout(500) await page.waitForTimeout(500)
await scene.expectPixelColor(edgeColorWhite, secondEdgeLocation, 15) // deleted await scene.expectPixelColor(edgeColorWhite, secondEdgeLocation, 15) // deleted
await scene.expectPixelColor(chamferColor, firstEdgeLocation, 15) // stayed await scene.expectPixelColor(chamferColor, firstEdgeLocation, 15) // stayed
@ -2123,17 +2096,18 @@ extrude001 = extrude(sketch001, length = -12)
|> line(endAbsolute = [profileStartX(%), profileStartY(%)], tag = $seg01) |> line(endAbsolute = [profileStartX(%), profileStartY(%)], tag = $seg01)
|> close() |> close()
extrude001 = extrude(sketch001, length = -12) extrude001 = extrude(sketch001, length = -12)
|> chamfer(length = 5, tags = [seg01]) // chamfer01 |> chamfer({ length = 5, tags = [seg01] }, %) // chamfer01
|> chamfer(length = 5, tags = [seg02]) // chamfer02 |> chamfer({ length = 5, tags = [seg02] }, %) // chamfer02
chamfer03 = chamfer(extrude001, length = 5, tags = [getOppositeEdge(seg01)]) chamfer03 = chamfer({ length = 5, tags = [getOppositeEdge(seg01)]}, extrude001)
chamfer04 = chamfer(extrude001, length = 5, tags = [getOppositeEdge(seg02)]) chamfer04 = chamfer({ length = 5, tags = [getOppositeEdge(seg02)]}, extrude001)
` `
const pipedChamferDeclaration = 'chamfer(length = 5, tags = [seg01])' const pipedChamferDeclaration = 'chamfer({ length = 5, tags = [seg01] }, %)'
const secondPipedChamferDeclaration = 'chamfer(length = 5, tags = [seg02])' const secondPipedChamferDeclaration =
'chamfer({ length = 5, tags = [seg02] }, %)'
const standaloneChamferDeclaration = const standaloneChamferDeclaration =
'chamfer03 = chamfer(extrude001, length = 5, tags = [getOppositeEdge(seg01)])' 'chamfer03 = chamfer({ length = 5, tags = [getOppositeEdge(seg01)]}, extrude001)'
const secondStandaloneChamferDeclaration = const secondStandaloneChamferDeclaration =
'chamfer04 = chamfer(extrude001, length = 5, tags = [getOppositeEdge(seg02)])' 'chamfer04 = chamfer({ length = 5, tags = [getOppositeEdge(seg02)]}, extrude001)'
// Locators // Locators
const pipedChamferEdgeLocation = { x: 600, y: 193 } const pipedChamferEdgeLocation = { x: 600, y: 193 }
@ -2202,7 +2176,7 @@ chamfer04 = chamfer(extrude001, length = 5, tags = [getOppositeEdge(seg02)])
0 0
) )
await operationButton.click({ button: 'left' }) await operationButton.click({ button: 'left' })
await page.keyboard.press('Delete') await page.keyboard.press('Backspace')
await page.waitForTimeout(500) await page.waitForTimeout(500)
}) })
await test.step('Verify piped chamfer is deleted but other chamfers are not (in the editor)', async () => { await test.step('Verify piped chamfer is deleted but other chamfers are not (in the editor)', async () => {
@ -2234,7 +2208,7 @@ chamfer04 = chamfer(extrude001, length = 5, tags = [getOppositeEdge(seg02)])
1 1
) )
await operationButton.click({ button: 'left' }) await operationButton.click({ button: 'left' })
await page.keyboard.press('Delete') await page.keyboard.press('Backspace')
await page.waitForTimeout(500) await page.waitForTimeout(500)
}) })
await test.step('Verify non-piped chamfer is deleted but other two chamfers are not (in the editor)', async () => { await test.step('Verify non-piped chamfer is deleted but other two chamfers are not (in the editor)', async () => {
@ -2438,7 +2412,7 @@ extrude001 = extrude(sketch001, length = 40)
await editor.closePane() await editor.closePane()
const operationButton = await toolbar.getFeatureTreeOperation('Shell', 0) const operationButton = await toolbar.getFeatureTreeOperation('Shell', 0)
await operationButton.click({ button: 'left' }) await operationButton.click({ button: 'left' })
await page.keyboard.press('Delete') await page.keyboard.press('Backspace')
await scene.expectPixelColor([99, 99, 99], testPoint, 15) await scene.expectPixelColor([99, 99, 99], testPoint, 15)
}) })
}) })
@ -2577,7 +2551,7 @@ profile001 = startProfileAt([-20, 20], sketch001)
const deleteOperation = async (operationButton: Locator) => { const deleteOperation = async (operationButton: Locator) => {
if (shouldUseKeyboard) { if (shouldUseKeyboard) {
await operationButton.click({ button: 'left' }) await operationButton.click({ button: 'left' })
await page.keyboard.press('Delete') await page.keyboard.press('Backspace')
} else { } else {
await operationButton.click({ button: 'right' }) await operationButton.click({ button: 'right' })
const editButton = page.getByTestId('context-menu-delete') const editButton = page.getByTestId('context-menu-delete')
@ -2838,107 +2812,4 @@ radius = 8.69
expect(editor.expectEditor.toContain(newCodeToFind)).toBeTruthy() 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
)
})
})
}) })

View File

@ -424,7 +424,7 @@ test(
test( test(
'when code with error first loads you get errors in console', 'when code with error first loads you get errors in console',
{ tag: '@electron' }, { tag: '@electron' },
async ({ context, page, editor }, testInfo) => { async ({ context, page }, testInfo) => {
await context.folderSetupFn(async (dir) => { await context.folderSetupFn(async (dir) => {
await fsp.mkdir(path.join(dir, 'broken-code'), { recursive: true }) await fsp.mkdir(path.join(dir, 'broken-code'), { recursive: true })
await fsp.copyFile( await fsp.copyFile(
@ -434,19 +434,16 @@ test(
}) })
await page.setBodyDimensions({ width: 1200, height: 500 }) await page.setBodyDimensions({ width: 1200, height: 500 })
await expect(page.getByText('broken-code')).toBeVisible() await expect(page.getByText('broken-code')).toBeVisible()
await page.getByText('broken-code').click() 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')).toBeAttached()
await expect(page.getByTestId('loading')).not.toBeAttached({ await expect(page.getByTestId('loading')).not.toBeAttached({
timeout: 20_000, 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 // error in guter
await expect(page.locator('.cm-lint-marker-error')).toBeVisible() await expect(page.locator('.cm-lint-marker-error')).toBeVisible()

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 54 KiB

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 76 KiB

After

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 51 KiB

After

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 66 KiB

After

Width:  |  Height:  |  Size: 67 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 145 KiB

After

Width:  |  Height:  |  Size: 143 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 129 KiB

After

Width:  |  Height:  |  Size: 127 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 49 KiB

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 55 KiB

After

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 49 KiB

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 54 KiB

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 54 KiB

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 49 KiB

After

Width:  |  Height:  |  Size: 49 KiB

View File

@ -937,16 +937,11 @@ export async function setupElectron({
testInfo, testInfo,
cleanProjectDir = true, cleanProjectDir = true,
appSettings, appSettings,
viewport,
}: { }: {
testInfo: TestInfo testInfo: TestInfo
folderSetupFn?: (projectDirName: string) => Promise<void> folderSetupFn?: (projectDirName: string) => Promise<void>
cleanProjectDir?: boolean cleanProjectDir?: boolean
appSettings?: Partial<SaveSettingsPayload> appSettings?: Partial<SaveSettingsPayload>
viewport: {
width: number
height: number
}
}): Promise<{ }): Promise<{
electronApp: ElectronApplication electronApp: ElectronApplication
context: BrowserContext context: BrowserContext
@ -977,14 +972,6 @@ export async function setupElectron({
...(process.env.ELECTRON_OVERRIDE_DIST_PATH ...(process.env.ELECTRON_OVERRIDE_DIST_PATH
? { executablePath: process.env.ELECTRON_OVERRIDE_DIST_PATH + 'electron' } ? { 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. // Do this once and then reuse window on subsequent calls.

View File

@ -94,8 +94,6 @@ test.describe('Testing Camera Movement', { tag: ['@skipWin'] }, () => {
await bakeInRetries(async () => { await bakeInRetries(async () => {
await page.mouse.move(700, 200) await page.mouse.move(700, 200)
await page.mouse.down({ button: 'right' }) await page.mouse.down({ button: 'right' })
await page.waitForTimeout(100)
const appLogoBBox = await page.getByTestId('app-logo').boundingBox() const appLogoBBox = await page.getByTestId('app-logo').boundingBox()
expect(appLogoBBox).not.toBeNull() expect(appLogoBBox).not.toBeNull()
if (!appLogoBBox) throw new Error('app logo not found') if (!appLogoBBox) throw new Error('app logo not found')
@ -103,9 +101,7 @@ test.describe('Testing Camera Movement', { tag: ['@skipWin'] }, () => {
appLogoBBox.x + appLogoBBox.width / 2, appLogoBBox.x + appLogoBBox.width / 2,
appLogoBBox.y + appLogoBBox.height / 2 appLogoBBox.y + appLogoBBox.height / 2
) )
await page.waitForTimeout(100)
await page.mouse.move(600, 303) await page.mouse.move(600, 303)
await page.waitForTimeout(100)
await page.mouse.up({ button: 'right' }) await page.mouse.up({ button: 'right' })
}, [4, -10.5, -120]) }, [4, -10.5, -120])
@ -362,7 +358,9 @@ test.describe('Testing Camera Movement', { tag: ['@skipWin'] }, () => {
exact: true, exact: true,
}) })
const userSettingsTab = page.getByRole('radio', { name: 'User' }) const userSettingsTab = page.getByRole('radio', { name: 'User' })
const mouseControlsSetting = () => page.locator('#camera-controls').first() const mouseControlsSetting = page
.locator('#mouseControls')
.getByRole('combobox')
const mouseControlSuccesToast = page.getByText( const mouseControlSuccesToast = page.getByText(
'Set mouse controls to "Solidworks"' 'Set mouse controls to "Solidworks"'
) )
@ -392,14 +390,7 @@ test.describe('Testing Camera Movement', { tag: ['@skipWin'] }, () => {
await settingsLink.click() await settingsLink.click()
await expect(settingsDialogHeading).toBeVisible() await expect(settingsDialogHeading).toBeVisible()
await userSettingsTab.click() await userSettingsTab.click()
const setting = mouseControlsSetting() await mouseControlsSetting.selectOption({ label: 'Solidworks' })
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 expect(mouseControlSuccesToast).toBeVisible()
await settingsCloseButton.click() await settingsCloseButton.click()
}) })

View File

@ -382,7 +382,7 @@ profile003 = startProfileAt([40.16, -120.48], sketch006)
'|> line(end = [0, -pipeLength])' '|> line(end = [0, -pipeLength])'
) )
await u.clearCommandLogs() await u.clearCommandLogs()
await page.keyboard.press('Delete') await page.keyboard.press('Backspace')
await u.expectCmdLog('[data-message-type="execution-done"]', 10_000) await u.expectCmdLog('[data-message-type="execution-done"]', 10_000)
await page.waitForTimeout(200) await page.waitForTimeout(200)
@ -439,7 +439,7 @@ profile003 = startProfileAt([40.16, -120.48], sketch006)
'|> startProfileAt([23.24, 136.52], %)' '|> startProfileAt([23.24, 136.52], %)'
) )
await u.clearCommandLogs() await u.clearCommandLogs()
await page.keyboard.press('Delete') await page.keyboard.press('Backspace')
await u.expectCmdLog('[data-message-type="execution-done"]', 10_000) await u.expectCmdLog('[data-message-type="execution-done"]', 10_000)
await page.waitForTimeout(200) await page.waitForTimeout(200)
await expect(u.codeLocator).not.toContainText(`sketch005 = startSketchOn({`) 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])' ' |> line(end = [20.91, -28.61])'
) )
await u.clearCommandLogs() await u.clearCommandLogs()
await page.keyboard.press('Delete') await page.keyboard.press('Backspace')
await u.expectCmdLog('[data-message-type="execution-done"]', 10_000) await u.expectCmdLog('[data-message-type="execution-done"]', 10_000)
await page.waitForTimeout(200) await page.waitForTimeout(200)
await expect(u.codeLocator).not.toContainText(codeToBeDeletedSnippet) 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)' '|> line(end = [170.36, -121.61], tag = $seg01)'
) )
await u.clearCommandLogs() await u.clearCommandLogs()
await page.keyboard.press('Delete') await page.keyboard.press('Backspace')
await expect(page.getByText('Unable to delete selection')).toBeVisible() 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) |> line(endAbsolute = [profileStartX(%), profileStartY(%)], tag = $seg02)
|> close() |> close()
extrude001 = extrude(sketch001, length = 100) extrude001 = extrude(sketch001, length = 100)
|> chamfer( |> chamfer({
length = 30, length = 30,
tags = [ tags = [
seg01, seg01,
getNextAdjacentEdge(yo), getNextAdjacentEdge(yo),
getNextAdjacentEdge(seg02), getNextAdjacentEdge(seg02),
getOppositeEdge(seg01) getOppositeEdge(seg01)
], ]
) }, %)
`) `)
await expect( await expect(
page.getByTestId('model-state-indicator-execution-done') page.getByTestId('model-state-indicator-execution-done')
@ -810,15 +810,15 @@ profile003 = startProfileAt([40.16, -120.48], sketch006)
await checkCodeAtHoverPosition( await checkCodeAtHoverPosition(
'oppositeChamfer', 'oppositeChamfer',
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( await checkCodeAtHoverPosition(
'baseChamfer', 'baseChamfer',
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() await u.openAndClearDebugPanel()
@ -848,15 +848,15 @@ profile003 = startProfileAt([40.16, -120.48], sketch006)
await checkCodeAtHoverPosition( await checkCodeAtHoverPosition(
'adjacentChamfer1', 'adjacentChamfer1',
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( await checkCodeAtHoverPosition(
'adjacentChamfer2', 'adjacentChamfer2',
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 ({ test("Extrude button should be disabled if there's no extrudable geometry when nothing is selected", async ({

View File

@ -633,7 +633,6 @@ test.describe('Testing settings', () => {
`Set default unit to "${unitOfMeasure}" as a user default` `Set default unit to "${unitOfMeasure}" as a user default`
) )
await expect(toastMessage).toBeVisible() await expect(toastMessage).toBeVisible()
await expect(toastMessage).not.toBeVisible()
}) })
} }
await changeUnitOfMeasureInUserTab('in') await changeUnitOfMeasureInUserTab('in')

View File

@ -68,6 +68,13 @@ type PWFunction = (
let firstUrl = '' 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 = ( export const test = (
desc: string, desc: string,
objOrFn: PWFunction | TestDetails, objOrFn: PWFunction | TestDetails,

1
exp
View File

@ -1 +0,0 @@
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()

View File

1
got
View File

@ -1 +0,0 @@
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()

View File

@ -8,13 +8,13 @@
"email": "info@zoo.dev", "email": "info@zoo.dev",
"url": "https://zoo.dev" "url": "https://zoo.dev"
}, },
"description": "Zoo Modeling App", "description": "Edit CAD visually or with code",
"main": ".vite/build/main.js", "main": ".vite/build/main.js",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@codemirror/autocomplete": "^6.17.0", "@codemirror/autocomplete": "^6.17.0",
"@codemirror/commands": "^6.6.0", "@codemirror/commands": "^6.6.0",
"@codemirror/language": "^6.10.8", "@codemirror/language": "^6.10.3",
"@codemirror/lint": "^6.8.4", "@codemirror/lint": "^6.8.4",
"@codemirror/search": "^6.5.6", "@codemirror/search": "^6.5.6",
"@codemirror/state": "^6.4.1", "@codemirror/state": "^6.4.1",
@ -40,7 +40,7 @@
"codemirror": "^6.0.1", "codemirror": "^6.0.1",
"decamelize": "^6.0.0", "decamelize": "^6.0.0",
"diff": "^7.0.0", "diff": "^7.0.0",
"electron-updater": "^6.6.0", "electron-updater": "^6.5.0",
"fuse.js": "^7.0.0", "fuse.js": "^7.0.0",
"html2canvas-pro": "^1.5.8", "html2canvas-pro": "^1.5.8",
"isomorphic-fetch": "^3.0.0", "isomorphic-fetch": "^3.0.0",
@ -69,15 +69,10 @@
"yargs": "^17.7.2" "yargs": "^17.7.2"
}, },
"scripts": { "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": "vite --port=3000 --host=0.0.0.0",
"start:prod": "vite preview --port=3000", "start:prod": "vite preview --port=3000",
"serve": "vite serve --port=3000", "serve": "vite serve --port=3000",
"build": "yarn install:rust && . $HOME/.cargo/env && yarn install:wasm-pack:sh && yarn build:wasm && vite build", "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:local": "vite build", "build:local": "vite build",
"build:both": "vite build", "build:both": "vite build",
"build:both:local": "yarn build:wasm && vite build", "build:both:local": "yarn build:wasm && vite build",
@ -89,13 +84,11 @@
"simpleserver:stop": "kill-port 3000", "simpleserver:stop": "kill-port 3000",
"fmt": "prettier --write ./src *.ts *.json *.js ./e2e ./packages", "fmt": "prettier --write ./src *.ts *.json *.js ./e2e ./packages",
"fmt-check": "prettier --check ./src *.ts *.json *.js ./e2e ./packages", "fmt-check": "prettier --check ./src *.ts *.json *.js ./e2e ./packages",
"fetch:wasm": "./scripts/get-latest-wasm-bundle.sh", "fetch:wasm": "./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/kcl-samples/next/manifest.json",
"fetch:samples": "echo \"Fetching latest KCL samples...\" && curl -o public/kcl-samples-manifest-fallback.json https://raw.githubusercontent.com/KittyCAD/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)",
"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-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: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 wasm-prep && cd src/wasm-lib && wasm-pack build --release --target web --out-dir pkg && cargo test -p kcl-lib export_bindings && cd ../.. && yarn isomorphic-copy-wasm && yarn fmt",
"build:wasm": "yarn 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\"", "remove-importmeta": "sed -i 's/import.meta.url/window.location.origin/g' \"./src/wasm-lib/pkg/wasm_lib.js\"; sed -i '' 's/import.meta.url/window.location.origin/g' \"./src/wasm-lib/pkg/wasm_lib.js\" || echo \"sed for both mac and linux\"",
"wasm-prep": "rimraf src/wasm-lib/pkg && mkdirp src/wasm-lib/pkg && rimraf src/wasm-lib/kcl/bindings", "wasm-prep": "rimraf src/wasm-lib/pkg && mkdirp src/wasm-lib/pkg && rimraf src/wasm-lib/kcl/bindings",
"lint-fix": "eslint --fix --ext .ts --ext .tsx src e2e packages/codemirror-lsp-client/src", "lint-fix": "eslint --fix --ext .ts --ext .tsx src e2e packages/codemirror-lsp-client/src",
@ -103,7 +96,6 @@
"files:set-version": "echo \"$(jq --arg v \"$VERSION\" '.version=$v' package.json --indent 2)\" > package.json", "files:set-version": "echo \"$(jq --arg v \"$VERSION\" '.version=$v' package.json --indent 2)\" > package.json",
"files:set-notes": "./scripts/set-files-notes.sh", "files:set-notes": "./scripts/set-files-notes.sh",
"files:flip-to-nightly": "./scripts/flip-files-to-nightly.sh", "files:flip-to-nightly": "./scripts/flip-files-to-nightly.sh",
"files:flip-to-nightly:windows": "./scripts/flip-files-to-nightly.ps1",
"files:invalidate-bucket": "./scripts/invalidate-files-bucket.sh", "files:invalidate-bucket": "./scripts/invalidate-files-bucket.sh",
"files:invalidate-bucket:nightly": "./scripts/invalidate-files-bucket.sh --nightly", "files:invalidate-bucket:nightly": "./scripts/invalidate-files-bucket.sh --nightly",
"postinstall": "yarn fetch:samples && yarn xstate:typegen && ./node_modules/.bin/electron-rebuild", "postinstall": "yarn fetch:samples && yarn xstate:typegen && ./node_modules/.bin/electron-rebuild",
@ -206,7 +198,7 @@
"postinstall-postinstall": "^2.1.0", "postinstall-postinstall": "^2.1.0",
"prettier": "^2.8.8", "prettier": "^2.8.8",
"setimmediate": "^1.0.5", "setimmediate": "^1.0.5",
"tailwindcss": "^3.4.17", "tailwindcss": "^3.4.1",
"ts-node": "^10.0.0", "ts-node": "^10.0.0",
"typescript": "^5.7.3", "typescript": "^5.7.3",
"typescript-eslint": "^8.23.0", "typescript-eslint": "^8.23.0",
@ -215,6 +207,7 @@
"vite-tsconfig-paths": "^4.3.2", "vite-tsconfig-paths": "^4.3.2",
"vitest": "^1.6.1", "vitest": "^1.6.1",
"vitest-webgl-canvas-mock": "^1.1.0", "vitest-webgl-canvas-mock": "^1.1.0",
"wasm-pack": "^0.13.1",
"ws": "^8.17.0", "ws": "^8.17.0",
"yarn": "^1.22.22" "yarn": "^1.22.22"
}, },

View File

@ -1,20 +0,0 @@
$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

View File

@ -1,13 +0,0 @@
$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"

View File

@ -6,12 +6,14 @@ import { useHotkeys } from 'react-hotkeys-hook'
import { useLoaderData, useNavigate } from 'react-router-dom' import { useLoaderData, useNavigate } from 'react-router-dom'
import { type IndexLoaderData } from 'lib/types' import { type IndexLoaderData } from 'lib/types'
import { PATHS } from 'lib/paths' import { PATHS } from 'lib/paths'
import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext'
import { onboardingPaths } from 'routes/Onboarding/paths' import { onboardingPaths } from 'routes/Onboarding/paths'
import { useEngineConnectionSubscriptions } from 'hooks/useEngineConnectionSubscriptions' import { useEngineConnectionSubscriptions } from 'hooks/useEngineConnectionSubscriptions'
import { codeManager, engineCommandManager } from 'lib/singletons' import { codeManager, engineCommandManager } from 'lib/singletons'
import { useAbsoluteFilePath } from 'hooks/useAbsoluteFilePath' import { useAbsoluteFilePath } from 'hooks/useAbsoluteFilePath'
import { isDesktop } from 'lib/isDesktop' import { isDesktop } from 'lib/isDesktop'
import { useLspContext } from 'components/LspProvider' import { useLspContext } from 'components/LspProvider'
import { useRefreshSettings } from 'hooks/useRefreshSettings'
import { ModelingSidebar } from 'components/ModelingSidebar/ModelingSidebar' import { ModelingSidebar } from 'components/ModelingSidebar/ModelingSidebar'
import { LowerRightControls } from 'components/LowerRightControls' import { LowerRightControls } from 'components/LowerRightControls'
import ModalContainer from 'react-modal-promise' import ModalContainer from 'react-modal-promise'
@ -28,7 +30,6 @@ import { useRouteLoaderData } from 'react-router-dom'
import { useEngineCommands } from 'components/EngineCommands' import { useEngineCommands } from 'components/EngineCommands'
import { commandBarActor } from 'machines/commandBarMachine' import { commandBarActor } from 'machines/commandBarMachine'
import { useToken } from 'machines/appMachine' import { useToken } from 'machines/appMachine'
import { useSettings } from 'machines/appMachine'
maybeWriteToDisk() maybeWriteToDisk()
.then(() => {}) .then(() => {})
.catch(() => {}) .catch(() => {})
@ -48,6 +49,7 @@ export function App() {
}) })
}) })
useRefreshSettings(PATHS.FILE + 'SETTINGS')
const navigate = useNavigate() const navigate = useNavigate()
const filePath = useAbsoluteFilePath() const filePath = useAbsoluteFilePath()
const { onProjectOpen } = useLspContext() const { onProjectOpen } = useLspContext()
@ -69,7 +71,7 @@ export function App() {
useHotKeyListener() useHotKeyListener()
const settings = useSettings() const { settings } = useSettingsAuthContext()
const token = useToken() const token = useToken()
const coreDumpManager = useMemo( const coreDumpManager = useMemo(
@ -79,7 +81,7 @@ export function App() {
const { const {
app: { onboardingStatus }, app: { onboardingStatus },
} = settings } = settings.context
useHotkeys('backspace', (e) => { useHotkeys('backspace', (e) => {
e.preventDefault() e.preventDefault()

View File

@ -28,8 +28,10 @@ import {
fileLoader, fileLoader,
homeLoader, homeLoader,
onboardingRedirectLoader, onboardingRedirectLoader,
settingsLoader,
telemetryLoader, telemetryLoader,
} from 'lib/routeLoaders' } from 'lib/routeLoaders'
import SettingsAuthProvider from 'components/SettingsAuthProvider'
import LspProvider from 'components/LspProvider' import LspProvider from 'components/LspProvider'
import { KclContextProvider } from 'lang/KclProvider' import { KclContextProvider } from 'lang/KclProvider'
import { ASK_TO_OPEN_QUERY_PARAM, BROWSER_PROJECT_NAME } from 'lib/constants' import { ASK_TO_OPEN_QUERY_PARAM, BROWSER_PROJECT_NAME } from 'lib/constants'
@ -43,28 +45,34 @@ import { AppStateProvider } from 'AppState'
import { reportRejection } from 'lib/trap' import { reportRejection } from 'lib/trap'
import { RouteProvider } from 'components/RouteProvider' import { RouteProvider } from 'components/RouteProvider'
import { ProjectsContextProvider } from 'components/ProjectsContextProvider' import { ProjectsContextProvider } from 'components/ProjectsContextProvider'
import { useToken } from 'machines/appMachine'
import { OpenInDesktopAppHandler } from 'components/OpenInDesktopAppHandler' import { OpenInDesktopAppHandler } from 'components/OpenInDesktopAppHandler'
import { useToken } from 'machines/appMachine'
const createRouter = isDesktop() ? createHashRouter : createBrowserRouter const createRouter = isDesktop() ? createHashRouter : createBrowserRouter
const router = createRouter([ const router = createRouter([
{ {
loader: settingsLoader,
id: PATHS.INDEX, 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: ( element: (
<OpenInDesktopAppHandler> <OpenInDesktopAppHandler>
<RouteProvider> <RouteProvider>
<LspProvider> <SettingsAuthProvider>
<ProjectsContextProvider> <LspProvider>
<KclContextProvider> <ProjectsContextProvider>
<AppStateProvider> <KclContextProvider>
<MachineManagerProvider> <AppStateProvider>
<Outlet /> <MachineManagerProvider>
</MachineManagerProvider> <Outlet />
</AppStateProvider> </MachineManagerProvider>
</KclContextProvider> </AppStateProvider>
</ProjectsContextProvider> </KclContextProvider>
</LspProvider> </ProjectsContextProvider>
</LspProvider>
</SettingsAuthProvider>
</RouteProvider> </RouteProvider>
</OpenInDesktopAppHandler> </OpenInDesktopAppHandler>
), ),
@ -112,6 +120,7 @@ const router = createRouter([
children: [ children: [
{ {
id: PATHS.FILE + 'SETTINGS', id: PATHS.FILE + 'SETTINGS',
loader: settingsLoader,
children: [ children: [
{ {
loader: onboardingRedirectLoader, loader: onboardingRedirectLoader,
@ -157,9 +166,11 @@ const router = createRouter([
index: true, index: true,
element: <></>, element: <></>,
id: PATHS.HOME + 'SETTINGS', id: PATHS.HOME + 'SETTINGS',
loader: settingsLoader,
}, },
{ {
path: makeUrlPathRelative(PATHS.SETTINGS), path: makeUrlPathRelative(PATHS.SETTINGS),
loader: settingsLoader,
element: <Settings />, element: <Settings />,
}, },
{ {

View File

@ -2,6 +2,7 @@ import { useRef, useEffect, useState, useMemo, Fragment } from 'react'
import { useModelingContext } from 'hooks/useModelingContext' import { useModelingContext } from 'hooks/useModelingContext'
import { cameraMouseDragGuards } from 'lib/cameraControls' import { cameraMouseDragGuards } from 'lib/cameraControls'
import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext'
import { ARROWHEAD, DEBUG_SHOW_BOTH_SCENES } from './sceneInfra' import { ARROWHEAD, DEBUG_SHOW_BOTH_SCENES } from './sceneInfra'
import { ReactCameraProperties } from './CameraControls' import { ReactCameraProperties } from './CameraControls'
import { throttle, toSync } from 'lib/utils' import { throttle, toSync } from 'lib/utils'
@ -47,7 +48,6 @@ import { ActionButton } from 'components/ActionButton'
import { err, reportRejection, trap } from 'lib/trap' import { err, reportRejection, trap } from 'lib/trap'
import { Node } from 'wasm-lib/kcl/bindings/Node' import { Node } from 'wasm-lib/kcl/bindings/Node'
import { commandBarActor } from 'machines/commandBarMachine' import { commandBarActor } from 'machines/commandBarMachine'
import { useSettings } from 'machines/appMachine'
function useShouldHideScene(): { hideClient: boolean; hideServer: boolean } { function useShouldHideScene(): { hideClient: boolean; hideServer: boolean } {
const [isCamMoving, setIsCamMoving] = useState(false) const [isCamMoving, setIsCamMoving] = useState(false)
@ -76,8 +76,8 @@ export const ClientSideScene = ({
cameraControls, cameraControls,
}: { }: {
cameraControls: ReturnType< cameraControls: ReturnType<
typeof useSettings typeof useSettingsAuthContext
>['modeling']['mouseControls']['current'] >['settings']['context']['modeling']['mouseControls']['current']
}) => { }) => {
const canvasRef = useRef<HTMLDivElement>(null) const canvasRef = useRef<HTMLDivElement>(null)
const { state, send, context } = useModelingContext() const { state, send, context } = useModelingContext()

View File

@ -133,11 +133,9 @@ function DisplayObj({
}} }}
onClick={(e) => { onClick={(e) => {
const range = topLevelRange(obj?.start || 0, obj.end || 0) const range = topLevelRange(obj?.start || 0, obj.end || 0)
const idInfo = codeToIdSelections( const idInfo = codeToIdSelections([
[{ codeRef: codeRefFromRange(range, kclManager.ast) }], { codeRef: codeRefFromRange(range, kclManager.ast) },
engineCommandManager.artifactGraph, ])[0]
engineCommandManager.artifactIndex
)[0]
const artifact = engineCommandManager.artifactGraph.get( const artifact = engineCommandManager.artifactGraph.get(
idInfo?.id || '' idInfo?.id || ''
) )

View File

@ -1,22 +1,24 @@
import { Switch } from '@headlessui/react' import { Switch } from '@headlessui/react'
import { settingsActor, useSettings } from 'machines/appMachine' import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext'
import { useEffect, useState } from 'react' import { useEffect, useState } from 'react'
export function CameraProjectionToggle() { export function CameraProjectionToggle() {
const settings = useSettings() const { settings } = useSettingsAuthContext()
const isCameraProjectionPerspective = const isCameraProjectionPerspective =
settings.modeling.cameraProjection.current === 'perspective' settings.context.modeling.cameraProjection.current === 'perspective'
const [checked, setChecked] = useState(isCameraProjectionPerspective) const [checked, setChecked] = useState(isCameraProjectionPerspective)
useEffect(() => { useEffect(() => {
setChecked(settings.modeling.cameraProjection.current === 'perspective') setChecked(
}, [settings.modeling.cameraProjection.current]) settings.context.modeling.cameraProjection.current === 'perspective'
)
}, [settings.context.modeling.cameraProjection.current])
return ( return (
<Switch <Switch
checked={checked} checked={checked}
onChange={(newValue) => { onChange={(newValue) => {
settingsActor.send({ settings.send({
type: 'set.modeling.cameraProjection', type: 'set.modeling.cameraProjection',
data: { data: {
level: 'user', level: 'user',

View File

@ -7,6 +7,7 @@ import {
} from '@codemirror/autocomplete' } from '@codemirror/autocomplete'
import { EditorView, keymap, ViewUpdate } from '@codemirror/view' import { EditorView, keymap, ViewUpdate } from '@codemirror/view'
import { CustomIcon } from 'components/CustomIcon' import { CustomIcon } from 'components/CustomIcon'
import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext'
import { CommandArgument, KclCommandValue } from 'lib/commandTypes' import { CommandArgument, KclCommandValue } from 'lib/commandTypes'
import { getSystemTheme } from 'lib/theme' import { getSystemTheme } from 'lib/theme'
import { useCalculateKclExpression } from 'lib/useCalculateKclExpression' import { useCalculateKclExpression } from 'lib/useCalculateKclExpression'
@ -19,7 +20,6 @@ import { createIdentifier, createVariableDeclaration } from 'lang/modifyAst'
import { useCodeMirror } from 'components/ModelingSidebar/ModelingPanes/CodeEditor' import { useCodeMirror } from 'components/ModelingSidebar/ModelingPanes/CodeEditor'
import { useSelector } from '@xstate/react' import { useSelector } from '@xstate/react'
import { commandBarActor, useCommandBarState } from 'machines/commandBarMachine' import { commandBarActor, useCommandBarState } from 'machines/commandBarMachine'
import { useSettings } from 'machines/appMachine'
import toast from 'react-hot-toast' import toast from 'react-hot-toast'
const machineContextSelector = (snapshot?: { const machineContextSelector = (snapshot?: {
@ -42,7 +42,7 @@ function CommandBarKclInput({
const previouslySetValue = commandBarState.context.argumentsToSubmit[ const previouslySetValue = commandBarState.context.argumentsToSubmit[
arg.name arg.name
] as KclCommandValue | undefined ] as KclCommandValue | undefined
const settings = useSettings() const { settings } = useSettingsAuthContext()
const argMachineContext = useSelector( const argMachineContext = useSelector(
arg.machineActor, arg.machineActor,
machineContextSelector machineContextSelector
@ -117,9 +117,9 @@ function CommandBarKclInput({
: defaultValue.length, : defaultValue.length,
}, },
theme: theme:
settings.app.theme.current === 'system' settings.context.app.theme.current === 'system'
? getSystemTheme() ? getSystemTheme()
: settings.app.theme.current, : settings.context.app.theme.current,
extensions: [ extensions: [
varMentionsExtension, varMentionsExtension,
EditorView.updateListener.of((vu: ViewUpdate) => { EditorView.updateListener.of((vu: ViewUpdate) => {

View File

@ -1,16 +1,16 @@
import { Dialog } from '@headlessui/react' import { Dialog } from '@headlessui/react'
import { ActionButton } from './ActionButton' import { ActionButton } from './ActionButton'
import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext'
import { useState } from 'react' import { useState } from 'react'
import { useSearchParams } from 'react-router-dom' import { useSearchParams } from 'react-router-dom'
import { CREATE_FILE_URL_PARAM } from 'lib/constants' import { CREATE_FILE_URL_PARAM } from 'lib/constants'
import { useSettings } from 'machines/appMachine'
const DownloadAppBanner = () => { const DownloadAppBanner = () => {
const [searchParams] = useSearchParams() const [searchParams] = useSearchParams()
const hasCreateFileParam = searchParams.has(CREATE_FILE_URL_PARAM) const hasCreateFileParam = searchParams.has(CREATE_FILE_URL_PARAM)
const settings = useSettings() const { settings } = useSettingsAuthContext()
const [isBannerDismissed, setIsBannerDismissed] = useState( const [isBannerDismissed, setIsBannerDismissed] = useState(
settings.app.dismissWebBanner.current settings.context.app.dismissWebBanner.current || hasCreateFileParam
) )
return ( return (

View File

@ -1,7 +1,7 @@
import { useMachine } from '@xstate/react' import { useMachine } from '@xstate/react'
import { useLocation, useNavigate, useRouteLoaderData } from 'react-router-dom' import { useNavigate, useRouteLoaderData } from 'react-router-dom'
import { type IndexLoaderData } from 'lib/types' import { type IndexLoaderData } from 'lib/types'
import { BROWSER_PATH, PATHS } from 'lib/paths' import { PATHS } from 'lib/paths'
import React, { createContext, useEffect, useMemo } from 'react' import React, { createContext, useEffect, useMemo } from 'react'
import { toast } from 'react-hot-toast' import { toast } from 'react-hot-toast'
import { import {
@ -27,10 +27,9 @@ import {
getKclSamplesManifest, getKclSamplesManifest,
KclSamplesManifestItem, KclSamplesManifestItem,
} from 'lib/getKclSamplesManifest' } from 'lib/getKclSamplesManifest'
import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext'
import { markOnce } from 'lib/performance' import { markOnce } from 'lib/performance'
import { commandBarActor } from 'machines/commandBarMachine' import { commandBarActor } from 'machines/commandBarMachine'
import { settingsActor, useSettings } from 'machines/appMachine'
import { createRouteCommands } from 'lib/commandBarConfigs/routeCommandConfig'
import { useToken } from 'machines/appMachine' import { useToken } from 'machines/appMachine'
type MachineContext<T extends AnyStateMachine> = { type MachineContext<T extends AnyStateMachine> = {
@ -49,51 +48,14 @@ export const FileMachineProvider = ({
children: React.ReactNode children: React.ReactNode
}) => { }) => {
const navigate = useNavigate() const navigate = useNavigate()
const location = useLocation() const { settings } = useSettingsAuthContext()
const token = useToken() const token = useToken()
const settings = useSettings()
const projectData = useRouteLoaderData(PATHS.FILE) as IndexLoaderData const projectData = useRouteLoaderData(PATHS.FILE) as IndexLoaderData
const { project, file } = projectData const { project, file } = projectData
const [kclSamples, setKclSamples] = React.useState<KclSamplesManifestItem[]>( 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(() => { useEffect(() => {
markOnce('code/didLoadFile') markOnce('code/didLoadFile')
async function fetchKclSamples() { async function fetchKclSamples() {
@ -361,7 +323,7 @@ export const FileMachineProvider = ({
authToken: token ?? '', authToken: token ?? '',
projectData, projectData,
settings: { settings: {
defaultUnit: settings.modeling.defaultUnit.current ?? 'mm', defaultUnit: settings?.context?.modeling.defaultUnit.current ?? 'mm',
}, },
specialPropsForSampleCommand: { specialPropsForSampleCommand: {
onSubmit: async (data) => { onSubmit: async (data) => {
@ -383,7 +345,7 @@ export const FileMachineProvider = ({
// Either way, we want to overwrite the defaultUnit project setting // Either way, we want to overwrite the defaultUnit project setting
// with the sample's setting. // with the sample's setting.
if (data.sampleUnits) { if (data.sampleUnits) {
settingsActor.send({ settings.send({
type: 'set.modeling.defaultUnit', type: 'set.modeling.defaultUnit',
data: { data: {
level: 'project', level: 'project',

View File

@ -1,5 +1,6 @@
import { Popover } from '@headlessui/react' import { Popover } from '@headlessui/react'
import Tooltip from './Tooltip' import Tooltip from './Tooltip'
import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext'
import { CustomIcon } from './CustomIcon' import { CustomIcon } from './CustomIcon'
import { useLocation, useNavigate } from 'react-router-dom' import { useLocation, useNavigate } from 'react-router-dom'
import { PATHS } from 'lib/paths' import { PATHS } from 'lib/paths'
@ -8,7 +9,6 @@ import { useAbsoluteFilePath } from 'hooks/useAbsoluteFilePath'
import { useLspContext } from './LspProvider' import { useLspContext } from './LspProvider'
import { openExternalBrowserIfDesktop } from 'lib/openWindow' import { openExternalBrowserIfDesktop } from 'lib/openWindow'
import { reportRejection } from 'lib/trap' import { reportRejection } from 'lib/trap'
import { settingsActor } from 'machines/appMachine'
const HelpMenuDivider = () => ( const HelpMenuDivider = () => (
<div className="h-[1px] bg-chalkboard-110 dark:bg-chalkboard-80" /> <div className="h-[1px] bg-chalkboard-110 dark:bg-chalkboard-80" />
@ -20,6 +20,7 @@ export function HelpMenu(props: React.PropsWithChildren) {
const filePath = useAbsoluteFilePath() const filePath = useAbsoluteFilePath()
const isInProject = location.pathname.includes(PATHS.FILE) const isInProject = location.pathname.includes(PATHS.FILE)
const navigate = useNavigate() const navigate = useNavigate()
const { settings } = useSettingsAuthContext()
return ( return (
<Popover className="relative"> <Popover className="relative">
@ -105,7 +106,7 @@ export function HelpMenu(props: React.PropsWithChildren) {
<HelpMenuItem <HelpMenuItem
as="button" as="button"
onClick={() => { onClick={() => {
settingsActor.send({ settings.send({
type: 'set.app.onboardingStatus', type: 'set.app.onboardingStatus',
data: { data: {
value: '', value: '',

View File

@ -10,6 +10,7 @@ import {
import { TEST, VITE_KC_API_BASE_URL } from 'env' import { TEST, VITE_KC_API_BASE_URL } from 'env'
import { kcl } from 'editor/plugins/lsp/kcl/language' import { kcl } from 'editor/plugins/lsp/kcl/language'
import { copilotPlugin } from 'editor/plugins/lsp/copilot' import { copilotPlugin } from 'editor/plugins/lsp/copilot'
import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext'
import { Extension } from '@codemirror/state' import { Extension } from '@codemirror/state'
import { LanguageSupport } from '@codemirror/language' import { LanguageSupport } from '@codemirror/language'
import { useNavigate } from 'react-router-dom' import { useNavigate } from 'react-router-dom'

View File

@ -22,6 +22,7 @@ import {
modelingMachineDefaultContext, modelingMachineDefaultContext,
} from 'machines/modelingMachine' } from 'machines/modelingMachine'
import { useSetupEngineManager } from 'hooks/useSetupEngineManager' import { useSetupEngineManager } from 'hooks/useSetupEngineManager'
import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext'
import { import {
isCursorInSketchCommandRange, isCursorInSketchCommandRange,
updateSketchDetailsNodePaths, updateSketchDetailsNodePaths,
@ -109,8 +110,6 @@ import { kclEditorActor } from 'machines/kclEditorMachine'
import { commandBarActor } from 'machines/commandBarMachine' import { commandBarActor } from 'machines/commandBarMachine'
import { useToken } from 'machines/appMachine' import { useToken } from 'machines/appMachine'
import { getNodePathFromSourceRange } from 'lang/queryAstNodePathUtils' import { getNodePathFromSourceRange } from 'lang/queryAstNodePathUtils'
import { useSettings } from 'machines/appMachine'
import { isDesktop } from 'lib/isDesktop'
type MachineContext<T extends AnyStateMachine> = { type MachineContext<T extends AnyStateMachine> = {
state: StateFrom<T> state: StateFrom<T>
@ -132,15 +131,19 @@ export const ModelingMachineProvider = ({
children: React.ReactNode children: React.ReactNode
}) => { }) => {
const { const {
app: { theme, enableSSAO, allowOrbitInSketchMode }, settings: {
modeling: { context: {
defaultUnit, app: { theme, enableSSAO, allowOrbitInSketchMode },
cameraProjection, modeling: {
highlightEdges, defaultUnit,
showScaleGrid, cameraProjection,
cameraOrbit, highlightEdges,
showScaleGrid,
cameraOrbit,
},
},
}, },
} = useSettings() } = useSettingsAuthContext()
const previousAllowOrbitInSketchMode = useRef(allowOrbitInSketchMode.current) const previousAllowOrbitInSketchMode = useRef(allowOrbitInSketchMode.current)
const navigate = useNavigate() const navigate = useNavigate()
const { context, send: fileMachineSend } = useFileContext() const { context, send: fileMachineSend } = useFileContext()
@ -803,7 +806,7 @@ export const ModelingMachineProvider = ({
engineCommandManager.artifactGraph engineCommandManager.artifactGraph
) )
if (err(plane)) return Promise.reject(plane) if (err(plane)) return Promise.reject(plane)
// if the user selected a segment, make sure we enter the right sketch as there can be multiple on a plane // if the user selected a segment, make sure we enter the right sketch as there can be multiple on a plan
// but still works if the user selected a plane/face by defaulting to the first path // but still works if the user selected a plane/face by defaulting to the first path
const mainPath = const mainPath =
artifact?.type === 'segment' || artifact?.type === 'solid2d' artifact?.type === 'segment' || artifact?.type === 'solid2d'
@ -1718,12 +1721,8 @@ export const ModelingMachineProvider = ({
previousAllowOrbitInSketchMode.current = allowOrbitInSketchMode.current previousAllowOrbitInSketchMode.current = allowOrbitInSketchMode.current
}, [allowOrbitInSketchMode]) }, [allowOrbitInSketchMode])
// Allow using the delete key to delete solids. Backspace only on macOS as Windows and Linux have dedicated Delete // Allow using the delete key to delete solids
const deleteKeys = useHotkeys(['backspace', 'delete', 'del'], () => {
isDesktop() && window.electron.os.isMac
? ['backspace', 'delete', 'del']
: ['delete', 'del']
useHotkeys(deleteKeys, () => {
modelingSend({ type: 'Delete selection' }) modelingSend({ type: 'Delete selection' })
}) })

View File

@ -1,12 +1,12 @@
import { ReactNode } from 'react' import { ReactNode } from 'react'
import styles from './ModelingPane.module.css' import styles from './ModelingPane.module.css'
import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext'
import { ActionButton } from 'components/ActionButton' import { ActionButton } from 'components/ActionButton'
import Tooltip from 'components/Tooltip' import Tooltip from 'components/Tooltip'
import { CustomIconName } from 'components/CustomIcon' import { CustomIconName } from 'components/CustomIcon'
import { IconDefinition } from '@fortawesome/free-solid-svg-icons' import { IconDefinition } from '@fortawesome/free-solid-svg-icons'
import { ActionIcon } from 'components/ActionIcon' import { ActionIcon } from 'components/ActionIcon'
import { onboardingPaths } from 'routes/Onboarding/paths' import { onboardingPaths } from 'routes/Onboarding/paths'
import { useSettings } from 'machines/appMachine'
export interface ModelingPaneProps { export interface ModelingPaneProps {
id: string id: string
@ -68,8 +68,8 @@ export const ModelingPane = ({
title, title,
...props ...props
}: ModelingPaneProps) => { }: ModelingPaneProps) => {
const settings = useSettings() const { settings } = useSettingsAuthContext()
const onboardingStatus = settings.app.onboardingStatus const onboardingStatus = settings.context.app.onboardingStatus
const pointerEventsCssClass = const pointerEventsCssClass =
onboardingStatus.current === onboardingPaths.CAMERA onboardingStatus.current === onboardingPaths.CAMERA
? 'pointer-events-none ' ? 'pointer-events-none '

View File

@ -324,18 +324,6 @@ 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() { function deleteOperation() {
if ( if (
props.item.type === 'StdLibCall' || props.item.type === 'StdLibCall' ||
@ -392,13 +380,6 @@ const OperationItem = (props: {
: []), : []),
...(props.item.type === 'StdLibCall' ...(props.item.type === 'StdLibCall'
? [ ? [
<ContextMenuItem
disabled={!stdLibMap[props.item.name]?.supportsAppearance}
onClick={enterAppearanceFlow}
data-testid="context-menu-set-appearance"
>
Set appearance
</ContextMenuItem>,
<ContextMenuItem <ContextMenuItem
disabled={!stdLibMap[props.item.name]?.prepareToEdit} disabled={!stdLibMap[props.item.name]?.prepareToEdit}
onClick={enterEditFlow} onClick={enterEditFlow}

View File

@ -1,4 +1,5 @@
import { TEST } from 'env' import { TEST } from 'env'
import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext'
import { Themes, getSystemTheme } from 'lib/theme' import { Themes, getSystemTheme } from 'lib/theme'
import { useEffect, useMemo, useRef } from 'react' import { useEffect, useMemo, useRef } from 'react'
import { highlightSelectionMatches, searchKeymap } from '@codemirror/search' import { highlightSelectionMatches, searchKeymap } from '@codemirror/search'
@ -50,7 +51,6 @@ import {
} from 'machines/kclEditorMachine' } from 'machines/kclEditorMachine'
import { useSelector } from '@xstate/react' import { useSelector } from '@xstate/react'
import { modelingMachineEvent } from 'editor/manager' import { modelingMachineEvent } from 'editor/manager'
import { useSettings } from 'machines/appMachine'
export const editorShortcutMeta = { export const editorShortcutMeta = {
formatCode: { formatCode: {
@ -63,7 +63,9 @@ export const editorShortcutMeta = {
} }
export const KclEditorPane = () => { export const KclEditorPane = () => {
const context = useSettings() const {
settings: { context },
} = useSettingsAuthContext()
const lastSelectionEvent = useSelector(kclEditorActor, selectionEventSelector) const lastSelectionEvent = useSelector(kclEditorActor, selectionEventSelector)
const editorIsMounted = useSelector(kclEditorActor, editorIsMountedSelector) const editorIsMounted = useSelector(kclEditorActor, editorIsMountedSelector)
const theme = const theme =

View File

@ -33,7 +33,7 @@ describe('processMemory', () => {
const output = processMemory(execState.variables) const output = processMemory(execState.variables)
expect(output.myVar).toEqual(5) expect(output.myVar).toEqual(5)
expect(output.otherVar).toEqual(3) expect(output.otherVar).toEqual(3)
expect(output.myFn).toEqual('__function__') expect(output.myFn).toEqual('__function(a)__')
expect(output.theExtrude).toEqual([ expect(output.theExtrude).toEqual([
{ {
type: 'extrudePlane', type: 'extrudePlane',

View File

@ -107,7 +107,9 @@ export const processMemory = (variables: VariableMap) => {
} }
//@ts-ignore //@ts-ignore
} else if (val.type === 'Function') { } else if (val.type === 'Function') {
processedMemory[key] = `__function__` processedMemory[key] = `__function(${(val as any)?.expression?.params
?.map?.(({ identifier }: any) => identifier?.name || '')
.join(', ')})__`
} }
} }
return processedMemory return processedMemory

View File

@ -1,3 +1,4 @@
import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext'
import { Resizable } from 're-resizable' import { Resizable } from 're-resizable'
import { import {
MouseEventHandler, MouseEventHandler,
@ -20,7 +21,6 @@ import { MachineManagerContext } from 'components/MachineManagerProvider'
import { onboardingPaths } from 'routes/Onboarding/paths' import { onboardingPaths } from 'routes/Onboarding/paths'
import { SIDEBAR_BUTTON_SUFFIX } from 'lib/constants' import { SIDEBAR_BUTTON_SUFFIX } from 'lib/constants'
import { commandBarActor } from 'machines/commandBarMachine' import { commandBarActor } from 'machines/commandBarMachine'
import { useSettings } from 'machines/appMachine'
interface ModelingSidebarProps { interface ModelingSidebarProps {
paneOpacity: '' | 'opacity-20' | 'opacity-40' paneOpacity: '' | 'opacity-20' | 'opacity-40'
@ -38,23 +38,23 @@ function getPlatformString(): 'web' | 'desktop' {
export function ModelingSidebar({ paneOpacity }: ModelingSidebarProps) { export function ModelingSidebar({ paneOpacity }: ModelingSidebarProps) {
const machineManager = useContext(MachineManagerContext) const machineManager = useContext(MachineManagerContext)
const kclContext = useKclContext() const kclContext = useKclContext()
const settings = useSettings() const { settings } = useSettingsAuthContext()
const onboardingStatus = settings.app.onboardingStatus const onboardingStatus = settings.context.app.onboardingStatus
const { send, context } = useModelingContext() const { send, context } = useModelingContext()
const pointerEventsCssClass = const pointerEventsCssClass =
onboardingStatus.current === onboardingPaths.CAMERA || onboardingStatus.current === onboardingPaths.CAMERA ||
context.store?.openPanes.length === 0 context.store?.openPanes.length === 0
? 'pointer-events-none ' ? 'pointer-events-none '
: 'pointer-events-auto ' : 'pointer-events-auto '
const showDebugPanel = settings.modeling.showDebugPanel const showDebugPanel = settings.context.modeling.showDebugPanel
const paneCallbackProps = useMemo( const paneCallbackProps = useMemo(
() => ({ () => ({
kclContext, kclContext,
settings, settings: settings.context,
platform: getPlatformString(), platform: getPlatformString(),
}), }),
[kclContext.diagnostics, settings] [kclContext.diagnostics, settings.context]
) )
const sidebarActions: SidebarAction[] = [ const sidebarActions: SidebarAction[] = [
@ -144,7 +144,7 @@ export function ModelingSidebar({ paneOpacity }: ModelingSidebarProps) {
}, },
}) })
} }
}, [settings.modeling.showDebugPanel]) }, [settings.context])
const togglePane = useCallback( const togglePane = useCallback(
(newPane: SidebarType) => { (newPane: SidebarType) => {

View File

@ -1,5 +1,6 @@
import { fireEvent, render, screen } from '@testing-library/react' import { fireEvent, render, screen } from '@testing-library/react'
import { BrowserRouter } from 'react-router-dom' import { BrowserRouter } from 'react-router-dom'
import { SettingsAuthProviderJest } from './SettingsAuthProvider'
import { import {
NETWORK_HEALTH_TEXT, NETWORK_HEALTH_TEXT,
NetworkHealthIndicator, NetworkHealthIndicator,
@ -8,7 +9,11 @@ import { NetworkHealthState } from 'hooks/useNetworkStatus'
function TestWrap({ children }: { children: React.ReactNode }) { function TestWrap({ children }: { children: React.ReactNode }) {
// wrap in router and xState context // wrap in router and xState context
return <BrowserRouter>{children}</BrowserRouter> return (
<BrowserRouter>
<SettingsAuthProviderJest>{children}</SettingsAuthProviderJest>
</BrowserRouter>
)
} }
// Our Playwright tests for this are much more comprehensive. // Our Playwright tests for this are much more comprehensive.

View File

@ -1,6 +1,7 @@
import { render, screen } from '@testing-library/react' import { render, screen } from '@testing-library/react'
import { BrowserRouter } from 'react-router-dom' import { BrowserRouter } from 'react-router-dom'
import ProjectSidebarMenu from './ProjectSidebarMenu' import ProjectSidebarMenu from './ProjectSidebarMenu'
import { SettingsAuthProviderJest } from './SettingsAuthProvider'
import { Project } from 'lib/project' import { Project } from 'lib/project'
const now = new Date() const now = new Date()
@ -31,7 +32,9 @@ describe('ProjectSidebarMenu tests', () => {
test('Disables popover menu by default', () => { test('Disables popover menu by default', () => {
render( render(
<BrowserRouter> <BrowserRouter>
<ProjectSidebarMenu project={projectWellFormed} /> <SettingsAuthProviderJest>
<ProjectSidebarMenu project={projectWellFormed} />
</SettingsAuthProviderJest>
</BrowserRouter> </BrowserRouter>
) )

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