Compare commits
1 Commits
pierremtb/
...
achalmers/
Author | SHA1 | Date | |
---|---|---|---|
078ffa02b0 |
32
.github/workflows/build-apps.yml
vendored
@ -362,17 +362,6 @@ jobs:
|
|||||||
- name: List artifacts
|
- name: List artifacts
|
||||||
run: "ls -R out"
|
run: "ls -R out"
|
||||||
|
|
||||||
- name: Set more complete nightly release notes
|
|
||||||
if: ${{ env.IS_NIGHTLY == 'true' }}
|
|
||||||
run: |
|
|
||||||
# Note: preferred going this way instead of a full clone in the checkout step,
|
|
||||||
# see https://github.com/actions/checkout/issues/1471
|
|
||||||
git fetch --prune --unshallow --tags
|
|
||||||
export TAG="nightly-${VERSION}"
|
|
||||||
export PREVIOUS_TAG=$(git describe --tags --match="nightly-v[0-9]*" --abbrev=0)
|
|
||||||
export NOTES=$(./scripts/get-nightly-changelog.sh)
|
|
||||||
yarn files:set-notes
|
|
||||||
|
|
||||||
- name: Authenticate to Google Cloud
|
- name: Authenticate to Google Cloud
|
||||||
if: ${{ env.IS_NIGHTLY == 'true' }}
|
if: ${{ env.IS_NIGHTLY == 'true' }}
|
||||||
uses: 'google-github-actions/auth@v2.1.7'
|
uses: 'google-github-actions/auth@v2.1.7'
|
||||||
@ -394,17 +383,12 @@ jobs:
|
|||||||
parent: false
|
parent: false
|
||||||
destination: 'dl.kittycad.io/releases/modeling-app/nightly'
|
destination: 'dl.kittycad.io/releases/modeling-app/nightly'
|
||||||
|
|
||||||
- name: Invalidate bucket cache on latest*.yml and last_download.json files
|
- name: Create draft release
|
||||||
if: ${{ env.IS_NIGHTLY == 'true' }}
|
uses: softprops/action-gh-release@v2
|
||||||
run: yarn files:invalidate-bucket:nightly
|
if: ${{ env.IS_RELEASE == 'true' }}
|
||||||
|
|
||||||
- name: Tag nightly commit
|
|
||||||
if: ${{ env.IS_NIGHTLY == 'true' }}
|
|
||||||
uses: actions/github-script@v7
|
|
||||||
with:
|
with:
|
||||||
script: |
|
name: ${{ env.VERSION }}
|
||||||
const { VERSION } = process.env
|
tag_name: ${{ env.VERSION }}
|
||||||
const { owner, repo } = context.repo
|
draft: true
|
||||||
const { sha } = context
|
generate_release_notes: true
|
||||||
const ref = `refs/tags/nightly-${VERSION}`
|
files: 'out/Zoo*'
|
||||||
github.rest.git.createRef({ owner, repo, sha, ref })
|
|
||||||
|
4
.github/workflows/e2e-tests.yml
vendored
@ -68,7 +68,7 @@ jobs:
|
|||||||
- name: Download Wasm Cache
|
- name: Download Wasm Cache
|
||||||
id: download-wasm
|
id: download-wasm
|
||||||
if: needs.check-rust-changes.outputs.rust-changed == 'false'
|
if: needs.check-rust-changes.outputs.rust-changed == 'false'
|
||||||
uses: dawidd6/action-download-artifact@v7
|
uses: dawidd6/action-download-artifact@v6
|
||||||
continue-on-error: true
|
continue-on-error: true
|
||||||
with:
|
with:
|
||||||
github_token: ${{secrets.GITHUB_TOKEN}}
|
github_token: ${{secrets.GITHUB_TOKEN}}
|
||||||
@ -255,7 +255,7 @@ jobs:
|
|||||||
- name: Download Wasm Cache
|
- name: Download Wasm Cache
|
||||||
id: download-wasm
|
id: download-wasm
|
||||||
if: needs.check-rust-changes.outputs.rust-changed == 'false'
|
if: needs.check-rust-changes.outputs.rust-changed == 'false'
|
||||||
uses: dawidd6/action-download-artifact@v7
|
uses: dawidd6/action-download-artifact@v6
|
||||||
continue-on-error: true
|
continue-on-error: true
|
||||||
with:
|
with:
|
||||||
github_token: ${{secrets.GITHUB_TOKEN}}
|
github_token: ${{secrets.GITHUB_TOKEN}}
|
||||||
|
12
.github/workflows/publish-apps-release.yml
vendored
@ -126,13 +126,11 @@ jobs:
|
|||||||
destination: 'dl.kittycad.io/releases/modeling-app'
|
destination: 'dl.kittycad.io/releases/modeling-app'
|
||||||
|
|
||||||
- name: Invalidate bucket cache on latest*.yml and last_download.json files
|
- name: Invalidate bucket cache on latest*.yml and last_download.json files
|
||||||
run: yarn files:invalidate-bucket
|
run: |
|
||||||
|
gcloud compute url-maps invalidate-cdn-cache dl-url-map --path="/releases/modeling-app/last_download.json" --async
|
||||||
- name: Upload release files to Github
|
gcloud compute url-maps invalidate-cdn-cache dl-url-map --path="/releases/modeling-app/latest-linux-arm64.yml" --async
|
||||||
if: ${{ github.event_name == 'release' }}
|
gcloud compute url-maps invalidate-cdn-cache dl-url-map --path="/releases/modeling-app/latest-mac.yml" --async
|
||||||
uses: softprops/action-gh-release@v2
|
gcloud compute url-maps invalidate-cdn-cache dl-url-map --path="/releases/modeling-app/latest.yml" --async
|
||||||
with:
|
|
||||||
files: 'out/Zoo*'
|
|
||||||
|
|
||||||
|
|
||||||
announce_release:
|
announce_release:
|
||||||
|
1
.gitignore
vendored
@ -61,7 +61,6 @@ Mac_App_Distribution.provisionprofile
|
|||||||
*.tsbuildinfo
|
*.tsbuildinfo
|
||||||
src/wasm-lib/pkg
|
src/wasm-lib/pkg
|
||||||
|
|
||||||
.eslintcache
|
|
||||||
venv
|
venv
|
||||||
.vite/
|
.vite/
|
||||||
|
|
||||||
|
43
INSTALL.md
@ -1,43 +0,0 @@
|
|||||||
# Setting Up Zoo Modeling App
|
|
||||||
|
|
||||||
Compared to other CAD software, getting Zoo Modeling App up and running is quick and straightforward across platforms. It's about 100MB to download and is quick to install.
|
|
||||||
|
|
||||||
## Windows
|
|
||||||
|
|
||||||
1. Download the [Zoo Modeling App installer](https://zoo.dev/modeling-app/download) for Windows and for your processor type.
|
|
||||||
|
|
||||||
2. Once downloaded, run the installer `Zoo Modeling App-{version}-{arch}-win.exe` which should take a few seconds.
|
|
||||||
|
|
||||||
3. The installation happens at `C:\Program Files\Zoo Modeling App`. A shortcut in the start menu is also created so you can run the app easily by clicking on it.
|
|
||||||
|
|
||||||
## macOS
|
|
||||||
|
|
||||||
1. Download the [Zoo Modeling App installer](https://zoo.dev/modeling-app/download) for macOS and for your processor type.
|
|
||||||
|
|
||||||
2. Once downloaded, open the disk image `Zoo Modeling App-{version}-{arch}-mac.dmg` and drag the applications to your `Applications` directory.
|
|
||||||
|
|
||||||
3. You can then open your `Applications` directory and double-click on `Zoo Modeling App` to open.
|
|
||||||
|
|
||||||
|
|
||||||
## Linux
|
|
||||||
|
|
||||||
1. Download the [Zoo Modeling App installer](https://zoo.dev/modeling-app/download) for Linux and for your processor type.
|
|
||||||
|
|
||||||
2. Install the dependencies needed to run the [AppImage format](https://appimage.org/).
|
|
||||||
- On Ubuntu, install the FUSE library with these commands in a terminal.
|
|
||||||
```bash
|
|
||||||
sudo apt update
|
|
||||||
sudo apt install libfuse2
|
|
||||||
```
|
|
||||||
- Optionally, follow [these steps](https://github.com/probonopd/go-appimage/blob/master/src/appimaged/README.md#initial-setup) to install `appimaged`. It is a daemon that makes interacting with AppImage files more seamless.
|
|
||||||
- Once installed, copy the downloaded `Zoo Modeling App-{version}-{arch}-linux.AppImage` to the directory of your choice, for instance `~/Applications`.
|
|
||||||
|
|
||||||
- `appimaged` should automatically find it and make it executable. If not, run:
|
|
||||||
```bash
|
|
||||||
chmod a+x ~/Applications/Zoo\ Modeling\ App-{version}-{arch}-linux.AppImage
|
|
||||||
```
|
|
||||||
|
|
||||||
3. You can double-click on the AppImage to run it, or in a terminal with this command:
|
|
||||||
```bash
|
|
||||||
~/Applications/Zoo\ Modeling\ App-{version}-{arch}-linux.AppImage
|
|
||||||
```
|
|
22
README.md
@ -99,7 +99,7 @@ yarn tron:start
|
|||||||
|
|
||||||
This will start the application and hot-reload on changes.
|
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/Ctrl-Shift-I.
|
||||||
|
|
||||||
To build, run `yarn tron:package`.
|
To build, run `yarn tron:package`.
|
||||||
|
|
||||||
@ -136,7 +136,7 @@ https://github.com/KittyCAD/modeling-app/issues/new
|
|||||||
|
|
||||||
#### 2. Push a new tag
|
#### 2. Push a new tag
|
||||||
|
|
||||||
Create a new tag and push it to the repo. The `semantic-release.sh` script will automatically bump the minor part, which we use the most. For instance going from `v0.27.0` to `v0.28.0`.
|
Create a new tag and push it to the repo (eg. `v0.28.0` for `$VERSION`)
|
||||||
|
|
||||||
```
|
```
|
||||||
VERSION=$(./scripts/semantic-release.sh)
|
VERSION=$(./scripts/semantic-release.sh)
|
||||||
@ -146,14 +146,16 @@ git push origin --tags
|
|||||||
|
|
||||||
This will trigger the `build-apps` workflow, set the version, build & sign the apps, and generate release files as well as updater-test artifacts.
|
This will trigger the `build-apps` workflow, set the version, build & sign the apps, and generate release files as well as updater-test artifacts.
|
||||||
|
|
||||||
The workflow should be listed right away [in this list](https://github.com/KittyCAD/modeling-app/actions/workflows/build-apps.yml?query=event%3Apush)).
|
Once the workflow succeeds, a draft release will be created at https://github.com/KittyCAD/modeling-app/releases.
|
||||||
|
|
||||||
#### 3. Manually test artifacts
|
#### 3. Manually test artifacts from the Cut Release PR
|
||||||
|
|
||||||
##### Release builds
|
##### Release builds
|
||||||
|
|
||||||
The release builds can be found under the `out-{arch}-{platform}` zip files, at the very bottom of the `build-apps` summary page for the workflow (triggered by the tag in 2.).
|
The release builds can be found under the `out-{arch}-{platform}` zip files, at the very bottom of the `build-apps` summary page for the workflow (triggered by the tag in 2.).
|
||||||
|
|
||||||
|
Alternatively, the draft release will also include these builds.
|
||||||
|
|
||||||
Manually test against this [list](https://github.com/KittyCAD/modeling-app/issues/3588) across Windows, MacOS, Linux and posting results as comments in the issue.
|
Manually test against this [list](https://github.com/KittyCAD/modeling-app/issues/3588) across Windows, MacOS, Linux and posting results as comments in the issue.
|
||||||
|
|
||||||
##### Updater-test builds
|
##### Updater-test builds
|
||||||
@ -176,11 +178,9 @@ If the prompt doesn't show up, start the app in command line to grab the electro
|
|||||||
|
|
||||||
#### 4. Publish the release
|
#### 4. Publish the release
|
||||||
|
|
||||||
Head over to https://github.com/KittyCAD/modeling-app/releases/new, pick the newly created tag and type it in the _Release title_ field as well.
|
Head over to https://github.com/KittyCAD/modeling-app/releases, paste in the changelog discussed in the issue, and publish the draft release created by the `build-apps` workflow from step 2.
|
||||||
|
|
||||||
Hit _Generate release notes_ as a starting point to discuss the changelog in the issue. Once done, make sure _Set as the latest release_ is checked, and hit _Publish release_.
|
A new Action kicks in at https://github.com/KittyCAD/modeling-app/actions, which can be found under `release` event filter. On success, the files will be uploaded to the public bucket and the announcement on Discord will be sent.
|
||||||
|
|
||||||
A new `publish-apps-release` will kick in and you should be able to find it [here](https://github.com/KittyCAD/modeling-app/actions?query=event%3Arelease). On success, the files will be uploaded to the public bucket as well as to the GitHub release, and the announcement on Discord will be sent.
|
|
||||||
|
|
||||||
#### 5. Close the issue
|
#### 5. Close the issue
|
||||||
|
|
||||||
@ -450,9 +450,3 @@ PS: for the debug panel, the following JSON is useful for snapping the camera
|
|||||||
## KCL
|
## KCL
|
||||||
|
|
||||||
For how to contribute to KCL, [see our KCL README](https://github.com/KittyCAD/modeling-app/tree/main/src/wasm-lib/kcl).
|
For how to contribute to KCL, [see our KCL README](https://github.com/KittyCAD/modeling-app/tree/main/src/wasm-lib/kcl).
|
||||||
|
|
||||||
### Logging
|
|
||||||
|
|
||||||
To display logging (to the terminal or console) set `ZOO_LOG=1`. This will log some warnings and simple performance metrics. To view these in test runs, use `-- --nocapture`.
|
|
||||||
|
|
||||||
To enable memory metrics, build with `--features dhat-heap`.
|
|
||||||
|
Before Width: | Height: | Size: 119 KiB After Width: | Height: | Size: 176 KiB |
Before Width: | Height: | Size: 259 KiB After Width: | Height: | Size: 157 KiB |
BIN
assets/icon.ico
Before Width: | Height: | Size: 114 KiB After Width: | Height: | Size: 183 KiB |
BIN
assets/icon.png
Before Width: | Height: | Size: 113 KiB After Width: | Height: | Size: 113 KiB |
@ -58,7 +58,7 @@ mountingPlate = extrude(thickness, mountingPlateSketch)
|
|||||||
|
|
||||||
```js
|
```js
|
||||||
// Sketch on the face of a chamfer.
|
// Sketch on the face of a chamfer.
|
||||||
fn cube(pos, scale) {
|
fn cube = (pos, scale) => {
|
||||||
sg = startSketchOn('XY')
|
sg = startSketchOn('XY')
|
||||||
|> startProfileAt(pos, %)
|
|> startProfileAt(pos, %)
|
||||||
|> line([0, scale], %)
|
|> line([0, scale], %)
|
||||||
|
@ -37,7 +37,7 @@ assertEqual(n, 3, 0.0001, "5/2 = 2.5, rounded up makes 3")
|
|||||||
startSketchOn('XZ')
|
startSketchOn('XZ')
|
||||||
|> circle({ center = [0, 0], radius = 2 }, %)
|
|> circle({ center = [0, 0], radius = 2 }, %)
|
||||||
|> extrude(5, %)
|
|> extrude(5, %)
|
||||||
|> patternTransform(n, fn(id) {
|
|> patternTransform(n, (id) => {
|
||||||
return { translate = [4 * id, 0, 0] }
|
return { translate = [4 * id, 0, 0] }
|
||||||
}, %)
|
}, %)
|
||||||
```
|
```
|
||||||
|
@ -29,7 +29,7 @@ map(array: [KclValue], map_fn: FunctionParam) -> [KclValue]
|
|||||||
|
|
||||||
```js
|
```js
|
||||||
r = 10 // radius
|
r = 10 // radius
|
||||||
fn drawCircle(id) {
|
fn drawCircle = (id) => {
|
||||||
return startSketchOn("XY")
|
return startSketchOn("XY")
|
||||||
|> circle({ center = [id * 2 * r, 0], radius = r }, %)
|
|> circle({ center = [id * 2 * r, 0], radius = r }, %)
|
||||||
}
|
}
|
||||||
@ -45,7 +45,7 @@ circles = map([1..3], drawCircle)
|
|||||||
```js
|
```js
|
||||||
r = 10 // radius
|
r = 10 // radius
|
||||||
// Call `map`, using an anonymous function instead of a named one.
|
// Call `map`, using an anonymous function instead of a named one.
|
||||||
circles = map([1..3], fn(id) {
|
circles = map([1..3], (id) => {
|
||||||
return startSketchOn("XY")
|
return startSketchOn("XY")
|
||||||
|> circle({ center = [id * 2 * r, 0], radius = r }, %)
|
|> circle({ center = [id * 2 * r, 0], radius = r }, %)
|
||||||
})
|
})
|
||||||
|
@ -12,7 +12,7 @@ to other modules.
|
|||||||
|
|
||||||
```
|
```
|
||||||
// util.kcl
|
// util.kcl
|
||||||
export fn increment(x) {
|
export fn increment = (x) => {
|
||||||
return x + 1
|
return x + 1
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@ -37,11 +37,11 @@ Multiple functions can be exported in a file.
|
|||||||
|
|
||||||
```
|
```
|
||||||
// util.kcl
|
// util.kcl
|
||||||
export fn increment(x) {
|
export fn increment = (x) => {
|
||||||
return x + 1
|
return x + 1
|
||||||
}
|
}
|
||||||
|
|
||||||
export fn decrement(x) {
|
export fn decrement = (x) => {
|
||||||
return x - 1
|
return x - 1
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
@ -30,7 +30,7 @@ patternTransform2d(total_instances: u32, transform_function: FunctionParam, soli
|
|||||||
|
|
||||||
```js
|
```js
|
||||||
// Each instance will be shifted along the X axis.
|
// Each instance will be shifted along the X axis.
|
||||||
fn transform(id) {
|
fn transform = (id) => {
|
||||||
return { translate = [4 * id, 0] }
|
return { translate = [4 * id, 0] }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -30,14 +30,14 @@ reduce(array: [KclValue], start: KclValue, reduce_fn: FunctionParam) -> KclValue
|
|||||||
|
|
||||||
```js
|
```js
|
||||||
// This function adds two numbers.
|
// This function adds two numbers.
|
||||||
fn add(a, b) {
|
fn add = (a, b) => {
|
||||||
return a + b
|
return a + b
|
||||||
}
|
}
|
||||||
|
|
||||||
// This function adds an array of numbers.
|
// This function adds an array of numbers.
|
||||||
// It uses the `reduce` function, to call the `add` function on every
|
// It uses the `reduce` function, to call the `add` function on every
|
||||||
// element of the `arr` parameter. The starting value is 0.
|
// element of the `arr` parameter. The starting value is 0.
|
||||||
fn sum(arr) {
|
fn sum = (arr) => {
|
||||||
return reduce(arr, 0, add)
|
return reduce(arr, 0, add)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -61,7 +61,7 @@ assertEqual(sum([1, 2, 3]), 6, 0.00001, "1 + 2 + 3 summed is 6")
|
|||||||
// an anonymous `add` function as its parameter, instead of declaring a
|
// an anonymous `add` function as its parameter, instead of declaring a
|
||||||
// named function outside.
|
// named function outside.
|
||||||
arr = [1, 2, 3]
|
arr = [1, 2, 3]
|
||||||
sum = reduce(arr, 0, fn(i, result_so_far) {
|
sum = reduce(arr, 0, (i, result_so_far) => {
|
||||||
return i + result_so_far
|
return i + result_so_far
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -74,7 +74,7 @@ assertEqual(sum, 6, 0.00001, "1 + 2 + 3 summed is 6")
|
|||||||
|
|
||||||
```js
|
```js
|
||||||
// Declare a function that sketches a decagon.
|
// Declare a function that sketches a decagon.
|
||||||
fn decagon(radius) {
|
fn decagon = (radius) => {
|
||||||
// Each side of the decagon is turned this many degrees from the previous angle.
|
// Each side of the decagon is turned this many degrees from the previous angle.
|
||||||
stepAngle = 1 / 10 * tau()
|
stepAngle = 1 / 10 * tau()
|
||||||
|
|
||||||
@ -84,7 +84,7 @@ fn decagon(radius) {
|
|||||||
// Use a `reduce` to draw the remaining decagon sides.
|
// Use a `reduce` to draw the remaining decagon sides.
|
||||||
// For each number in the array 1..10, run the given function,
|
// For each number in the array 1..10, run the given function,
|
||||||
// which takes a partially-sketched decagon and adds one more edge to it.
|
// which takes a partially-sketched decagon and adds one more edge to it.
|
||||||
fullDecagon = reduce([1..10], startOfDecagonSketch, fn(i, partialDecagon) {
|
fullDecagon = reduce([1..10], startOfDecagonSketch, (i, partialDecagon) => {
|
||||||
// Draw one edge of the decagon.
|
// Draw one edge of the decagon.
|
||||||
x = cos(stepAngle * i) * radius
|
x = cos(stepAngle * i) * radius
|
||||||
y = sin(stepAngle * i) * radius
|
y = sin(stepAngle * i) * radius
|
||||||
|
@ -36,7 +36,7 @@ cube = startSketchAt([0, 0])
|
|||||||
|> close(%)
|
|> close(%)
|
||||||
|> extrude(5, %)
|
|> extrude(5, %)
|
||||||
|
|
||||||
fn cylinder(radius, tag) {
|
fn cylinder = (radius, tag) => {
|
||||||
return startSketchAt([0, 0])
|
return startSketchAt([0, 0])
|
||||||
|> circle({
|
|> circle({
|
||||||
radius = radius,
|
radius = radius,
|
||||||
|
@ -36,7 +36,7 @@ cube = startSketchAt([0, 0])
|
|||||||
|> close(%)
|
|> close(%)
|
||||||
|> extrude(5, %)
|
|> extrude(5, %)
|
||||||
|
|
||||||
fn cylinder(radius, tag) {
|
fn cylinder = (radius, tag) => {
|
||||||
return startSketchAt([0, 0])
|
return startSketchAt([0, 0])
|
||||||
|> circle({
|
|> circle({
|
||||||
radius = radius,
|
radius = radius,
|
||||||
|
21018
docs/kcl/std.json
@ -41,7 +41,7 @@ If you want to get a value from an array you can use the index like so:
|
|||||||
An object is defined with `{}` braces. Here is an example object:
|
An object is defined with `{}` braces. Here is an example object:
|
||||||
|
|
||||||
```
|
```
|
||||||
myObj = { a = 0, b = "thing" }
|
myObj = {a: 0, b: "thing"}
|
||||||
```
|
```
|
||||||
|
|
||||||
We support two different ways of getting properties from objects, you can call
|
We support two different ways of getting properties from objects, you can call
|
||||||
@ -54,7 +54,7 @@ We also have support for defining your own functions. Functions can take in any
|
|||||||
type of argument. Below is an example of the syntax:
|
type of argument. Below is an example of the syntax:
|
||||||
|
|
||||||
```
|
```
|
||||||
fn myFn(x) {
|
fn myFn = (x) => {
|
||||||
return x
|
return x
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@ -90,12 +90,12 @@ startSketchOn('XZ')
|
|||||||
|> startProfileAt(origin, %)
|
|> startProfileAt(origin, %)
|
||||||
|> angledLine([0, 191.26], %, $rectangleSegmentA001)
|
|> angledLine([0, 191.26], %, $rectangleSegmentA001)
|
||||||
|> angledLine([
|
|> angledLine([
|
||||||
segAng(rectangleSegmentA001) - 90,
|
segAng(rectangleSegmentA001, %) - 90,
|
||||||
196.99
|
196.99
|
||||||
], %, $rectangleSegmentB001)
|
], %, $rectangleSegmentB001)
|
||||||
|> angledLine([
|
|> angledLine([
|
||||||
segAng(rectangleSegmentA001),
|
segAng(rectangleSegmentA001, %),
|
||||||
-segLen(rectangleSegmentA001)
|
-segLen(rectangleSegmentA001, %)
|
||||||
], %, $rectangleSegmentC001)
|
], %, $rectangleSegmentC001)
|
||||||
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||||
|> close(%)
|
|> close(%)
|
||||||
@ -118,20 +118,20 @@ use the tag `rectangleSegmentA001` in any function or expression in the file.
|
|||||||
However if the code was written like this:
|
However if the code was written like this:
|
||||||
|
|
||||||
```
|
```
|
||||||
fn rect(origin) {
|
fn rect = (origin) => {
|
||||||
return startSketchOn('XZ')
|
return startSketchOn('XZ')
|
||||||
|> startProfileAt(origin, %)
|
|> startProfileAt(origin, %)
|
||||||
|> angledLine([0, 191.26], %, $rectangleSegmentA001)
|
|> angledLine([0, 191.26], %, $rectangleSegmentA001)
|
||||||
|> angledLine([
|
|> angledLine([
|
||||||
segAng(rectangleSegmentA001) - 90,
|
segAng(rectangleSegmentA001, %) - 90,
|
||||||
196.99
|
196.99
|
||||||
], %, $rectangleSegmentB001)
|
], %, $rectangleSegmentB001)
|
||||||
|> angledLine([
|
|> angledLine([
|
||||||
segAng(rectangleSegmentA001),
|
segAng(rectangleSegmentA001, %),
|
||||||
-segLen(rectangleSegmentA001)
|
-segLen(rectangleSegmentA001, %)
|
||||||
], %, $rectangleSegmentC001)
|
], %, $rectangleSegmentC001)
|
||||||
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||||
|> close(%)
|
|> close(%)
|
||||||
}
|
}
|
||||||
|
|
||||||
rect([0, 0])
|
rect([0, 0])
|
||||||
@ -146,31 +146,28 @@ Tags are accessible through the sketch group they are declared in.
|
|||||||
For example the following code works.
|
For example the following code works.
|
||||||
|
|
||||||
```
|
```
|
||||||
fn rect(origin) {
|
fn rect = (origin) => {
|
||||||
return startSketchOn('XZ')
|
return startSketchOn('XZ')
|
||||||
|> startProfileAt(origin, %)
|
|> startProfileAt(origin, %)
|
||||||
|> angledLine([0, 191.26], %, $rectangleSegmentA001)
|
|> angledLine([0, 191.26], %, $rectangleSegmentA001)
|
||||||
|> angledLine([
|
|> angledLine([
|
||||||
segAng(rectangleSegmentA001) - 90,
|
segAng(rectangleSegmentA001, %) - 90,
|
||||||
196.99
|
196.99
|
||||||
], %, $rectangleSegmentB001)
|
], %, $rectangleSegmentB001)
|
||||||
|> angledLine([
|
|> angledLine([
|
||||||
segAng(rectangleSegmentA001),
|
segAng(rectangleSegmentA001, %),
|
||||||
-segLen(rectangleSegmentA001)
|
-segLen(rectangleSegmentA001, %)
|
||||||
], %, $rectangleSegmentC001)
|
], %, $rectangleSegmentC001)
|
||||||
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||||
|> close(%)
|
|> close(%)
|
||||||
}
|
}
|
||||||
|
|
||||||
rect([0, 0])
|
rect([0, 0])
|
||||||
myRect = rect([20, 0])
|
myRect = rect([20, 0])
|
||||||
|
|
||||||
myRect
|
myRect
|
||||||
|> extrude(10, %)
|
|> extrude(10, %)
|
||||||
|> fillet({
|
|> fillet({radius: 0.5, tags: [myRect.tags.rectangleSegmentA001]}, %)
|
||||||
radius = 0.5,
|
|
||||||
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
|
||||||
|
161
docs/kcl/types/BinaryOperator.md
Normal file
@ -0,0 +1,161 @@
|
|||||||
|
---
|
||||||
|
title: "BinaryOperator"
|
||||||
|
excerpt: ""
|
||||||
|
layout: manual
|
||||||
|
---
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
**This schema accepts exactly one of the following:**
|
||||||
|
|
||||||
|
Add two numbers.
|
||||||
|
|
||||||
|
**enum:** `+`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
Subtract two numbers.
|
||||||
|
|
||||||
|
**enum:** `-`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
Multiply two numbers.
|
||||||
|
|
||||||
|
**enum:** `*`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
Divide two numbers.
|
||||||
|
|
||||||
|
**enum:** `/`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
Modulo two numbers.
|
||||||
|
|
||||||
|
**enum:** `%`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
Raise a number to a power.
|
||||||
|
|
||||||
|
**enum:** `^`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
Are two numbers equal?
|
||||||
|
|
||||||
|
**enum:** `==`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
Are two numbers not equal?
|
||||||
|
|
||||||
|
**enum:** `!=`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
Is left greater than right
|
||||||
|
|
||||||
|
**enum:** `>`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
Is left greater than or equal to right
|
||||||
|
|
||||||
|
**enum:** `>=`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
Is left less than right
|
||||||
|
|
||||||
|
**enum:** `<`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
Is left less than or equal to right
|
||||||
|
|
||||||
|
**enum:** `<=`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
161
docs/kcl/types/BinaryPart.md
Normal file
@ -0,0 +1,161 @@
|
|||||||
|
---
|
||||||
|
title: "BinaryPart"
|
||||||
|
excerpt: ""
|
||||||
|
layout: manual
|
||||||
|
---
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
**This schema accepts exactly one of the following:**
|
||||||
|
|
||||||
|
|
||||||
|
**Type:** `object`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
| Property | Type | Description | Required |
|
||||||
|
|----------|------|-------------|----------|
|
||||||
|
| `type` |enum: `Literal`| | No |
|
||||||
|
| `value` |[`LiteralValue`](/docs/kcl/types/LiteralValue)| | No |
|
||||||
|
| `raw` |`string`| | No |
|
||||||
|
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||||
|
| `start` |`integer`| | No |
|
||||||
|
| `end` |`integer`| | No |
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
**Type:** `object`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
| Property | Type | Description | Required |
|
||||||
|
|----------|------|-------------|----------|
|
||||||
|
| `type` |enum: [`Identifier`](/docs/kcl/types/Identifier)| | No |
|
||||||
|
| `name` |`string`| | No |
|
||||||
|
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||||
|
| `start` |`integer`| | No |
|
||||||
|
| `end` |`integer`| | No |
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
**Type:** `object`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
| Property | Type | Description | Required |
|
||||||
|
|----------|------|-------------|----------|
|
||||||
|
| `type` |enum: `BinaryExpression`| | No |
|
||||||
|
| `operator` |[`BinaryOperator`](/docs/kcl/types/BinaryOperator)| | No |
|
||||||
|
| `left` |[`BinaryPart`](/docs/kcl/types/BinaryPart)| | No |
|
||||||
|
| `right` |[`BinaryPart`](/docs/kcl/types/BinaryPart)| | No |
|
||||||
|
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||||
|
| `start` |`integer`| | No |
|
||||||
|
| `end` |`integer`| | No |
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
**Type:** `object`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
| Property | Type | Description | Required |
|
||||||
|
|----------|------|-------------|----------|
|
||||||
|
| `type` |enum: `CallExpression`| | No |
|
||||||
|
| `callee` |[`Identifier`](/docs/kcl/types/Identifier)| | No |
|
||||||
|
| `arguments` |`[` [`Expr`](/docs/kcl/types/Expr) `]`| | No |
|
||||||
|
| `optional` |`boolean`| | No |
|
||||||
|
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||||
|
| `start` |`integer`| | No |
|
||||||
|
| `end` |`integer`| | No |
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
**Type:** `object`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
| Property | Type | Description | Required |
|
||||||
|
|----------|------|-------------|----------|
|
||||||
|
| `type` |enum: `UnaryExpression`| | No |
|
||||||
|
| `operator` |[`UnaryOperator`](/docs/kcl/types/UnaryOperator)| | No |
|
||||||
|
| `argument` |[`BinaryPart`](/docs/kcl/types/BinaryPart)| | No |
|
||||||
|
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||||
|
| `start` |`integer`| | No |
|
||||||
|
| `end` |`integer`| | No |
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
**Type:** `object`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
| Property | Type | Description | Required |
|
||||||
|
|----------|------|-------------|----------|
|
||||||
|
| `type` |enum: `MemberExpression`| | No |
|
||||||
|
| `object` |[`MemberObject`](/docs/kcl/types/MemberObject)| | No |
|
||||||
|
| `property` |[`LiteralIdentifier`](/docs/kcl/types/LiteralIdentifier)| | No |
|
||||||
|
| `computed` |`boolean`| | No |
|
||||||
|
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||||
|
| `start` |`integer`| | No |
|
||||||
|
| `end` |`integer`| | No |
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
**Type:** `object`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
| Property | Type | Description | Required |
|
||||||
|
|----------|------|-------------|----------|
|
||||||
|
| `type` |enum: `IfExpression`| | No |
|
||||||
|
| `cond` |[`Expr`](/docs/kcl/types/Expr)| | No |
|
||||||
|
| `then_val` |[`Program`](/docs/kcl/types/Program)| | No |
|
||||||
|
| `else_ifs` |`[` [`ElseIf`](/docs/kcl/types/ElseIf) `]`| | No |
|
||||||
|
| `final_else` |[`Program`](/docs/kcl/types/Program)| | No |
|
||||||
|
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||||
|
| `start` |`integer`| | No |
|
||||||
|
| `end` |`integer`| | No |
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
97
docs/kcl/types/BodyItem.md
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
---
|
||||||
|
title: "BodyItem"
|
||||||
|
excerpt: ""
|
||||||
|
layout: manual
|
||||||
|
---
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
**This schema accepts exactly one of the following:**
|
||||||
|
|
||||||
|
|
||||||
|
**Type:** `object`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
| Property | Type | Description | Required |
|
||||||
|
|----------|------|-------------|----------|
|
||||||
|
| `type` |enum: `ImportStatement`| | No |
|
||||||
|
| `items` |`[` [`ImportItem`](/docs/kcl/types/ImportItem) `]`| | No |
|
||||||
|
| `path` |`string`| | No |
|
||||||
|
| `raw_path` |`string`| | No |
|
||||||
|
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||||
|
| `start` |`integer`| | No |
|
||||||
|
| `end` |`integer`| | No |
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
**Type:** `object`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
| Property | Type | Description | Required |
|
||||||
|
|----------|------|-------------|----------|
|
||||||
|
| `type` |enum: `ExpressionStatement`| | No |
|
||||||
|
| `expression` |[`Expr`](/docs/kcl/types/Expr)| | No |
|
||||||
|
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||||
|
| `start` |`integer`| | No |
|
||||||
|
| `end` |`integer`| | No |
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
**Type:** `object`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
| Property | Type | Description | Required |
|
||||||
|
|----------|------|-------------|----------|
|
||||||
|
| `type` |enum: `VariableDeclaration`| | No |
|
||||||
|
| `declarations` |`[` [`VariableDeclarator`](/docs/kcl/types/VariableDeclarator) `]`| | No |
|
||||||
|
| `visibility` |[`ItemVisibility`](/docs/kcl/types/ItemVisibility)| | No |
|
||||||
|
| `kind` |[`VariableKind`](/docs/kcl/types/VariableKind)| | No |
|
||||||
|
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||||
|
| `start` |`integer`| | No |
|
||||||
|
| `end` |`integer`| | No |
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
**Type:** `object`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
| Property | Type | Description | Required |
|
||||||
|
|----------|------|-------------|----------|
|
||||||
|
| `type` |enum: `ReturnStatement`| | No |
|
||||||
|
| `argument` |[`Expr`](/docs/kcl/types/Expr)| | No |
|
||||||
|
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||||
|
| `start` |`integer`| | No |
|
||||||
|
| `end` |`integer`| | No |
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
41
docs/kcl/types/CommentStyle.md
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
---
|
||||||
|
title: "CommentStyle"
|
||||||
|
excerpt: ""
|
||||||
|
layout: manual
|
||||||
|
---
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
**This schema accepts exactly one of the following:**
|
||||||
|
|
||||||
|
Like // foo
|
||||||
|
|
||||||
|
**enum:** `line`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
Like /* foo */
|
||||||
|
|
||||||
|
**enum:** `block`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
24
docs/kcl/types/ElseIf.md
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
---
|
||||||
|
title: "ElseIf"
|
||||||
|
excerpt: ""
|
||||||
|
layout: manual
|
||||||
|
---
|
||||||
|
|
||||||
|
|
||||||
|
**Type:** `object`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
| Property | Type | Description | Required |
|
||||||
|
|----------|------|-------------|----------|
|
||||||
|
| `cond` |[`Expr`](/docs/kcl/types/Expr)| | No |
|
||||||
|
| `then_val` |[`Program`](/docs/kcl/types/Program)| | No |
|
||||||
|
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||||
|
| `start` |`integer`| | No |
|
||||||
|
| `end` |`integer`| | No |
|
||||||
|
|
||||||
|
|
16
docs/kcl/types/EnvironmentRef.md
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
---
|
||||||
|
title: "EnvironmentRef"
|
||||||
|
excerpt: "An index pointing to an environment."
|
||||||
|
layout: manual
|
||||||
|
---
|
||||||
|
|
||||||
|
An index pointing to an environment.
|
||||||
|
|
||||||
|
**Type:** `integer` (`uint`)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
318
docs/kcl/types/Expr.md
Normal file
@ -0,0 +1,318 @@
|
|||||||
|
---
|
||||||
|
title: "Expr"
|
||||||
|
excerpt: "An expression can be evaluated to yield a single KCL value."
|
||||||
|
layout: manual
|
||||||
|
---
|
||||||
|
|
||||||
|
An expression can be evaluated to yield a single KCL value.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
**This schema accepts exactly one of the following:**
|
||||||
|
|
||||||
|
|
||||||
|
**Type:** `object`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
| Property | Type | Description | Required |
|
||||||
|
|----------|------|-------------|----------|
|
||||||
|
| `type` |enum: `Literal`| | No |
|
||||||
|
| `value` |[`LiteralValue`](/docs/kcl/types/LiteralValue)| An expression can be evaluated to yield a single KCL value. | No |
|
||||||
|
| `raw` |`string`| | No |
|
||||||
|
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||||
|
| `start` |`integer`| | No |
|
||||||
|
| `end` |`integer`| | No |
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
**Type:** `object`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
| Property | Type | Description | Required |
|
||||||
|
|----------|------|-------------|----------|
|
||||||
|
| `type` |enum: [`Identifier`](/docs/kcl/types/Identifier)| | No |
|
||||||
|
| `name` |`string`| | No |
|
||||||
|
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||||
|
| `start` |`integer`| | No |
|
||||||
|
| `end` |`integer`| | No |
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
**Type:** `object`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
| Property | Type | Description | Required |
|
||||||
|
|----------|------|-------------|----------|
|
||||||
|
| `type` |enum: [`TagDeclarator`](/docs/kcl/types#tag-declaration)| | No |
|
||||||
|
| `value` |`string`| | No |
|
||||||
|
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||||
|
| `start` |`integer`| | No |
|
||||||
|
| `end` |`integer`| | No |
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
**Type:** `object`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
| Property | Type | Description | Required |
|
||||||
|
|----------|------|-------------|----------|
|
||||||
|
| `type` |enum: `BinaryExpression`| | No |
|
||||||
|
| `operator` |[`BinaryOperator`](/docs/kcl/types/BinaryOperator)| An expression can be evaluated to yield a single KCL value. | No |
|
||||||
|
| `left` |[`BinaryPart`](/docs/kcl/types/BinaryPart)| An expression can be evaluated to yield a single KCL value. | No |
|
||||||
|
| `right` |[`BinaryPart`](/docs/kcl/types/BinaryPart)| An expression can be evaluated to yield a single KCL value. | No |
|
||||||
|
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||||
|
| `start` |`integer`| | No |
|
||||||
|
| `end` |`integer`| | No |
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
**Type:** `object`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
| Property | Type | Description | Required |
|
||||||
|
|----------|------|-------------|----------|
|
||||||
|
| `type` |enum: [`FunctionExpression`](/docs/kcl/types/FunctionExpression)| | No |
|
||||||
|
| `params` |`[` [`Parameter`](/docs/kcl/types/Parameter) `]`| | No |
|
||||||
|
| `body` |[`Program`](/docs/kcl/types/Program)| An expression can be evaluated to yield a single KCL value. | No |
|
||||||
|
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||||
|
| `start` |`integer`| | No |
|
||||||
|
| `end` |`integer`| | No |
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
**Type:** `object`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
| Property | Type | Description | Required |
|
||||||
|
|----------|------|-------------|----------|
|
||||||
|
| `type` |enum: `CallExpression`| | No |
|
||||||
|
| `callee` |[`Identifier`](/docs/kcl/types/Identifier)| An expression can be evaluated to yield a single KCL value. | No |
|
||||||
|
| `arguments` |`[` [`Expr`](/docs/kcl/types/Expr) `]`| | No |
|
||||||
|
| `optional` |`boolean`| | No |
|
||||||
|
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||||
|
| `start` |`integer`| | No |
|
||||||
|
| `end` |`integer`| | No |
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
**Type:** `object`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
| Property | Type | Description | Required |
|
||||||
|
|----------|------|-------------|----------|
|
||||||
|
| `type` |enum: `PipeExpression`| | No |
|
||||||
|
| `body` |`[` [`Expr`](/docs/kcl/types/Expr) `]`| | No |
|
||||||
|
| `nonCodeMeta` |[`NonCodeMeta`](/docs/kcl/types/NonCodeMeta)| An expression can be evaluated to yield a single KCL value. | No |
|
||||||
|
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||||
|
| `start` |`integer`| | No |
|
||||||
|
| `end` |`integer`| | No |
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
**Type:** `object`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
| Property | Type | Description | Required |
|
||||||
|
|----------|------|-------------|----------|
|
||||||
|
| `type` |enum: `PipeSubstitution`| | No |
|
||||||
|
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||||
|
| `start` |`integer`| | No |
|
||||||
|
| `end` |`integer`| | No |
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
**Type:** `object`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
| Property | Type | Description | Required |
|
||||||
|
|----------|------|-------------|----------|
|
||||||
|
| `type` |enum: `ArrayExpression`| | No |
|
||||||
|
| `elements` |`[` [`Expr`](/docs/kcl/types/Expr) `]`| | No |
|
||||||
|
| `nonCodeMeta` |[`NonCodeMeta`](/docs/kcl/types/NonCodeMeta)| An expression can be evaluated to yield a single KCL value. | No |
|
||||||
|
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||||
|
| `start` |`integer`| | No |
|
||||||
|
| `end` |`integer`| | No |
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
**Type:** `object`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
| Property | Type | Description | Required |
|
||||||
|
|----------|------|-------------|----------|
|
||||||
|
| `type` |enum: `ArrayRangeExpression`| | No |
|
||||||
|
| `startElement` |[`Expr`](/docs/kcl/types/Expr)| An expression can be evaluated to yield a single KCL value. | No |
|
||||||
|
| `endElement` |[`Expr`](/docs/kcl/types/Expr)| An expression can be evaluated to yield a single KCL value. | No |
|
||||||
|
| `endInclusive` |`boolean`| Is the `end_element` included in the range? | No |
|
||||||
|
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||||
|
| `start` |`integer`| | No |
|
||||||
|
| `end` |`integer`| | No |
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
**Type:** `object`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
| Property | Type | Description | Required |
|
||||||
|
|----------|------|-------------|----------|
|
||||||
|
| `type` |enum: `ObjectExpression`| | No |
|
||||||
|
| `properties` |`[` [`ObjectProperty`](/docs/kcl/types/ObjectProperty) `]`| | No |
|
||||||
|
| `nonCodeMeta` |[`NonCodeMeta`](/docs/kcl/types/NonCodeMeta)| An expression can be evaluated to yield a single KCL value. | No |
|
||||||
|
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||||
|
| `start` |`integer`| | No |
|
||||||
|
| `end` |`integer`| | No |
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
**Type:** `object`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
| Property | Type | Description | Required |
|
||||||
|
|----------|------|-------------|----------|
|
||||||
|
| `type` |enum: `MemberExpression`| | No |
|
||||||
|
| `object` |[`MemberObject`](/docs/kcl/types/MemberObject)| An expression can be evaluated to yield a single KCL value. | No |
|
||||||
|
| `property` |[`LiteralIdentifier`](/docs/kcl/types/LiteralIdentifier)| An expression can be evaluated to yield a single KCL value. | No |
|
||||||
|
| `computed` |`boolean`| | No |
|
||||||
|
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||||
|
| `start` |`integer`| | No |
|
||||||
|
| `end` |`integer`| | No |
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
**Type:** `object`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
| Property | Type | Description | Required |
|
||||||
|
|----------|------|-------------|----------|
|
||||||
|
| `type` |enum: `UnaryExpression`| | No |
|
||||||
|
| `operator` |[`UnaryOperator`](/docs/kcl/types/UnaryOperator)| An expression can be evaluated to yield a single KCL value. | No |
|
||||||
|
| `argument` |[`BinaryPart`](/docs/kcl/types/BinaryPart)| An expression can be evaluated to yield a single KCL value. | No |
|
||||||
|
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||||
|
| `start` |`integer`| | No |
|
||||||
|
| `end` |`integer`| | No |
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
**Type:** `object`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
| Property | Type | Description | Required |
|
||||||
|
|----------|------|-------------|----------|
|
||||||
|
| `type` |enum: `IfExpression`| | No |
|
||||||
|
| `cond` |[`Expr`](/docs/kcl/types/Expr)| An expression can be evaluated to yield a single KCL value. | No |
|
||||||
|
| `then_val` |[`Program`](/docs/kcl/types/Program)| An expression can be evaluated to yield a single KCL value. | No |
|
||||||
|
| `else_ifs` |`[` [`ElseIf`](/docs/kcl/types/ElseIf) `]`| | No |
|
||||||
|
| `final_else` |[`Program`](/docs/kcl/types/Program)| An expression can be evaluated to yield a single KCL value. | No |
|
||||||
|
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||||
|
| `start` |`integer`| | No |
|
||||||
|
| `end` |`integer`| | No |
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
KCL value for an optional parameter which was not given an argument. (remember, parameters are in the function declaration, arguments are in the function call/application).
|
||||||
|
|
||||||
|
**Type:** `object`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
| Property | Type | Description | Required |
|
||||||
|
|----------|------|-------------|----------|
|
||||||
|
| `type` |enum: `None`| | No |
|
||||||
|
| `start` |`integer`| | No |
|
||||||
|
| `end` |`integer`| | No |
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
24
docs/kcl/types/FunctionExpression.md
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
---
|
||||||
|
title: "FunctionExpression"
|
||||||
|
excerpt: ""
|
||||||
|
layout: manual
|
||||||
|
---
|
||||||
|
|
||||||
|
|
||||||
|
**Type:** `object`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
| Property | Type | Description | Required |
|
||||||
|
|----------|------|-------------|----------|
|
||||||
|
| `params` |`[` [`Parameter`](/docs/kcl/types/Parameter) `]`| | No |
|
||||||
|
| `body` |[`Program`](/docs/kcl/types/Program)| | No |
|
||||||
|
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||||
|
| `start` |`integer`| | No |
|
||||||
|
| `end` |`integer`| | No |
|
||||||
|
|
||||||
|
|
23
docs/kcl/types/Identifier.md
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
---
|
||||||
|
title: "Identifier"
|
||||||
|
excerpt: ""
|
||||||
|
layout: manual
|
||||||
|
---
|
||||||
|
|
||||||
|
|
||||||
|
**Type:** `object`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
| Property | Type | Description | Required |
|
||||||
|
|----------|------|-------------|----------|
|
||||||
|
| `name` |`string`| | No |
|
||||||
|
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||||
|
| `start` |`integer`| | No |
|
||||||
|
| `end` |`integer`| | No |
|
||||||
|
|
||||||
|
|
24
docs/kcl/types/ImportItem.md
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
---
|
||||||
|
title: "ImportItem"
|
||||||
|
excerpt: ""
|
||||||
|
layout: manual
|
||||||
|
---
|
||||||
|
|
||||||
|
|
||||||
|
**Type:** `object`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
| Property | Type | Description | Required |
|
||||||
|
|----------|------|-------------|----------|
|
||||||
|
| `name` |[`Identifier`](/docs/kcl/types/Identifier)| Name of the item to import. | No |
|
||||||
|
| `alias` |[`Identifier`](/docs/kcl/types/Identifier)| Rename the item using an identifier after "as". | No |
|
||||||
|
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||||
|
| `start` |`integer`| | No |
|
||||||
|
| `end` |`integer`| | No |
|
||||||
|
|
||||||
|
|
16
docs/kcl/types/ItemVisibility.md
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
---
|
||||||
|
title: "ItemVisibility"
|
||||||
|
excerpt: ""
|
||||||
|
layout: manual
|
||||||
|
---
|
||||||
|
|
||||||
|
|
||||||
|
**enum:** `default`, `export`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -317,6 +317,7 @@ Data for an imported geometry.
|
|||||||
| Property | Type | Description | Required |
|
| Property | Type | Description | Required |
|
||||||
|----------|------|-------------|----------|
|
|----------|------|-------------|----------|
|
||||||
| `type` |enum: `Function`| | No |
|
| `type` |enum: `Function`| | No |
|
||||||
|
| `expression` |[`FunctionExpression`](/docs/kcl/types/FunctionExpression)| Any KCL value. | No |
|
||||||
| `memory` |[`ProgramMemory`](/docs/kcl/types/ProgramMemory)| Any KCL value. | No |
|
| `memory` |[`ProgramMemory`](/docs/kcl/types/ProgramMemory)| Any KCL value. | No |
|
||||||
| `__meta` |`[` [`Metadata`](/docs/kcl/types/Metadata) `]`| | No |
|
| `__meta` |`[` [`Metadata`](/docs/kcl/types/Metadata) `]`| | No |
|
||||||
|
|
||||||
|
56
docs/kcl/types/LiteralIdentifier.md
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
---
|
||||||
|
title: "LiteralIdentifier"
|
||||||
|
excerpt: ""
|
||||||
|
layout: manual
|
||||||
|
---
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
**This schema accepts exactly one of the following:**
|
||||||
|
|
||||||
|
|
||||||
|
**Type:** `object`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
| Property | Type | Description | Required |
|
||||||
|
|----------|------|-------------|----------|
|
||||||
|
| `type` |enum: [`Identifier`](/docs/kcl/types/Identifier)| | No |
|
||||||
|
| `name` |`string`| | No |
|
||||||
|
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||||
|
| `start` |`integer`| | No |
|
||||||
|
| `end` |`integer`| | No |
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
**Type:** `object`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
| Property | Type | Description | Required |
|
||||||
|
|----------|------|-------------|----------|
|
||||||
|
| `type` |enum: `Literal`| | No |
|
||||||
|
| `value` |[`LiteralValue`](/docs/kcl/types/LiteralValue)| | No |
|
||||||
|
| `raw` |`string`| | No |
|
||||||
|
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||||
|
| `start` |`integer`| | No |
|
||||||
|
| `end` |`integer`| | No |
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
47
docs/kcl/types/LiteralValue.md
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
---
|
||||||
|
title: "LiteralValue"
|
||||||
|
excerpt: ""
|
||||||
|
layout: manual
|
||||||
|
---
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
**This schema accepts any of the following:**
|
||||||
|
|
||||||
|
|
||||||
|
**Type:** `number` (`double`)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
**Type:** `string`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
**Type:** `boolean`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
57
docs/kcl/types/MemberObject.md
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
---
|
||||||
|
title: "MemberObject"
|
||||||
|
excerpt: ""
|
||||||
|
layout: manual
|
||||||
|
---
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
**This schema accepts exactly one of the following:**
|
||||||
|
|
||||||
|
|
||||||
|
**Type:** `object`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
| Property | Type | Description | Required |
|
||||||
|
|----------|------|-------------|----------|
|
||||||
|
| `type` |enum: `MemberExpression`| | No |
|
||||||
|
| `object` |[`MemberObject`](/docs/kcl/types/MemberObject)| | No |
|
||||||
|
| `property` |[`LiteralIdentifier`](/docs/kcl/types/LiteralIdentifier)| | No |
|
||||||
|
| `computed` |`boolean`| | No |
|
||||||
|
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||||
|
| `start` |`integer`| | No |
|
||||||
|
| `end` |`integer`| | No |
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
**Type:** `object`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
| Property | Type | Description | Required |
|
||||||
|
|----------|------|-------------|----------|
|
||||||
|
| `type` |enum: [`Identifier`](/docs/kcl/types/Identifier)| | No |
|
||||||
|
| `name` |`string`| | No |
|
||||||
|
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||||
|
| `start` |`integer`| | No |
|
||||||
|
| `end` |`integer`| | No |
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
22
docs/kcl/types/NonCodeMeta.md
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
---
|
||||||
|
title: "NonCodeMeta"
|
||||||
|
excerpt: ""
|
||||||
|
layout: manual
|
||||||
|
---
|
||||||
|
|
||||||
|
|
||||||
|
**Type:** `object`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
| Property | Type | Description | Required |
|
||||||
|
|----------|------|-------------|----------|
|
||||||
|
| `nonCodeNodes` |`object`| | No |
|
||||||
|
| `startNodes` |`[` [`NonCodeNode`](/docs/kcl/types/NonCodeNode) `]`| | No |
|
||||||
|
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||||
|
|
||||||
|
|
23
docs/kcl/types/NonCodeNode.md
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
---
|
||||||
|
title: "NonCodeNode"
|
||||||
|
excerpt: ""
|
||||||
|
layout: manual
|
||||||
|
---
|
||||||
|
|
||||||
|
|
||||||
|
**Type:** `object`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
| Property | Type | Description | Required |
|
||||||
|
|----------|------|-------------|----------|
|
||||||
|
| `value` |[`NonCodeValue`](/docs/kcl/types/NonCodeValue)| | No |
|
||||||
|
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||||
|
| `start` |`integer`| | No |
|
||||||
|
| `end` |`integer`| | No |
|
||||||
|
|
||||||
|
|
86
docs/kcl/types/NonCodeValue.md
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
---
|
||||||
|
title: "NonCodeValue"
|
||||||
|
excerpt: ""
|
||||||
|
layout: manual
|
||||||
|
---
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
**This schema accepts exactly one of the following:**
|
||||||
|
|
||||||
|
An inline comment. Here are examples: `1 + 1 // This is an inline comment`. `1 + 1 /* Here's another */`.
|
||||||
|
|
||||||
|
**Type:** `object`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
| Property | Type | Description | Required |
|
||||||
|
|----------|------|-------------|----------|
|
||||||
|
| `type` |enum: `inlineComment`| | No |
|
||||||
|
| `value` |`string`| | No |
|
||||||
|
| `style` |[`CommentStyle`](/docs/kcl/types/CommentStyle)| | No |
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
A block comment. An example of this is the following: ```python,no_run /* This is a block comment */ 1 + 1 ``` Now this is important. The block comment is attached to the next line. This is always the case. Also the block comment doesn't have a new line above it. If it did it would be a `NewLineBlockComment`.
|
||||||
|
|
||||||
|
**Type:** `object`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
| Property | Type | Description | Required |
|
||||||
|
|----------|------|-------------|----------|
|
||||||
|
| `type` |enum: `blockComment`| | No |
|
||||||
|
| `value` |`string`| | No |
|
||||||
|
| `style` |[`CommentStyle`](/docs/kcl/types/CommentStyle)| | No |
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
A block comment that has a new line above it. The user explicitly added a new line above the block comment.
|
||||||
|
|
||||||
|
**Type:** `object`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
| Property | Type | Description | Required |
|
||||||
|
|----------|------|-------------|----------|
|
||||||
|
| `type` |enum: `newLineBlockComment`| | No |
|
||||||
|
| `value` |`string`| | No |
|
||||||
|
| `style` |[`CommentStyle`](/docs/kcl/types/CommentStyle)| | No |
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
**Type:** `object`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
| Property | Type | Description | Required |
|
||||||
|
|----------|------|-------------|----------|
|
||||||
|
| `type` |enum: `newLine`| | No |
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
24
docs/kcl/types/ObjectProperty.md
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
---
|
||||||
|
title: "ObjectProperty"
|
||||||
|
excerpt: ""
|
||||||
|
layout: manual
|
||||||
|
---
|
||||||
|
|
||||||
|
|
||||||
|
**Type:** `object`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
| Property | Type | Description | Required |
|
||||||
|
|----------|------|-------------|----------|
|
||||||
|
| `key` |[`Identifier`](/docs/kcl/types/Identifier)| | No |
|
||||||
|
| `value` |[`Expr`](/docs/kcl/types/Expr)| | No |
|
||||||
|
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||||
|
| `start` |`integer`| | No |
|
||||||
|
| `end` |`integer`| | No |
|
||||||
|
|
||||||
|
|
23
docs/kcl/types/Parameter.md
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
---
|
||||||
|
title: "Parameter"
|
||||||
|
excerpt: "Parameter of a KCL function."
|
||||||
|
layout: manual
|
||||||
|
---
|
||||||
|
|
||||||
|
Parameter of a KCL function.
|
||||||
|
|
||||||
|
**Type:** `object`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
| Property | Type | Description | Required |
|
||||||
|
|----------|------|-------------|----------|
|
||||||
|
| `identifier` |[`Identifier`](/docs/kcl/types/Identifier)| The parameter's label or name. | No |
|
||||||
|
| `optional` |`boolean`| Is the parameter optional? | No |
|
||||||
|
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||||
|
|
||||||
|
|
26
docs/kcl/types/Program.md
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
---
|
||||||
|
title: "Program"
|
||||||
|
excerpt: "A KCL program top level, or function body."
|
||||||
|
layout: manual
|
||||||
|
---
|
||||||
|
|
||||||
|
A KCL program top level, or function body.
|
||||||
|
|
||||||
|
**Type:** `object`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
| Property | Type | Description | Required |
|
||||||
|
|----------|------|-------------|----------|
|
||||||
|
| `body` |`[` [`BodyItem`](/docs/kcl/types/BodyItem) `]`| | No |
|
||||||
|
| `nonCodeMeta` |[`NonCodeMeta`](/docs/kcl/types/NonCodeMeta)| A KCL program top level, or function body. | No |
|
||||||
|
| `shebang` |[`Shebang`](/docs/kcl/types/Shebang)| | No |
|
||||||
|
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||||
|
| `start` |`integer`| | No |
|
||||||
|
| `end` |`integer`| | No |
|
||||||
|
|
||||||
|
|
23
docs/kcl/types/Shebang.md
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
---
|
||||||
|
title: "Shebang"
|
||||||
|
excerpt: "A shebang. This is a special type of comment that is at the top of the file. It looks like this: ```python,no_run #!/usr/bin/env python ```"
|
||||||
|
layout: manual
|
||||||
|
---
|
||||||
|
|
||||||
|
A shebang. This is a special type of comment that is at the top of the file. It looks like this: ```python,no_run #!/usr/bin/env python ```
|
||||||
|
|
||||||
|
**Type:** `object`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
| Property | Type | Description | Required |
|
||||||
|
|----------|------|-------------|----------|
|
||||||
|
| `content` |`string`| | No |
|
||||||
|
| `start` |`integer`| | No |
|
||||||
|
| `end` |`integer`| | No |
|
||||||
|
|
||||||
|
|
15
docs/kcl/types/Uint.md
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
---
|
||||||
|
title: "Uint"
|
||||||
|
excerpt: ""
|
||||||
|
layout: manual
|
||||||
|
---
|
||||||
|
|
||||||
|
|
||||||
|
**Type:** `integer` (`uint32`)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
41
docs/kcl/types/UnaryOperator.md
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
---
|
||||||
|
title: "UnaryOperator"
|
||||||
|
excerpt: ""
|
||||||
|
layout: manual
|
||||||
|
---
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
**This schema accepts exactly one of the following:**
|
||||||
|
|
||||||
|
Negate a number.
|
||||||
|
|
||||||
|
**enum:** `-`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
Negate a boolean.
|
||||||
|
|
||||||
|
**enum:** `!`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
24
docs/kcl/types/VariableDeclarator.md
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
---
|
||||||
|
title: "VariableDeclarator"
|
||||||
|
excerpt: ""
|
||||||
|
layout: manual
|
||||||
|
---
|
||||||
|
|
||||||
|
|
||||||
|
**Type:** `object`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
| Property | Type | Description | Required |
|
||||||
|
|----------|------|-------------|----------|
|
||||||
|
| `id` |[`Identifier`](/docs/kcl/types/Identifier)| The identifier of the variable. | No |
|
||||||
|
| `init` |[`Expr`](/docs/kcl/types/Expr)| The value of the variable. | No |
|
||||||
|
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||||
|
| `start` |`integer`| | No |
|
||||||
|
| `end` |`integer`| | No |
|
||||||
|
|
||||||
|
|
41
docs/kcl/types/VariableKind.md
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
---
|
||||||
|
title: "VariableKind"
|
||||||
|
excerpt: ""
|
||||||
|
layout: manual
|
||||||
|
---
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
**This schema accepts exactly one of the following:**
|
||||||
|
|
||||||
|
Declare a named constant.
|
||||||
|
|
||||||
|
**enum:** `const`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
Declare a function.
|
||||||
|
|
||||||
|
**enum:** `fn`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -458,8 +458,8 @@ test.describe('Editor tests', () => {
|
|||||||
|
|
||||||
/* add the following code to the editor ($ error is not a valid line)
|
/* add the following code to the editor ($ error is not a valid line)
|
||||||
$ error
|
$ error
|
||||||
topAng = 30
|
const topAng = 30
|
||||||
bottomAng = 25
|
const bottomAng = 25
|
||||||
*/
|
*/
|
||||||
await u.codeLocator.click()
|
await u.codeLocator.click()
|
||||||
await page.keyboard.type('$ error')
|
await page.keyboard.type('$ error')
|
||||||
@ -474,14 +474,12 @@ test.describe('Editor tests', () => {
|
|||||||
await page.keyboard.type('bottomAng = 25')
|
await page.keyboard.type('bottomAng = 25')
|
||||||
await page.keyboard.press('Enter')
|
await page.keyboard.press('Enter')
|
||||||
|
|
||||||
// error in gutter
|
// error in guter
|
||||||
await expect(page.locator('.cm-lint-marker-error')).toBeVisible()
|
await expect(page.locator('.cm-lint-marker-error')).toBeVisible()
|
||||||
|
|
||||||
// error text on hover
|
// error text on hover
|
||||||
await page.hover('.cm-lint-marker-error')
|
await page.hover('.cm-lint-marker-error')
|
||||||
await expect(
|
await expect(page.getByText('Unexpected token: $').first()).toBeVisible()
|
||||||
page.getByText('Tag names must not be empty').first()
|
|
||||||
).toBeVisible()
|
|
||||||
|
|
||||||
// select the line that's causing the error and delete it
|
// select the line that's causing the error and delete it
|
||||||
await page.getByText('$ error').click()
|
await page.getByText('$ error').click()
|
||||||
@ -520,10 +518,7 @@ test.describe('Editor tests', () => {
|
|||||||
await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible()
|
await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible()
|
||||||
})
|
})
|
||||||
|
|
||||||
// TODO currently multiple source ranges are not supported
|
test('error with 2 source ranges gets 2 diagnostics', async ({ page }) => {
|
||||||
test.skip('error with 2 source ranges gets 2 diagnostics', async ({
|
|
||||||
page,
|
|
||||||
}) => {
|
|
||||||
const u = await getUtils(page)
|
const u = await getUtils(page)
|
||||||
await page.addInitScript(async () => {
|
await page.addInitScript(async () => {
|
||||||
localStorage.setItem(
|
localStorage.setItem(
|
||||||
|
@ -45,6 +45,7 @@ test.describe('integrations tests', () => {
|
|||||||
{
|
{
|
||||||
title: 'test-sample',
|
title: 'test-sample',
|
||||||
fileCount: 1,
|
fileCount: 1,
|
||||||
|
folderCount: 1,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
sortBy: 'last-modified-desc',
|
sortBy: 'last-modified-desc',
|
||||||
@ -232,6 +233,7 @@ test.describe('when using the file tree to', () => {
|
|||||||
{
|
{
|
||||||
title: projectName,
|
title: projectName,
|
||||||
fileCount: 2,
|
fileCount: 2,
|
||||||
|
folderCount: 2, // TODO: This is a pre-existing bug, there are no folders within the project
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
sortBy: 'last-modified-desc',
|
sortBy: 'last-modified-desc',
|
||||||
|
@ -4,6 +4,7 @@ import { expect } from '@playwright/test'
|
|||||||
interface ProjectCardState {
|
interface ProjectCardState {
|
||||||
title: string
|
title: string
|
||||||
fileCount: number
|
fileCount: number
|
||||||
|
folderCount: number
|
||||||
}
|
}
|
||||||
|
|
||||||
interface HomePageState {
|
interface HomePageState {
|
||||||
@ -60,13 +61,15 @@ export class HomePageFixture {
|
|||||||
const projectCards = await this.projectCard.all()
|
const projectCards = await this.projectCard.all()
|
||||||
const projectCardStates: Array<ProjectCardState> = []
|
const projectCardStates: Array<ProjectCardState> = []
|
||||||
for (const projectCard of projectCards) {
|
for (const projectCard of projectCards) {
|
||||||
const [title, fileCount] = await Promise.all([
|
const [title, fileCount, folderCount] = await Promise.all([
|
||||||
(await projectCard.locator(this.projectCardTitle).textContent()) || '',
|
(await projectCard.locator(this.projectCardTitle).textContent()) || '',
|
||||||
Number(await projectCard.locator(this.projectCardFile).textContent()),
|
Number(await projectCard.locator(this.projectCardFile).textContent()),
|
||||||
|
Number(await projectCard.locator(this.projectCardFolder).textContent()),
|
||||||
])
|
])
|
||||||
projectCardStates.push({
|
projectCardStates.push({
|
||||||
title: title,
|
title: title,
|
||||||
fileCount,
|
fileCount,
|
||||||
|
folderCount,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
return projectCardStates
|
return projectCardStates
|
||||||
|
@ -28,7 +28,6 @@ type SceneSerialised = {
|
|||||||
|
|
||||||
type ClickHandler = (clickParams?: mouseParams) => Promise<void | boolean>
|
type ClickHandler = (clickParams?: mouseParams) => Promise<void | boolean>
|
||||||
type MoveHandler = (moveParams?: mouseParams) => Promise<void | boolean>
|
type MoveHandler = (moveParams?: mouseParams) => Promise<void | boolean>
|
||||||
type DblClickHandler = (clickParams?: mouseParams) => Promise<void | boolean>
|
|
||||||
type DragToHandler = (dragParams: mouseDragToParams) => Promise<void | boolean>
|
type DragToHandler = (dragParams: mouseDragToParams) => Promise<void | boolean>
|
||||||
type DragFromHandler = (
|
type DragFromHandler = (
|
||||||
dragParams: mouseDragFromParams
|
dragParams: mouseDragFromParams
|
||||||
@ -69,7 +68,7 @@ export class SceneFixture {
|
|||||||
x: number,
|
x: number,
|
||||||
y: number,
|
y: number,
|
||||||
{ steps }: { steps: number } = { steps: 20 }
|
{ steps }: { steps: number } = { steps: 20 }
|
||||||
): [ClickHandler, MoveHandler, DblClickHandler] =>
|
): [ClickHandler, MoveHandler] =>
|
||||||
[
|
[
|
||||||
(clickParams?: mouseParams) => {
|
(clickParams?: mouseParams) => {
|
||||||
if (clickParams?.pixelDiff) {
|
if (clickParams?.pixelDiff) {
|
||||||
@ -91,16 +90,6 @@ export class SceneFixture {
|
|||||||
}
|
}
|
||||||
return this.page.mouse.move(x, y, { steps })
|
return this.page.mouse.move(x, y, { steps })
|
||||||
},
|
},
|
||||||
(clickParams?: mouseParams) => {
|
|
||||||
if (clickParams?.pixelDiff) {
|
|
||||||
return doAndWaitForImageDiff(
|
|
||||||
this.page,
|
|
||||||
() => this.page.mouse.dblclick(x, y),
|
|
||||||
clickParams.pixelDiff
|
|
||||||
)
|
|
||||||
}
|
|
||||||
return this.page.mouse.dblclick(x, y)
|
|
||||||
},
|
|
||||||
] as const
|
] as const
|
||||||
makeDragHelpers = (
|
makeDragHelpers = (
|
||||||
x: number,
|
x: number,
|
||||||
|
@ -6,8 +6,6 @@ export class ToolbarFixture {
|
|||||||
public page: Page
|
public page: Page
|
||||||
|
|
||||||
extrudeButton!: Locator
|
extrudeButton!: Locator
|
||||||
loftButton!: Locator
|
|
||||||
shellButton!: Locator
|
|
||||||
offsetPlaneButton!: Locator
|
offsetPlaneButton!: Locator
|
||||||
startSketchBtn!: Locator
|
startSketchBtn!: Locator
|
||||||
lineBtn!: Locator
|
lineBtn!: Locator
|
||||||
@ -28,8 +26,6 @@ export class ToolbarFixture {
|
|||||||
reConstruct = (page: Page) => {
|
reConstruct = (page: Page) => {
|
||||||
this.page = page
|
this.page = page
|
||||||
this.extrudeButton = page.getByTestId('extrude')
|
this.extrudeButton = page.getByTestId('extrude')
|
||||||
this.loftButton = page.getByTestId('loft')
|
|
||||||
this.shellButton = page.getByTestId('shell')
|
|
||||||
this.offsetPlaneButton = page.getByTestId('plane-offset')
|
this.offsetPlaneButton = page.getByTestId('plane-offset')
|
||||||
this.startSketchBtn = page.getByTestId('sketch')
|
this.startSketchBtn = page.getByTestId('sketch')
|
||||||
this.lineBtn = page.getByTestId('line')
|
this.lineBtn = page.getByTestId('line')
|
||||||
|
@ -552,82 +552,6 @@ test(`Verify axis, origin, and horizontal snapping`, async ({
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
test(`Verify user can double-click to edit a sketch`, async ({
|
|
||||||
app,
|
|
||||||
editor,
|
|
||||||
toolbar,
|
|
||||||
scene,
|
|
||||||
}) => {
|
|
||||||
const initialCode = `closedSketch = startSketchOn('XZ')
|
|
||||||
|> circle({ center = [8, 5], radius = 2 }, %)
|
|
||||||
openSketch = startSketchOn('XY')
|
|
||||||
|> startProfileAt([-5, 0], %)
|
|
||||||
|> lineTo([0, 5], %)
|
|
||||||
|> xLine(5, %)
|
|
||||||
|> tangentialArcTo([10, 0], %)
|
|
||||||
`
|
|
||||||
await app.initialise(initialCode)
|
|
||||||
|
|
||||||
const pointInsideCircle = {
|
|
||||||
x: app.viewPortSize.width * 0.63,
|
|
||||||
y: app.viewPortSize.height * 0.5,
|
|
||||||
}
|
|
||||||
const pointOnPathAfterSketching = {
|
|
||||||
x: app.viewPortSize.width * 0.58,
|
|
||||||
y: app.viewPortSize.height * 0.5,
|
|
||||||
}
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
||||||
const [_clickOpenPath, moveToOpenPath, dblClickOpenPath] =
|
|
||||||
scene.makeMouseHelpers(
|
|
||||||
pointOnPathAfterSketching.x,
|
|
||||||
pointOnPathAfterSketching.y
|
|
||||||
)
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
||||||
const [_clickCircle, moveToCircle, dblClickCircle] = scene.makeMouseHelpers(
|
|
||||||
pointInsideCircle.x,
|
|
||||||
pointInsideCircle.y
|
|
||||||
)
|
|
||||||
|
|
||||||
const exitSketch = async () => {
|
|
||||||
await test.step(`Exit sketch mode`, async () => {
|
|
||||||
await toolbar.exitSketchBtn.click()
|
|
||||||
await expect(toolbar.exitSketchBtn).not.toBeVisible()
|
|
||||||
await expect(toolbar.startSketchBtn).toBeEnabled()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
await test.step(`Double-click on the closed sketch`, async () => {
|
|
||||||
await moveToCircle()
|
|
||||||
await dblClickCircle()
|
|
||||||
await expect(toolbar.startSketchBtn).not.toBeVisible()
|
|
||||||
await expect(toolbar.exitSketchBtn).toBeVisible()
|
|
||||||
await editor.expectState({
|
|
||||||
activeLines: [`|>circle({center=[8,5],radius=2},%)`],
|
|
||||||
highlightedCode: 'circle({center=[8,5],radius=2},%)',
|
|
||||||
diagnostics: [],
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
await exitSketch()
|
|
||||||
|
|
||||||
await test.step(`Double-click on the open sketch`, async () => {
|
|
||||||
await moveToOpenPath()
|
|
||||||
await scene.expectPixelColor([250, 250, 250], pointOnPathAfterSketching, 15)
|
|
||||||
// There is a full execution after exiting sketch that clears the scene.
|
|
||||||
await app.page.waitForTimeout(500)
|
|
||||||
await dblClickOpenPath()
|
|
||||||
await expect(toolbar.startSketchBtn).not.toBeVisible()
|
|
||||||
await expect(toolbar.exitSketchBtn).toBeVisible()
|
|
||||||
// Wait for enter sketch mode to complete
|
|
||||||
await app.page.waitForTimeout(500)
|
|
||||||
await editor.expectState({
|
|
||||||
activeLines: [`|>xLine(5,%)`],
|
|
||||||
highlightedCode: 'xLine(5,%)',
|
|
||||||
diagnostics: [],
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
test(`Offset plane point-and-click`, async ({
|
test(`Offset plane point-and-click`, async ({
|
||||||
app,
|
app,
|
||||||
scene,
|
scene,
|
||||||
@ -677,259 +601,3 @@ test(`Offset plane point-and-click`, async ({
|
|||||||
await scene.expectPixelColor([74, 74, 74], testPoint, 15)
|
await scene.expectPixelColor([74, 74, 74], testPoint, 15)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
const loftPointAndClickCases = [
|
|
||||||
{ shouldPreselect: true },
|
|
||||||
{ shouldPreselect: false },
|
|
||||||
]
|
|
||||||
loftPointAndClickCases.forEach(({ shouldPreselect }) => {
|
|
||||||
test(`Loft point-and-click (preselected sketches: ${shouldPreselect})`, async ({
|
|
||||||
app,
|
|
||||||
page,
|
|
||||||
scene,
|
|
||||||
editor,
|
|
||||||
toolbar,
|
|
||||||
cmdBar,
|
|
||||||
}) => {
|
|
||||||
const initialCode = `sketch001 = startSketchOn('XZ')
|
|
||||||
|> circle({ center = [0, 0], radius = 30 }, %)
|
|
||||||
plane001 = offsetPlane('XZ', 50)
|
|
||||||
sketch002 = startSketchOn(plane001)
|
|
||||||
|> circle({ center = [0, 0], radius = 20 }, %)
|
|
||||||
`
|
|
||||||
await app.initialise(initialCode)
|
|
||||||
|
|
||||||
// One dumb hardcoded screen pixel value
|
|
||||||
const testPoint = { x: 575, y: 200 }
|
|
||||||
const [clickOnSketch1] = scene.makeMouseHelpers(testPoint.x, testPoint.y)
|
|
||||||
const [clickOnSketch2] = scene.makeMouseHelpers(
|
|
||||||
testPoint.x,
|
|
||||||
testPoint.y + 80
|
|
||||||
)
|
|
||||||
const loftDeclaration = 'loft001 = loft([sketch001, sketch002])'
|
|
||||||
|
|
||||||
await test.step(`Look for the white of the sketch001 shape`, async () => {
|
|
||||||
await scene.expectPixelColor([254, 254, 254], testPoint, 15)
|
|
||||||
})
|
|
||||||
|
|
||||||
async function selectSketches() {
|
|
||||||
await clickOnSketch1()
|
|
||||||
await page.keyboard.down('Shift')
|
|
||||||
await clickOnSketch2()
|
|
||||||
await app.page.waitForTimeout(500)
|
|
||||||
await page.keyboard.up('Shift')
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!shouldPreselect) {
|
|
||||||
await test.step(`Go through the command bar flow without preselected sketches`, async () => {
|
|
||||||
await toolbar.loftButton.click()
|
|
||||||
await cmdBar.expectState({
|
|
||||||
stage: 'arguments',
|
|
||||||
currentArgKey: 'selection',
|
|
||||||
currentArgValue: '',
|
|
||||||
headerArguments: { Selection: '' },
|
|
||||||
highlightedHeaderArg: 'selection',
|
|
||||||
commandName: 'Loft',
|
|
||||||
})
|
|
||||||
await selectSketches()
|
|
||||||
await cmdBar.progressCmdBar()
|
|
||||||
await cmdBar.expectState({
|
|
||||||
stage: 'review',
|
|
||||||
headerArguments: { Selection: '2 faces' },
|
|
||||||
commandName: 'Loft',
|
|
||||||
})
|
|
||||||
await cmdBar.progressCmdBar()
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
await test.step(`Preselect the two sketches`, async () => {
|
|
||||||
await selectSketches()
|
|
||||||
})
|
|
||||||
|
|
||||||
await test.step(`Go through the command bar flow with preselected sketches`, async () => {
|
|
||||||
await toolbar.loftButton.click()
|
|
||||||
await cmdBar.progressCmdBar()
|
|
||||||
await cmdBar.expectState({
|
|
||||||
stage: 'review',
|
|
||||||
headerArguments: { Selection: '2 faces' },
|
|
||||||
commandName: 'Loft',
|
|
||||||
})
|
|
||||||
await cmdBar.progressCmdBar()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
await test.step(`Confirm code is added to the editor, scene has changed`, async () => {
|
|
||||||
await editor.expectEditor.toContain(loftDeclaration)
|
|
||||||
await editor.expectState({
|
|
||||||
diagnostics: [],
|
|
||||||
activeLines: [loftDeclaration],
|
|
||||||
highlightedCode: '',
|
|
||||||
})
|
|
||||||
await scene.expectPixelColor([89, 89, 89], testPoint, 15)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
const shellPointAndClickCapCases = [
|
|
||||||
{ shouldPreselect: true },
|
|
||||||
{ shouldPreselect: false },
|
|
||||||
]
|
|
||||||
shellPointAndClickCapCases.forEach(({ shouldPreselect }) => {
|
|
||||||
test(`Shell point-and-click cap (preselected sketches: ${shouldPreselect})`, async ({
|
|
||||||
app,
|
|
||||||
scene,
|
|
||||||
editor,
|
|
||||||
toolbar,
|
|
||||||
cmdBar,
|
|
||||||
}) => {
|
|
||||||
const initialCode = `sketch001 = startSketchOn('XZ')
|
|
||||||
|> circle({ center = [0, 0], radius = 30 }, %)
|
|
||||||
extrude001 = extrude(30, sketch001)
|
|
||||||
`
|
|
||||||
await app.initialise(initialCode)
|
|
||||||
|
|
||||||
// One dumb hardcoded screen pixel value
|
|
||||||
const testPoint = { x: 575, y: 200 }
|
|
||||||
const [clickOnCap] = scene.makeMouseHelpers(testPoint.x, testPoint.y)
|
|
||||||
const shellDeclaration =
|
|
||||||
"shell001 = shell({ faces = ['end'], thickness = 5 }, extrude001)"
|
|
||||||
|
|
||||||
await test.step(`Look for the grey of the shape`, async () => {
|
|
||||||
await scene.expectPixelColor([127, 127, 127], testPoint, 15)
|
|
||||||
})
|
|
||||||
|
|
||||||
if (!shouldPreselect) {
|
|
||||||
await test.step(`Go through the command bar flow without preselected faces`, async () => {
|
|
||||||
await toolbar.shellButton.click()
|
|
||||||
await cmdBar.expectState({
|
|
||||||
stage: 'arguments',
|
|
||||||
currentArgKey: 'selection',
|
|
||||||
currentArgValue: '',
|
|
||||||
headerArguments: {
|
|
||||||
Selection: '',
|
|
||||||
Thickness: '',
|
|
||||||
},
|
|
||||||
highlightedHeaderArg: 'selection',
|
|
||||||
commandName: 'Shell',
|
|
||||||
})
|
|
||||||
await clickOnCap()
|
|
||||||
await app.page.waitForTimeout(500)
|
|
||||||
await cmdBar.progressCmdBar()
|
|
||||||
await cmdBar.progressCmdBar()
|
|
||||||
await cmdBar.expectState({
|
|
||||||
stage: 'review',
|
|
||||||
headerArguments: {
|
|
||||||
Selection: '1 cap',
|
|
||||||
Thickness: '5',
|
|
||||||
},
|
|
||||||
commandName: 'Shell',
|
|
||||||
})
|
|
||||||
await cmdBar.progressCmdBar()
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
await test.step(`Preselect the cap`, async () => {
|
|
||||||
await clickOnCap()
|
|
||||||
await app.page.waitForTimeout(500)
|
|
||||||
})
|
|
||||||
|
|
||||||
await test.step(`Go through the command bar flow with a preselected face (cap)`, async () => {
|
|
||||||
await toolbar.shellButton.click()
|
|
||||||
await cmdBar.progressCmdBar()
|
|
||||||
await cmdBar.progressCmdBar()
|
|
||||||
await cmdBar.expectState({
|
|
||||||
stage: 'review',
|
|
||||||
headerArguments: {
|
|
||||||
Selection: '1 cap',
|
|
||||||
Thickness: '5',
|
|
||||||
},
|
|
||||||
commandName: 'Shell',
|
|
||||||
})
|
|
||||||
await cmdBar.progressCmdBar()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
await test.step(`Confirm code is added to the editor, scene has changed`, async () => {
|
|
||||||
await editor.expectEditor.toContain(shellDeclaration)
|
|
||||||
await editor.expectState({
|
|
||||||
diagnostics: [],
|
|
||||||
activeLines: [shellDeclaration],
|
|
||||||
highlightedCode: '',
|
|
||||||
})
|
|
||||||
await scene.expectPixelColor([146, 146, 146], testPoint, 15)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
test('Shell point-and-click wall', async ({
|
|
||||||
app,
|
|
||||||
page,
|
|
||||||
scene,
|
|
||||||
editor,
|
|
||||||
toolbar,
|
|
||||||
cmdBar,
|
|
||||||
}) => {
|
|
||||||
const initialCode = `sketch001 = startSketchOn('XY')
|
|
||||||
|> startProfileAt([-20, 20], %)
|
|
||||||
|> xLine(40, %)
|
|
||||||
|> yLine(-60, %)
|
|
||||||
|> xLine(-40, %)
|
|
||||||
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
|
||||||
|> close(%)
|
|
||||||
extrude001 = extrude(40, sketch001)
|
|
||||||
`
|
|
||||||
await app.initialise(initialCode)
|
|
||||||
|
|
||||||
// One dumb hardcoded screen pixel value
|
|
||||||
const testPoint = { x: 580, y: 180 }
|
|
||||||
const [clickOnCap] = scene.makeMouseHelpers(testPoint.x, testPoint.y)
|
|
||||||
const [clickOnWall] = scene.makeMouseHelpers(testPoint.x, testPoint.y + 70)
|
|
||||||
const mutatedCode = 'xLine(-40, %, $seg01)'
|
|
||||||
const shellDeclaration =
|
|
||||||
"shell001 = shell({ faces = ['end', seg01], thickness = 5}, extrude001)"
|
|
||||||
const formattedOutLastLine = '}, extrude001)'
|
|
||||||
|
|
||||||
await test.step(`Look for the grey of the shape`, async () => {
|
|
||||||
await scene.expectPixelColor([99, 99, 99], testPoint, 15)
|
|
||||||
})
|
|
||||||
|
|
||||||
await test.step(`Go through the command bar flow, selecting a wall and keeping default thickness`, async () => {
|
|
||||||
await toolbar.shellButton.click()
|
|
||||||
await cmdBar.expectState({
|
|
||||||
stage: 'arguments',
|
|
||||||
currentArgKey: 'selection',
|
|
||||||
currentArgValue: '',
|
|
||||||
headerArguments: {
|
|
||||||
Selection: '',
|
|
||||||
Thickness: '',
|
|
||||||
},
|
|
||||||
highlightedHeaderArg: 'selection',
|
|
||||||
commandName: 'Shell',
|
|
||||||
})
|
|
||||||
await clickOnCap()
|
|
||||||
await page.keyboard.down('Shift')
|
|
||||||
await clickOnWall()
|
|
||||||
await app.page.waitForTimeout(500)
|
|
||||||
await page.keyboard.up('Shift')
|
|
||||||
await cmdBar.progressCmdBar()
|
|
||||||
await cmdBar.progressCmdBar()
|
|
||||||
await cmdBar.expectState({
|
|
||||||
stage: 'review',
|
|
||||||
headerArguments: {
|
|
||||||
Selection: '1 cap, 1 face',
|
|
||||||
Thickness: '5',
|
|
||||||
},
|
|
||||||
commandName: 'Shell',
|
|
||||||
})
|
|
||||||
await cmdBar.progressCmdBar()
|
|
||||||
})
|
|
||||||
|
|
||||||
await test.step(`Confirm code is added to the editor, scene has changed`, async () => {
|
|
||||||
await editor.expectEditor.toContain(mutatedCode)
|
|
||||||
await editor.expectEditor.toContain(shellDeclaration)
|
|
||||||
await editor.expectState({
|
|
||||||
diagnostics: [],
|
|
||||||
activeLines: [formattedOutLastLine],
|
|
||||||
highlightedCode: '',
|
|
||||||
})
|
|
||||||
await scene.expectPixelColor([49, 49, 49], testPoint, 15)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
@ -136,335 +136,6 @@ test(
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
test(
|
|
||||||
'open a file in a project works and renders, open another file in different project with errors, it should clear the scene',
|
|
||||||
{ tag: '@electron' },
|
|
||||||
async ({ browserName }, testInfo) => {
|
|
||||||
const { electronApp, page } = await setupElectron({
|
|
||||||
testInfo,
|
|
||||||
folderSetupFn: async (dir) => {
|
|
||||||
const bracketDir = join(dir, 'bracket')
|
|
||||||
await fsp.mkdir(bracketDir, { recursive: true })
|
|
||||||
await fsp.copyFile(
|
|
||||||
executorInputPath('focusrite_scarlett_mounting_braket.kcl'),
|
|
||||||
join(bracketDir, 'main.kcl')
|
|
||||||
)
|
|
||||||
const errorDir = join(dir, 'broken-code')
|
|
||||||
await fsp.mkdir(errorDir, { recursive: true })
|
|
||||||
await fsp.copyFile(
|
|
||||||
executorInputPath('broken-code-test.kcl'),
|
|
||||||
join(errorDir, 'main.kcl')
|
|
||||||
)
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
await page.setViewportSize({ width: 1200, height: 500 })
|
|
||||||
const u = await getUtils(page)
|
|
||||||
|
|
||||||
page.on('console', console.log)
|
|
||||||
|
|
||||||
const pointOnModel = { x: 630, y: 280 }
|
|
||||||
|
|
||||||
await test.step('Opening the bracket project should load the stream', async () => {
|
|
||||||
// expect to see the text bracket
|
|
||||||
await expect(page.getByText('bracket')).toBeVisible()
|
|
||||||
|
|
||||||
await page.getByText('bracket').click()
|
|
||||||
|
|
||||||
await expect(page.getByTestId('loading')).toBeAttached()
|
|
||||||
await expect(page.getByTestId('loading')).not.toBeAttached({
|
|
||||||
timeout: 20_000,
|
|
||||||
})
|
|
||||||
|
|
||||||
await expect(
|
|
||||||
page.getByRole('button', { name: 'Start Sketch' })
|
|
||||||
).toBeEnabled({
|
|
||||||
timeout: 20_000,
|
|
||||||
})
|
|
||||||
|
|
||||||
// gray at this pixel means the stream has loaded in the most
|
|
||||||
// user way we can verify it (pixel color)
|
|
||||||
await expect
|
|
||||||
.poll(() => u.getGreatestPixDiff(pointOnModel, [85, 85, 85]), {
|
|
||||||
timeout: 10_000,
|
|
||||||
})
|
|
||||||
.toBeLessThan(15)
|
|
||||||
})
|
|
||||||
|
|
||||||
await test.step('Clicking the logo takes us back to the projects page / home', async () => {
|
|
||||||
await page.getByTestId('app-logo').click()
|
|
||||||
|
|
||||||
await expect(page.getByRole('link', { name: 'bracket' })).toBeVisible()
|
|
||||||
await expect(page.getByText('broken-code')).toBeVisible()
|
|
||||||
await expect(page.getByText('bracket')).toBeVisible()
|
|
||||||
await expect(page.getByText('New Project')).toBeVisible()
|
|
||||||
})
|
|
||||||
await test.step('opening broken code project should clear the scene and show the error', async () => {
|
|
||||||
// Go back home.
|
|
||||||
await expect(page.getByText('broken-code')).toBeVisible()
|
|
||||||
|
|
||||||
await page.getByText('broken-code').click()
|
|
||||||
|
|
||||||
// error in guter
|
|
||||||
await expect(page.locator('.cm-lint-marker-error')).toBeVisible()
|
|
||||||
|
|
||||||
// error text on hover
|
|
||||||
await page.hover('.cm-lint-marker-error')
|
|
||||||
const crypticErrorText = `Expected a tag declarator`
|
|
||||||
await expect(page.getByText(crypticErrorText).first()).toBeVisible()
|
|
||||||
|
|
||||||
// black pixel means the scene has been cleared.
|
|
||||||
await expect
|
|
||||||
.poll(() => u.getGreatestPixDiff(pointOnModel, [30, 30, 30]), {
|
|
||||||
timeout: 10_000,
|
|
||||||
})
|
|
||||||
.toBeLessThan(15)
|
|
||||||
})
|
|
||||||
|
|
||||||
await electronApp.close()
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
test(
|
|
||||||
'open a file in a project works and renders, open another file in different project that is empty, it should clear the scene',
|
|
||||||
{ tag: '@electron' },
|
|
||||||
async ({ browserName }, testInfo) => {
|
|
||||||
const { electronApp, page } = await setupElectron({
|
|
||||||
testInfo,
|
|
||||||
folderSetupFn: async (dir) => {
|
|
||||||
const bracketDir = join(dir, 'bracket')
|
|
||||||
await fsp.mkdir(bracketDir, { recursive: true })
|
|
||||||
await fsp.copyFile(
|
|
||||||
executorInputPath('focusrite_scarlett_mounting_braket.kcl'),
|
|
||||||
join(bracketDir, 'main.kcl')
|
|
||||||
)
|
|
||||||
const emptyDir = join(dir, 'empty')
|
|
||||||
await fsp.mkdir(emptyDir, { recursive: true })
|
|
||||||
await fsp.writeFile(join(emptyDir, 'main.kcl'), '')
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
await page.setViewportSize({ width: 1200, height: 500 })
|
|
||||||
const u = await getUtils(page)
|
|
||||||
|
|
||||||
page.on('console', console.log)
|
|
||||||
|
|
||||||
const pointOnModel = { x: 630, y: 280 }
|
|
||||||
|
|
||||||
await test.step('Opening the bracket project should load the stream', async () => {
|
|
||||||
// expect to see the text bracket
|
|
||||||
await expect(page.getByText('bracket')).toBeVisible()
|
|
||||||
|
|
||||||
await page.getByText('bracket').click()
|
|
||||||
|
|
||||||
await expect(page.getByTestId('loading')).toBeAttached()
|
|
||||||
await expect(page.getByTestId('loading')).not.toBeAttached({
|
|
||||||
timeout: 20_000,
|
|
||||||
})
|
|
||||||
|
|
||||||
await expect(
|
|
||||||
page.getByRole('button', { name: 'Start Sketch' })
|
|
||||||
).toBeEnabled({
|
|
||||||
timeout: 20_000,
|
|
||||||
})
|
|
||||||
|
|
||||||
// gray at this pixel means the stream has loaded in the most
|
|
||||||
// user way we can verify it (pixel color)
|
|
||||||
await expect
|
|
||||||
.poll(() => u.getGreatestPixDiff(pointOnModel, [85, 85, 85]), {
|
|
||||||
timeout: 10_000,
|
|
||||||
})
|
|
||||||
.toBeLessThan(15)
|
|
||||||
})
|
|
||||||
|
|
||||||
await test.step('Clicking the logo takes us back to the projects page / home', async () => {
|
|
||||||
await page.getByTestId('app-logo').click()
|
|
||||||
|
|
||||||
await expect(page.getByRole('link', { name: 'bracket' })).toBeVisible()
|
|
||||||
await expect(page.getByText('empty')).toBeVisible()
|
|
||||||
await expect(page.getByText('bracket')).toBeVisible()
|
|
||||||
await expect(page.getByText('New Project')).toBeVisible()
|
|
||||||
})
|
|
||||||
await test.step('opening empty code project should clear the scene', async () => {
|
|
||||||
// Go back home.
|
|
||||||
await expect(page.getByText('empty')).toBeVisible()
|
|
||||||
|
|
||||||
await page.getByText('empty').click()
|
|
||||||
|
|
||||||
// Ensure the code is empty.
|
|
||||||
await expect(u.codeLocator).toContainText('')
|
|
||||||
expect(u.codeLocator.innerHTML.length).toBeLessThan(2)
|
|
||||||
|
|
||||||
// planes colors means the scene has been cleared.
|
|
||||||
await expect
|
|
||||||
.poll(() => u.getGreatestPixDiff(pointOnModel, [92, 53, 53]), {
|
|
||||||
timeout: 10_000,
|
|
||||||
})
|
|
||||||
.toBeLessThan(15)
|
|
||||||
})
|
|
||||||
|
|
||||||
await electronApp.close()
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
test(
|
|
||||||
'open a file in a project works and renders, open empty file, it should clear the scene',
|
|
||||||
{ tag: '@electron' },
|
|
||||||
async ({ browserName }, testInfo) => {
|
|
||||||
const { electronApp, page } = await setupElectron({
|
|
||||||
testInfo,
|
|
||||||
folderSetupFn: async (dir) => {
|
|
||||||
const bracketDir = join(dir, 'bracket')
|
|
||||||
await fsp.mkdir(bracketDir, { recursive: true })
|
|
||||||
await fsp.copyFile(
|
|
||||||
executorInputPath('focusrite_scarlett_mounting_braket.kcl'),
|
|
||||||
join(bracketDir, 'main.kcl')
|
|
||||||
)
|
|
||||||
|
|
||||||
await fsp.writeFile(join(bracketDir, 'empty.kcl'), '')
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
await page.setViewportSize({ width: 1200, height: 500 })
|
|
||||||
const u = await getUtils(page)
|
|
||||||
|
|
||||||
page.on('console', console.log)
|
|
||||||
|
|
||||||
const pointOnModel = { x: 630, y: 280 }
|
|
||||||
|
|
||||||
await test.step('Opening the bracket project should load the stream', async () => {
|
|
||||||
// expect to see the text bracket
|
|
||||||
await expect(page.getByText('bracket')).toBeVisible()
|
|
||||||
|
|
||||||
await page.getByText('bracket').click()
|
|
||||||
|
|
||||||
await expect(page.getByTestId('loading')).toBeAttached()
|
|
||||||
await expect(page.getByTestId('loading')).not.toBeAttached({
|
|
||||||
timeout: 20_000,
|
|
||||||
})
|
|
||||||
|
|
||||||
await expect(
|
|
||||||
page.getByRole('button', { name: 'Start Sketch' })
|
|
||||||
).toBeEnabled({
|
|
||||||
timeout: 20_000,
|
|
||||||
})
|
|
||||||
|
|
||||||
// gray at this pixel means the stream has loaded in the most
|
|
||||||
// user way we can verify it (pixel color)
|
|
||||||
await expect
|
|
||||||
.poll(() => u.getGreatestPixDiff(pointOnModel, [85, 85, 85]), {
|
|
||||||
timeout: 10_000,
|
|
||||||
})
|
|
||||||
.toBeLessThan(15)
|
|
||||||
})
|
|
||||||
await test.step('creating a empty file should clear the scene', async () => {
|
|
||||||
// open the file pane.
|
|
||||||
await page.getByTestId('files-pane-button').click()
|
|
||||||
|
|
||||||
// OPen the other file.
|
|
||||||
const file = page.getByRole('button', { name: 'empty.kcl' })
|
|
||||||
await expect(file).toBeVisible()
|
|
||||||
|
|
||||||
await file.click()
|
|
||||||
|
|
||||||
// planes colors means the scene has been cleared.
|
|
||||||
await expect
|
|
||||||
.poll(() => u.getGreatestPixDiff(pointOnModel, [92, 53, 53]), {
|
|
||||||
timeout: 10_000,
|
|
||||||
})
|
|
||||||
.toBeLessThan(15)
|
|
||||||
|
|
||||||
// Ensure the code is empty.
|
|
||||||
await expect(u.codeLocator).toContainText('')
|
|
||||||
expect(u.codeLocator.innerHTML.length).toBeLessThan(2)
|
|
||||||
})
|
|
||||||
|
|
||||||
await electronApp.close()
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
test(
|
|
||||||
'open a file in a project works and renders, open another file in the same project with errors, it should clear the scene',
|
|
||||||
{ tag: '@electron' },
|
|
||||||
async ({ browserName }, testInfo) => {
|
|
||||||
const { electronApp, page } = await setupElectron({
|
|
||||||
testInfo,
|
|
||||||
folderSetupFn: async (dir) => {
|
|
||||||
const bracketDir = join(dir, 'bracket')
|
|
||||||
await fsp.mkdir(bracketDir, { recursive: true })
|
|
||||||
await fsp.copyFile(
|
|
||||||
executorInputPath('focusrite_scarlett_mounting_braket.kcl'),
|
|
||||||
join(bracketDir, 'main.kcl')
|
|
||||||
)
|
|
||||||
await fsp.copyFile(
|
|
||||||
executorInputPath('broken-code-test.kcl'),
|
|
||||||
join(bracketDir, 'broken-code-test.kcl')
|
|
||||||
)
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
await page.setViewportSize({ width: 1200, height: 500 })
|
|
||||||
const u = await getUtils(page)
|
|
||||||
|
|
||||||
page.on('console', console.log)
|
|
||||||
|
|
||||||
const pointOnModel = { x: 630, y: 280 }
|
|
||||||
|
|
||||||
await test.step('Opening the bracket project should load the stream', async () => {
|
|
||||||
// expect to see the text bracket
|
|
||||||
await expect(page.getByText('bracket')).toBeVisible()
|
|
||||||
|
|
||||||
await page.getByText('bracket').click()
|
|
||||||
|
|
||||||
await expect(page.getByTestId('loading')).toBeAttached()
|
|
||||||
await expect(page.getByTestId('loading')).not.toBeAttached({
|
|
||||||
timeout: 20_000,
|
|
||||||
})
|
|
||||||
|
|
||||||
await expect(
|
|
||||||
page.getByRole('button', { name: 'Start Sketch' })
|
|
||||||
).toBeEnabled({
|
|
||||||
timeout: 20_000,
|
|
||||||
})
|
|
||||||
|
|
||||||
// gray at this pixel means the stream has loaded in the most
|
|
||||||
// user way we can verify it (pixel color)
|
|
||||||
await expect
|
|
||||||
.poll(() => u.getGreatestPixDiff(pointOnModel, [85, 85, 85]), {
|
|
||||||
timeout: 10_000,
|
|
||||||
})
|
|
||||||
.toBeLessThan(15)
|
|
||||||
})
|
|
||||||
await test.step('opening broken code file should clear the scene and show the error', async () => {
|
|
||||||
// open the file pane.
|
|
||||||
await page.getByTestId('files-pane-button').click()
|
|
||||||
|
|
||||||
// OPen the other file.
|
|
||||||
const file = page.getByRole('button', { name: 'broken-code-test.kcl' })
|
|
||||||
await expect(file).toBeVisible()
|
|
||||||
|
|
||||||
await file.click()
|
|
||||||
|
|
||||||
// error in guter
|
|
||||||
await expect(page.locator('.cm-lint-marker-error')).toBeVisible()
|
|
||||||
|
|
||||||
// error text on hover
|
|
||||||
await page.hover('.cm-lint-marker-error')
|
|
||||||
const crypticErrorText = `Expected a tag declarator`
|
|
||||||
await expect(page.getByText(crypticErrorText).first()).toBeVisible()
|
|
||||||
|
|
||||||
// black pixel means the scene has been cleared.
|
|
||||||
await expect
|
|
||||||
.poll(() => u.getGreatestPixDiff(pointOnModel, [30, 30, 30]), {
|
|
||||||
timeout: 10_000,
|
|
||||||
})
|
|
||||||
.toBeLessThan(15)
|
|
||||||
})
|
|
||||||
|
|
||||||
await electronApp.close()
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
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' },
|
||||||
|
@ -550,7 +550,7 @@ sketch001 = startSketchAt([-0, -0])
|
|||||||
const u = await getUtils(page)
|
const u = await getUtils(page)
|
||||||
|
|
||||||
// Constants and locators
|
// Constants and locators
|
||||||
const planeColor: [number, number, number] = [161, 220, 155]
|
const planeColor: [number, number, number] = [170, 220, 170]
|
||||||
const bgColor: [number, number, number] = [27, 27, 27]
|
const bgColor: [number, number, number] = [27, 27, 27]
|
||||||
const middlePixelIsColor = async (color: [number, number, number]) => {
|
const middlePixelIsColor = async (color: [number, number, number]) => {
|
||||||
return u.getGreatestPixDiff({ x: 600, y: 250 }, color)
|
return u.getGreatestPixDiff({ x: 600, y: 250 }, color)
|
||||||
|
@ -7,8 +7,6 @@ try {
|
|||||||
.split('\n')
|
.split('\n')
|
||||||
.filter((line) => line && line.length > 1)
|
.filter((line) => line && line.length > 1)
|
||||||
.forEach((line) => {
|
.forEach((line) => {
|
||||||
// Allow line comments.
|
|
||||||
if (line.trimStart().startsWith('#')) return
|
|
||||||
const [key, value] = line.split('=')
|
const [key, value] = line.split('=')
|
||||||
// prefer env vars over secrets file
|
// prefer env vars over secrets file
|
||||||
secrets[key] = process.env[key] || (value as any).replaceAll('"', '')
|
secrets[key] = process.env[key] || (value as any).replaceAll('"', '')
|
||||||
|
@ -943,110 +943,6 @@ sketch002 = startSketchOn(extrude001, 'END')
|
|||||||
`.replace(/\s/g, '')
|
`.replace(/\s/g, '')
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
/* TODO: once we fix bug turn on.
|
|
||||||
test('empty-scene default-planes act as expected when spaces in file', async ({
|
|
||||||
page,
|
|
||||||
browserName,
|
|
||||||
}) => {
|
|
||||||
|
|
||||||
const u = await getUtils(page)
|
|
||||||
await page.setViewportSize({ width: 1200, height: 500 })
|
|
||||||
|
|
||||||
await u.waitForAuthSkipAppStart()
|
|
||||||
|
|
||||||
await u.openDebugPanel()
|
|
||||||
await u.expectCmdLog('[data-message-type="execution-done"]')
|
|
||||||
await u.closeDebugPanel()
|
|
||||||
|
|
||||||
const XYPlanePoint = { x: 774, y: 116 } as const
|
|
||||||
const unHoveredColor: [number, number, number] = [47, 47, 93]
|
|
||||||
expect(
|
|
||||||
await u.getGreatestPixDiff(XYPlanePoint, unHoveredColor)
|
|
||||||
).toBeLessThan(8)
|
|
||||||
|
|
||||||
await page.mouse.move(XYPlanePoint.x, XYPlanePoint.y)
|
|
||||||
await page.waitForTimeout(200)
|
|
||||||
|
|
||||||
// color should not change for having been hovered
|
|
||||||
expect(
|
|
||||||
await u.getGreatestPixDiff(XYPlanePoint, unHoveredColor)
|
|
||||||
).toBeLessThan(8)
|
|
||||||
|
|
||||||
await u.openAndClearDebugPanel()
|
|
||||||
|
|
||||||
// Fill with spaces
|
|
||||||
await u.codeLocator.fill(`
|
|
||||||
`)
|
|
||||||
|
|
||||||
await u.openDebugPanel()
|
|
||||||
await u.expectCmdLog('[data-message-type="execution-done"]')
|
|
||||||
await u.closeDebugPanel()
|
|
||||||
|
|
||||||
expect(
|
|
||||||
await u.getGreatestPixDiff(XYPlanePoint, unHoveredColor)
|
|
||||||
).toBeLessThan(8)
|
|
||||||
|
|
||||||
await page.mouse.move(XYPlanePoint.x, XYPlanePoint.y)
|
|
||||||
await page.waitForTimeout(200)
|
|
||||||
|
|
||||||
// color should not change for having been hovered
|
|
||||||
expect(
|
|
||||||
await u.getGreatestPixDiff(XYPlanePoint, unHoveredColor)
|
|
||||||
).toBeLessThan(8)
|
|
||||||
})
|
|
||||||
|
|
||||||
test('empty-scene default-planes act as expected when only code comments in file', async ({
|
|
||||||
page,
|
|
||||||
browserName,
|
|
||||||
}) => {
|
|
||||||
|
|
||||||
const u = await getUtils(page)
|
|
||||||
await page.setViewportSize({ width: 1200, height: 500 })
|
|
||||||
|
|
||||||
await u.waitForAuthSkipAppStart()
|
|
||||||
|
|
||||||
await u.openDebugPanel()
|
|
||||||
await u.expectCmdLog('[data-message-type="execution-done"]')
|
|
||||||
await u.closeDebugPanel()
|
|
||||||
|
|
||||||
const XYPlanePoint = { x: 774, y: 116 } as const
|
|
||||||
const unHoveredColor: [number, number, number] = [47, 47, 93]
|
|
||||||
expect(
|
|
||||||
await u.getGreatestPixDiff(XYPlanePoint, unHoveredColor)
|
|
||||||
).toBeLessThan(8)
|
|
||||||
|
|
||||||
await page.mouse.move(XYPlanePoint.x, XYPlanePoint.y)
|
|
||||||
await page.waitForTimeout(200)
|
|
||||||
|
|
||||||
// color should not change for having been hovered
|
|
||||||
expect(
|
|
||||||
await u.getGreatestPixDiff(XYPlanePoint, unHoveredColor)
|
|
||||||
).toBeLessThan(8)
|
|
||||||
|
|
||||||
await u.openAndClearDebugPanel()
|
|
||||||
|
|
||||||
// Fill with spaces
|
|
||||||
await u.codeLocator.fill(`// this is a code comments ya nerds
|
|
||||||
`)
|
|
||||||
|
|
||||||
await u.openDebugPanel()
|
|
||||||
await u.expectCmdLog('[data-message-type="execution-done"]')
|
|
||||||
await u.closeDebugPanel()
|
|
||||||
|
|
||||||
expect(
|
|
||||||
await u.getGreatestPixDiff(XYPlanePoint, unHoveredColor)
|
|
||||||
).toBeLessThan(8)
|
|
||||||
|
|
||||||
await page.mouse.move(XYPlanePoint.x, XYPlanePoint.y)
|
|
||||||
await page.waitForTimeout(200)
|
|
||||||
|
|
||||||
// color should not change for having been hovered
|
|
||||||
expect(
|
|
||||||
await u.getGreatestPixDiff(XYPlanePoint, unHoveredColor)
|
|
||||||
).toBeLessThan(8)
|
|
||||||
})*/
|
|
||||||
|
|
||||||
test('empty-scene default-planes act as expected', async ({
|
test('empty-scene default-planes act as expected', async ({
|
||||||
page,
|
page,
|
||||||
browserName,
|
browserName,
|
||||||
|
Before Width: | Height: | Size: 33 KiB After Width: | Height: | Size: 33 KiB |
Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 34 KiB |
Before Width: | Height: | Size: 50 KiB After Width: | Height: | Size: 53 KiB |
Before Width: | Height: | Size: 51 KiB After Width: | Height: | Size: 54 KiB |
Before Width: | Height: | Size: 37 KiB After Width: | Height: | Size: 37 KiB |
Before Width: | Height: | Size: 40 KiB After Width: | Height: | Size: 40 KiB |
Before Width: | Height: | Size: 40 KiB After Width: | Height: | Size: 40 KiB |
Before Width: | Height: | Size: 38 KiB After Width: | Height: | Size: 38 KiB |
Before Width: | Height: | Size: 39 KiB After Width: | Height: | Size: 39 KiB |
Before Width: | Height: | Size: 42 KiB After Width: | Height: | Size: 42 KiB |
Before Width: | Height: | Size: 36 KiB After Width: | Height: | Size: 36 KiB |
@ -26,17 +26,7 @@ test.describe('Testing constraints', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
const u = await getUtils(page)
|
const u = await getUtils(page)
|
||||||
// constants and locators
|
const PUR = 400 / 37.5 //pixeltoUnitRatio
|
||||||
const lengthValue = {
|
|
||||||
old: '20',
|
|
||||||
new: '25',
|
|
||||||
}
|
|
||||||
const cmdBarKclInput = page
|
|
||||||
.getByTestId('cmd-bar-arg-value')
|
|
||||||
.getByRole('textbox')
|
|
||||||
const cmdBarSubmitButton = page.getByRole('button', {
|
|
||||||
name: 'arrow right Continue',
|
|
||||||
})
|
|
||||||
await page.setViewportSize({ width: 1200, height: 500 })
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
|
|
||||||
await u.waitForAuthSkipAppStart()
|
await u.waitForAuthSkipAppStart()
|
||||||
@ -46,26 +36,26 @@ test.describe('Testing constraints', () => {
|
|||||||
await u.closeDebugPanel()
|
await u.closeDebugPanel()
|
||||||
|
|
||||||
// Click the line of code for line.
|
// Click the line of code for line.
|
||||||
// TODO remove this and reinstate `await topHorzSegmentClick()`
|
await page.getByText(`line([0, 20], %)`).click() // TODO remove this and reinstate // await topHorzSegmentClick()
|
||||||
await page.getByText(`line([0, ${lengthValue.old}], %)`).click()
|
|
||||||
await page.waitForTimeout(100)
|
await page.waitForTimeout(100)
|
||||||
|
|
||||||
// enter sketch again
|
// enter sketch again
|
||||||
await page.getByRole('button', { name: 'Edit Sketch' }).click()
|
await page.getByRole('button', { name: 'Edit Sketch' }).click()
|
||||||
await page.waitForTimeout(500) // wait for animation
|
await page.waitForTimeout(500) // wait for animation
|
||||||
|
|
||||||
|
const startXPx = 500
|
||||||
|
await page.mouse.move(startXPx + PUR * 15, 250 - PUR * 10)
|
||||||
|
await page.keyboard.down('Shift')
|
||||||
|
await page.mouse.click(834, 244)
|
||||||
|
await page.keyboard.up('Shift')
|
||||||
|
|
||||||
await page
|
await page
|
||||||
.getByRole('button', { name: 'dimension Length', exact: true })
|
.getByRole('button', { name: 'dimension Length', exact: true })
|
||||||
.click()
|
.click()
|
||||||
await expect(cmdBarKclInput).toHaveText('20')
|
await page.getByText('Add constraining value').click()
|
||||||
await cmdBarKclInput.fill(lengthValue.new)
|
|
||||||
await expect(
|
|
||||||
page.getByText(`Can't calculate`),
|
|
||||||
`Something went wrong with the KCL expression evaluation`
|
|
||||||
).not.toBeVisible()
|
|
||||||
await cmdBarSubmitButton.click()
|
|
||||||
|
|
||||||
await expect(page.locator('.cm-content')).toHaveText(
|
await expect(page.locator('.cm-content')).toHaveText(
|
||||||
`length001 = ${lengthValue.new}sketch001 = startSketchOn('XY') |> startProfileAt([-10, -10], %) |> line([20, 0], %) |> angledLine([90, length001], %) |> xLine(-20, %)`
|
`length001 = 20sketch001 = startSketchOn('XY') |> startProfileAt([-10, -10], %) |> line([20, 0], %) |> angledLine([90, length001], %) |> xLine(-20, %)`
|
||||||
)
|
)
|
||||||
|
|
||||||
// Make sure we didn't pop out of sketch mode.
|
// Make sure we didn't pop out of sketch mode.
|
||||||
@ -76,6 +66,7 @@ test.describe('Testing constraints', () => {
|
|||||||
await page.waitForTimeout(500) // wait for animation
|
await page.waitForTimeout(500) // wait for animation
|
||||||
|
|
||||||
// Exit sketch
|
// Exit sketch
|
||||||
|
await page.mouse.move(startXPx + PUR * 15, 250 - PUR * 10)
|
||||||
await page.keyboard.press('Escape')
|
await page.keyboard.press('Escape')
|
||||||
await expect(
|
await expect(
|
||||||
page.getByRole('button', { name: 'Exit Sketch' })
|
page.getByRole('button', { name: 'Exit Sketch' })
|
||||||
@ -533,7 +524,7 @@ part002 = startSketchOn('XZ')
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
test.describe('Test Angle constraint single selection', () => {
|
test.describe('Test Angle/Length constraint single selection', () => {
|
||||||
const cases = [
|
const cases = [
|
||||||
{
|
{
|
||||||
testName: 'Angle - Add variable',
|
testName: 'Angle - Add variable',
|
||||||
@ -547,6 +538,18 @@ part002 = startSketchOn('XZ')
|
|||||||
constraint: 'angle',
|
constraint: 'angle',
|
||||||
value: '83, 78.33',
|
value: '83, 78.33',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
testName: 'Length - Add variable',
|
||||||
|
addVariable: true,
|
||||||
|
constraint: 'length',
|
||||||
|
value: '83, length001',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
testName: 'Length - No variable',
|
||||||
|
addVariable: false,
|
||||||
|
constraint: 'length',
|
||||||
|
value: '83, 78.33',
|
||||||
|
},
|
||||||
] as const
|
] as const
|
||||||
for (const { testName, addVariable, value, constraint } of cases) {
|
for (const { testName, addVariable, value, constraint } of cases) {
|
||||||
test(`${testName}`, async ({ page }) => {
|
test(`${testName}`, async ({ page }) => {
|
||||||
@ -605,90 +608,6 @@ part002 = startSketchOn('XZ')
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
test.describe('Test Length constraint single selection', () => {
|
|
||||||
const cases = [
|
|
||||||
{
|
|
||||||
testName: 'Length - Add variable',
|
|
||||||
addVariable: true,
|
|
||||||
constraint: 'length',
|
|
||||||
value: '83, length001',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
testName: 'Length - No variable',
|
|
||||||
addVariable: false,
|
|
||||||
constraint: 'length',
|
|
||||||
value: '83, 78.33',
|
|
||||||
},
|
|
||||||
] as const
|
|
||||||
for (const { testName, addVariable, value, constraint } of cases) {
|
|
||||||
test(`${testName}`, async ({ page }) => {
|
|
||||||
// constants and locators
|
|
||||||
const cmdBarKclInput = page
|
|
||||||
.getByTestId('cmd-bar-arg-value')
|
|
||||||
.getByRole('textbox')
|
|
||||||
const cmdBarKclVariableNameInput =
|
|
||||||
page.getByPlaceholder('Variable name')
|
|
||||||
const cmdBarSubmitButton = page.getByRole('button', {
|
|
||||||
name: 'arrow right Continue',
|
|
||||||
})
|
|
||||||
|
|
||||||
await page.addInitScript(async () => {
|
|
||||||
localStorage.setItem(
|
|
||||||
'persistCode',
|
|
||||||
`yo = 5
|
|
||||||
part001 = startSketchOn('XZ')
|
|
||||||
|> startProfileAt([-7.54, -26.74], %)
|
|
||||||
|> line([74.36, 130.4], %)
|
|
||||||
|> line([78.92, -120.11], %)
|
|
||||||
|> line([9.16, 77.79], %)
|
|
||||||
|> line([51.19, 48.97], %)
|
|
||||||
part002 = startSketchOn('XZ')
|
|
||||||
|> startProfileAt([299.05, 231.45], %)
|
|
||||||
|> xLine(-425.34, %, $seg_what)
|
|
||||||
|> yLine(-264.06, %)
|
|
||||||
|> xLine(segLen(seg_what), %)
|
|
||||||
|> lineTo([profileStartX(%), profileStartY(%)], %)`
|
|
||||||
)
|
|
||||||
})
|
|
||||||
const u = await getUtils(page)
|
|
||||||
await page.setViewportSize({ width: 1200, height: 500 })
|
|
||||||
|
|
||||||
await u.waitForAuthSkipAppStart()
|
|
||||||
|
|
||||||
await page.getByText('line([74.36, 130.4], %)').click()
|
|
||||||
await page.getByRole('button', { name: 'Edit Sketch' }).click()
|
|
||||||
|
|
||||||
const line3 = await u.getSegmentBodyCoords(
|
|
||||||
`[data-overlay-index="${2}"]`
|
|
||||||
)
|
|
||||||
|
|
||||||
await page.mouse.click(line3.x, line3.y)
|
|
||||||
await page
|
|
||||||
.getByRole('button', {
|
|
||||||
name: 'Length: open menu',
|
|
||||||
})
|
|
||||||
.click()
|
|
||||||
await page.getByTestId('dropdown-constraint-' + constraint).click()
|
|
||||||
|
|
||||||
if (!addVariable) {
|
|
||||||
await test.step(`Clear the variable input`, async () => {
|
|
||||||
await cmdBarKclVariableNameInput.clear()
|
|
||||||
await cmdBarKclVariableNameInput.press('Backspace')
|
|
||||||
})
|
|
||||||
}
|
|
||||||
await expect(cmdBarKclInput).toHaveText('78.33')
|
|
||||||
await cmdBarSubmitButton.click()
|
|
||||||
|
|
||||||
const changedCode = `|> angledLine([${value}], %)`
|
|
||||||
await expect(page.locator('.cm-content')).toContainText(changedCode)
|
|
||||||
// checking active assures the cursor is where it should be
|
|
||||||
await expect(page.locator('.cm-activeLine')).toHaveText(changedCode)
|
|
||||||
|
|
||||||
// checking the count of the overlays is a good proxy check that the client sketch scene is in a good state
|
|
||||||
await expect(page.getByTestId('segment-overlay')).toHaveCount(4)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
test.describe('Many segments - no modal constraints', () => {
|
test.describe('Many segments - no modal constraints', () => {
|
||||||
const cases = [
|
const cases = [
|
||||||
{
|
{
|
||||||
@ -949,15 +868,6 @@ part002 = startSketchOn('XZ')
|
|||||||
|> line([3.13, -2.4], %)`
|
|> line([3.13, -2.4], %)`
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
// constants and locators
|
|
||||||
const cmdBarKclInput = page
|
|
||||||
.getByTestId('cmd-bar-arg-value')
|
|
||||||
.getByRole('textbox')
|
|
||||||
const cmdBarSubmitButton = page.getByRole('button', {
|
|
||||||
name: 'arrow right Continue',
|
|
||||||
})
|
|
||||||
|
|
||||||
const u = await getUtils(page)
|
const u = await getUtils(page)
|
||||||
await page.setViewportSize({ width: 1200, height: 500 })
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
|
|
||||||
@ -1018,8 +928,8 @@ part002 = startSketchOn('XZ')
|
|||||||
// await page.getByRole('button', { name: 'length', exact: true }).click()
|
// await page.getByRole('button', { name: 'length', exact: true }).click()
|
||||||
await page.getByTestId('dropdown-constraint-length').click()
|
await page.getByTestId('dropdown-constraint-length').click()
|
||||||
|
|
||||||
await cmdBarKclInput.fill('10')
|
await page.getByLabel('length Value').fill('10')
|
||||||
await cmdBarSubmitButton.click()
|
await page.getByRole('button', { name: 'Add constraining value' }).click()
|
||||||
|
|
||||||
activeLinesContent = await page.locator('.cm-activeLine').all()
|
activeLinesContent = await page.locator('.cm-activeLine').all()
|
||||||
await expect(activeLinesContent[0]).toHaveText(`|> xLine(length001, %)`)
|
await expect(activeLinesContent[0]).toHaveText(`|> xLine(length001, %)`)
|
||||||
|
@ -23,7 +23,7 @@ test.describe('Test toggling perspective', () => {
|
|||||||
y: screenHeight * 0.4,
|
y: screenHeight * 0.4,
|
||||||
}
|
}
|
||||||
const backgroundColor: [number, number, number] = [29, 29, 29]
|
const backgroundColor: [number, number, number] = [29, 29, 29]
|
||||||
const xzPlaneColor: [number, number, number] = [82, 55, 96]
|
const xzPlaneColor: [number, number, number] = [50, 50, 99]
|
||||||
const locationToHaveColor = async (color: [number, number, number]) => {
|
const locationToHaveColor = async (color: [number, number, number]) => {
|
||||||
return u.getGreatestPixDiff(checkedScreenLocation, color)
|
return u.getGreatestPixDiff(checkedScreenLocation, color)
|
||||||
}
|
}
|
||||||
|
@ -91,14 +91,7 @@ test.describe('Testing segment overlays', () => {
|
|||||||
await page.getByTestId('constraint-symbol-popover').count()
|
await page.getByTestId('constraint-symbol-popover').count()
|
||||||
).toBeGreaterThan(0)
|
).toBeGreaterThan(0)
|
||||||
await unconstrainedLocator.click()
|
await unconstrainedLocator.click()
|
||||||
await expect(
|
await page.getByText('Add variable').click()
|
||||||
page.getByTestId('cmd-bar-arg-value').getByRole('textbox')
|
|
||||||
).toBeFocused()
|
|
||||||
await page
|
|
||||||
.getByRole('button', {
|
|
||||||
name: 'arrow right Continue',
|
|
||||||
})
|
|
||||||
.click()
|
|
||||||
await expect(page.locator('.cm-content')).toContainText(expectFinal)
|
await expect(page.locator('.cm-content')).toContainText(expectFinal)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -158,14 +151,7 @@ test.describe('Testing segment overlays', () => {
|
|||||||
await page.getByTestId('constraint-symbol-popover').count()
|
await page.getByTestId('constraint-symbol-popover').count()
|
||||||
).toBeGreaterThan(0)
|
).toBeGreaterThan(0)
|
||||||
await unconstrainedLocator.click()
|
await unconstrainedLocator.click()
|
||||||
await expect(
|
await page.getByText('Add variable').click()
|
||||||
page.getByTestId('cmd-bar-arg-value').getByRole('textbox')
|
|
||||||
).toBeFocused()
|
|
||||||
await page
|
|
||||||
.getByRole('button', {
|
|
||||||
name: 'arrow right Continue',
|
|
||||||
})
|
|
||||||
.click()
|
|
||||||
await expect(page.locator('.cm-content')).toContainText(
|
await expect(page.locator('.cm-content')).toContainText(
|
||||||
expectAfterUnconstrained
|
expectAfterUnconstrained
|
||||||
)
|
)
|
||||||
|
11
package.json
@ -81,7 +81,6 @@
|
|||||||
"simpleserver": "yarn pretest && http-server ./public --cors -p 3000",
|
"simpleserver": "yarn pretest && http-server ./public --cors -p 3000",
|
||||||
"simpleserver:ci": "yarn pretest && http-server ./public --cors -p 3000 &",
|
"simpleserver:ci": "yarn pretest && http-server ./public --cors -p 3000 &",
|
||||||
"simpleserver:bg": "yarn pretest && http-server ./public --cors -p 3000 &",
|
"simpleserver:bg": "yarn pretest && http-server ./public --cors -p 3000 &",
|
||||||
"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": "./get-latest-wasm-bundle.sh",
|
"fetch:wasm": "./get-latest-wasm-bundle.sh",
|
||||||
@ -96,8 +95,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:invalidate-bucket": "./scripts/invalidate-files-bucket.sh",
|
|
||||||
"files:invalidate-bucket:nightly": "./scripts/invalidate-files-bucket.sh --nightly",
|
|
||||||
"postinstall": "yarn fetch:samples && yarn xstate:typegen && ./node_modules/.bin/electron-rebuild",
|
"postinstall": "yarn fetch:samples && yarn xstate:typegen && ./node_modules/.bin/electron-rebuild",
|
||||||
"xstate:typegen": "yarn xstate typegen \"src/**/*.ts?(x)\"",
|
"xstate:typegen": "yarn xstate typegen \"src/**/*.ts?(x)\"",
|
||||||
"make:dev": "make dev",
|
"make:dev": "make dev",
|
||||||
@ -161,7 +158,6 @@
|
|||||||
"@electron/rebuild": "^3.6.0",
|
"@electron/rebuild": "^3.6.0",
|
||||||
"@iarna/toml": "^2.2.5",
|
"@iarna/toml": "^2.2.5",
|
||||||
"@lezer/generator": "^1.7.1",
|
"@lezer/generator": "^1.7.1",
|
||||||
"@nabla/vite-plugin-eslint": "^2.0.5",
|
|
||||||
"@playwright/test": "^1.46.1",
|
"@playwright/test": "^1.46.1",
|
||||||
"@testing-library/jest-dom": "^5.14.1",
|
"@testing-library/jest-dom": "^5.14.1",
|
||||||
"@testing-library/react": "^15.0.2",
|
"@testing-library/react": "^15.0.2",
|
||||||
@ -174,7 +170,7 @@
|
|||||||
"@types/pixelmatch": "^5.2.6",
|
"@types/pixelmatch": "^5.2.6",
|
||||||
"@types/pngjs": "^6.0.4",
|
"@types/pngjs": "^6.0.4",
|
||||||
"@types/react": "^18.3.4",
|
"@types/react": "^18.3.4",
|
||||||
"@types/react-dom": "^18.3.1",
|
"@types/react-dom": "^18.2.25",
|
||||||
"@types/react-modal": "^3.16.3",
|
"@types/react-modal": "^3.16.3",
|
||||||
"@types/three": "^0.163.0",
|
"@types/three": "^0.163.0",
|
||||||
"@types/ua-parser-js": "^0.7.39",
|
"@types/ua-parser-js": "^0.7.39",
|
||||||
@ -196,7 +192,7 @@
|
|||||||
"eslint-plugin-css-modules": "^2.12.0",
|
"eslint-plugin-css-modules": "^2.12.0",
|
||||||
"eslint-plugin-import": "^2.30.0",
|
"eslint-plugin-import": "^2.30.0",
|
||||||
"eslint-plugin-suggest-no-throw": "^1.0.0",
|
"eslint-plugin-suggest-no-throw": "^1.0.0",
|
||||||
"happy-dom": "^15.11.7",
|
"happy-dom": "^15.10.2",
|
||||||
"http-server": "^14.1.1",
|
"http-server": "^14.1.1",
|
||||||
"husky": "^9.1.5",
|
"husky": "^9.1.5",
|
||||||
"kill-port": "^2.0.1",
|
"kill-port": "^2.0.1",
|
||||||
@ -211,11 +207,12 @@
|
|||||||
"ts-node": "^10.0.0",
|
"ts-node": "^10.0.0",
|
||||||
"typescript": "^5.7.2",
|
"typescript": "^5.7.2",
|
||||||
"vite": "^5.4.6",
|
"vite": "^5.4.6",
|
||||||
|
"vite-plugin-eslint": "^1.8.1",
|
||||||
"vite-plugin-package-version": "^1.1.0",
|
"vite-plugin-package-version": "^1.1.0",
|
||||||
"vite-tsconfig-paths": "^4.3.2",
|
"vite-tsconfig-paths": "^4.3.2",
|
||||||
"vitest": "^1.6.0",
|
"vitest": "^1.6.0",
|
||||||
"vitest-webgl-canvas-mock": "^1.1.0",
|
"vitest-webgl-canvas-mock": "^1.1.0",
|
||||||
"wasm-pack": "^0.13.1",
|
"wasm-pack": "^0.13.0",
|
||||||
"ws": "^8.17.0",
|
"ws": "^8.17.0",
|
||||||
"yarn": "^1.22.22"
|
"yarn": "^1.22.22"
|
||||||
}
|
}
|
||||||
|
@ -1,5 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
echo "## What's Changed"
|
|
||||||
git log ${PREVIOUS_TAG}..HEAD --oneline --pretty=format:%s | grep -v Bump | awk '{print "* "toupper(substr($0,0,1))substr($0,2)}'
|
|
||||||
echo ""
|
|
||||||
echo "**Full Changelog**: https://github.com/KittyCAD/modeling-app/compare/${PREVIOUS_TAG}...${TAG}"
|
|
@ -1,11 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
base_dir="/releases/modeling-app"
|
|
||||||
if [[ $1 = "--nightly" ]]; then
|
|
||||||
base_dir="/releases/modeling-app/nightly"
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "Invalidating json and yml files at $base_dir in the download bucket"
|
|
||||||
gcloud compute url-maps invalidate-cdn-cache dl-url-map --path="$base_dir/last_download.json" --async
|
|
||||||
gcloud compute url-maps invalidate-cdn-cache dl-url-map --path="$base_dir/latest-linux-arm64.yml" --async
|
|
||||||
gcloud compute url-maps invalidate-cdn-cache dl-url-map --path="$base_dir/latest-mac.yml" --async
|
|
||||||
gcloud compute url-maps invalidate-cdn-cache dl-url-map --path="$base_dir/latest.yml" --async
|
|
@ -200,10 +200,7 @@ function CoreDump() {
|
|||||||
() => new CoreDumpManager(engineCommandManager, codeManager, token),
|
() => new CoreDumpManager(engineCommandManager, codeManager, token),
|
||||||
[]
|
[]
|
||||||
)
|
)
|
||||||
// TODO: revisit once progress is made on upstream issue
|
useHotkeyWrapper(['mod + shift + .'], () => {
|
||||||
// https://github.com/JohannesKlauss/react-hotkeys-hook/issues/1064
|
|
||||||
// const hotkey = process.platform !== 'linux' ? 'mod + shift + .' : 'mod + shift + >'
|
|
||||||
useHotkeyWrapper(['mod + shift + .', 'mod + shift + >'], () => {
|
|
||||||
toast
|
toast
|
||||||
.promise(
|
.promise(
|
||||||
coreDump(coreDumpManager, true),
|
coreDump(coreDumpManager, true),
|
||||||
|
@ -155,6 +155,7 @@ export class CameraControls {
|
|||||||
this.camera.zoom = camProps.zoom || 1
|
this.camera.zoom = camProps.zoom || 1
|
||||||
}
|
}
|
||||||
this.camera.updateProjectionMatrix()
|
this.camera.updateProjectionMatrix()
|
||||||
|
console.log('doing this thing', camProps)
|
||||||
this.update(true)
|
this.update(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -272,26 +273,14 @@ export class CameraControls {
|
|||||||
camSettings.center.y,
|
camSettings.center.y,
|
||||||
camSettings.center.z
|
camSettings.center.z
|
||||||
)
|
)
|
||||||
const orientation = new Quaternion(
|
const quat = new Quaternion(
|
||||||
camSettings.orientation.x,
|
camSettings.orientation.x,
|
||||||
camSettings.orientation.y,
|
camSettings.orientation.y,
|
||||||
camSettings.orientation.z,
|
camSettings.orientation.z,
|
||||||
camSettings.orientation.w
|
camSettings.orientation.w
|
||||||
).invert()
|
).invert()
|
||||||
|
|
||||||
const newUp = new Vector3(
|
this.camera.up.copy(new Vector3(0, 1, 0).applyQuaternion(quat))
|
||||||
camSettings.up.x,
|
|
||||||
camSettings.up.y,
|
|
||||||
camSettings.up.z
|
|
||||||
)
|
|
||||||
this.camera.quaternion.set(
|
|
||||||
orientation.x,
|
|
||||||
orientation.y,
|
|
||||||
orientation.z,
|
|
||||||
orientation.w
|
|
||||||
)
|
|
||||||
this.camera.up.copy(newUp)
|
|
||||||
this.camera.updateProjectionMatrix()
|
|
||||||
if (this.camera instanceof PerspectiveCamera && camSettings.ortho) {
|
if (this.camera instanceof PerspectiveCamera && camSettings.ortho) {
|
||||||
this.useOrthographicCamera()
|
this.useOrthographicCamera()
|
||||||
}
|
}
|
||||||
@ -1175,7 +1164,7 @@ export class CameraControls {
|
|||||||
this.camera.updateProjectionMatrix()
|
this.camera.updateProjectionMatrix()
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.syncDirection === 'clientToEngine' || forceUpdate) {
|
if (this.syncDirection === 'clientToEngine' || forceUpdate)
|
||||||
this.throttledUpdateEngineCamera({
|
this.throttledUpdateEngineCamera({
|
||||||
quaternion: this.camera.quaternion,
|
quaternion: this.camera.quaternion,
|
||||||
position: this.camera.position,
|
position: this.camera.position,
|
||||||
@ -1183,7 +1172,6 @@ export class CameraControls {
|
|||||||
isPerspective: this.isPerspective,
|
isPerspective: this.isPerspective,
|
||||||
target: this.target,
|
target: this.target,
|
||||||
})
|
})
|
||||||
}
|
|
||||||
this.deferReactUpdate(this.reactCameraProperties)
|
this.deferReactUpdate(this.reactCameraProperties)
|
||||||
Object.values(this._camChangeCallbacks).forEach((cb) => cb())
|
Object.values(this._camChangeCallbacks).forEach((cb) => cb())
|
||||||
}
|
}
|
||||||
|
@ -29,9 +29,6 @@ import {
|
|||||||
Expr,
|
Expr,
|
||||||
parse,
|
parse,
|
||||||
recast,
|
recast,
|
||||||
defaultSourceRange,
|
|
||||||
resultIsOk,
|
|
||||||
ProgramMemory,
|
|
||||||
} from 'lang/wasm'
|
} from 'lang/wasm'
|
||||||
import { CustomIcon, CustomIconName } from 'components/CustomIcon'
|
import { CustomIcon, CustomIconName } from 'components/CustomIcon'
|
||||||
import { ConstrainInfo } from 'lang/std/stdTypes'
|
import { ConstrainInfo } from 'lang/std/stdTypes'
|
||||||
@ -415,15 +412,14 @@ export async function deleteSegment({
|
|||||||
if (err(modifiedAst)) return Promise.reject(modifiedAst)
|
if (err(modifiedAst)) return Promise.reject(modifiedAst)
|
||||||
|
|
||||||
const newCode = recast(modifiedAst)
|
const newCode = recast(modifiedAst)
|
||||||
const pResult = parse(newCode)
|
modifiedAst = parse(newCode)
|
||||||
if (err(pResult) || !resultIsOk(pResult)) return Promise.reject(pResult)
|
if (err(modifiedAst)) return Promise.reject(modifiedAst)
|
||||||
modifiedAst = pResult.program
|
|
||||||
|
|
||||||
const testExecute = await executeAst({
|
const testExecute = await executeAst({
|
||||||
ast: modifiedAst,
|
ast: modifiedAst,
|
||||||
|
idGenerator: kclManager.execState.idGenerator,
|
||||||
|
useFakeExecutor: true,
|
||||||
engineCommandManager: engineCommandManager,
|
engineCommandManager: engineCommandManager,
|
||||||
// We make sure to send an empty program memory to denote we mean mock mode.
|
|
||||||
programMemoryOverride: ProgramMemory.empty(),
|
|
||||||
})
|
})
|
||||||
if (testExecute.errors.length) {
|
if (testExecute.errors.length) {
|
||||||
toast.error('Segment tag used outside of current Sketch. Could not delete.')
|
toast.error('Segment tag used outside of current Sketch. Could not delete.')
|
||||||
@ -505,8 +501,7 @@ const ConstraintSymbol = ({
|
|||||||
constrainInfo: ConstrainInfo
|
constrainInfo: ConstrainInfo
|
||||||
verticalPosition: 'top' | 'bottom'
|
verticalPosition: 'top' | 'bottom'
|
||||||
}) => {
|
}) => {
|
||||||
const { commandBarSend } = useCommandsContext()
|
const { context, send } = useModelingContext()
|
||||||
const { context } = useModelingContext()
|
|
||||||
const varNameMap: {
|
const varNameMap: {
|
||||||
[key in ConstrainInfo['type']]: {
|
[key in ConstrainInfo['type']]: {
|
||||||
varName: string
|
varName: string
|
||||||
@ -595,9 +590,7 @@ const ConstraintSymbol = ({
|
|||||||
if (err(_node)) return
|
if (err(_node)) return
|
||||||
const node = _node.node
|
const node = _node.node
|
||||||
|
|
||||||
const range: SourceRange = node
|
const range: SourceRange = node ? [node.start, node.end] : [0, 0]
|
||||||
? [node.start, node.end, true]
|
|
||||||
: defaultSourceRange()
|
|
||||||
|
|
||||||
if (_type === 'intersectionTag') return null
|
if (_type === 'intersectionTag') return null
|
||||||
|
|
||||||
@ -619,34 +612,25 @@ const ConstraintSymbol = ({
|
|||||||
editorManager.setHighlightRange([range])
|
editorManager.setHighlightRange([range])
|
||||||
}}
|
}}
|
||||||
onMouseLeave={() => {
|
onMouseLeave={() => {
|
||||||
editorManager.setHighlightRange([defaultSourceRange()])
|
editorManager.setHighlightRange([[0, 0]])
|
||||||
}}
|
}}
|
||||||
// disabled={isConstrained || !convertToVarEnabled}
|
// disabled={isConstrained || !convertToVarEnabled}
|
||||||
// disabled={implicitDesc} TODO why does this change styles that are hard to override?
|
// disabled={implicitDesc} TODO why does this change styles that are hard to override?
|
||||||
onClick={toSync(async () => {
|
onClick={toSync(async () => {
|
||||||
if (!isConstrained) {
|
if (!isConstrained) {
|
||||||
commandBarSend({
|
send({
|
||||||
type: 'Find and select command',
|
type: 'Convert to variable',
|
||||||
data: {
|
data: {
|
||||||
name: 'Constrain with named value',
|
pathToNode,
|
||||||
groupId: 'modeling',
|
variableName: varName,
|
||||||
argDefaultValues: {
|
|
||||||
currentValue: {
|
|
||||||
pathToNode,
|
|
||||||
variableName: varName,
|
|
||||||
valueText: value,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
} else if (isConstrained) {
|
} else if (isConstrained) {
|
||||||
try {
|
try {
|
||||||
const pResult = parse(recast(kclManager.ast))
|
const parsed = parse(recast(kclManager.ast))
|
||||||
if (trap(pResult) || !resultIsOk(pResult))
|
if (trap(parsed)) return Promise.reject(parsed)
|
||||||
return Promise.reject(pResult)
|
|
||||||
|
|
||||||
const _node1 = getNodeFromPath<CallExpression>(
|
const _node1 = getNodeFromPath<CallExpression>(
|
||||||
pResult.program!,
|
parsed,
|
||||||
pathToNode,
|
pathToNode,
|
||||||
'CallExpression',
|
'CallExpression',
|
||||||
true
|
true
|
||||||
|
@ -48,9 +48,6 @@ import {
|
|||||||
VariableDeclarator,
|
VariableDeclarator,
|
||||||
sketchFromKclValue,
|
sketchFromKclValue,
|
||||||
sketchFromKclValueOptional,
|
sketchFromKclValueOptional,
|
||||||
defaultSourceRange,
|
|
||||||
sourceRangeFromRust,
|
|
||||||
resultIsOk,
|
|
||||||
} from 'lang/wasm'
|
} from 'lang/wasm'
|
||||||
import {
|
import {
|
||||||
engineCommandManager,
|
engineCommandManager,
|
||||||
@ -498,9 +495,10 @@ export class SceneEntities {
|
|||||||
|
|
||||||
const { execState } = await executeAst({
|
const { execState } = await executeAst({
|
||||||
ast: truncatedAst,
|
ast: truncatedAst,
|
||||||
|
useFakeExecutor: true,
|
||||||
engineCommandManager: this.engineCommandManager,
|
engineCommandManager: this.engineCommandManager,
|
||||||
// We make sure to send an empty program memory to denote we mean mock mode.
|
|
||||||
programMemoryOverride,
|
programMemoryOverride,
|
||||||
|
idGenerator: kclManager.execState.idGenerator,
|
||||||
})
|
})
|
||||||
const programMemory = execState.memory
|
const programMemory = execState.memory
|
||||||
const sketch = sketchFromPathToNode({
|
const sketch = sketchFromPathToNode({
|
||||||
@ -532,7 +530,7 @@ export class SceneEntities {
|
|||||||
|
|
||||||
const segPathToNode = getNodePathFromSourceRange(
|
const segPathToNode = getNodePathFromSourceRange(
|
||||||
maybeModdedAst,
|
maybeModdedAst,
|
||||||
sourceRangeFromRust(sketch.start.__geoMeta.sourceRange)
|
sketch.start.__geoMeta.sourceRange
|
||||||
)
|
)
|
||||||
if (sketch?.paths?.[0]?.type !== 'Circle') {
|
if (sketch?.paths?.[0]?.type !== 'Circle') {
|
||||||
const _profileStart = createProfileStartHandle({
|
const _profileStart = createProfileStartHandle({
|
||||||
@ -554,7 +552,7 @@ export class SceneEntities {
|
|||||||
sketch.paths.forEach((segment, index) => {
|
sketch.paths.forEach((segment, index) => {
|
||||||
let segPathToNode = getNodePathFromSourceRange(
|
let segPathToNode = getNodePathFromSourceRange(
|
||||||
maybeModdedAst,
|
maybeModdedAst,
|
||||||
sourceRangeFromRust(segment.__geoMeta.sourceRange)
|
segment.__geoMeta.sourceRange
|
||||||
)
|
)
|
||||||
if (
|
if (
|
||||||
draftExpressionsIndices &&
|
draftExpressionsIndices &&
|
||||||
@ -563,12 +561,12 @@ export class SceneEntities {
|
|||||||
const previousSegment = sketch.paths[index - 1] || sketch.start
|
const previousSegment = sketch.paths[index - 1] || sketch.start
|
||||||
const previousSegmentPathToNode = getNodePathFromSourceRange(
|
const previousSegmentPathToNode = getNodePathFromSourceRange(
|
||||||
maybeModdedAst,
|
maybeModdedAst,
|
||||||
sourceRangeFromRust(previousSegment.__geoMeta.sourceRange)
|
previousSegment.__geoMeta.sourceRange
|
||||||
)
|
)
|
||||||
const bodyIndex = previousSegmentPathToNode[1][0]
|
const bodyIndex = previousSegmentPathToNode[1][0]
|
||||||
segPathToNode = getNodePathFromSourceRange(
|
segPathToNode = getNodePathFromSourceRange(
|
||||||
truncatedAst,
|
truncatedAst,
|
||||||
sourceRangeFromRust(segment.__geoMeta.sourceRange)
|
segment.__geoMeta.sourceRange
|
||||||
)
|
)
|
||||||
segPathToNode[1][0] = bodyIndex
|
segPathToNode[1][0] = bodyIndex
|
||||||
}
|
}
|
||||||
@ -577,10 +575,7 @@ export class SceneEntities {
|
|||||||
index <= draftExpressionsIndices.end &&
|
index <= draftExpressionsIndices.end &&
|
||||||
index >= draftExpressionsIndices.start
|
index >= draftExpressionsIndices.start
|
||||||
const isSelected = selectionRanges?.graphSelections.some((selection) =>
|
const isSelected = selectionRanges?.graphSelections.some((selection) =>
|
||||||
isOverlap(
|
isOverlap(selection?.codeRef?.range, segment.__geoMeta.sourceRange)
|
||||||
selection?.codeRef?.range,
|
|
||||||
sourceRangeFromRust(segment.__geoMeta.sourceRange)
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
let seg: Group
|
let seg: Group
|
||||||
@ -662,11 +657,13 @@ export class SceneEntities {
|
|||||||
}
|
}
|
||||||
updateAstAndRejigSketch = async (
|
updateAstAndRejigSketch = async (
|
||||||
sketchPathToNode: PathToNode,
|
sketchPathToNode: PathToNode,
|
||||||
modifiedAst: Node<Program>,
|
modifiedAst: Node<Program> | Error,
|
||||||
forward: [number, number, number],
|
forward: [number, number, number],
|
||||||
up: [number, number, number],
|
up: [number, number, number],
|
||||||
origin: [number, number, number]
|
origin: [number, number, number]
|
||||||
) => {
|
) => {
|
||||||
|
if (err(modifiedAst)) return modifiedAst
|
||||||
|
|
||||||
const nextAst = await kclManager.updateAst(modifiedAst, false)
|
const nextAst = await kclManager.updateAst(modifiedAst, false)
|
||||||
await this.tearDownSketch({ removeAxis: false })
|
await this.tearDownSketch({ removeAxis: false })
|
||||||
sceneInfra.resetMouseListeners()
|
sceneInfra.resetMouseListeners()
|
||||||
@ -701,7 +698,8 @@ export class SceneEntities {
|
|||||||
'VariableDeclaration'
|
'VariableDeclaration'
|
||||||
)
|
)
|
||||||
if (trap(_node1)) return Promise.reject(_node1)
|
if (trap(_node1)) return Promise.reject(_node1)
|
||||||
const variableDeclarationName = _node1.node?.declaration.id?.name || ''
|
const variableDeclarationName =
|
||||||
|
_node1.node?.declarations?.[0]?.id?.name || ''
|
||||||
|
|
||||||
const sg = sketchFromKclValue(
|
const sg = sketchFromKclValue(
|
||||||
kclManager.programMemory.get(variableDeclarationName),
|
kclManager.programMemory.get(variableDeclarationName),
|
||||||
@ -723,9 +721,8 @@ export class SceneEntities {
|
|||||||
pathToNode: sketchPathToNode,
|
pathToNode: sketchPathToNode,
|
||||||
})
|
})
|
||||||
if (trap(mod)) return Promise.reject(mod)
|
if (trap(mod)) return Promise.reject(mod)
|
||||||
const pResult = parse(recast(mod.modifiedAst))
|
const modifiedAst = parse(recast(mod.modifiedAst))
|
||||||
if (trap(pResult) || !resultIsOk(pResult)) return Promise.reject(pResult)
|
if (trap(modifiedAst)) return Promise.reject(modifiedAst)
|
||||||
const modifiedAst = pResult.program
|
|
||||||
|
|
||||||
const draftExpressionsIndices = { start: index, end: index }
|
const draftExpressionsIndices = { start: index, end: index }
|
||||||
|
|
||||||
@ -901,9 +898,10 @@ export class SceneEntities {
|
|||||||
'VariableDeclaration'
|
'VariableDeclaration'
|
||||||
)
|
)
|
||||||
if (trap(_node1)) return Promise.reject(_node1)
|
if (trap(_node1)) return Promise.reject(_node1)
|
||||||
const variableDeclarationName = _node1.node?.declaration.id?.name || ''
|
const variableDeclarationName =
|
||||||
const startSketchOn = _node1.node?.declaration
|
_node1.node?.declarations?.[0]?.id?.name || ''
|
||||||
const startSketchOnInit = startSketchOn?.init
|
const startSketchOn = _node1.node?.declarations
|
||||||
|
const startSketchOnInit = startSketchOn?.[0]?.init
|
||||||
|
|
||||||
const tags: [string, string, string] = [
|
const tags: [string, string, string] = [
|
||||||
findUniqueName(_ast, 'rectangleSegmentA'),
|
findUniqueName(_ast, 'rectangleSegmentA'),
|
||||||
@ -911,14 +909,14 @@ export class SceneEntities {
|
|||||||
findUniqueName(_ast, 'rectangleSegmentC'),
|
findUniqueName(_ast, 'rectangleSegmentC'),
|
||||||
]
|
]
|
||||||
|
|
||||||
startSketchOn.init = createPipeExpression([
|
startSketchOn[0].init = createPipeExpression([
|
||||||
startSketchOnInit,
|
startSketchOnInit,
|
||||||
...getRectangleCallExpressions(rectangleOrigin, tags),
|
...getRectangleCallExpressions(rectangleOrigin, tags),
|
||||||
])
|
])
|
||||||
|
|
||||||
const pResult = parse(recast(_ast))
|
let _recastAst = parse(recast(_ast))
|
||||||
if (trap(pResult) || !resultIsOk(pResult)) return Promise.reject(pResult)
|
if (trap(_recastAst)) return Promise.reject(_recastAst)
|
||||||
_ast = pResult.program
|
_ast = _recastAst
|
||||||
|
|
||||||
const { programMemoryOverride, truncatedAst } = await this.setupSketch({
|
const { programMemoryOverride, truncatedAst } = await this.setupSketch({
|
||||||
sketchPathToNode,
|
sketchPathToNode,
|
||||||
@ -941,7 +939,7 @@ export class SceneEntities {
|
|||||||
'VariableDeclaration'
|
'VariableDeclaration'
|
||||||
)
|
)
|
||||||
if (trap(_node)) return Promise.reject(_node)
|
if (trap(_node)) return Promise.reject(_node)
|
||||||
const sketchInit = _node.node?.declaration.init
|
const sketchInit = _node.node?.declarations?.[0]?.init
|
||||||
|
|
||||||
const x = (args.intersectionPoint.twoD.x || 0) - rectangleOrigin[0]
|
const x = (args.intersectionPoint.twoD.x || 0) - rectangleOrigin[0]
|
||||||
const y = (args.intersectionPoint.twoD.y || 0) - rectangleOrigin[1]
|
const y = (args.intersectionPoint.twoD.y || 0) - rectangleOrigin[1]
|
||||||
@ -952,9 +950,10 @@ export class SceneEntities {
|
|||||||
|
|
||||||
const { execState } = await executeAst({
|
const { execState } = await executeAst({
|
||||||
ast: truncatedAst,
|
ast: truncatedAst,
|
||||||
|
useFakeExecutor: true,
|
||||||
engineCommandManager: this.engineCommandManager,
|
engineCommandManager: this.engineCommandManager,
|
||||||
// We make sure to send an empty program memory to denote we mean mock mode.
|
|
||||||
programMemoryOverride,
|
programMemoryOverride,
|
||||||
|
idGenerator: kclManager.execState.idGenerator,
|
||||||
})
|
})
|
||||||
const programMemory = execState.memory
|
const programMemory = execState.memory
|
||||||
this.sceneProgramMemory = programMemory
|
this.sceneProgramMemory = programMemory
|
||||||
@ -990,7 +989,7 @@ export class SceneEntities {
|
|||||||
'VariableDeclaration'
|
'VariableDeclaration'
|
||||||
)
|
)
|
||||||
if (trap(_node)) return
|
if (trap(_node)) return
|
||||||
const sketchInit = _node.node?.declaration.init
|
const sketchInit = _node.node?.declarations?.[0]?.init
|
||||||
|
|
||||||
if (sketchInit.type !== 'PipeExpression') {
|
if (sketchInit.type !== 'PipeExpression') {
|
||||||
return
|
return
|
||||||
@ -999,10 +998,9 @@ export class SceneEntities {
|
|||||||
updateRectangleSketch(sketchInit, x, y, tags[0])
|
updateRectangleSketch(sketchInit, x, y, tags[0])
|
||||||
|
|
||||||
const newCode = recast(_ast)
|
const newCode = recast(_ast)
|
||||||
const pResult = parse(newCode)
|
let _recastAst = parse(newCode)
|
||||||
if (trap(pResult) || !resultIsOk(pResult))
|
if (trap(_recastAst)) return
|
||||||
return Promise.reject(pResult)
|
_ast = _recastAst
|
||||||
_ast = pResult.program
|
|
||||||
|
|
||||||
// Update the primary AST and unequip the rectangle tool
|
// Update the primary AST and unequip the rectangle tool
|
||||||
await kclManager.executeAstMock(_ast)
|
await kclManager.executeAstMock(_ast)
|
||||||
@ -1015,9 +1013,10 @@ export class SceneEntities {
|
|||||||
|
|
||||||
const { execState } = await executeAst({
|
const { execState } = await executeAst({
|
||||||
ast: _ast,
|
ast: _ast,
|
||||||
|
useFakeExecutor: true,
|
||||||
engineCommandManager: this.engineCommandManager,
|
engineCommandManager: this.engineCommandManager,
|
||||||
// We make sure to send an empty program memory to denote we mean mock mode.
|
|
||||||
programMemoryOverride,
|
programMemoryOverride,
|
||||||
|
idGenerator: kclManager.execState.idGenerator,
|
||||||
})
|
})
|
||||||
const programMemory = execState.memory
|
const programMemory = execState.memory
|
||||||
|
|
||||||
@ -1056,9 +1055,10 @@ export class SceneEntities {
|
|||||||
if (trap(_node1)) return Promise.reject(_node1)
|
if (trap(_node1)) return Promise.reject(_node1)
|
||||||
|
|
||||||
// startSketchOn already exists
|
// startSketchOn already exists
|
||||||
const variableDeclarationName = _node1.node?.declaration.id?.name || ''
|
const variableDeclarationName =
|
||||||
const startSketchOn = _node1.node?.declaration
|
_node1.node?.declarations?.[0]?.id?.name || ''
|
||||||
const startSketchOnInit = startSketchOn?.init
|
const startSketchOn = _node1.node?.declarations
|
||||||
|
const startSketchOnInit = startSketchOn?.[0]?.init
|
||||||
|
|
||||||
const tags: [string, string, string] = [
|
const tags: [string, string, string] = [
|
||||||
findUniqueName(_ast, 'rectangleSegmentA'),
|
findUniqueName(_ast, 'rectangleSegmentA'),
|
||||||
@ -1066,14 +1066,14 @@ export class SceneEntities {
|
|||||||
findUniqueName(_ast, 'rectangleSegmentC'),
|
findUniqueName(_ast, 'rectangleSegmentC'),
|
||||||
]
|
]
|
||||||
|
|
||||||
startSketchOn.init = createPipeExpression([
|
startSketchOn[0].init = createPipeExpression([
|
||||||
startSketchOnInit,
|
startSketchOnInit,
|
||||||
...getRectangleCallExpressions(rectangleOrigin, tags),
|
...getRectangleCallExpressions(rectangleOrigin, tags),
|
||||||
])
|
])
|
||||||
|
|
||||||
const pResult = parse(recast(_ast))
|
let _recastAst = parse(recast(_ast))
|
||||||
if (trap(pResult) || !resultIsOk(pResult)) return Promise.reject(pResult)
|
if (trap(_recastAst)) return Promise.reject(_recastAst)
|
||||||
_ast = pResult.program
|
_ast = _recastAst
|
||||||
|
|
||||||
const { programMemoryOverride, truncatedAst } = await this.setupSketch({
|
const { programMemoryOverride, truncatedAst } = await this.setupSketch({
|
||||||
sketchPathToNode,
|
sketchPathToNode,
|
||||||
@ -1096,7 +1096,7 @@ export class SceneEntities {
|
|||||||
'VariableDeclaration'
|
'VariableDeclaration'
|
||||||
)
|
)
|
||||||
if (trap(_node)) return Promise.reject(_node)
|
if (trap(_node)) return Promise.reject(_node)
|
||||||
const sketchInit = _node.node?.declaration.init
|
const sketchInit = _node.node?.declarations?.[0]?.init
|
||||||
|
|
||||||
const x = (args.intersectionPoint.twoD.x || 0) - rectangleOrigin[0]
|
const x = (args.intersectionPoint.twoD.x || 0) - rectangleOrigin[0]
|
||||||
const y = (args.intersectionPoint.twoD.y || 0) - rectangleOrigin[1]
|
const y = (args.intersectionPoint.twoD.y || 0) - rectangleOrigin[1]
|
||||||
@ -1114,9 +1114,10 @@ export class SceneEntities {
|
|||||||
|
|
||||||
const { execState } = await executeAst({
|
const { execState } = await executeAst({
|
||||||
ast: truncatedAst,
|
ast: truncatedAst,
|
||||||
|
useFakeExecutor: true,
|
||||||
engineCommandManager: this.engineCommandManager,
|
engineCommandManager: this.engineCommandManager,
|
||||||
// We make sure to send an empty program memory to denote we mean mock mode.
|
|
||||||
programMemoryOverride,
|
programMemoryOverride,
|
||||||
|
idGenerator: kclManager.execState.idGenerator,
|
||||||
})
|
})
|
||||||
const programMemory = execState.memory
|
const programMemory = execState.memory
|
||||||
this.sceneProgramMemory = programMemory
|
this.sceneProgramMemory = programMemory
|
||||||
@ -1152,7 +1153,7 @@ export class SceneEntities {
|
|||||||
'VariableDeclaration'
|
'VariableDeclaration'
|
||||||
)
|
)
|
||||||
if (trap(_node)) return
|
if (trap(_node)) return
|
||||||
const sketchInit = _node.node?.declaration.init
|
const sketchInit = _node.node?.declarations?.[0]?.init
|
||||||
|
|
||||||
if (sketchInit.type === 'PipeExpression') {
|
if (sketchInit.type === 'PipeExpression') {
|
||||||
updateCenterRectangleSketch(
|
updateCenterRectangleSketch(
|
||||||
@ -1164,10 +1165,9 @@ export class SceneEntities {
|
|||||||
rectangleOrigin[1]
|
rectangleOrigin[1]
|
||||||
)
|
)
|
||||||
|
|
||||||
const pResult = parse(recast(_ast))
|
let _recastAst = parse(recast(_ast))
|
||||||
if (trap(pResult) || !resultIsOk(pResult))
|
if (trap(_recastAst)) return
|
||||||
return Promise.reject(pResult)
|
_ast = _recastAst
|
||||||
_ast = pResult.program
|
|
||||||
|
|
||||||
// Update the primary AST and unequip the rectangle tool
|
// Update the primary AST and unequip the rectangle tool
|
||||||
await kclManager.executeAstMock(_ast)
|
await kclManager.executeAstMock(_ast)
|
||||||
@ -1180,9 +1180,10 @@ export class SceneEntities {
|
|||||||
|
|
||||||
const { execState } = await executeAst({
|
const { execState } = await executeAst({
|
||||||
ast: _ast,
|
ast: _ast,
|
||||||
|
useFakeExecutor: true,
|
||||||
engineCommandManager: this.engineCommandManager,
|
engineCommandManager: this.engineCommandManager,
|
||||||
// We make sure to send an empty program memory to denote we mean mock mode.
|
|
||||||
programMemoryOverride,
|
programMemoryOverride,
|
||||||
|
idGenerator: kclManager.execState.idGenerator,
|
||||||
})
|
})
|
||||||
const programMemory = execState.memory
|
const programMemory = execState.memory
|
||||||
|
|
||||||
@ -1221,11 +1222,12 @@ export class SceneEntities {
|
|||||||
'VariableDeclaration'
|
'VariableDeclaration'
|
||||||
)
|
)
|
||||||
if (trap(_node1)) return Promise.reject(_node1)
|
if (trap(_node1)) return Promise.reject(_node1)
|
||||||
const variableDeclarationName = _node1.node?.declaration.id?.name || ''
|
const variableDeclarationName =
|
||||||
const startSketchOn = _node1.node?.declaration
|
_node1.node?.declarations?.[0]?.id?.name || ''
|
||||||
const startSketchOnInit = startSketchOn?.init
|
const startSketchOn = _node1.node?.declarations
|
||||||
|
const startSketchOnInit = startSketchOn?.[0]?.init
|
||||||
|
|
||||||
startSketchOn.init = createPipeExpression([
|
startSketchOn[0].init = createPipeExpression([
|
||||||
startSketchOnInit,
|
startSketchOnInit,
|
||||||
createCallExpressionStdLib('circle', [
|
createCallExpressionStdLib('circle', [
|
||||||
createObjectExpression({
|
createObjectExpression({
|
||||||
@ -1239,9 +1241,9 @@ export class SceneEntities {
|
|||||||
]),
|
]),
|
||||||
])
|
])
|
||||||
|
|
||||||
const pResult = parse(recast(_ast))
|
let _recastAst = parse(recast(_ast))
|
||||||
if (trap(pResult) || !resultIsOk(pResult)) return Promise.reject(pResult)
|
if (trap(_recastAst)) return Promise.reject(_recastAst)
|
||||||
_ast = pResult.program
|
_ast = _recastAst
|
||||||
|
|
||||||
// do a quick mock execution to get the program memory up-to-date
|
// do a quick mock execution to get the program memory up-to-date
|
||||||
await kclManager.executeAstMock(_ast)
|
await kclManager.executeAstMock(_ast)
|
||||||
@ -1267,7 +1269,7 @@ export class SceneEntities {
|
|||||||
)
|
)
|
||||||
let modded = structuredClone(truncatedAst)
|
let modded = structuredClone(truncatedAst)
|
||||||
if (trap(_node)) return
|
if (trap(_node)) return
|
||||||
const sketchInit = _node.node.declaration.init
|
const sketchInit = _node.node?.declarations?.[0]?.init
|
||||||
|
|
||||||
const x = (args.intersectionPoint.twoD.x || 0) - circleCenter[0]
|
const x = (args.intersectionPoint.twoD.x || 0) - circleCenter[0]
|
||||||
const y = (args.intersectionPoint.twoD.y || 0) - circleCenter[1]
|
const y = (args.intersectionPoint.twoD.y || 0) - circleCenter[1]
|
||||||
@ -1297,9 +1299,10 @@ export class SceneEntities {
|
|||||||
|
|
||||||
const { execState } = await executeAst({
|
const { execState } = await executeAst({
|
||||||
ast: modded,
|
ast: modded,
|
||||||
|
useFakeExecutor: true,
|
||||||
engineCommandManager: this.engineCommandManager,
|
engineCommandManager: this.engineCommandManager,
|
||||||
// We make sure to send an empty program memory to denote we mean mock mode.
|
|
||||||
programMemoryOverride,
|
programMemoryOverride,
|
||||||
|
idGenerator: kclManager.execState.idGenerator,
|
||||||
})
|
})
|
||||||
const programMemory = execState.memory
|
const programMemory = execState.memory
|
||||||
this.sceneProgramMemory = programMemory
|
this.sceneProgramMemory = programMemory
|
||||||
@ -1335,7 +1338,7 @@ export class SceneEntities {
|
|||||||
'VariableDeclaration'
|
'VariableDeclaration'
|
||||||
)
|
)
|
||||||
if (trap(_node)) return
|
if (trap(_node)) return
|
||||||
const sketchInit = _node.node?.declaration.init
|
const sketchInit = _node.node?.declarations?.[0]?.init
|
||||||
|
|
||||||
let modded = structuredClone(_ast)
|
let modded = structuredClone(_ast)
|
||||||
if (sketchInit.type === 'PipeExpression') {
|
if (sketchInit.type === 'PipeExpression') {
|
||||||
@ -1362,10 +1365,9 @@ export class SceneEntities {
|
|||||||
|
|
||||||
const newCode = recast(modded)
|
const newCode = recast(modded)
|
||||||
if (err(newCode)) return
|
if (err(newCode)) return
|
||||||
const pResult = parse(newCode)
|
let _recastAst = parse(newCode)
|
||||||
if (trap(pResult) || !resultIsOk(pResult))
|
if (trap(_recastAst)) return Promise.reject(_recastAst)
|
||||||
return Promise.reject(pResult)
|
_ast = _recastAst
|
||||||
_ast = pResult.program
|
|
||||||
|
|
||||||
// Update the primary AST and unequip the rectangle tool
|
// Update the primary AST and unequip the rectangle tool
|
||||||
await kclManager.executeAstMock(_ast)
|
await kclManager.executeAstMock(_ast)
|
||||||
@ -1658,7 +1660,7 @@ export class SceneEntities {
|
|||||||
kclManager.programMemory,
|
kclManager.programMemory,
|
||||||
{
|
{
|
||||||
type: 'sourceRange',
|
type: 'sourceRange',
|
||||||
sourceRange: [node.start, node.end, true],
|
sourceRange: [node.start, node.end],
|
||||||
},
|
},
|
||||||
getChangeSketchInput()
|
getChangeSketchInput()
|
||||||
)
|
)
|
||||||
@ -1681,9 +1683,10 @@ export class SceneEntities {
|
|||||||
codeManager.updateCodeEditor(code)
|
codeManager.updateCodeEditor(code)
|
||||||
const { execState } = await executeAst({
|
const { execState } = await executeAst({
|
||||||
ast: truncatedAst,
|
ast: truncatedAst,
|
||||||
|
useFakeExecutor: true,
|
||||||
engineCommandManager: this.engineCommandManager,
|
engineCommandManager: this.engineCommandManager,
|
||||||
// We make sure to send an empty program memory to denote we mean mock mode.
|
|
||||||
programMemoryOverride,
|
programMemoryOverride,
|
||||||
|
idGenerator: kclManager.execState.idGenerator,
|
||||||
})
|
})
|
||||||
const programMemory = execState.memory
|
const programMemory = execState.memory
|
||||||
this.sceneProgramMemory = programMemory
|
this.sceneProgramMemory = programMemory
|
||||||
@ -1747,7 +1750,7 @@ export class SceneEntities {
|
|||||||
): (() => SegmentOverlayPayload | null) => {
|
): (() => SegmentOverlayPayload | null) => {
|
||||||
const segPathToNode = getNodePathFromSourceRange(
|
const segPathToNode = getNodePathFromSourceRange(
|
||||||
modifiedAst,
|
modifiedAst,
|
||||||
sourceRangeFromRust(segment.__geoMeta.sourceRange)
|
segment.__geoMeta.sourceRange
|
||||||
)
|
)
|
||||||
const sgPaths = sketch.paths
|
const sgPaths = sketch.paths
|
||||||
const originalPathToNodeStr = JSON.stringify(segPathToNode)
|
const originalPathToNodeStr = JSON.stringify(segPathToNode)
|
||||||
@ -1898,10 +1901,8 @@ export class SceneEntities {
|
|||||||
SEGMENT_BODIES_PLUS_PROFILE_START
|
SEGMENT_BODIES_PLUS_PROFILE_START
|
||||||
)
|
)
|
||||||
if (parent?.userData?.pathToNode) {
|
if (parent?.userData?.pathToNode) {
|
||||||
const pResult = parse(recast(kclManager.ast))
|
const updatedAst = parse(recast(kclManager.ast))
|
||||||
if (trap(pResult) || !resultIsOk(pResult))
|
if (trap(updatedAst)) return
|
||||||
return Promise.reject(pResult)
|
|
||||||
const updatedAst = pResult.program
|
|
||||||
const _node = getNodeFromPath<Node<CallExpression>>(
|
const _node = getNodeFromPath<Node<CallExpression>>(
|
||||||
updatedAst,
|
updatedAst,
|
||||||
parent.userData.pathToNode,
|
parent.userData.pathToNode,
|
||||||
@ -1909,7 +1910,7 @@ export class SceneEntities {
|
|||||||
)
|
)
|
||||||
if (trap(_node, { suppress: true })) return
|
if (trap(_node, { suppress: true })) return
|
||||||
const node = _node.node
|
const node = _node.node
|
||||||
editorManager.setHighlightRange([[node.start, node.end, true]])
|
editorManager.setHighlightRange([[node.start, node.end]])
|
||||||
const yellow = 0xffff00
|
const yellow = 0xffff00
|
||||||
colorSegment(selected, yellow)
|
colorSegment(selected, yellow)
|
||||||
const extraSegmentGroup = parent.getObjectByName(EXTRA_SEGMENT_HANDLE)
|
const extraSegmentGroup = parent.getObjectByName(EXTRA_SEGMENT_HANDLE)
|
||||||
@ -1954,10 +1955,10 @@ export class SceneEntities {
|
|||||||
})
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
editorManager.setHighlightRange([defaultSourceRange()])
|
editorManager.setHighlightRange([[0, 0]])
|
||||||
},
|
},
|
||||||
onMouseLeave: ({ selected, ...rest }: OnMouseEnterLeaveArgs) => {
|
onMouseLeave: ({ selected, ...rest }: OnMouseEnterLeaveArgs) => {
|
||||||
editorManager.setHighlightRange([defaultSourceRange()])
|
editorManager.setHighlightRange([[0, 0]])
|
||||||
const parent = getParentGroup(
|
const parent = getParentGroup(
|
||||||
selected,
|
selected,
|
||||||
SEGMENT_BODIES_PLUS_PROFILE_START
|
SEGMENT_BODIES_PLUS_PROFILE_START
|
||||||
@ -2056,7 +2057,7 @@ function prepareTruncatedMemoryAndAst(
|
|||||||
'VariableDeclaration'
|
'VariableDeclaration'
|
||||||
)
|
)
|
||||||
if (err(_node)) return _node
|
if (err(_node)) return _node
|
||||||
const variableDeclarationName = _node.node?.declaration.id?.name || ''
|
const variableDeclarationName = _node.node?.declarations?.[0]?.id?.name || ''
|
||||||
const sg = sketchFromKclValue(
|
const sg = sketchFromKclValue(
|
||||||
programMemory.get(variableDeclarationName),
|
programMemory.get(variableDeclarationName),
|
||||||
variableDeclarationName
|
variableDeclarationName
|
||||||
@ -2081,30 +2082,28 @@ function prepareTruncatedMemoryAndAst(
|
|||||||
])
|
])
|
||||||
}
|
}
|
||||||
;(
|
;(
|
||||||
(_ast.body[bodyIndex] as VariableDeclaration).declaration
|
(_ast.body[bodyIndex] as VariableDeclaration).declarations[0]
|
||||||
.init as PipeExpression
|
.init as PipeExpression
|
||||||
).body.push(newSegment)
|
).body.push(newSegment)
|
||||||
// update source ranges to section we just added.
|
// update source ranges to section we just added.
|
||||||
// hacks like this wouldn't be needed if the AST put pathToNode info in memory/sketch segments
|
// hacks like this wouldn't be needed if the AST put pathToNode info in memory/sketch segments
|
||||||
const pResult = parse(recast(_ast)) // get source ranges correct since unfortunately we still rely on them
|
const updatedSrcRangeAst = parse(recast(_ast)) // get source ranges correct since unfortunately we still rely on them
|
||||||
if (trap(pResult) || !resultIsOk(pResult))
|
if (err(updatedSrcRangeAst)) return updatedSrcRangeAst
|
||||||
return Error('Unexpected compilation error')
|
|
||||||
const updatedSrcRangeAst = pResult.program
|
|
||||||
|
|
||||||
const lastPipeItem = (
|
const lastPipeItem = (
|
||||||
(updatedSrcRangeAst.body[bodyIndex] as VariableDeclaration).declaration
|
(updatedSrcRangeAst.body[bodyIndex] as VariableDeclaration)
|
||||||
.init as PipeExpression
|
.declarations[0].init as PipeExpression
|
||||||
).body.slice(-1)[0]
|
).body.slice(-1)[0]
|
||||||
|
|
||||||
;(
|
;(
|
||||||
(_ast.body[bodyIndex] as VariableDeclaration).declaration
|
(_ast.body[bodyIndex] as VariableDeclaration).declarations[0]
|
||||||
.init as PipeExpression
|
.init as PipeExpression
|
||||||
).body.slice(-1)[0].start = lastPipeItem.start
|
).body.slice(-1)[0].start = lastPipeItem.start
|
||||||
|
|
||||||
_ast.end = lastPipeItem.end
|
_ast.end = lastPipeItem.end
|
||||||
const varDec = _ast.body[bodyIndex] as Node<VariableDeclaration>
|
const varDec = _ast.body[bodyIndex] as Node<VariableDeclaration>
|
||||||
varDec.end = lastPipeItem.end
|
varDec.end = lastPipeItem.end
|
||||||
const declarator = varDec.declaration
|
const declarator = varDec.declarations[0]
|
||||||
declarator.end = lastPipeItem.end
|
declarator.end = lastPipeItem.end
|
||||||
const init = declarator.init as Node<PipeExpression>
|
const init = declarator.init as Node<PipeExpression>
|
||||||
init.end = lastPipeItem.end
|
init.end = lastPipeItem.end
|
||||||
@ -2141,7 +2140,7 @@ function prepareTruncatedMemoryAndAst(
|
|||||||
if (node.type !== 'VariableDeclaration') {
|
if (node.type !== 'VariableDeclaration') {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
const name = node.declaration.id.name
|
const name = node.declarations[0].id.name
|
||||||
const memoryItem = programMemory.get(name)
|
const memoryItem = programMemory.get(name)
|
||||||
if (!memoryItem) {
|
if (!memoryItem) {
|
||||||
continue
|
continue
|
||||||
|
@ -5,7 +5,6 @@ import { useEffect, useRef, useState } from 'react'
|
|||||||
import { trap } from 'lib/trap'
|
import { trap } from 'lib/trap'
|
||||||
import { codeToIdSelections } from 'lib/selections'
|
import { codeToIdSelections } from 'lib/selections'
|
||||||
import { codeRefFromRange } from 'lang/std/artifactGraph'
|
import { codeRefFromRange } from 'lang/std/artifactGraph'
|
||||||
import { defaultSourceRange } from 'lang/wasm'
|
|
||||||
|
|
||||||
export function AstExplorer() {
|
export function AstExplorer() {
|
||||||
const { context } = useModelingContext()
|
const { context } = useModelingContext()
|
||||||
@ -47,7 +46,7 @@ export function AstExplorer() {
|
|||||||
<div
|
<div
|
||||||
className="h-full relative"
|
className="h-full relative"
|
||||||
onMouseLeave={(e) => {
|
onMouseLeave={(e) => {
|
||||||
editorManager.setHighlightRange([defaultSourceRange()])
|
editorManager.setHighlightRange([[0, 0]])
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<pre className="text-xs">
|
<pre className="text-xs">
|
||||||
@ -116,19 +115,15 @@ function DisplayObj({
|
|||||||
hasCursor ? 'bg-violet-100/80 dark:bg-violet-100/25' : ''
|
hasCursor ? 'bg-violet-100/80 dark:bg-violet-100/25' : ''
|
||||||
}`}
|
}`}
|
||||||
onMouseEnter={(e) => {
|
onMouseEnter={(e) => {
|
||||||
editorManager.setHighlightRange([[obj?.start || 0, obj.end, true]])
|
editorManager.setHighlightRange([[obj?.start || 0, obj.end]])
|
||||||
e.stopPropagation()
|
e.stopPropagation()
|
||||||
}}
|
}}
|
||||||
onMouseMove={(e) => {
|
onMouseMove={(e) => {
|
||||||
e.stopPropagation()
|
e.stopPropagation()
|
||||||
editorManager.setHighlightRange([[obj?.start || 0, obj.end, true]])
|
editorManager.setHighlightRange([[obj?.start || 0, obj.end]])
|
||||||
}}
|
}}
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
const range: [number, number, boolean] = [
|
const range: [number, number] = [obj?.start || 0, obj.end || 0]
|
||||||
obj?.start || 0,
|
|
||||||
obj.end || 0,
|
|
||||||
true,
|
|
||||||
]
|
|
||||||
const idInfo = codeToIdSelections([
|
const idInfo = codeToIdSelections([
|
||||||
{ codeRef: codeRefFromRange(range, kclManager.ast) },
|
{ codeRef: codeRefFromRange(range, kclManager.ast) },
|
||||||
])[0]
|
])[0]
|
||||||
|
@ -1,11 +1,5 @@
|
|||||||
import { useEffect, useState, useRef } from 'react'
|
import { useEffect, useState, useRef } from 'react'
|
||||||
import {
|
import { parse, BinaryPart, Expr, ProgramMemory } from '../lang/wasm'
|
||||||
parse,
|
|
||||||
BinaryPart,
|
|
||||||
Expr,
|
|
||||||
ProgramMemory,
|
|
||||||
resultIsOk,
|
|
||||||
} from '../lang/wasm'
|
|
||||||
import {
|
import {
|
||||||
createIdentifier,
|
createIdentifier,
|
||||||
createLiteral,
|
createLiteral,
|
||||||
@ -147,9 +141,8 @@ export function useCalc({
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
try {
|
try {
|
||||||
const code = `const __result__ = ${value}`
|
const code = `const __result__ = ${value}`
|
||||||
const pResult = parse(code)
|
const ast = parse(code)
|
||||||
if (trap(pResult) || !resultIsOk(pResult)) return
|
if (trap(ast)) return
|
||||||
const ast = pResult.program
|
|
||||||
const _programMem: ProgramMemory = ProgramMemory.empty()
|
const _programMem: ProgramMemory = ProgramMemory.empty()
|
||||||
for (const { key, value } of availableVarInfo.variables) {
|
for (const { key, value } of availableVarInfo.variables) {
|
||||||
const error = _programMem.set(key, {
|
const error = _programMem.set(key, {
|
||||||
@ -163,17 +156,18 @@ export function useCalc({
|
|||||||
executeAst({
|
executeAst({
|
||||||
ast,
|
ast,
|
||||||
engineCommandManager,
|
engineCommandManager,
|
||||||
// We make sure to send an empty program memory to denote we mean mock mode.
|
useFakeExecutor: true,
|
||||||
programMemoryOverride: kclManager.programMemory.clone(),
|
programMemoryOverride: kclManager.programMemory.clone(),
|
||||||
|
idGenerator: kclManager.execState.idGenerator,
|
||||||
}).then(({ execState }) => {
|
}).then(({ execState }) => {
|
||||||
const resultDeclaration = ast.body.find(
|
const resultDeclaration = ast.body.find(
|
||||||
(a) =>
|
(a) =>
|
||||||
a.type === 'VariableDeclaration' &&
|
a.type === 'VariableDeclaration' &&
|
||||||
a.declaration.id?.name === '__result__'
|
a.declarations?.[0]?.id?.name === '__result__'
|
||||||
)
|
)
|
||||||
const init =
|
const init =
|
||||||
resultDeclaration?.type === 'VariableDeclaration' &&
|
resultDeclaration?.type === 'VariableDeclaration' &&
|
||||||
resultDeclaration?.declaration.init
|
resultDeclaration?.declarations?.[0]?.init
|
||||||
const result = execState.memory?.get('__result__')?.value
|
const result = execState.memory?.get('__result__')?.value
|
||||||
setCalcResult(typeof result === 'number' ? String(result) : 'NAN')
|
setCalcResult(typeof result === 'number' ? String(result) : 'NAN')
|
||||||
init && setValueNode(init)
|
init && setValueNode(init)
|
||||||
|
@ -8,16 +8,11 @@ import { getSystemTheme } from 'lib/theme'
|
|||||||
import { useCalculateKclExpression } from 'lib/useCalculateKclExpression'
|
import { useCalculateKclExpression } from 'lib/useCalculateKclExpression'
|
||||||
import { roundOff } from 'lib/utils'
|
import { roundOff } from 'lib/utils'
|
||||||
import { varMentions } from 'lib/varCompletionExtension'
|
import { varMentions } from 'lib/varCompletionExtension'
|
||||||
import { useEffect, useMemo, useRef, useState } from 'react'
|
import { useEffect, useRef, useState } from 'react'
|
||||||
import { useHotkeys } from 'react-hotkeys-hook'
|
import { useHotkeys } from 'react-hotkeys-hook'
|
||||||
import styles from './CommandBarKclInput.module.css'
|
import styles from './CommandBarKclInput.module.css'
|
||||||
import { createIdentifier, createVariableDeclaration } from 'lang/modifyAst'
|
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'
|
|
||||||
|
|
||||||
const machineContextSelector = (snapshot?: {
|
|
||||||
context: Record<string, unknown>
|
|
||||||
}) => snapshot?.context
|
|
||||||
|
|
||||||
function CommandBarKclInput({
|
function CommandBarKclInput({
|
||||||
arg,
|
arg,
|
||||||
@ -36,44 +31,12 @@ function CommandBarKclInput({
|
|||||||
arg.name
|
arg.name
|
||||||
] as KclCommandValue | undefined
|
] as KclCommandValue | undefined
|
||||||
const { settings } = useSettingsAuthContext()
|
const { settings } = useSettingsAuthContext()
|
||||||
const argMachineContext = useSelector(
|
const defaultValue = (arg.defaultValue as string) || ''
|
||||||
arg.machineActor,
|
|
||||||
machineContextSelector
|
|
||||||
)
|
|
||||||
const defaultValue = useMemo(
|
|
||||||
() =>
|
|
||||||
arg.defaultValue
|
|
||||||
? arg.defaultValue instanceof Function
|
|
||||||
? arg.defaultValue(commandBarState.context, argMachineContext)
|
|
||||||
: arg.defaultValue
|
|
||||||
: '',
|
|
||||||
[arg.defaultValue, commandBarState.context, argMachineContext]
|
|
||||||
)
|
|
||||||
const initialVariableName = useMemo(() => {
|
|
||||||
// Use the configured variable name if it exists
|
|
||||||
if (arg.variableName !== undefined) {
|
|
||||||
return arg.variableName instanceof Function
|
|
||||||
? arg.variableName(commandBarState.context, argMachineContext)
|
|
||||||
: arg.variableName
|
|
||||||
}
|
|
||||||
// or derive it from the previously set value or the argument name
|
|
||||||
return previouslySetValue && 'variableName' in previouslySetValue
|
|
||||||
? previouslySetValue.variableName
|
|
||||||
: arg.name
|
|
||||||
}, [
|
|
||||||
arg.variableName,
|
|
||||||
commandBarState.context,
|
|
||||||
argMachineContext,
|
|
||||||
arg.name,
|
|
||||||
previouslySetValue,
|
|
||||||
])
|
|
||||||
const [value, setValue] = useState(
|
const [value, setValue] = useState(
|
||||||
previouslySetValue?.valueText || defaultValue || ''
|
previouslySetValue?.valueText || defaultValue || ''
|
||||||
)
|
)
|
||||||
const [createNewVariable, setCreateNewVariable] = useState(
|
const [createNewVariable, setCreateNewVariable] = useState(
|
||||||
(previouslySetValue && 'variableName' in previouslySetValue) ||
|
previouslySetValue && 'variableName' in previouslySetValue
|
||||||
arg.createVariableByDefault ||
|
|
||||||
false
|
|
||||||
)
|
)
|
||||||
const [canSubmit, setCanSubmit] = useState(true)
|
const [canSubmit, setCanSubmit] = useState(true)
|
||||||
useHotkeys('mod + k, mod + /', () => commandBarSend({ type: 'Close' }))
|
useHotkeys('mod + k, mod + /', () => commandBarSend({ type: 'Close' }))
|
||||||
@ -89,7 +52,10 @@ function CommandBarKclInput({
|
|||||||
isNewVariableNameUnique,
|
isNewVariableNameUnique,
|
||||||
} = useCalculateKclExpression({
|
} = useCalculateKclExpression({
|
||||||
value,
|
value,
|
||||||
initialVariableName,
|
initialVariableName:
|
||||||
|
previouslySetValue && 'variableName' in previouslySetValue
|
||||||
|
? previouslySetValue.variableName
|
||||||
|
: arg.name,
|
||||||
})
|
})
|
||||||
const varMentionData: Completion[] = prevVariables.map((v) => ({
|
const varMentionData: Completion[] = prevVariables.map((v) => ({
|
||||||
label: v.key,
|
label: v.key,
|
||||||
|
@ -266,7 +266,6 @@ const FileTreeItem = ({
|
|||||||
// Let the lsp servers know we closed a file.
|
// Let the lsp servers know we closed a file.
|
||||||
onFileClose(currentFile?.path || null, project?.path || null)
|
onFileClose(currentFile?.path || null, project?.path || null)
|
||||||
onFileOpen(fileOrDir.path, project?.path || null)
|
onFileOpen(fileOrDir.path, project?.path || null)
|
||||||
kclManager.switchedFiles = true
|
|
||||||
|
|
||||||
// Open kcl files
|
// Open kcl files
|
||||||
navigate(`${PATHS.FILE}/${encodeURIComponent(fileOrDir.path)}`)
|
navigate(`${PATHS.FILE}/${encodeURIComponent(fileOrDir.path)}`)
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { APP_VERSION, RELEASE_URL } from 'routes/Settings'
|
import { APP_VERSION } from 'routes/Settings'
|
||||||
import { CustomIcon } from 'components/CustomIcon'
|
import { CustomIcon } from 'components/CustomIcon'
|
||||||
import Tooltip from 'components/Tooltip'
|
import Tooltip from 'components/Tooltip'
|
||||||
import { PATHS } from 'lib/paths'
|
import { PATHS } from 'lib/paths'
|
||||||
@ -72,8 +72,10 @@ export function LowerRightControls({
|
|||||||
<menu className="flex items-center justify-end gap-3 pointer-events-auto">
|
<menu className="flex items-center justify-end gap-3 pointer-events-auto">
|
||||||
{!location.pathname.startsWith(PATHS.HOME) && <ModelStateIndicator />}
|
{!location.pathname.startsWith(PATHS.HOME) && <ModelStateIndicator />}
|
||||||
<a
|
<a
|
||||||
onClick={openExternalBrowserIfDesktop(RELEASE_URL)}
|
onClick={openExternalBrowserIfDesktop(
|
||||||
href={RELEASE_URL}
|
`https://github.com/KittyCAD/modeling-app/releases/tag/v${APP_VERSION}`
|
||||||
|
)}
|
||||||
|
href={`https://github.com/KittyCAD/modeling-app/releases/tag/v${APP_VERSION}`}
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
className={'!no-underline font-mono text-xs ' + linkOverrideClassName}
|
className={'!no-underline font-mono text-xs ' + linkOverrideClassName}
|
||||||
|
@ -41,10 +41,7 @@ import {
|
|||||||
angleBetweenInfo,
|
angleBetweenInfo,
|
||||||
applyConstraintAngleBetween,
|
applyConstraintAngleBetween,
|
||||||
} from './Toolbar/SetAngleBetween'
|
} from './Toolbar/SetAngleBetween'
|
||||||
import {
|
import { applyConstraintAngleLength } from './Toolbar/setAngleLength'
|
||||||
applyConstraintAngleLength,
|
|
||||||
applyConstraintLength,
|
|
||||||
} from './Toolbar/setAngleLength'
|
|
||||||
import {
|
import {
|
||||||
canSweepSelection,
|
canSweepSelection,
|
||||||
handleSelectionBatch,
|
handleSelectionBatch,
|
||||||
@ -53,9 +50,6 @@ import {
|
|||||||
isSketchPipe,
|
isSketchPipe,
|
||||||
Selections,
|
Selections,
|
||||||
updateSelections,
|
updateSelections,
|
||||||
canLoftSelection,
|
|
||||||
canRevolveSelection,
|
|
||||||
canShellSelection,
|
|
||||||
} from 'lib/selections'
|
} from 'lib/selections'
|
||||||
import { applyConstraintIntersect } from './Toolbar/Intersect'
|
import { applyConstraintIntersect } from './Toolbar/Intersect'
|
||||||
import { applyConstraintAbsDistance } from './Toolbar/SetAbsDistance'
|
import { applyConstraintAbsDistance } from './Toolbar/SetAbsDistance'
|
||||||
@ -67,15 +61,13 @@ import {
|
|||||||
getSketchOrientationDetails,
|
getSketchOrientationDetails,
|
||||||
} from 'clientSideScene/sceneEntities'
|
} from 'clientSideScene/sceneEntities'
|
||||||
import {
|
import {
|
||||||
insertNamedConstant,
|
moveValueIntoNewVariablePath,
|
||||||
replaceValueAtNodePath,
|
|
||||||
sketchOnExtrudedFace,
|
sketchOnExtrudedFace,
|
||||||
sketchOnOffsetPlane,
|
sketchOnOffsetPlane,
|
||||||
startSketchOnDefault,
|
startSketchOnDefault,
|
||||||
} from 'lang/modifyAst'
|
} from 'lang/modifyAst'
|
||||||
import { PathToNode, Program, parse, recast, resultIsOk } from 'lang/wasm'
|
import { Program, parse, recast } from 'lang/wasm'
|
||||||
import {
|
import {
|
||||||
doesSceneHaveExtrudedSketch,
|
|
||||||
doesSceneHaveSweepableSketch,
|
doesSceneHaveSweepableSketch,
|
||||||
getNodePathFromSourceRange,
|
getNodePathFromSourceRange,
|
||||||
isSingleCursorInPipe,
|
isSingleCursorInPipe,
|
||||||
@ -86,10 +78,11 @@ import toast from 'react-hot-toast'
|
|||||||
import { EditorSelection, Transaction } from '@codemirror/state'
|
import { EditorSelection, Transaction } from '@codemirror/state'
|
||||||
import { useLoaderData, useNavigate, useSearchParams } from 'react-router-dom'
|
import { useLoaderData, useNavigate, useSearchParams } from 'react-router-dom'
|
||||||
import { letEngineAnimateAndSyncCamAfter } from 'clientSideScene/CameraControls'
|
import { letEngineAnimateAndSyncCamAfter } from 'clientSideScene/CameraControls'
|
||||||
|
import { getVarNameModal } from 'hooks/useToolbarGuards'
|
||||||
import { err, reportRejection, trap } from 'lib/trap'
|
import { err, reportRejection, trap } from 'lib/trap'
|
||||||
import { useCommandsContext } from 'hooks/useCommandsContext'
|
import { useCommandsContext } from 'hooks/useCommandsContext'
|
||||||
import { modelingMachineEvent } from 'editor/manager'
|
import { modelingMachineEvent } from 'editor/manager'
|
||||||
import { hasValidEdgeTreatmentSelection } from 'lang/modifyAst/addEdgeTreatment'
|
import { hasValidFilletSelection } from 'lang/modifyAst/addFillet'
|
||||||
import {
|
import {
|
||||||
ExportIntent,
|
ExportIntent,
|
||||||
EngineConnectionStateType,
|
EngineConnectionStateType,
|
||||||
@ -576,59 +569,6 @@ export const ModelingMachineProvider = ({
|
|||||||
if (err(canSweep)) return false
|
if (err(canSweep)) return false
|
||||||
return canSweep
|
return canSweep
|
||||||
},
|
},
|
||||||
'has valid revolve selection': ({ context: { selectionRanges } }) => {
|
|
||||||
// A user can begin extruding if they either have 1+ faces selected or nothing selected
|
|
||||||
// TODO: I believe this guard only allows for extruding a single face at a time
|
|
||||||
const hasNoSelection =
|
|
||||||
selectionRanges.graphSelections.length === 0 ||
|
|
||||||
isRangeBetweenCharacters(selectionRanges) ||
|
|
||||||
isSelectionLastLine(selectionRanges, codeManager.code)
|
|
||||||
|
|
||||||
if (hasNoSelection) {
|
|
||||||
// they have no selection, we should enable the button
|
|
||||||
// so they can select the face through the cmdbar
|
|
||||||
// BUT only if there's extrudable geometry
|
|
||||||
return doesSceneHaveSweepableSketch(kclManager.ast)
|
|
||||||
}
|
|
||||||
if (!isSketchPipe(selectionRanges)) return false
|
|
||||||
|
|
||||||
const canSweep = canRevolveSelection(selectionRanges)
|
|
||||||
if (err(canSweep)) return false
|
|
||||||
return canSweep
|
|
||||||
},
|
|
||||||
'has valid loft selection': ({ context: { selectionRanges } }) => {
|
|
||||||
const hasNoSelection =
|
|
||||||
selectionRanges.graphSelections.length === 0 ||
|
|
||||||
isRangeBetweenCharacters(selectionRanges) ||
|
|
||||||
isSelectionLastLine(selectionRanges, codeManager.code)
|
|
||||||
|
|
||||||
if (hasNoSelection) {
|
|
||||||
const count = 2
|
|
||||||
return doesSceneHaveSweepableSketch(kclManager.ast, count)
|
|
||||||
}
|
|
||||||
|
|
||||||
const canLoft = canLoftSelection(selectionRanges)
|
|
||||||
if (err(canLoft)) return false
|
|
||||||
return canLoft
|
|
||||||
},
|
|
||||||
'has valid shell selection': ({
|
|
||||||
context: { selectionRanges },
|
|
||||||
event,
|
|
||||||
}) => {
|
|
||||||
const hasNoSelection =
|
|
||||||
selectionRanges.graphSelections.length === 0 ||
|
|
||||||
isRangeBetweenCharacters(selectionRanges) ||
|
|
||||||
isSelectionLastLine(selectionRanges, codeManager.code)
|
|
||||||
|
|
||||||
if (hasNoSelection) {
|
|
||||||
return doesSceneHaveExtrudedSketch(kclManager.ast)
|
|
||||||
}
|
|
||||||
|
|
||||||
const canShell = canShellSelection(selectionRanges)
|
|
||||||
console.log('canShellSelection', canShellSelection(selectionRanges))
|
|
||||||
if (err(canShell)) return false
|
|
||||||
return canShell
|
|
||||||
},
|
|
||||||
'has valid selection for deletion': ({
|
'has valid selection for deletion': ({
|
||||||
context: { selectionRanges },
|
context: { selectionRanges },
|
||||||
}) => {
|
}) => {
|
||||||
@ -636,10 +576,8 @@ export const ModelingMachineProvider = ({
|
|||||||
if (selectionRanges.graphSelections.length <= 0) return false
|
if (selectionRanges.graphSelections.length <= 0) return false
|
||||||
return true
|
return true
|
||||||
},
|
},
|
||||||
'has valid edge treatment selection': ({
|
'has valid fillet selection': ({ context: { selectionRanges } }) => {
|
||||||
context: { selectionRanges },
|
return hasValidFilletSelection({
|
||||||
}) => {
|
|
||||||
return hasValidEdgeTreatmentSelection({
|
|
||||||
selectionRanges,
|
selectionRanges,
|
||||||
ast: kclManager.ast,
|
ast: kclManager.ast,
|
||||||
code: codeManager.code,
|
code: codeManager.code,
|
||||||
@ -656,11 +594,15 @@ export const ModelingMachineProvider = ({
|
|||||||
)
|
)
|
||||||
},
|
},
|
||||||
'Has exportable geometry': () => {
|
'Has exportable geometry': () => {
|
||||||
if (!kclManager.hasErrors() && kclManager.ast.body.length > 0)
|
if (
|
||||||
|
kclManager.kclErrors.length === 0 &&
|
||||||
|
kclManager.ast.body.length > 0
|
||||||
|
)
|
||||||
return true
|
return true
|
||||||
else {
|
else {
|
||||||
let errorMessage = 'Unable to Export '
|
let errorMessage = 'Unable to Export '
|
||||||
if (kclManager.hasErrors()) errorMessage += 'due to KCL Errors'
|
if (kclManager.kclErrors.length > 0)
|
||||||
|
errorMessage += 'due to KCL Errors'
|
||||||
else if (kclManager.ast.body.length === 0)
|
else if (kclManager.ast.body.length === 0)
|
||||||
errorMessage += 'due to Empty Scene'
|
errorMessage += 'due to Empty Scene'
|
||||||
console.error(errorMessage)
|
console.error(errorMessage)
|
||||||
@ -778,11 +720,7 @@ export const ModelingMachineProvider = ({
|
|||||||
constraint: 'setHorzDistance',
|
constraint: 'setHorzDistance',
|
||||||
selectionRanges,
|
selectionRanges,
|
||||||
})
|
})
|
||||||
const pResult = parse(recast(modifiedAst))
|
const _modifiedAst = parse(recast(modifiedAst))
|
||||||
if (trap(pResult) || !resultIsOk(pResult))
|
|
||||||
return Promise.reject(new Error('Unexpected compilation error'))
|
|
||||||
const _modifiedAst = pResult.program
|
|
||||||
|
|
||||||
if (!sketchDetails)
|
if (!sketchDetails)
|
||||||
return Promise.reject(new Error('No sketch details'))
|
return Promise.reject(new Error('No sketch details'))
|
||||||
const updatedPathToNode = updatePathToNodeFromMap(
|
const updatedPathToNode = updatePathToNodeFromMap(
|
||||||
@ -823,10 +761,7 @@ export const ModelingMachineProvider = ({
|
|||||||
constraint: 'setVertDistance',
|
constraint: 'setVertDistance',
|
||||||
selectionRanges,
|
selectionRanges,
|
||||||
})
|
})
|
||||||
const pResult = parse(recast(modifiedAst))
|
const _modifiedAst = parse(recast(modifiedAst))
|
||||||
if (trap(pResult) || !resultIsOk(pResult))
|
|
||||||
return Promise.reject(new Error('Unexpected compilation error'))
|
|
||||||
const _modifiedAst = pResult.program
|
|
||||||
if (!sketchDetails)
|
if (!sketchDetails)
|
||||||
return Promise.reject(new Error('No sketch details'))
|
return Promise.reject(new Error('No sketch details'))
|
||||||
const updatedPathToNode = updatePathToNodeFromMap(
|
const updatedPathToNode = updatePathToNodeFromMap(
|
||||||
@ -874,10 +809,7 @@ export const ModelingMachineProvider = ({
|
|||||||
selectionRanges,
|
selectionRanges,
|
||||||
angleOrLength: 'setAngle',
|
angleOrLength: 'setAngle',
|
||||||
}))
|
}))
|
||||||
const pResult = parse(recast(modifiedAst))
|
const _modifiedAst = parse(recast(modifiedAst))
|
||||||
if (trap(pResult) || !resultIsOk(pResult))
|
|
||||||
return Promise.reject(new Error('Unexpected compilation error'))
|
|
||||||
const _modifiedAst = pResult.program
|
|
||||||
if (err(_modifiedAst)) return Promise.reject(_modifiedAst)
|
if (err(_modifiedAst)) return Promise.reject(_modifiedAst)
|
||||||
|
|
||||||
if (!sketchDetails)
|
if (!sketchDetails)
|
||||||
@ -913,22 +845,13 @@ export const ModelingMachineProvider = ({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
astConstrainLength: fromPromise(
|
'Get length info': fromPromise(
|
||||||
async ({
|
async ({ input: { selectionRanges, sketchDetails } }) => {
|
||||||
input: { selectionRanges, sketchDetails, lengthValue },
|
const { modifiedAst, pathToNodeMap } =
|
||||||
}) => {
|
await applyConstraintAngleLength({
|
||||||
if (!lengthValue)
|
selectionRanges,
|
||||||
return Promise.reject(new Error('No length value'))
|
})
|
||||||
const constraintResult = await applyConstraintLength({
|
const _modifiedAst = parse(recast(modifiedAst))
|
||||||
selectionRanges,
|
|
||||||
length: lengthValue,
|
|
||||||
})
|
|
||||||
if (err(constraintResult)) return Promise.reject(constraintResult)
|
|
||||||
const { modifiedAst, pathToNodeMap } = constraintResult
|
|
||||||
const pResult = parse(recast(modifiedAst))
|
|
||||||
if (trap(pResult) || !resultIsOk(pResult))
|
|
||||||
return Promise.reject(new Error('Unexpected compilation error'))
|
|
||||||
const _modifiedAst = pResult.program
|
|
||||||
if (!sketchDetails)
|
if (!sketchDetails)
|
||||||
return Promise.reject(new Error('No sketch details'))
|
return Promise.reject(new Error('No sketch details'))
|
||||||
const updatedPathToNode = updatePathToNodeFromMap(
|
const updatedPathToNode = updatePathToNodeFromMap(
|
||||||
@ -968,10 +891,7 @@ export const ModelingMachineProvider = ({
|
|||||||
await applyConstraintIntersect({
|
await applyConstraintIntersect({
|
||||||
selectionRanges,
|
selectionRanges,
|
||||||
})
|
})
|
||||||
const pResult = parse(recast(modifiedAst))
|
const _modifiedAst = parse(recast(modifiedAst))
|
||||||
if (trap(pResult) || !resultIsOk(pResult))
|
|
||||||
return Promise.reject(new Error('Unexpected compilation error'))
|
|
||||||
const _modifiedAst = pResult.program
|
|
||||||
if (!sketchDetails)
|
if (!sketchDetails)
|
||||||
return Promise.reject(new Error('No sketch details'))
|
return Promise.reject(new Error('No sketch details'))
|
||||||
const updatedPathToNode = updatePathToNodeFromMap(
|
const updatedPathToNode = updatePathToNodeFromMap(
|
||||||
@ -1012,10 +932,7 @@ export const ModelingMachineProvider = ({
|
|||||||
constraint: 'xAbs',
|
constraint: 'xAbs',
|
||||||
selectionRanges,
|
selectionRanges,
|
||||||
})
|
})
|
||||||
const pResult = parse(recast(modifiedAst))
|
const _modifiedAst = parse(recast(modifiedAst))
|
||||||
if (trap(pResult) || !resultIsOk(pResult))
|
|
||||||
return Promise.reject(new Error('Unexpected compilation error'))
|
|
||||||
const _modifiedAst = pResult.program
|
|
||||||
if (!sketchDetails)
|
if (!sketchDetails)
|
||||||
return Promise.reject(new Error('No sketch details'))
|
return Promise.reject(new Error('No sketch details'))
|
||||||
const updatedPathToNode = updatePathToNodeFromMap(
|
const updatedPathToNode = updatePathToNodeFromMap(
|
||||||
@ -1056,10 +973,7 @@ export const ModelingMachineProvider = ({
|
|||||||
constraint: 'yAbs',
|
constraint: 'yAbs',
|
||||||
selectionRanges,
|
selectionRanges,
|
||||||
})
|
})
|
||||||
const pResult = parse(recast(modifiedAst))
|
const _modifiedAst = parse(recast(modifiedAst))
|
||||||
if (trap(pResult) || !resultIsOk(pResult))
|
|
||||||
return Promise.reject(new Error('Unexpected compilation error'))
|
|
||||||
const _modifiedAst = pResult.program
|
|
||||||
if (!sketchDetails)
|
if (!sketchDetails)
|
||||||
return Promise.reject(new Error('No sketch details'))
|
return Promise.reject(new Error('No sketch details'))
|
||||||
const updatedPathToNode = updatePathToNodeFromMap(
|
const updatedPathToNode = updatePathToNodeFromMap(
|
||||||
@ -1093,88 +1007,33 @@ export const ModelingMachineProvider = ({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
'Apply named value constraint': fromPromise(
|
'Get convert to variable info': fromPromise(
|
||||||
async ({ input: { selectionRanges, sketchDetails, data } }) => {
|
async ({ input: { selectionRanges, sketchDetails, data } }) => {
|
||||||
if (!sketchDetails) {
|
if (!sketchDetails)
|
||||||
return Promise.reject(new Error('No sketch details'))
|
return Promise.reject(new Error('No sketch details'))
|
||||||
}
|
const { variableName } = await getVarNameModal({
|
||||||
if (!data) {
|
valueName: data?.variableName || 'var',
|
||||||
return Promise.reject(new Error('No data from command flow'))
|
})
|
||||||
}
|
let parsed = parse(recast(kclManager.ast))
|
||||||
let pResult = parse(recast(kclManager.ast))
|
|
||||||
if (trap(pResult) || !resultIsOk(pResult))
|
|
||||||
return Promise.reject(new Error('Unexpected compilation error'))
|
|
||||||
let parsed = pResult.program
|
|
||||||
|
|
||||||
let result: {
|
|
||||||
modifiedAst: Node<Program>
|
|
||||||
pathToReplaced: PathToNode | null
|
|
||||||
} = {
|
|
||||||
modifiedAst: parsed,
|
|
||||||
pathToReplaced: null,
|
|
||||||
}
|
|
||||||
// If the user provided a constant name,
|
|
||||||
// we need to insert the named constant
|
|
||||||
// and then replace the node with the constant's name.
|
|
||||||
if ('variableName' in data.namedValue) {
|
|
||||||
const astAfterReplacement = replaceValueAtNodePath({
|
|
||||||
ast: parsed,
|
|
||||||
pathToNode: data.currentValue.pathToNode,
|
|
||||||
newExpressionString: data.namedValue.variableName,
|
|
||||||
})
|
|
||||||
if (trap(astAfterReplacement)) {
|
|
||||||
return Promise.reject(astAfterReplacement)
|
|
||||||
}
|
|
||||||
const parseResultAfterInsertion = parse(
|
|
||||||
recast(
|
|
||||||
insertNamedConstant({
|
|
||||||
node: astAfterReplacement.modifiedAst,
|
|
||||||
newExpression: data.namedValue,
|
|
||||||
})
|
|
||||||
)
|
|
||||||
)
|
|
||||||
if (
|
|
||||||
trap(parseResultAfterInsertion) ||
|
|
||||||
!resultIsOk(parseResultAfterInsertion)
|
|
||||||
)
|
|
||||||
return Promise.reject(parseResultAfterInsertion)
|
|
||||||
result = {
|
|
||||||
modifiedAst: parseResultAfterInsertion.program,
|
|
||||||
pathToReplaced: astAfterReplacement.pathToReplaced,
|
|
||||||
}
|
|
||||||
} else if ('valueText' in data.namedValue) {
|
|
||||||
// If they didn't provide a constant name,
|
|
||||||
// just replace the node with the value.
|
|
||||||
const astAfterReplacement = replaceValueAtNodePath({
|
|
||||||
ast: parsed,
|
|
||||||
pathToNode: data.currentValue.pathToNode,
|
|
||||||
newExpressionString: data.namedValue.valueText,
|
|
||||||
})
|
|
||||||
if (trap(astAfterReplacement)) {
|
|
||||||
return Promise.reject(astAfterReplacement)
|
|
||||||
}
|
|
||||||
// The `replacer` function returns a pathToNode that assumes
|
|
||||||
// an identifier is also being inserted into the AST, creating an off-by-one error.
|
|
||||||
// This corrects that error, but TODO we should fix this upstream
|
|
||||||
// to avoid this kind of error in the future.
|
|
||||||
astAfterReplacement.pathToReplaced[1][0] =
|
|
||||||
(astAfterReplacement.pathToReplaced[1][0] as number) - 1
|
|
||||||
result = astAfterReplacement
|
|
||||||
}
|
|
||||||
|
|
||||||
pResult = parse(recast(result.modifiedAst))
|
|
||||||
if (trap(pResult) || !resultIsOk(pResult))
|
|
||||||
return Promise.reject(new Error('Unexpected compilation error'))
|
|
||||||
parsed = pResult.program
|
|
||||||
|
|
||||||
if (trap(parsed)) return Promise.reject(parsed)
|
if (trap(parsed)) return Promise.reject(parsed)
|
||||||
parsed = parsed as Node<Program>
|
parsed = parsed as Node<Program>
|
||||||
if (!result.pathToReplaced)
|
|
||||||
|
const { modifiedAst: _modifiedAst, pathToReplacedNode } =
|
||||||
|
moveValueIntoNewVariablePath(
|
||||||
|
parsed,
|
||||||
|
kclManager.programMemory,
|
||||||
|
data?.pathToNode || [],
|
||||||
|
variableName
|
||||||
|
)
|
||||||
|
parsed = parse(recast(_modifiedAst))
|
||||||
|
if (trap(parsed)) return Promise.reject(parsed)
|
||||||
|
parsed = parsed as Node<Program>
|
||||||
|
if (!pathToReplacedNode)
|
||||||
return Promise.reject(new Error('No path to replaced node'))
|
return Promise.reject(new Error('No path to replaced node'))
|
||||||
|
|
||||||
const updatedAst =
|
const updatedAst =
|
||||||
await sceneEntitiesManager.updateAstAndRejigSketch(
|
await sceneEntitiesManager.updateAstAndRejigSketch(
|
||||||
result.pathToReplaced || [],
|
pathToReplacedNode || [],
|
||||||
parsed,
|
parsed,
|
||||||
sketchDetails.zAxis,
|
sketchDetails.zAxis,
|
||||||
sketchDetails.yAxis,
|
sketchDetails.yAxis,
|
||||||
@ -1187,7 +1046,7 @@ export const ModelingMachineProvider = ({
|
|||||||
)
|
)
|
||||||
|
|
||||||
const selection = updateSelections(
|
const selection = updateSelections(
|
||||||
{ 0: result.pathToReplaced },
|
{ 0: pathToReplacedNode },
|
||||||
selectionRanges,
|
selectionRanges,
|
||||||
updatedAst.newAst
|
updatedAst.newAst
|
||||||
)
|
)
|
||||||
@ -1195,7 +1054,7 @@ export const ModelingMachineProvider = ({
|
|||||||
return {
|
return {
|
||||||
selectionType: 'completeSelection',
|
selectionType: 'completeSelection',
|
||||||
selection,
|
selection,
|
||||||
updatedPathToNode: result.pathToReplaced,
|
updatedPathToNode: pathToReplacedNode,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
|
@ -76,7 +76,7 @@ export const ModelingPane = ({
|
|||||||
return (
|
return (
|
||||||
<section
|
<section
|
||||||
{...props}
|
{...props}
|
||||||
aria-label={title && typeof title === 'string' ? title : ''}
|
title={title && typeof title === 'string' ? title : ''}
|
||||||
data-testid={detailsTestId}
|
data-testid={detailsTestId}
|
||||||
id={id}
|
id={id}
|
||||||
className={
|
className={
|
||||||
|
@ -40,9 +40,7 @@ export const KclEditorMenu = ({ children }: PropsWithChildren) => {
|
|||||||
<Menu.Items className="absolute right-0 left-auto w-72 flex flex-col gap-1 divide-y divide-chalkboard-20 dark:divide-chalkboard-70 align-stretch px-0 py-1 bg-chalkboard-10 dark:bg-chalkboard-100 rounded-sm shadow-lg border border-solid border-chalkboard-20/50 dark:border-chalkboard-80/50">
|
<Menu.Items className="absolute right-0 left-auto w-72 flex flex-col gap-1 divide-y divide-chalkboard-20 dark:divide-chalkboard-70 align-stretch px-0 py-1 bg-chalkboard-10 dark:bg-chalkboard-100 rounded-sm shadow-lg border border-solid border-chalkboard-20/50 dark:border-chalkboard-80/50">
|
||||||
<Menu.Item>
|
<Menu.Item>
|
||||||
<button
|
<button
|
||||||
onClick={() => {
|
onClick={() => kclManager.format()}
|
||||||
kclManager.format().catch(reportRejection)
|
|
||||||
}}
|
|
||||||
className={styles.button}
|
className={styles.button}
|
||||||
>
|
>
|
||||||
<span>Format code</span>
|
<span>Format code</span>
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { processMemory } from './MemoryPane'
|
import { processMemory } from './MemoryPane'
|
||||||
import { enginelessExecutor } from '../../../lib/testHelpers'
|
import { enginelessExecutor } from '../../../lib/testHelpers'
|
||||||
import { assertParse, initPromise, ProgramMemory } from '../../../lang/wasm'
|
import { initPromise, parse, ProgramMemory } from '../../../lang/wasm'
|
||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
await initPromise
|
await initPromise
|
||||||
@ -28,16 +28,12 @@ describe('processMemory', () => {
|
|||||||
|> lineTo([0.98, 5.16], %)
|
|> lineTo([0.98, 5.16], %)
|
||||||
|> lineTo([2.15, 4.32], %)
|
|> lineTo([2.15, 4.32], %)
|
||||||
// |> rx(90, %)`
|
// |> rx(90, %)`
|
||||||
const ast = assertParse(code)
|
const ast = parse(code)
|
||||||
const execState = await enginelessExecutor(ast, ProgramMemory.empty())
|
const execState = await enginelessExecutor(ast, ProgramMemory.empty())
|
||||||
const output = processMemory(execState.memory)
|
const output = processMemory(execState.memory)
|
||||||
expect(output.myVar).toEqual(5)
|
expect(output.myVar).toEqual(5)
|
||||||
expect(output.otherVar).toEqual(3)
|
expect(output.otherVar).toEqual(3)
|
||||||
expect(output).toEqual({
|
expect(output).toEqual({
|
||||||
HALF_TURN: 180,
|
|
||||||
QUARTER_TURN: 90,
|
|
||||||
THREE_QUARTER_TURN: 270,
|
|
||||||
ZERO: 0,
|
|
||||||
myVar: 5,
|
myVar: 5,
|
||||||
myFn: '__function(a)__',
|
myFn: '__function(a)__',
|
||||||
otherVar: 3,
|
otherVar: 3,
|
||||||
|
@ -90,7 +90,7 @@ export const sidebarPanes: SidebarPane[] = [
|
|||||||
keybinding: 'Shift + C',
|
keybinding: 'Shift + C',
|
||||||
showBadge: {
|
showBadge: {
|
||||||
value: ({ kclContext }) => {
|
value: ({ kclContext }) => {
|
||||||
return kclContext.diagnostics.length
|
return kclContext.errors.length
|
||||||
},
|
},
|
||||||
onClick: (e) => {
|
onClick: (e) => {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
|
@ -53,7 +53,7 @@ export function ModelingSidebar({ paneOpacity }: ModelingSidebarProps) {
|
|||||||
settings: settings.context,
|
settings: settings.context,
|
||||||
platform: getPlatformString(),
|
platform: getPlatformString(),
|
||||||
}),
|
}),
|
||||||
[kclContext.diagnostics, settings.context]
|
[kclContext.errors, settings.context]
|
||||||
)
|
)
|
||||||
|
|
||||||
const sidebarActions: SidebarAction[] = [
|
const sidebarActions: SidebarAction[] = [
|
||||||
|
@ -10,7 +10,7 @@ import { APP_NAME } from 'lib/constants'
|
|||||||
import { useCommandsContext } from 'hooks/useCommandsContext'
|
import { useCommandsContext } from 'hooks/useCommandsContext'
|
||||||
import { CustomIcon } from './CustomIcon'
|
import { CustomIcon } from './CustomIcon'
|
||||||
import { useLspContext } from './LspProvider'
|
import { useLspContext } from './LspProvider'
|
||||||
import { engineCommandManager, kclManager } from 'lib/singletons'
|
import { engineCommandManager } from 'lib/singletons'
|
||||||
import { MachineManagerContext } from 'components/MachineManagerProvider'
|
import { MachineManagerContext } from 'components/MachineManagerProvider'
|
||||||
import usePlatform from 'hooks/usePlatform'
|
import usePlatform from 'hooks/usePlatform'
|
||||||
import { useAbsoluteFilePath } from 'hooks/useAbsoluteFilePath'
|
import { useAbsoluteFilePath } from 'hooks/useAbsoluteFilePath'
|
||||||
@ -68,7 +68,8 @@ function AppLogoLink({
|
|||||||
data-testid="app-logo"
|
data-testid="app-logo"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
onProjectClose(file || null, project?.path || null, false)
|
onProjectClose(file || null, project?.path || null, false)
|
||||||
kclManager.switchedFiles = true
|
// Clear the scene and end the session.
|
||||||
|
engineCommandManager.endSession()
|
||||||
}}
|
}}
|
||||||
to={PATHS.HOME}
|
to={PATHS.HOME}
|
||||||
className={wrapperClassName + ' hover:before:brightness-110'}
|
className={wrapperClassName + ' hover:before:brightness-110'}
|
||||||
@ -189,7 +190,8 @@ function ProjectMenuPopover({
|
|||||||
className: !isDesktop() ? 'hidden' : '',
|
className: !isDesktop() ? 'hidden' : '',
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
onProjectClose(file || null, project?.path || null, true)
|
onProjectClose(file || null, project?.path || null, true)
|
||||||
kclManager.switchedFiles = true
|
// Clear the scene and end the session.
|
||||||
|
engineCommandManager.endSession()
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
].filter(
|
].filter(
|
||||||
|
@ -10,7 +10,7 @@ interface AllKeybindingsFieldsProps {}
|
|||||||
|
|
||||||
export const AllKeybindingsFields = forwardRef(
|
export const AllKeybindingsFields = forwardRef(
|
||||||
(
|
(
|
||||||
_props: AllKeybindingsFieldsProps,
|
props: AllKeybindingsFieldsProps,
|
||||||
scrollRef: ForwardedRef<HTMLDivElement>
|
scrollRef: ForwardedRef<HTMLDivElement>
|
||||||
) => {
|
) => {
|
||||||
// This is how we will get the interaction map from the context
|
// This is how we will get the interaction map from the context
|
||||||
@ -25,7 +25,7 @@ export const AllKeybindingsFields = forwardRef(
|
|||||||
.map(([category, categoryItems]) => (
|
.map(([category, categoryItems]) => (
|
||||||
<div className="flex flex-col gap-4 px-2 pr-4">
|
<div className="flex flex-col gap-4 px-2 pr-4">
|
||||||
<h2
|
<h2
|
||||||
id={`category-${category.replaceAll(/\s/g, '-')}`}
|
id={`category-${category}`}
|
||||||
className="text-xl mt-6 first-of-type:mt-0 capitalize font-bold"
|
className="text-xl mt-6 first-of-type:mt-0 capitalize font-bold"
|
||||||
>
|
>
|
||||||
{category}
|
{category}
|
||||||
|
@ -13,7 +13,7 @@ import { isDesktop } from 'lib/isDesktop'
|
|||||||
import { ActionButton } from 'components/ActionButton'
|
import { ActionButton } from 'components/ActionButton'
|
||||||
import { SettingsFieldInput } from './SettingsFieldInput'
|
import { SettingsFieldInput } from './SettingsFieldInput'
|
||||||
import toast from 'react-hot-toast'
|
import toast from 'react-hot-toast'
|
||||||
import { APP_VERSION, IS_NIGHTLY, RELEASE_URL } from 'routes/Settings'
|
import { APP_VERSION, PACKAGE_NAME } from 'routes/Settings'
|
||||||
import { PATHS } from 'lib/paths'
|
import { PATHS } from 'lib/paths'
|
||||||
import {
|
import {
|
||||||
createAndOpenNewTutorialProject,
|
createAndOpenNewTutorialProject,
|
||||||
@ -246,8 +246,10 @@ export const AllSettingsFields = forwardRef(
|
|||||||
to inject the version from package.json */}
|
to inject the version from package.json */}
|
||||||
App version {APP_VERSION}.{' '}
|
App version {APP_VERSION}.{' '}
|
||||||
<a
|
<a
|
||||||
onClick={openExternalBrowserIfDesktop(RELEASE_URL)}
|
onClick={openExternalBrowserIfDesktop(
|
||||||
href={RELEASE_URL}
|
`https://github.com/KittyCAD/modeling-app/releases/tag/v${APP_VERSION}`
|
||||||
|
)}
|
||||||
|
href={`https://github.com/KittyCAD/modeling-app/releases/tag/v${APP_VERSION}`}
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
>
|
>
|
||||||
@ -269,7 +271,7 @@ export const AllSettingsFields = forwardRef(
|
|||||||
, and start a discussion if you don't see it! Your feedback will
|
, and start a discussion if you don't see it! Your feedback will
|
||||||
help us prioritize what to build next.
|
help us prioritize what to build next.
|
||||||
</p>
|
</p>
|
||||||
{!IS_NIGHTLY && (
|
{PACKAGE_NAME.indexOf('-nightly') === -1 && (
|
||||||
<p className="max-w-2xl mt-6">
|
<p className="max-w-2xl mt-6">
|
||||||
Want to experience the latest and (hopefully) greatest from our
|
Want to experience the latest and (hopefully) greatest from our
|
||||||
main development branch?{' '}
|
main development branch?{' '}
|
||||||
|
@ -19,7 +19,7 @@ export function KeybindingsSectionsList({
|
|||||||
key={category}
|
key={category}
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
scrollRef.current
|
scrollRef.current
|
||||||
?.querySelector(`#category-${category.replaceAll(/\s/g, '-')}`)
|
?.querySelector(`#category-${category}`)
|
||||||
?.scrollIntoView({
|
?.scrollIntoView({
|
||||||
block: 'center',
|
block: 'center',
|
||||||
behavior: 'smooth',
|
behavior: 'smooth',
|
||||||
|
@ -18,8 +18,6 @@ import { useRouteLoaderData } from 'react-router-dom'
|
|||||||
import { PATHS } from 'lib/paths'
|
import { PATHS } from 'lib/paths'
|
||||||
import { IndexLoaderData } from 'lib/types'
|
import { IndexLoaderData } from 'lib/types'
|
||||||
import { useCommandsContext } from 'hooks/useCommandsContext'
|
import { useCommandsContext } from 'hooks/useCommandsContext'
|
||||||
import { err, reportRejection } from 'lib/trap'
|
|
||||||
import { getArtifactOfTypes } from 'lang/std/artifactGraph'
|
|
||||||
|
|
||||||
enum StreamState {
|
enum StreamState {
|
||||||
Playing = 'playing',
|
Playing = 'playing',
|
||||||
@ -282,49 +280,12 @@ export const Stream = () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* On double-click of sketch entities we automatically enter sketch mode with the selected sketch,
|
|
||||||
* allowing for quick editing of sketches. TODO: This should be moved to a more central place.
|
|
||||||
*/
|
|
||||||
const enterSketchModeIfSelectingSketch: MouseEventHandler<HTMLDivElement> = (
|
|
||||||
e
|
|
||||||
) => {
|
|
||||||
if (
|
|
||||||
!isNetworkOkay ||
|
|
||||||
!videoRef.current ||
|
|
||||||
state.matches('Sketch') ||
|
|
||||||
state.matches({ idle: 'showPlanes' }) ||
|
|
||||||
sceneInfra.camControls.wasDragging === true ||
|
|
||||||
!btnName(e.nativeEvent).left
|
|
||||||
) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
sendSelectEventToEngine(e, videoRef.current)
|
|
||||||
.then(({ entity_id }) => {
|
|
||||||
if (!entity_id) {
|
|
||||||
// No entity selected. This is benign
|
|
||||||
return
|
|
||||||
}
|
|
||||||
const path = getArtifactOfTypes(
|
|
||||||
{ key: entity_id, types: ['path', 'solid2D', 'segment'] },
|
|
||||||
engineCommandManager.artifactGraph
|
|
||||||
)
|
|
||||||
if (err(path)) {
|
|
||||||
return path
|
|
||||||
}
|
|
||||||
sceneInfra.modelingSend({ type: 'Enter sketch' })
|
|
||||||
})
|
|
||||||
.catch(reportRejection)
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className="absolute inset-0 z-0"
|
className="absolute inset-0 z-0"
|
||||||
id="stream"
|
id="stream"
|
||||||
data-testid="stream"
|
data-testid="stream"
|
||||||
onClick={handleMouseUp}
|
onClick={handleMouseUp}
|
||||||
onDoubleClick={enterSketchModeIfSelectingSketch}
|
|
||||||
onContextMenu={(e) => e.preventDefault()}
|
onContextMenu={(e) => e.preventDefault()}
|
||||||
onContextMenuCapture={(e) => e.preventDefault()}
|
onContextMenuCapture={(e) => e.preventDefault()}
|
||||||
>
|
>
|
||||||
|
@ -40,10 +40,7 @@ export function removeConstrainingValuesInfo({
|
|||||||
otherSelections: [],
|
otherSelections: [],
|
||||||
graphSelections: nodes.map(
|
graphSelections: nodes.map(
|
||||||
(node): Selection => ({
|
(node): Selection => ({
|
||||||
codeRef: codeRefFromRange(
|
codeRef: codeRefFromRange([node.start, node.end], kclManager.ast),
|
||||||
[node.start, node.end, true],
|
|
||||||
kclManager.ast
|
|
||||||
),
|
|
||||||
})
|
})
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
|
@ -22,7 +22,6 @@ import { removeDoubleNegatives } from '../AvailableVarsHelpers'
|
|||||||
import { normaliseAngle } from '../../lib/utils'
|
import { normaliseAngle } from '../../lib/utils'
|
||||||
import { kclManager } from 'lib/singletons'
|
import { kclManager } from 'lib/singletons'
|
||||||
import { err } from 'lib/trap'
|
import { err } from 'lib/trap'
|
||||||
import { KclCommandValue } from 'lib/commandTypes'
|
|
||||||
|
|
||||||
const getModalInfo = createSetAngleLengthModal(SetAngleLengthModal)
|
const getModalInfo = createSetAngleLengthModal(SetAngleLengthModal)
|
||||||
|
|
||||||
@ -64,57 +63,6 @@ export function angleLengthInfo({
|
|||||||
return { enabled, transforms }
|
return { enabled, transforms }
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function applyConstraintLength({
|
|
||||||
length,
|
|
||||||
selectionRanges,
|
|
||||||
}: {
|
|
||||||
length: KclCommandValue
|
|
||||||
selectionRanges: Selections
|
|
||||||
}) {
|
|
||||||
const ast = kclManager.ast
|
|
||||||
const angleLength = angleLengthInfo({ selectionRanges })
|
|
||||||
if (err(angleLength)) return angleLength
|
|
||||||
const { transforms } = angleLength
|
|
||||||
|
|
||||||
let distanceExpression: Expr = length.valueAst
|
|
||||||
|
|
||||||
/**
|
|
||||||
* To be "constrained", the value must be a binary expression, a named value, or a function call.
|
|
||||||
* If it has a variable name, we need to insert a variable declaration at the correct index.
|
|
||||||
*/
|
|
||||||
if (
|
|
||||||
'variableName' in length &&
|
|
||||||
length.variableName &&
|
|
||||||
length.insertIndex !== undefined
|
|
||||||
) {
|
|
||||||
const newBody = [...ast.body]
|
|
||||||
newBody.splice(length.insertIndex, 0, length.variableDeclarationAst)
|
|
||||||
ast.body = newBody
|
|
||||||
distanceExpression = createIdentifier(length.variableName)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isExprBinaryPart(distanceExpression)) {
|
|
||||||
return new Error('Invalid valueNode, is not a BinaryPart')
|
|
||||||
}
|
|
||||||
|
|
||||||
const retval = transformAstSketchLines({
|
|
||||||
ast,
|
|
||||||
selectionRanges,
|
|
||||||
transformInfos: transforms,
|
|
||||||
programMemory: kclManager.programMemory,
|
|
||||||
referenceSegName: '',
|
|
||||||
forceValueUsedInTransform: distanceExpression,
|
|
||||||
})
|
|
||||||
if (err(retval)) return Promise.reject(retval)
|
|
||||||
|
|
||||||
const { modifiedAst: _modifiedAst, pathToNodeMap } = retval
|
|
||||||
|
|
||||||
return {
|
|
||||||
modifiedAst: _modifiedAst,
|
|
||||||
pathToNodeMap,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function applyConstraintAngleLength({
|
export async function applyConstraintAngleLength({
|
||||||
selectionRanges,
|
selectionRanges,
|
||||||
angleOrLength = 'setLength',
|
angleOrLength = 'setLength',
|
||||||
|