Compare commits
118 Commits
grackle-la
...
kurt-remov
Author | SHA1 | Date | |
---|---|---|---|
cf10557f65 | |||
af97256ef2 | |||
40479d177f | |||
b88359dee2 | |||
f4c0347104 | |||
ad36b5f5fa | |||
b798cf19d3 | |||
7cfa897561 | |||
0d8804005a | |||
cbd26d29fa | |||
e501a542ac | |||
7cb4f4d101 | |||
1162f5f4c4 | |||
3975e6d8f5 | |||
d68d7a7e00 | |||
b135b97de6 | |||
de5885ce0b | |||
ad7c544754 | |||
4d77875bdc | |||
3377923dcb | |||
c6005660c8 | |||
66e62c6037 | |||
0a4a517bb4 | |||
70f3ded7e2 | |||
095108252b | |||
20b1c93f12 | |||
3747a1b993 | |||
198feb7d44 | |||
c7a8b8313e | |||
1576dc3256 | |||
341a3b7609 | |||
ecb42b89a6 | |||
f00ee3a44a | |||
900e3b96ad | |||
15fae05659 | |||
2730b6d152 | |||
602e7afef6 | |||
d9bcadb062 | |||
19f669b94c | |||
d9ef471385 | |||
39f8b306a2 | |||
19925d22c1 | |||
e1af4b4219 | |||
c699611f5b | |||
00ede7ec1a | |||
f30601bd2c | |||
cfbc77b62f | |||
808830d29e | |||
e714103655 | |||
fbcb96add5 | |||
7386ccf1bf | |||
6e73578933 | |||
b88d5c8799 | |||
5430c1fa66 | |||
c0d4bb6c9f | |||
25260a88c3 | |||
b6d6f0f4c1 | |||
b1276b2ed8 | |||
5f0f3f40d0 | |||
f1ea9b6ece | |||
b94c5be1af | |||
8378eb1e94 | |||
05f98a8c39 | |||
386571fa60 | |||
b0abdf4f70 | |||
81e70e139f | |||
d6bfc38d62 | |||
ada66de92d | |||
8f133f9662 | |||
b360dbb961 | |||
eca3dc2967 | |||
ae36ab6982 | |||
8cb6cf1b8a | |||
3c235c890a | |||
b6dfd30840 | |||
65d128eecd | |||
77b7c602f2 | |||
fa0e61a2be | |||
1cf35a611e | |||
952d0e4c7c | |||
0f85de9df8 | |||
0e8eed3f82 | |||
5b43a5075f | |||
f5ed4e37b2 | |||
19c8da1a86 | |||
a25f89aaba | |||
aeebe5416f | |||
661788b8b0 | |||
ac24563159 | |||
d17342dfb8 | |||
2e93b58ae6 | |||
6593656b08 | |||
47be749ec7 | |||
a03e7f5c41 | |||
b78e9fa131 | |||
c629233eaa | |||
f640f7a5e0 | |||
64398381a9 | |||
0bc5534056 | |||
9fc1df7c1d | |||
a5879ceeda | |||
379c30824e | |||
a4d3263b88 | |||
c1f661ab52 | |||
7d887a1497 | |||
4ca341e132 | |||
c6249f36d2 | |||
dcbe5d7f75 | |||
390cb2d51d | |||
98f7a564ea | |||
05f9e3c290 | |||
09760fc2e9 | |||
18ffc43e89 | |||
de63e4f19f | |||
b70b271e6b | |||
08b7cdc5f6 | |||
6efe6b54c0 | |||
69f72d62e0 |
@ -1,3 +1,3 @@
|
|||||||
[codespell]
|
[codespell]
|
||||||
ignore-words-list: crate,everytime
|
ignore-words-list: crate,everytime,inout,co-ordinate,ot,nwo
|
||||||
skip: **/target,node_modules,build,**/Cargo.lock
|
skip: **/target,node_modules,build,**/Cargo.lock
|
||||||
|
@ -1 +1,2 @@
|
|||||||
src/wasm-lib/*
|
src/wasm-lib/*
|
||||||
|
*.typegen.ts
|
||||||
|
@ -17,12 +17,12 @@
|
|||||||
"never"
|
"never"
|
||||||
],
|
],
|
||||||
"react-hooks/exhaustive-deps": "off",
|
"react-hooks/exhaustive-deps": "off",
|
||||||
"@typescript-eslint/no-floating-promises": "warn"
|
|
||||||
},
|
},
|
||||||
"overrides": [
|
"overrides": [
|
||||||
{
|
{
|
||||||
"files": ["e2e/**/*.ts"], // Update the pattern based on your file structure
|
"files": ["e2e/**/*.ts"], // Update the pattern based on your file structure
|
||||||
"rules": {
|
"rules": {
|
||||||
|
"@typescript-eslint/no-floating-promises": "warn",
|
||||||
"testing-library/prefer-screen-queries": "off"
|
"testing-library/prefer-screen-queries": "off"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
53
.github/workflows/ci.yml
vendored
@ -46,6 +46,7 @@ jobs:
|
|||||||
workspaces: './src/wasm-lib'
|
workspaces: './src/wasm-lib'
|
||||||
|
|
||||||
- run: yarn build:wasm
|
- run: yarn build:wasm
|
||||||
|
- run: yarn xstate:typegen
|
||||||
- run: yarn tsc
|
- run: yarn tsc
|
||||||
|
|
||||||
|
|
||||||
@ -85,8 +86,6 @@ jobs:
|
|||||||
|
|
||||||
- run: yarn test:nowatch
|
- run: yarn test:nowatch
|
||||||
|
|
||||||
- run: yarn test:cov
|
|
||||||
|
|
||||||
|
|
||||||
prepare-json-files:
|
prepare-json-files:
|
||||||
runs-on: ubuntu-latest # seperate job on Ubuntu for easy string manipulations (compared to Windows)
|
runs-on: ubuntu-latest # seperate job on Ubuntu for easy string manipulations (compared to Windows)
|
||||||
@ -125,7 +124,7 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
os: [macos-latest, ubuntu-latest, windows-latest]
|
os: [macos-14, ubuntu-latest, windows-latest]
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
@ -188,10 +187,10 @@ jobs:
|
|||||||
- name: Fix format
|
- name: Fix format
|
||||||
run: yarn fmt
|
run: yarn fmt
|
||||||
|
|
||||||
- name: Install Universal target (MacOS only)
|
- name: Install x86 target for Universal builds (MacOS only)
|
||||||
if: matrix.os == 'macos-latest'
|
if: matrix.os == 'macos-14'
|
||||||
run: |
|
run: |
|
||||||
rustup target add aarch64-apple-darwin
|
rustup target add x86_64-apple-darwin
|
||||||
|
|
||||||
- name: Prepare certificate and variables (Windows only)
|
- name: Prepare certificate and variables (Windows only)
|
||||||
if: ${{ matrix.os == 'windows-latest' && env.BUILD_RELEASE == 'true' }}
|
if: ${{ matrix.os == 'windows-latest' && env.BUILD_RELEASE == 'true' }}
|
||||||
@ -225,7 +224,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
includeRelease: false
|
includeRelease: false
|
||||||
includeDebug: true
|
includeDebug: true
|
||||||
args: ${{ matrix.os == 'macos-latest' && '--target universal-apple-darwin' || '' }}
|
args: ${{ matrix.os == 'macos-14' && '--target universal-apple-darwin' || '' }}
|
||||||
|
|
||||||
- name: Build the app (release) and sign
|
- name: Build the app (release) and sign
|
||||||
uses: tauri-apps/tauri-action@v0
|
uses: tauri-apps/tauri-action@v0
|
||||||
@ -241,12 +240,12 @@ jobs:
|
|||||||
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
|
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
|
||||||
TAURI_CONF_ARGS: "--config ${{ matrix.os == 'windows-latest' && 'src-tauri\\tauri.release.conf.json' || 'src-tauri/tauri.release.conf.json' }}"
|
TAURI_CONF_ARGS: "--config ${{ matrix.os == 'windows-latest' && 'src-tauri\\tauri.release.conf.json' || 'src-tauri/tauri.release.conf.json' }}"
|
||||||
with:
|
with:
|
||||||
args: "${{ matrix.os == 'macos-latest' && '--target universal-apple-darwin' || '' }} ${{ env.TAURI_CONF_ARGS }}"
|
args: "${{ matrix.os == 'macos-14' && '--target universal-apple-darwin' || '' }} ${{ env.TAURI_CONF_ARGS }}"
|
||||||
|
|
||||||
- uses: actions/upload-artifact@v3
|
- uses: actions/upload-artifact@v3
|
||||||
if: matrix.os != 'ubuntu-latest'
|
if: matrix.os != 'ubuntu-latest'
|
||||||
env:
|
env:
|
||||||
PREFIX: ${{ matrix.os == 'macos-latest' && 'src-tauri/target/universal-apple-darwin' || 'src-tauri/target' }}
|
PREFIX: ${{ matrix.os == 'macos-14' && 'src-tauri/target/universal-apple-darwin' || 'src-tauri/target' }}
|
||||||
MODE: ${{ env.BUILD_RELEASE == 'true' && 'release' || 'debug' }}
|
MODE: ${{ env.BUILD_RELEASE == 'true' && 'release' || 'debug' }}
|
||||||
with:
|
with:
|
||||||
path: "${{ env.PREFIX }}/${{ env.MODE }}/bundle/*/*"
|
path: "${{ env.PREFIX }}/${{ env.MODE }}/bundle/*/*"
|
||||||
@ -254,7 +253,7 @@ jobs:
|
|||||||
- name: Run e2e tests (linux only)
|
- name: Run e2e tests (linux only)
|
||||||
if: matrix.os == 'ubuntu-latest'
|
if: matrix.os == 'ubuntu-latest'
|
||||||
run: |
|
run: |
|
||||||
cargo install tauri-driver
|
cargo install tauri-driver@0.1.3
|
||||||
source .env.${{ env.BUILD_RELEASE == 'true' && 'production' || 'development' }}
|
source .env.${{ env.BUILD_RELEASE == 'true' && 'production' || 'development' }}
|
||||||
export VITE_KC_API_BASE_URL
|
export VITE_KC_API_BASE_URL
|
||||||
xvfb-run yarn test:e2e:tauri
|
xvfb-run yarn test:e2e:tauri
|
||||||
@ -337,17 +336,17 @@ jobs:
|
|||||||
cat last_download.json
|
cat last_download.json
|
||||||
|
|
||||||
- name: Authenticate to Google Cloud
|
- name: Authenticate to Google Cloud
|
||||||
uses: 'google-github-actions/auth@v2.0.0'
|
uses: 'google-github-actions/auth@v2.1.1'
|
||||||
with:
|
with:
|
||||||
credentials_json: '${{ secrets.GOOGLE_CLOUD_DL_SA }}'
|
credentials_json: '${{ secrets.GOOGLE_CLOUD_DL_SA }}'
|
||||||
|
|
||||||
- name: Set up Google Cloud SDK
|
- name: Set up Google Cloud SDK
|
||||||
uses: google-github-actions/setup-gcloud@v2.0.0
|
uses: google-github-actions/setup-gcloud@v2.1.0
|
||||||
with:
|
with:
|
||||||
project_id: kittycadapi
|
project_id: kittycadapi
|
||||||
|
|
||||||
- name: Upload release files to public bucket
|
- name: Upload release files to public bucket
|
||||||
uses: google-github-actions/upload-cloud-storage@v2.0.0
|
uses: google-github-actions/upload-cloud-storage@v2.1.0
|
||||||
with:
|
with:
|
||||||
path: artifact
|
path: artifact
|
||||||
glob: '*/Zoo*'
|
glob: '*/Zoo*'
|
||||||
@ -355,13 +354,13 @@ jobs:
|
|||||||
destination: ${{ env.BUCKET_DIR }}/${{ env.VERSION }}
|
destination: ${{ env.BUCKET_DIR }}/${{ env.VERSION }}
|
||||||
|
|
||||||
- name: Upload update endpoint to public bucket
|
- name: Upload update endpoint to public bucket
|
||||||
uses: google-github-actions/upload-cloud-storage@v2.0.0
|
uses: google-github-actions/upload-cloud-storage@v2.1.0
|
||||||
with:
|
with:
|
||||||
path: last_update.json
|
path: last_update.json
|
||||||
destination: ${{ env.BUCKET_DIR }}
|
destination: ${{ env.BUCKET_DIR }}
|
||||||
|
|
||||||
- name: Upload download endpoint to public bucket
|
- name: Upload download endpoint to public bucket
|
||||||
uses: google-github-actions/upload-cloud-storage@v2.0.0
|
uses: google-github-actions/upload-cloud-storage@v2.1.0
|
||||||
with:
|
with:
|
||||||
path: last_download.json
|
path: last_download.json
|
||||||
destination: ${{ env.BUCKET_DIR }}
|
destination: ${{ env.BUCKET_DIR }}
|
||||||
@ -371,3 +370,27 @@ jobs:
|
|||||||
uses: softprops/action-gh-release@v1
|
uses: softprops/action-gh-release@v1
|
||||||
with:
|
with:
|
||||||
files: 'artifact/*/Zoo*'
|
files: 'artifact/*/Zoo*'
|
||||||
|
|
||||||
|
announce_release:
|
||||||
|
needs: [publish-apps-release]
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Check out code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Set up Python
|
||||||
|
uses: actions/setup-python@v5
|
||||||
|
with:
|
||||||
|
python-version: '3.x'
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: |
|
||||||
|
python -m pip install --upgrade pip
|
||||||
|
pip install requests
|
||||||
|
|
||||||
|
- name: Announce Release
|
||||||
|
env:
|
||||||
|
DISCORD_WEBHOOK_URL: ${{ secrets.DISCORD_WEBHOOK_URL }}
|
||||||
|
RELEASE_VERSION: ${{ github.event.release.tag_name }}
|
||||||
|
RELEASE_BODY: ${{ github.event.release.body}}
|
||||||
|
run: python public/announce_release.py
|
2
.github/workflows/playwright.yml
vendored
@ -79,7 +79,7 @@ jobs:
|
|||||||
|
|
||||||
playwright-macos:
|
playwright-macos:
|
||||||
timeout-minutes: 60
|
timeout-minutes: 60
|
||||||
runs-on: macos-latest
|
runs-on: macos-14
|
||||||
needs: playwright-ubuntu
|
needs: playwright-ubuntu
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
4
.gitignore
vendored
@ -50,3 +50,7 @@ e2e/playwright/export-snapshots/*embedded.gltf
|
|||||||
/playwright-report/
|
/playwright-report/
|
||||||
/blob-report/
|
/blob-report/
|
||||||
/playwright/.cache/
|
/playwright/.cache/
|
||||||
|
|
||||||
|
|
||||||
|
## generated files
|
||||||
|
src/**/*.typegen.ts
|
||||||
|
@ -10,4 +10,4 @@ src/wasm-lib/kcl/bindings
|
|||||||
e2e/playwright/export-snapshots
|
e2e/playwright/export-snapshots
|
||||||
|
|
||||||
# XState generated files
|
# XState generated files
|
||||||
src/machines/modelingMachine.typegen.ts
|
src/machines/**.typegen.ts
|
||||||
|
@ -136,6 +136,11 @@ Before you submit a contribution PR to this repo, please ensure that:
|
|||||||
VERSION=x.y.z yarn run bump-jsons
|
VERSION=x.y.z yarn run bump-jsons
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Alternatively you can try the experimental `make-release.sh` bash script that will create the branch with the updated json files for you.
|
||||||
|
run `./make-release.sh` for a patch update
|
||||||
|
run `./make-release.sh "minor"` for minor
|
||||||
|
run `./make-release.sh "major"` for major
|
||||||
|
|
||||||
The PR may serve as a place to discuss the human-readable changelog and extra QA. A quick way of getting PR's merged since the last bump is to [use this PR filter](https://github.com/KittyCAD/modeling-app/pulls?q=is%3Apr+sort%3Aupdated-desc+is%3Amerged+), open up the browser console and past in the following
|
The PR may serve as a place to discuss the human-readable changelog and extra QA. A quick way of getting PR's merged since the last bump is to [use this PR filter](https://github.com/KittyCAD/modeling-app/pulls?q=is%3Apr+sort%3Aupdated-desc+is%3Amerged+), open up the browser console and past in the following
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
@ -182,7 +187,7 @@ For more information on fuzzing you can check out
|
|||||||
First time running plawright locally, you'll need to add the secrets file
|
First time running plawright locally, you'll need to add the secrets file
|
||||||
```bash
|
```bash
|
||||||
touch ./e2e/playwright/playwright-secrets.env
|
touch ./e2e/playwright/playwright-secrets.env
|
||||||
echo 'token="your-token"\nsnapshottoken="your-snapshot-token"' > ./e2e/playwright/playwright-secrets2.env
|
printf 'token="your-token"\nsnapshottoken="your-snapshot-token"' > ./e2e/playwright/playwright-secrets.env
|
||||||
```
|
```
|
||||||
then replace "your-token" with a dev token from dev.zoo.dev/account/api-tokens
|
then replace "your-token" with a dev token from dev.zoo.dev/account/api-tokens
|
||||||
|
|
||||||
|
16
docs/kcl/KNOWN-ISSUES.md
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
# Known Issues
|
||||||
|
|
||||||
|
The following are bugs that are not in modeling-app or kcl itself. These bugs
|
||||||
|
once fixed in engine will just start working here with no language changes.
|
||||||
|
|
||||||
|
- **Sketch on Face**: If your sketch is outside the edges of the face (on which you
|
||||||
|
are sketching) you will get multiple models returned instead of one single
|
||||||
|
model for that sketch and its underlying 3D object.
|
||||||
|
|
||||||
|
- **Patterns**: If you try and pass a pattern to `hole` currently only the first
|
||||||
|
item in the pattern is being subtracted. This is an engine bug that is being
|
||||||
|
worked on.
|
||||||
|
|
||||||
|
- **Import**: Right now you can import a file, even if that file has brep data
|
||||||
|
you cannot edit it. You also cannot move or transform the imported objects at
|
||||||
|
all. In the future, after v1, the engine will account for this.
|
31354
docs/kcl/std.json
5791
docs/kcl/std.md
Before Width: | Height: | Size: 80 KiB After Width: | Height: | Size: 193 KiB |
Before Width: | Height: | Size: 80 KiB After Width: | Height: | Size: 193 KiB |
Before Width: | Height: | Size: 56 KiB After Width: | Height: | Size: 259 KiB |
Before Width: | Height: | Size: 56 KiB After Width: | Height: | Size: 220 KiB |
Before Width: | Height: | Size: 56 KiB After Width: | Height: | Size: 220 KiB |
Before Width: | Height: | Size: 56 KiB After Width: | Height: | Size: 220 KiB |
Before Width: | Height: | Size: 80 KiB After Width: | Height: | Size: 193 KiB |
@ -1,7 +1,7 @@
|
|||||||
ISO-10303-21;
|
ISO-10303-21;
|
||||||
HEADER;
|
HEADER;
|
||||||
FILE_DESCRIPTION((('kittycad.io export')), '2;1');
|
FILE_DESCRIPTION((('zoo.dev export')), '2;1');
|
||||||
FILE_NAME('dump.step', '1970-01-01T00:00:00.0+00:00', ('Author unknown'), ('Organization unknown'), 'kittycad.io beta', 'kittycad.io', 'Authorization unknown');
|
FILE_NAME('dump.step', '1970-01-01T00:00:00.0+00:00', ('Author unknown'), ('Organization unknown'), 'zoo.dev beta', 'zoo.dev', 'Authorization unknown');
|
||||||
FILE_SCHEMA(('AP203_CONFIGURATION_CONTROLLED_3D_DESIGN_OF_MECHANICAL_PARTS_AND_ASSEMBLIES_MIM_LF'));
|
FILE_SCHEMA(('AP203_CONFIGURATION_CONTROLLED_3D_DESIGN_OF_MECHANICAL_PARTS_AND_ASSEMBLIES_MIM_LF'));
|
||||||
ENDSEC;
|
ENDSEC;
|
||||||
DATA;
|
DATA;
|
||||||
@ -19,59 +19,59 @@ DATA;
|
|||||||
);
|
);
|
||||||
#4 = CARTESIAN_POINT('NONE', (0, 0, -0));
|
#4 = CARTESIAN_POINT('NONE', (0, 0, -0));
|
||||||
#5 = VERTEX_POINT('NONE', #4);
|
#5 = VERTEX_POINT('NONE', #4);
|
||||||
#6 = CARTESIAN_POINT('NONE', (0, -0.0254, -0));
|
#6 = CARTESIAN_POINT('NONE', (0, -0.64516, -0));
|
||||||
#7 = VERTEX_POINT('NONE', #6);
|
#7 = VERTEX_POINT('NONE', #6);
|
||||||
#8 = CARTESIAN_POINT('NONE', (0, -0.0254, 0.1016));
|
#8 = CARTESIAN_POINT('NONE', (0, -0.64516, 2.58064));
|
||||||
#9 = VERTEX_POINT('NONE', #8);
|
#9 = VERTEX_POINT('NONE', #8);
|
||||||
#10 = CARTESIAN_POINT('NONE', (0, 0, 0.1016));
|
#10 = CARTESIAN_POINT('NONE', (0, 0, 2.58064));
|
||||||
#11 = VERTEX_POINT('NONE', #10);
|
#11 = VERTEX_POINT('NONE', #10);
|
||||||
#12 = CARTESIAN_POINT('NONE', (0.07861346939195568, -0.0254, -0));
|
#12 = CARTESIAN_POINT('NONE', (1.996782122555674, -0.64516, -0));
|
||||||
#13 = VERTEX_POINT('NONE', #12);
|
#13 = VERTEX_POINT('NONE', #12);
|
||||||
#14 = CARTESIAN_POINT('NONE', (0.07861346939195568, -0.0254, 0.1016));
|
#14 = CARTESIAN_POINT('NONE', (1.996782122555674, -0.64516, 2.58064));
|
||||||
#15 = VERTEX_POINT('NONE', #14);
|
#15 = VERTEX_POINT('NONE', #14);
|
||||||
#16 = CARTESIAN_POINT('NONE', (0.1511633881344551, -0.07619999999999998, -0));
|
#16 = CARTESIAN_POINT('NONE', (3.839550058615159, -1.9354799999999992, -0));
|
||||||
#17 = VERTEX_POINT('NONE', #16);
|
#17 = VERTEX_POINT('NONE', #16);
|
||||||
#18 = CARTESIAN_POINT('NONE', (0.1511633881344551, -0.07619999999999998, 0.1016));
|
#18 = CARTESIAN_POINT('NONE', (3.839550058615159, -1.9354799999999992, 2.58064));
|
||||||
#19 = VERTEX_POINT('NONE', #18);
|
#19 = VERTEX_POINT('NONE', #18);
|
||||||
#20 = CARTESIAN_POINT('NONE', (0.2413, -0.0762, -0));
|
#20 = CARTESIAN_POINT('NONE', (6.12902, -1.93548, -0));
|
||||||
#21 = VERTEX_POINT('NONE', #20);
|
#21 = VERTEX_POINT('NONE', #20);
|
||||||
#22 = CARTESIAN_POINT('NONE', (0.2413, -0.0762, 0.1016));
|
#22 = CARTESIAN_POINT('NONE', (6.12902, -1.93548, 2.58064));
|
||||||
#23 = VERTEX_POINT('NONE', #22);
|
#23 = VERTEX_POINT('NONE', #22);
|
||||||
#24 = CARTESIAN_POINT('NONE', (0.2413, -0.0635, -0));
|
#24 = CARTESIAN_POINT('NONE', (6.12902, -1.6129, -0));
|
||||||
#25 = VERTEX_POINT('NONE', #24);
|
#25 = VERTEX_POINT('NONE', #24);
|
||||||
#26 = CARTESIAN_POINT('NONE', (0.2413, -0.0635, 0.1016));
|
#26 = CARTESIAN_POINT('NONE', (6.12902, -1.6129, 2.58064));
|
||||||
#27 = VERTEX_POINT('NONE', #26);
|
#27 = VERTEX_POINT('NONE', #26);
|
||||||
#28 = CARTESIAN_POINT('NONE', (0.1551676827532182, -0.0635, -0));
|
#28 = CARTESIAN_POINT('NONE', (3.9412591419317424, -1.6129, -0));
|
||||||
#29 = VERTEX_POINT('NONE', #28);
|
#29 = VERTEX_POINT('NONE', #28);
|
||||||
#30 = CARTESIAN_POINT('NONE', (0.1551676827532182, -0.0635, 0.1016));
|
#30 = CARTESIAN_POINT('NONE', (3.9412591419317424, -1.6129, 2.58064));
|
||||||
#31 = VERTEX_POINT('NONE', #30);
|
#31 = VERTEX_POINT('NONE', #30);
|
||||||
#32 = CARTESIAN_POINT('NONE', (0.06448028432509392, 0, -0));
|
#32 = CARTESIAN_POINT('NONE', (1.6377992218573856, 0, -0));
|
||||||
#33 = VERTEX_POINT('NONE', #32);
|
#33 = VERTEX_POINT('NONE', #32);
|
||||||
#34 = CARTESIAN_POINT('NONE', (0.06448028432509392, 0, 0.1016));
|
#34 = CARTESIAN_POINT('NONE', (1.6377992218573856, 0, 2.58064));
|
||||||
#35 = VERTEX_POINT('NONE', #34);
|
#35 = VERTEX_POINT('NONE', #34);
|
||||||
#36 = CARTESIAN_POINT('NONE', (0.14618599799650817, 0.03810000000000001, -0));
|
#36 = CARTESIAN_POINT('NONE', (3.7131243491113075, 0.9677400000000002, -0));
|
||||||
#37 = VERTEX_POINT('NONE', #36);
|
#37 = VERTEX_POINT('NONE', #36);
|
||||||
#38 = CARTESIAN_POINT('NONE', (0.14618599799650817, 0.03810000000000001, 0.1016));
|
#38 = CARTESIAN_POINT('NONE', (3.7131243491113075, 0.9677400000000002, 2.58064));
|
||||||
#39 = VERTEX_POINT('NONE', #38);
|
#39 = VERTEX_POINT('NONE', #38);
|
||||||
#40 = CARTESIAN_POINT('NONE', (0.2413, 0.0381, -0));
|
#40 = CARTESIAN_POINT('NONE', (6.12902, 0.9677399999999998, -0));
|
||||||
#41 = VERTEX_POINT('NONE', #40);
|
#41 = VERTEX_POINT('NONE', #40);
|
||||||
#42 = CARTESIAN_POINT('NONE', (0.2413, 0.0381, 0.1016));
|
#42 = CARTESIAN_POINT('NONE', (6.12902, 0.9677399999999998, 2.58064));
|
||||||
#43 = VERTEX_POINT('NONE', #42);
|
#43 = VERTEX_POINT('NONE', #42);
|
||||||
#44 = CARTESIAN_POINT('NONE', (0.2413, 0.0508, -0));
|
#44 = CARTESIAN_POINT('NONE', (6.12902, 1.29032, -0));
|
||||||
#45 = VERTEX_POINT('NONE', #44);
|
#45 = VERTEX_POINT('NONE', #44);
|
||||||
#46 = CARTESIAN_POINT('NONE', (0.2413, 0.0508, 0.1016));
|
#46 = CARTESIAN_POINT('NONE', (6.12902, 1.29032, 2.58064));
|
||||||
#47 = VERTEX_POINT('NONE', #46);
|
#47 = VERTEX_POINT('NONE', #46);
|
||||||
#48 = CARTESIAN_POINT('NONE', (0.14337047578094278, 0.0508, -0));
|
#48 = CARTESIAN_POINT('NONE', (3.6416100848359463, 1.29032, -0));
|
||||||
#49 = VERTEX_POINT('NONE', #48);
|
#49 = VERTEX_POINT('NONE', #48);
|
||||||
#50 = CARTESIAN_POINT('NONE', (0.14337047578094278, 0.0508, 0.1016));
|
#50 = CARTESIAN_POINT('NONE', (3.6416100848359463, 1.29032, 2.58064));
|
||||||
#51 = VERTEX_POINT('NONE', #50);
|
#51 = VERTEX_POINT('NONE', #50);
|
||||||
#52 = CARTESIAN_POINT('NONE', (0.08889999999999999, 0.0254, -0));
|
#52 = CARTESIAN_POINT('NONE', (2.2580599999999995, 0.64516, -0));
|
||||||
#53 = VERTEX_POINT('NONE', #52);
|
#53 = VERTEX_POINT('NONE', #52);
|
||||||
#54 = CARTESIAN_POINT('NONE', (0.08889999999999999, 0.0254, 0.1016));
|
#54 = CARTESIAN_POINT('NONE', (2.2580599999999995, 0.64516, 2.58064));
|
||||||
#55 = VERTEX_POINT('NONE', #54);
|
#55 = VERTEX_POINT('NONE', #54);
|
||||||
#56 = CARTESIAN_POINT('NONE', (0, 0.0254, -0));
|
#56 = CARTESIAN_POINT('NONE', (0, 0.64516, -0));
|
||||||
#57 = VERTEX_POINT('NONE', #56);
|
#57 = VERTEX_POINT('NONE', #56);
|
||||||
#58 = CARTESIAN_POINT('NONE', (0, 0.0254, 0.1016));
|
#58 = CARTESIAN_POINT('NONE', (0, 0.64516, 2.58064));
|
||||||
#59 = VERTEX_POINT('NONE', #58);
|
#59 = VERTEX_POINT('NONE', #58);
|
||||||
#60 = DIRECTION('NONE', (0, -1, 0));
|
#60 = DIRECTION('NONE', (0, -1, 0));
|
||||||
#61 = VECTOR('NONE', #60, 1);
|
#61 = VECTOR('NONE', #60, 1);
|
||||||
@ -79,11 +79,11 @@ DATA;
|
|||||||
#63 = LINE('NONE', #62, #61);
|
#63 = LINE('NONE', #62, #61);
|
||||||
#64 = DIRECTION('NONE', (0, 0, 1));
|
#64 = DIRECTION('NONE', (0, 0, 1));
|
||||||
#65 = VECTOR('NONE', #64, 1);
|
#65 = VECTOR('NONE', #64, 1);
|
||||||
#66 = CARTESIAN_POINT('NONE', (0, -0.0254, -0));
|
#66 = CARTESIAN_POINT('NONE', (0, -0.64516, -0));
|
||||||
#67 = LINE('NONE', #66, #65);
|
#67 = LINE('NONE', #66, #65);
|
||||||
#68 = DIRECTION('NONE', (0, -1, 0));
|
#68 = DIRECTION('NONE', (0, -1, 0));
|
||||||
#69 = VECTOR('NONE', #68, 1);
|
#69 = VECTOR('NONE', #68, 1);
|
||||||
#70 = CARTESIAN_POINT('NONE', (0, 0, 0.1016));
|
#70 = CARTESIAN_POINT('NONE', (0, 0, 2.58064));
|
||||||
#71 = LINE('NONE', #70, #69);
|
#71 = LINE('NONE', #70, #69);
|
||||||
#72 = DIRECTION('NONE', (0, 0, 1));
|
#72 = DIRECTION('NONE', (0, 0, 1));
|
||||||
#73 = VECTOR('NONE', #72, 1);
|
#73 = VECTOR('NONE', #72, 1);
|
||||||
@ -91,155 +91,155 @@ DATA;
|
|||||||
#75 = LINE('NONE', #74, #73);
|
#75 = LINE('NONE', #74, #73);
|
||||||
#76 = DIRECTION('NONE', (1, 0, 0));
|
#76 = DIRECTION('NONE', (1, 0, 0));
|
||||||
#77 = VECTOR('NONE', #76, 1);
|
#77 = VECTOR('NONE', #76, 1);
|
||||||
#78 = CARTESIAN_POINT('NONE', (0, -0.0254, -0));
|
#78 = CARTESIAN_POINT('NONE', (0, -0.64516, -0));
|
||||||
#79 = LINE('NONE', #78, #77);
|
#79 = LINE('NONE', #78, #77);
|
||||||
#80 = DIRECTION('NONE', (0, 0, 1));
|
#80 = DIRECTION('NONE', (0, 0, 1));
|
||||||
#81 = VECTOR('NONE', #80, 1);
|
#81 = VECTOR('NONE', #80, 1);
|
||||||
#82 = CARTESIAN_POINT('NONE', (0.07861346939195568, -0.0254, -0));
|
#82 = CARTESIAN_POINT('NONE', (1.996782122555674, -0.64516, -0));
|
||||||
#83 = LINE('NONE', #82, #81);
|
#83 = LINE('NONE', #82, #81);
|
||||||
#84 = DIRECTION('NONE', (1, 0, 0));
|
#84 = DIRECTION('NONE', (1, 0, 0));
|
||||||
#85 = VECTOR('NONE', #84, 1);
|
#85 = VECTOR('NONE', #84, 1);
|
||||||
#86 = CARTESIAN_POINT('NONE', (0, -0.0254, 0.1016));
|
#86 = CARTESIAN_POINT('NONE', (0, -0.64516, 2.58064));
|
||||||
#87 = LINE('NONE', #86, #85);
|
#87 = LINE('NONE', #86, #85);
|
||||||
#88 = DIRECTION('NONE', (0.8191520442889919, -0.5735764363510459, 0));
|
#88 = DIRECTION('NONE', (0.819152044288992, -0.5735764363510459, 0));
|
||||||
#89 = VECTOR('NONE', #88, 1);
|
#89 = VECTOR('NONE', #88, 1);
|
||||||
#90 = CARTESIAN_POINT('NONE', (0.07861346939195568, -0.0254, -0));
|
#90 = CARTESIAN_POINT('NONE', (1.996782122555674, -0.64516, -0));
|
||||||
#91 = LINE('NONE', #90, #89);
|
#91 = LINE('NONE', #90, #89);
|
||||||
#92 = DIRECTION('NONE', (0, 0, 1));
|
#92 = DIRECTION('NONE', (0, 0, 1));
|
||||||
#93 = VECTOR('NONE', #92, 1);
|
#93 = VECTOR('NONE', #92, 1);
|
||||||
#94 = CARTESIAN_POINT('NONE', (0.1511633881344551, -0.07619999999999998, -0));
|
#94 = CARTESIAN_POINT('NONE', (3.839550058615159, -1.9354799999999992, -0));
|
||||||
#95 = LINE('NONE', #94, #93);
|
#95 = LINE('NONE', #94, #93);
|
||||||
#96 = DIRECTION('NONE', (0.8191520442889919, -0.5735764363510459, 0));
|
#96 = DIRECTION('NONE', (0.819152044288992, -0.5735764363510459, 0));
|
||||||
#97 = VECTOR('NONE', #96, 1);
|
#97 = VECTOR('NONE', #96, 1);
|
||||||
#98 = CARTESIAN_POINT('NONE', (0.07861346939195568, -0.0254, 0.1016));
|
#98 = CARTESIAN_POINT('NONE', (1.996782122555674, -0.64516, 2.58064));
|
||||||
#99 = LINE('NONE', #98, #97);
|
#99 = LINE('NONE', #98, #97);
|
||||||
#100 = DIRECTION('NONE', (1, -0.0000000000000003079278779307945, 0));
|
#100 = DIRECTION('NONE', (1, -0.00000000000000038794063361359933, 0));
|
||||||
#101 = VECTOR('NONE', #100, 1);
|
#101 = VECTOR('NONE', #100, 1);
|
||||||
#102 = CARTESIAN_POINT('NONE', (0.1511633881344551, -0.07619999999999998, -0));
|
#102 = CARTESIAN_POINT('NONE', (3.839550058615159, -1.9354799999999992, -0));
|
||||||
#103 = LINE('NONE', #102, #101);
|
#103 = LINE('NONE', #102, #101);
|
||||||
#104 = DIRECTION('NONE', (0, 0, 1));
|
#104 = DIRECTION('NONE', (0, 0, 1));
|
||||||
#105 = VECTOR('NONE', #104, 1);
|
#105 = VECTOR('NONE', #104, 1);
|
||||||
#106 = CARTESIAN_POINT('NONE', (0.2413, -0.0762, -0));
|
#106 = CARTESIAN_POINT('NONE', (6.12902, -1.93548, -0));
|
||||||
#107 = LINE('NONE', #106, #105);
|
#107 = LINE('NONE', #106, #105);
|
||||||
#108 = DIRECTION('NONE', (1, -0.0000000000000003079278779307945, 0));
|
#108 = DIRECTION('NONE', (1, -0.00000000000000038794063361359933, 0));
|
||||||
#109 = VECTOR('NONE', #108, 1);
|
#109 = VECTOR('NONE', #108, 1);
|
||||||
#110 = CARTESIAN_POINT('NONE', (0.1511633881344551, -0.07619999999999998, 0.1016));
|
#110 = CARTESIAN_POINT('NONE', (3.839550058615159, -1.9354799999999992, 2.58064));
|
||||||
#111 = LINE('NONE', #110, #109);
|
#111 = LINE('NONE', #110, #109);
|
||||||
#112 = DIRECTION('NONE', (0, 1, 0));
|
#112 = DIRECTION('NONE', (0, 1, 0));
|
||||||
#113 = VECTOR('NONE', #112, 1);
|
#113 = VECTOR('NONE', #112, 1);
|
||||||
#114 = CARTESIAN_POINT('NONE', (0.2413, -0.0762, -0));
|
#114 = CARTESIAN_POINT('NONE', (6.12902, -1.93548, -0));
|
||||||
#115 = LINE('NONE', #114, #113);
|
#115 = LINE('NONE', #114, #113);
|
||||||
#116 = DIRECTION('NONE', (0, 0, 1));
|
#116 = DIRECTION('NONE', (0, 0, 1));
|
||||||
#117 = VECTOR('NONE', #116, 1);
|
#117 = VECTOR('NONE', #116, 1);
|
||||||
#118 = CARTESIAN_POINT('NONE', (0.2413, -0.0635, -0));
|
#118 = CARTESIAN_POINT('NONE', (6.12902, -1.6129, -0));
|
||||||
#119 = LINE('NONE', #118, #117);
|
#119 = LINE('NONE', #118, #117);
|
||||||
#120 = DIRECTION('NONE', (0, 1, 0));
|
#120 = DIRECTION('NONE', (0, 1, 0));
|
||||||
#121 = VECTOR('NONE', #120, 1);
|
#121 = VECTOR('NONE', #120, 1);
|
||||||
#122 = CARTESIAN_POINT('NONE', (0.2413, -0.0762, 0.1016));
|
#122 = CARTESIAN_POINT('NONE', (6.12902, -1.93548, 2.58064));
|
||||||
#123 = LINE('NONE', #122, #121);
|
#123 = LINE('NONE', #122, #121);
|
||||||
#124 = DIRECTION('NONE', (-1, 0, 0));
|
#124 = DIRECTION('NONE', (-1, 0, 0));
|
||||||
#125 = VECTOR('NONE', #124, 1);
|
#125 = VECTOR('NONE', #124, 1);
|
||||||
#126 = CARTESIAN_POINT('NONE', (0.2413, -0.0635, -0));
|
#126 = CARTESIAN_POINT('NONE', (6.12902, -1.6129, -0));
|
||||||
#127 = LINE('NONE', #126, #125);
|
#127 = LINE('NONE', #126, #125);
|
||||||
#128 = DIRECTION('NONE', (0, 0, 1));
|
#128 = DIRECTION('NONE', (0, 0, 1));
|
||||||
#129 = VECTOR('NONE', #128, 1);
|
#129 = VECTOR('NONE', #128, 1);
|
||||||
#130 = CARTESIAN_POINT('NONE', (0.1551676827532182, -0.0635, -0));
|
#130 = CARTESIAN_POINT('NONE', (3.9412591419317424, -1.6129, -0));
|
||||||
#131 = LINE('NONE', #130, #129);
|
#131 = LINE('NONE', #130, #129);
|
||||||
#132 = DIRECTION('NONE', (-1, 0, 0));
|
#132 = DIRECTION('NONE', (-1, 0, 0));
|
||||||
#133 = VECTOR('NONE', #132, 1);
|
#133 = VECTOR('NONE', #132, 1);
|
||||||
#134 = CARTESIAN_POINT('NONE', (0.2413, -0.0635, 0.1016));
|
#134 = CARTESIAN_POINT('NONE', (6.12902, -1.6129, 2.58064));
|
||||||
#135 = LINE('NONE', #134, #133);
|
#135 = LINE('NONE', #134, #133);
|
||||||
#136 = DIRECTION('NONE', (-0.8191520442889919, 0.573576436351046, 0));
|
#136 = DIRECTION('NONE', (-0.8191520442889919, 0.573576436351046, 0));
|
||||||
#137 = VECTOR('NONE', #136, 1);
|
#137 = VECTOR('NONE', #136, 1);
|
||||||
#138 = CARTESIAN_POINT('NONE', (0.1551676827532182, -0.0635, -0));
|
#138 = CARTESIAN_POINT('NONE', (3.9412591419317424, -1.6129, -0));
|
||||||
#139 = LINE('NONE', #138, #137);
|
#139 = LINE('NONE', #138, #137);
|
||||||
#140 = DIRECTION('NONE', (0, 0, 1));
|
#140 = DIRECTION('NONE', (0, 0, 1));
|
||||||
#141 = VECTOR('NONE', #140, 1);
|
#141 = VECTOR('NONE', #140, 1);
|
||||||
#142 = CARTESIAN_POINT('NONE', (0.06448028432509392, 0, -0));
|
#142 = CARTESIAN_POINT('NONE', (1.6377992218573856, 0, -0));
|
||||||
#143 = LINE('NONE', #142, #141);
|
#143 = LINE('NONE', #142, #141);
|
||||||
#144 = DIRECTION('NONE', (-0.8191520442889919, 0.573576436351046, 0));
|
#144 = DIRECTION('NONE', (-0.8191520442889919, 0.573576436351046, 0));
|
||||||
#145 = VECTOR('NONE', #144, 1);
|
#145 = VECTOR('NONE', #144, 1);
|
||||||
#146 = CARTESIAN_POINT('NONE', (0.1551676827532182, -0.0635, 0.1016));
|
#146 = CARTESIAN_POINT('NONE', (3.9412591419317424, -1.6129, 2.58064));
|
||||||
#147 = LINE('NONE', #146, #145);
|
#147 = LINE('NONE', #146, #145);
|
||||||
#148 = DIRECTION('NONE', (0.90630778703665, 0.4226182617406993, 0));
|
#148 = DIRECTION('NONE', (0.90630778703665, 0.4226182617406992, 0));
|
||||||
#149 = VECTOR('NONE', #148, 1);
|
#149 = VECTOR('NONE', #148, 1);
|
||||||
#150 = CARTESIAN_POINT('NONE', (0.06448028432509392, 0, -0));
|
#150 = CARTESIAN_POINT('NONE', (1.6377992218573856, 0, -0));
|
||||||
#151 = LINE('NONE', #150, #149);
|
#151 = LINE('NONE', #150, #149);
|
||||||
#152 = DIRECTION('NONE', (0, 0, 1));
|
#152 = DIRECTION('NONE', (0, 0, 1));
|
||||||
#153 = VECTOR('NONE', #152, 1);
|
#153 = VECTOR('NONE', #152, 1);
|
||||||
#154 = CARTESIAN_POINT('NONE', (0.14618599799650817, 0.03810000000000001, -0));
|
#154 = CARTESIAN_POINT('NONE', (3.7131243491113075, 0.9677400000000002, -0));
|
||||||
#155 = LINE('NONE', #154, #153);
|
#155 = LINE('NONE', #154, #153);
|
||||||
#156 = DIRECTION('NONE', (0.90630778703665, 0.4226182617406993, 0));
|
#156 = DIRECTION('NONE', (0.90630778703665, 0.4226182617406992, 0));
|
||||||
#157 = VECTOR('NONE', #156, 1);
|
#157 = VECTOR('NONE', #156, 1);
|
||||||
#158 = CARTESIAN_POINT('NONE', (0.06448028432509392, 0, 0.1016));
|
#158 = CARTESIAN_POINT('NONE', (1.6377992218573856, 0, 2.58064));
|
||||||
#159 = LINE('NONE', #158, #157);
|
#159 = LINE('NONE', #158, #157);
|
||||||
#160 = DIRECTION('NONE', (1, -0.00000000000000007295344279228718, 0));
|
#160 = DIRECTION('NONE', (1, -0.0000000000000001378647737807002, 0));
|
||||||
#161 = VECTOR('NONE', #160, 1);
|
#161 = VECTOR('NONE', #160, 1);
|
||||||
#162 = CARTESIAN_POINT('NONE', (0.14618599799650817, 0.03810000000000001, -0));
|
#162 = CARTESIAN_POINT('NONE', (3.7131243491113075, 0.9677400000000002, -0));
|
||||||
#163 = LINE('NONE', #162, #161);
|
#163 = LINE('NONE', #162, #161);
|
||||||
#164 = DIRECTION('NONE', (0, 0, 1));
|
#164 = DIRECTION('NONE', (0, 0, 1));
|
||||||
#165 = VECTOR('NONE', #164, 1);
|
#165 = VECTOR('NONE', #164, 1);
|
||||||
#166 = CARTESIAN_POINT('NONE', (0.2413, 0.0381, -0));
|
#166 = CARTESIAN_POINT('NONE', (6.12902, 0.9677399999999998, -0));
|
||||||
#167 = LINE('NONE', #166, #165);
|
#167 = LINE('NONE', #166, #165);
|
||||||
#168 = DIRECTION('NONE', (1, -0.00000000000000007295344279228718, 0));
|
#168 = DIRECTION('NONE', (1, -0.0000000000000001378647737807002, 0));
|
||||||
#169 = VECTOR('NONE', #168, 1);
|
#169 = VECTOR('NONE', #168, 1);
|
||||||
#170 = CARTESIAN_POINT('NONE', (0.14618599799650817, 0.03810000000000001, 0.1016));
|
#170 = CARTESIAN_POINT('NONE', (3.7131243491113075, 0.9677400000000002, 2.58064));
|
||||||
#171 = LINE('NONE', #170, #169);
|
#171 = LINE('NONE', #170, #169);
|
||||||
#172 = DIRECTION('NONE', (0, 1, 0));
|
#172 = DIRECTION('NONE', (0, 1, 0));
|
||||||
#173 = VECTOR('NONE', #172, 1);
|
#173 = VECTOR('NONE', #172, 1);
|
||||||
#174 = CARTESIAN_POINT('NONE', (0.2413, 0.0381, -0));
|
#174 = CARTESIAN_POINT('NONE', (6.12902, 0.9677399999999998, -0));
|
||||||
#175 = LINE('NONE', #174, #173);
|
#175 = LINE('NONE', #174, #173);
|
||||||
#176 = DIRECTION('NONE', (0, 0, 1));
|
#176 = DIRECTION('NONE', (0, 0, 1));
|
||||||
#177 = VECTOR('NONE', #176, 1);
|
#177 = VECTOR('NONE', #176, 1);
|
||||||
#178 = CARTESIAN_POINT('NONE', (0.2413, 0.0508, -0));
|
#178 = CARTESIAN_POINT('NONE', (6.12902, 1.29032, -0));
|
||||||
#179 = LINE('NONE', #178, #177);
|
#179 = LINE('NONE', #178, #177);
|
||||||
#180 = DIRECTION('NONE', (0, 1, 0));
|
#180 = DIRECTION('NONE', (0, 1, 0));
|
||||||
#181 = VECTOR('NONE', #180, 1);
|
#181 = VECTOR('NONE', #180, 1);
|
||||||
#182 = CARTESIAN_POINT('NONE', (0.2413, 0.0381, 0.1016));
|
#182 = CARTESIAN_POINT('NONE', (6.12902, 0.9677399999999998, 2.58064));
|
||||||
#183 = LINE('NONE', #182, #181);
|
#183 = LINE('NONE', #182, #181);
|
||||||
#184 = DIRECTION('NONE', (-1, 0, 0));
|
#184 = DIRECTION('NONE', (-1, 0, 0));
|
||||||
#185 = VECTOR('NONE', #184, 1);
|
#185 = VECTOR('NONE', #184, 1);
|
||||||
#186 = CARTESIAN_POINT('NONE', (0.2413, 0.0508, -0));
|
#186 = CARTESIAN_POINT('NONE', (6.12902, 1.29032, -0));
|
||||||
#187 = LINE('NONE', #186, #185);
|
#187 = LINE('NONE', #186, #185);
|
||||||
#188 = DIRECTION('NONE', (0, 0, 1));
|
#188 = DIRECTION('NONE', (0, 0, 1));
|
||||||
#189 = VECTOR('NONE', #188, 1);
|
#189 = VECTOR('NONE', #188, 1);
|
||||||
#190 = CARTESIAN_POINT('NONE', (0.14337047578094278, 0.0508, -0));
|
#190 = CARTESIAN_POINT('NONE', (3.6416100848359463, 1.29032, -0));
|
||||||
#191 = LINE('NONE', #190, #189);
|
#191 = LINE('NONE', #190, #189);
|
||||||
#192 = DIRECTION('NONE', (-1, 0, 0));
|
#192 = DIRECTION('NONE', (-1, 0, 0));
|
||||||
#193 = VECTOR('NONE', #192, 1);
|
#193 = VECTOR('NONE', #192, 1);
|
||||||
#194 = CARTESIAN_POINT('NONE', (0.2413, 0.0508, 0.1016));
|
#194 = CARTESIAN_POINT('NONE', (6.12902, 1.29032, 2.58064));
|
||||||
#195 = LINE('NONE', #194, #193);
|
#195 = LINE('NONE', #194, #193);
|
||||||
#196 = DIRECTION('NONE', (-0.90630778703665, -0.42261826174069944, 0));
|
#196 = DIRECTION('NONE', (-0.90630778703665, -0.4226182617406995, 0));
|
||||||
#197 = VECTOR('NONE', #196, 1);
|
#197 = VECTOR('NONE', #196, 1);
|
||||||
#198 = CARTESIAN_POINT('NONE', (0.14337047578094278, 0.0508, -0));
|
#198 = CARTESIAN_POINT('NONE', (3.6416100848359463, 1.29032, -0));
|
||||||
#199 = LINE('NONE', #198, #197);
|
#199 = LINE('NONE', #198, #197);
|
||||||
#200 = DIRECTION('NONE', (0, 0, 1));
|
#200 = DIRECTION('NONE', (0, 0, 1));
|
||||||
#201 = VECTOR('NONE', #200, 1);
|
#201 = VECTOR('NONE', #200, 1);
|
||||||
#202 = CARTESIAN_POINT('NONE', (0.08889999999999999, 0.0254, -0));
|
#202 = CARTESIAN_POINT('NONE', (2.2580599999999995, 0.64516, -0));
|
||||||
#203 = LINE('NONE', #202, #201);
|
#203 = LINE('NONE', #202, #201);
|
||||||
#204 = DIRECTION('NONE', (-0.90630778703665, -0.42261826174069944, 0));
|
#204 = DIRECTION('NONE', (-0.90630778703665, -0.4226182617406995, 0));
|
||||||
#205 = VECTOR('NONE', #204, 1);
|
#205 = VECTOR('NONE', #204, 1);
|
||||||
#206 = CARTESIAN_POINT('NONE', (0.14337047578094278, 0.0508, 0.1016));
|
#206 = CARTESIAN_POINT('NONE', (3.6416100848359463, 1.29032, 2.58064));
|
||||||
#207 = LINE('NONE', #206, #205);
|
#207 = LINE('NONE', #206, #205);
|
||||||
#208 = DIRECTION('NONE', (-1, 0, 0));
|
#208 = DIRECTION('NONE', (-1, 0, 0));
|
||||||
#209 = VECTOR('NONE', #208, 1);
|
#209 = VECTOR('NONE', #208, 1);
|
||||||
#210 = CARTESIAN_POINT('NONE', (0.08889999999999999, 0.0254, -0));
|
#210 = CARTESIAN_POINT('NONE', (2.2580599999999995, 0.64516, -0));
|
||||||
#211 = LINE('NONE', #210, #209);
|
#211 = LINE('NONE', #210, #209);
|
||||||
#212 = DIRECTION('NONE', (0, 0, 1));
|
#212 = DIRECTION('NONE', (0, 0, 1));
|
||||||
#213 = VECTOR('NONE', #212, 1);
|
#213 = VECTOR('NONE', #212, 1);
|
||||||
#214 = CARTESIAN_POINT('NONE', (0, 0.0254, -0));
|
#214 = CARTESIAN_POINT('NONE', (0, 0.64516, -0));
|
||||||
#215 = LINE('NONE', #214, #213);
|
#215 = LINE('NONE', #214, #213);
|
||||||
#216 = DIRECTION('NONE', (-1, 0, 0));
|
#216 = DIRECTION('NONE', (-1, 0, 0));
|
||||||
#217 = VECTOR('NONE', #216, 1);
|
#217 = VECTOR('NONE', #216, 1);
|
||||||
#218 = CARTESIAN_POINT('NONE', (0.08889999999999999, 0.0254, 0.1016));
|
#218 = CARTESIAN_POINT('NONE', (2.2580599999999995, 0.64516, 2.58064));
|
||||||
#219 = LINE('NONE', #218, #217);
|
#219 = LINE('NONE', #218, #217);
|
||||||
#220 = DIRECTION('NONE', (0, -1, 0));
|
#220 = DIRECTION('NONE', (0, -1, 0));
|
||||||
#221 = VECTOR('NONE', #220, 1);
|
#221 = VECTOR('NONE', #220, 1);
|
||||||
#222 = CARTESIAN_POINT('NONE', (0, 0.0254, -0));
|
#222 = CARTESIAN_POINT('NONE', (0, 0.64516, -0));
|
||||||
#223 = LINE('NONE', #222, #221);
|
#223 = LINE('NONE', #222, #221);
|
||||||
#224 = DIRECTION('NONE', (0, -1, 0));
|
#224 = DIRECTION('NONE', (0, -1, 0));
|
||||||
#225 = VECTOR('NONE', #224, 1);
|
#225 = VECTOR('NONE', #224, 1);
|
||||||
#226 = CARTESIAN_POINT('NONE', (0, 0.0254, 0.1016));
|
#226 = CARTESIAN_POINT('NONE', (0, 0.64516, 2.58064));
|
||||||
#227 = LINE('NONE', #226, #225);
|
#227 = LINE('NONE', #226, #225);
|
||||||
#228 = EDGE_CURVE('NONE', #5, #7, #63, .T.);
|
#228 = EDGE_CURVE('NONE', #5, #7, #63, .T.);
|
||||||
#229 = EDGE_CURVE('NONE', #7, #9, #67, .T.);
|
#229 = EDGE_CURVE('NONE', #7, #9, #67, .T.);
|
||||||
@ -383,67 +383,67 @@ DATA;
|
|||||||
#367 = ORIENTED_EDGE('NONE', *, *, #267, .T.);
|
#367 = ORIENTED_EDGE('NONE', *, *, #267, .T.);
|
||||||
#368 = ORIENTED_EDGE('NONE', *, *, #269, .T.);
|
#368 = ORIENTED_EDGE('NONE', *, *, #269, .T.);
|
||||||
#369 = EDGE_LOOP('NONE', (#355, #356, #357, #358, #359, #360, #361, #362, #363, #364, #365, #366, #367, #368));
|
#369 = EDGE_LOOP('NONE', (#355, #356, #357, #358, #359, #360, #361, #362, #363, #364, #365, #366, #367, #368));
|
||||||
#370 = CARTESIAN_POINT('NONE', (0, -0.0127, 0.0508));
|
#370 = CARTESIAN_POINT('NONE', (0, -0.3225799999999985, 1.2903199999999995));
|
||||||
#371 = DIRECTION('NONE', (-1, 0, -0));
|
#371 = DIRECTION('NONE', (-1, -0, 0));
|
||||||
#372 = AXIS2_PLACEMENT_3D('NONE', #370, #371, $);
|
#372 = AXIS2_PLACEMENT_3D('NONE', #370, #371, $);
|
||||||
#373 = PLANE('NONE', #372);
|
#373 = PLANE('NONE', #372);
|
||||||
#374 = CARTESIAN_POINT('NONE', (0.039306734695977924, -0.025399999999999995, 0.0508));
|
#374 = CARTESIAN_POINT('NONE', (0.9983910612778368, -0.6451599999999998, 1.2903199999999997));
|
||||||
#375 = DIRECTION('NONE', (0, -1, -0));
|
#375 = DIRECTION('NONE', (0, -1, 0));
|
||||||
#376 = AXIS2_PLACEMENT_3D('NONE', #374, #375, $);
|
#376 = AXIS2_PLACEMENT_3D('NONE', #374, #375, $);
|
||||||
#377 = PLANE('NONE', #376);
|
#377 = PLANE('NONE', #376);
|
||||||
#378 = CARTESIAN_POINT('NONE', (0.11488842876320533, -0.05079999999999996, 0.05079999999999999));
|
#378 = CARTESIAN_POINT('NONE', (2.918166090585415, -1.2903199999999988, 1.2903199999999997));
|
||||||
#379 = DIRECTION('NONE', (-0.5735764363510459, -0.819152044288992, 0));
|
#379 = DIRECTION('NONE', (-0.5735764363510459, -0.8191520442889919, 0));
|
||||||
#380 = AXIS2_PLACEMENT_3D('NONE', #378, #379, $);
|
#380 = AXIS2_PLACEMENT_3D('NONE', #378, #379, $);
|
||||||
#381 = PLANE('NONE', #380);
|
#381 = PLANE('NONE', #380);
|
||||||
#382 = CARTESIAN_POINT('NONE', (0.19623169406722757, -0.07619999999999999, 0.0508));
|
#382 = CARTESIAN_POINT('NONE', (4.984285029307579, -1.9354799999999992, 1.2903199999999997));
|
||||||
#383 = DIRECTION('NONE', (0, -1, -0));
|
#383 = DIRECTION('NONE', (0, -1, 0));
|
||||||
#384 = AXIS2_PLACEMENT_3D('NONE', #382, #383, $);
|
#384 = AXIS2_PLACEMENT_3D('NONE', #382, #383, $);
|
||||||
#385 = PLANE('NONE', #384);
|
#385 = PLANE('NONE', #384);
|
||||||
#386 = CARTESIAN_POINT('NONE', (0.2413, -0.06985, 0.0508));
|
#386 = CARTESIAN_POINT('NONE', (6.129019999999999, -1.7741899999999997, 1.2903199999999997));
|
||||||
#387 = DIRECTION('NONE', (1, 0, -0));
|
#387 = DIRECTION('NONE', (1, -0, 0));
|
||||||
#388 = AXIS2_PLACEMENT_3D('NONE', #386, #387, $);
|
#388 = AXIS2_PLACEMENT_3D('NONE', #386, #387, $);
|
||||||
#389 = PLANE('NONE', #388);
|
#389 = PLANE('NONE', #388);
|
||||||
#390 = CARTESIAN_POINT('NONE', (0.19823384137660915, -0.0635, 0.0508));
|
#390 = CARTESIAN_POINT('NONE', (5.035139570965871, -1.6128999999999998, 1.2903199999999997));
|
||||||
#391 = DIRECTION('NONE', (0, 1, -0));
|
#391 = DIRECTION('NONE', (0, 1, -0));
|
||||||
#392 = AXIS2_PLACEMENT_3D('NONE', #390, #391, $);
|
#392 = AXIS2_PLACEMENT_3D('NONE', #390, #391, $);
|
||||||
#393 = PLANE('NONE', #392);
|
#393 = PLANE('NONE', #392);
|
||||||
#394 = CARTESIAN_POINT('NONE', (0.10982398353915601, -0.03174999999999997, 0.0508));
|
#394 = CARTESIAN_POINT('NONE', (2.7895291818945633, -0.8064499999999998, 1.2903199999999995));
|
||||||
#395 = DIRECTION('NONE', (0.573576436351046, 0.8191520442889918, -0));
|
#395 = DIRECTION('NONE', (0.5735764363510459, 0.8191520442889918, -0));
|
||||||
#396 = AXIS2_PLACEMENT_3D('NONE', #394, #395, $);
|
#396 = AXIS2_PLACEMENT_3D('NONE', #394, #395, $);
|
||||||
#397 = PLANE('NONE', #396);
|
#397 = PLANE('NONE', #396);
|
||||||
#398 = CARTESIAN_POINT('NONE', (0.105333141160801, 0.019049999999999987, 0.0508));
|
#398 = CARTESIAN_POINT('NONE', (2.6754617854843468, 0.4838700000000003, 1.2903199999999997));
|
||||||
#399 = DIRECTION('NONE', (0.4226182617406993, -0.90630778703665, -0));
|
#399 = DIRECTION('NONE', (0.4226182617406992, -0.90630778703665, 0));
|
||||||
#400 = AXIS2_PLACEMENT_3D('NONE', #398, #399, $);
|
#400 = AXIS2_PLACEMENT_3D('NONE', #398, #399, $);
|
||||||
#401 = PLANE('NONE', #400);
|
#401 = PLANE('NONE', #400);
|
||||||
#402 = CARTESIAN_POINT('NONE', (0.19374299899825406, 0.0381, 0.0508));
|
#402 = CARTESIAN_POINT('NONE', (4.921072174555653, 0.9677399999999998, 1.2903199999999995));
|
||||||
#403 = DIRECTION('NONE', (0, -1, -0));
|
#403 = DIRECTION('NONE', (0, -1, 0));
|
||||||
#404 = AXIS2_PLACEMENT_3D('NONE', #402, #403, $);
|
#404 = AXIS2_PLACEMENT_3D('NONE', #402, #403, $);
|
||||||
#405 = PLANE('NONE', #404);
|
#405 = PLANE('NONE', #404);
|
||||||
#406 = CARTESIAN_POINT('NONE', (0.2413, 0.044449999999999996, 0.0508));
|
#406 = CARTESIAN_POINT('NONE', (6.129019999999998, 1.1290299999999989, 1.2903199999999995));
|
||||||
#407 = DIRECTION('NONE', (1, 0, -0));
|
#407 = DIRECTION('NONE', (1, -0, 0));
|
||||||
#408 = AXIS2_PLACEMENT_3D('NONE', #406, #407, $);
|
#408 = AXIS2_PLACEMENT_3D('NONE', #406, #407, $);
|
||||||
#409 = PLANE('NONE', #408);
|
#409 = PLANE('NONE', #408);
|
||||||
#410 = CARTESIAN_POINT('NONE', (0.19233523789047138, 0.0508, 0.0508));
|
#410 = CARTESIAN_POINT('NONE', (4.8853150424179725, 1.2903199999999997, 1.2903199999999997));
|
||||||
#411 = DIRECTION('NONE', (0, 1, -0));
|
#411 = DIRECTION('NONE', (0, 1, -0));
|
||||||
#412 = AXIS2_PLACEMENT_3D('NONE', #410, #411, $);
|
#412 = AXIS2_PLACEMENT_3D('NONE', #410, #411, $);
|
||||||
#413 = PLANE('NONE', #412);
|
#413 = PLANE('NONE', #412);
|
||||||
#414 = CARTESIAN_POINT('NONE', (0.11613523789047137, 0.0381, 0.05079999999999999));
|
#414 = CARTESIAN_POINT('NONE', (2.9498350424179733, 0.9677399999999998, 1.2903199999999997));
|
||||||
#415 = DIRECTION('NONE', (-0.42261826174069966, 0.90630778703665, -0));
|
#415 = DIRECTION('NONE', (-0.42261826174069933, 0.9063077870366499, -0));
|
||||||
#416 = AXIS2_PLACEMENT_3D('NONE', #414, #415, $);
|
#416 = AXIS2_PLACEMENT_3D('NONE', #414, #415, $);
|
||||||
#417 = PLANE('NONE', #416);
|
#417 = PLANE('NONE', #416);
|
||||||
#418 = CARTESIAN_POINT('NONE', (0.044449999999999996, 0.0254, 0.0508));
|
#418 = CARTESIAN_POINT('NONE', (1.1290299999999998, 0.6451599999999998, 1.29032));
|
||||||
#419 = DIRECTION('NONE', (0, 1, -0));
|
#419 = DIRECTION('NONE', (0, 1, -0));
|
||||||
#420 = AXIS2_PLACEMENT_3D('NONE', #418, #419, $);
|
#420 = AXIS2_PLACEMENT_3D('NONE', #418, #419, $);
|
||||||
#421 = PLANE('NONE', #420);
|
#421 = PLANE('NONE', #420);
|
||||||
#422 = CARTESIAN_POINT('NONE', (0, 0.0127, 0.0508));
|
#422 = CARTESIAN_POINT('NONE', (0, 0.32257999999999987, 1.2903199999999995));
|
||||||
#423 = DIRECTION('NONE', (-1, 0, -0));
|
#423 = DIRECTION('NONE', (-1, -0, 0));
|
||||||
#424 = AXIS2_PLACEMENT_3D('NONE', #422, #423, $);
|
#424 = AXIS2_PLACEMENT_3D('NONE', #422, #423, $);
|
||||||
#425 = PLANE('NONE', #424);
|
#425 = PLANE('NONE', #424);
|
||||||
#426 = CARTESIAN_POINT('NONE', (0, 0, -0));
|
#426 = CARTESIAN_POINT('NONE', (0, 0, -0));
|
||||||
#427 = DIRECTION('NONE', (0, 0, 1));
|
#427 = DIRECTION('NONE', (0, 0, 1));
|
||||||
#428 = AXIS2_PLACEMENT_3D('NONE', #426, #427, $);
|
#428 = AXIS2_PLACEMENT_3D('NONE', #426, #427, $);
|
||||||
#429 = PLANE('NONE', #428);
|
#429 = PLANE('NONE', #428);
|
||||||
#430 = CARTESIAN_POINT('NONE', (0, 0, 0.1016));
|
#430 = CARTESIAN_POINT('NONE', (0, 0, 2.58064));
|
||||||
#431 = DIRECTION('NONE', (0, 0, 1));
|
#431 = DIRECTION('NONE', (0, 0, 1));
|
||||||
#432 = AXIS2_PLACEMENT_3D('NONE', #430, #431, $);
|
#432 = AXIS2_PLACEMENT_3D('NONE', #430, #431, $);
|
||||||
#433 = PLANE('NONE', #432);
|
#433 = PLANE('NONE', #432);
|
||||||
@ -475,7 +475,7 @@ DATA;
|
|||||||
#459 = ADVANCED_FACE('NONE', (#458), #421, .T.);
|
#459 = ADVANCED_FACE('NONE', (#458), #421, .T.);
|
||||||
#460 = FACE_OUTER_BOUND('NONE', #339, .T.);
|
#460 = FACE_OUTER_BOUND('NONE', #339, .T.);
|
||||||
#461 = ADVANCED_FACE('NONE', (#460), #425, .T.);
|
#461 = ADVANCED_FACE('NONE', (#460), #425, .T.);
|
||||||
#462 = FACE_OUTER_BOUND('NONE', #354, .T.);
|
#462 = FACE_OUTER_BOUND('NONE', #354, .F.);
|
||||||
#463 = ADVANCED_FACE('NONE', (#462), #429, .F.);
|
#463 = ADVANCED_FACE('NONE', (#462), #429, .F.);
|
||||||
#464 = FACE_OUTER_BOUND('NONE', #369, .T.);
|
#464 = FACE_OUTER_BOUND('NONE', #369, .T.);
|
||||||
#465 = ADVANCED_FACE('NONE', (#464), #433, .T.);
|
#465 = ADVANCED_FACE('NONE', (#464), #433, .T.);
|
||||||
|
Before Width: | Height: | Size: 57 KiB After Width: | Height: | Size: 221 KiB |
@ -1,478 +1,478 @@
|
|||||||
solid unnamed
|
solid unnamed
|
||||||
facet normal -1 0 0
|
facet normal -1 0 0
|
||||||
outer loop
|
outer loop
|
||||||
vertex 0 -4 0
|
vertex 0 -101.600006 0
|
||||||
vertex 0 -0 0
|
vertex 0 -0 0
|
||||||
vertex 0 -4 -1
|
vertex 0 -101.600006 -25.400002
|
||||||
endloop
|
endloop
|
||||||
endfacet
|
endfacet
|
||||||
facet normal -1 0 0
|
facet normal -1 0 0
|
||||||
outer loop
|
outer loop
|
||||||
vertex 0 -4 -1
|
vertex 0 -101.600006 -25.400002
|
||||||
vertex 0 -0 0
|
vertex 0 -0 0
|
||||||
vertex 0 -0 -1
|
vertex 0 -0 -25.400002
|
||||||
endloop
|
endloop
|
||||||
endfacet
|
endfacet
|
||||||
facet normal 0 0 -1
|
facet normal 0 0 -1
|
||||||
outer loop
|
outer loop
|
||||||
vertex 0 -4 -1
|
vertex 0 -101.600006 -25.400002
|
||||||
vertex 0 -0 -1
|
vertex 0 -0 -25.400002
|
||||||
vertex 3.0950184 -4 -1
|
vertex 78.613464 -101.600006 -25.400002
|
||||||
endloop
|
endloop
|
||||||
endfacet
|
endfacet
|
||||||
facet normal 0 0 -1
|
facet normal 0 0 -1
|
||||||
outer loop
|
outer loop
|
||||||
vertex 3.0950184 -4 -1
|
vertex 78.613464 -101.600006 -25.400002
|
||||||
vertex 0 -0 -1
|
vertex 0 -0 -25.400002
|
||||||
vertex 3.0950184 -0 -1
|
vertex 78.613464 -0 -25.400002
|
||||||
endloop
|
endloop
|
||||||
endfacet
|
endfacet
|
||||||
facet normal -0.57357645 0 -0.81915206
|
facet normal -0.5735764 0 -0.8191522
|
||||||
outer loop
|
outer loop
|
||||||
vertex 3.0950184 -4 -1
|
vertex 78.613464 -101.600006 -25.400002
|
||||||
vertex 3.0950184 -0 -1
|
vertex 78.613464 -0 -25.400002
|
||||||
vertex 5.9513144 -4 -3
|
vertex 151.16339 -101.600006 -76.2
|
||||||
endloop
|
endloop
|
||||||
endfacet
|
endfacet
|
||||||
facet normal -0.57357645 0 -0.81915206
|
facet normal -0.5735764 0 -0.8191522
|
||||||
outer loop
|
outer loop
|
||||||
vertex 5.9513144 -4 -3
|
vertex 151.16339 -101.600006 -76.2
|
||||||
vertex 3.0950184 -0 -1
|
vertex 78.613464 -0 -25.400002
|
||||||
vertex 5.9513144 -0 -3
|
vertex 151.16339 -0 -76.2
|
||||||
endloop
|
endloop
|
||||||
endfacet
|
endfacet
|
||||||
facet normal 0 0 -1
|
facet normal 0 0 -1
|
||||||
outer loop
|
outer loop
|
||||||
vertex 5.9513144 -4 -3
|
vertex 151.16339 -101.600006 -76.2
|
||||||
vertex 5.9513144 -0 -3
|
vertex 151.16339 -0 -76.2
|
||||||
vertex 9.5 -4 -3
|
vertex 241.3 -101.600006 -76.2
|
||||||
endloop
|
endloop
|
||||||
endfacet
|
endfacet
|
||||||
facet normal 0 0 -1
|
facet normal 0 0 -1
|
||||||
outer loop
|
outer loop
|
||||||
vertex 9.5 -4 -3
|
vertex 241.3 -101.600006 -76.2
|
||||||
vertex 5.9513144 -0 -3
|
vertex 151.16339 -0 -76.2
|
||||||
vertex 9.5 -0 -3
|
vertex 241.3 -0 -76.2
|
||||||
endloop
|
endloop
|
||||||
endfacet
|
endfacet
|
||||||
facet normal 1 0 0
|
facet normal 1 0 0
|
||||||
outer loop
|
outer loop
|
||||||
vertex 9.5 -4 -3
|
vertex 241.3 -101.600006 -76.2
|
||||||
vertex 9.5 -0 -3
|
vertex 241.3 -0 -76.2
|
||||||
vertex 9.5 -4 -2.5
|
vertex 241.3 -101.600006 -63.5
|
||||||
endloop
|
endloop
|
||||||
endfacet
|
endfacet
|
||||||
facet normal 1 -0 0
|
facet normal 1 -0 0
|
||||||
outer loop
|
outer loop
|
||||||
vertex 9.5 -4 -2.5
|
vertex 241.3 -101.600006 -63.5
|
||||||
vertex 9.5 -0 -3
|
vertex 241.3 -0 -76.2
|
||||||
vertex 9.5 -0 -2.5
|
vertex 241.3 -0 -63.5
|
||||||
|
endloop
|
||||||
|
endfacet
|
||||||
|
facet normal 0 -0 1
|
||||||
|
outer loop
|
||||||
|
vertex 241.3 -101.600006 -63.5
|
||||||
|
vertex 241.3 -0 -63.5
|
||||||
|
vertex 155.16768 -101.600006 -63.5
|
||||||
|
endloop
|
||||||
|
endfacet
|
||||||
|
facet normal 0 0 1
|
||||||
|
outer loop
|
||||||
|
vertex 155.16768 -101.600006 -63.5
|
||||||
|
vertex 241.3 -0 -63.5
|
||||||
|
vertex 155.16768 -0 -63.5
|
||||||
|
endloop
|
||||||
|
endfacet
|
||||||
|
facet normal 0.5735765 0 0.81915194
|
||||||
|
outer loop
|
||||||
|
vertex 87.15214 -101.600006 -15.875
|
||||||
|
vertex 109.82398 -101.600006 -31.75
|
||||||
|
vertex 109.82398 -0 -31.75
|
||||||
|
endloop
|
||||||
|
endfacet
|
||||||
|
facet normal 0.57357645 0 0.819152
|
||||||
|
outer loop
|
||||||
|
vertex 109.82398 -101.600006 -31.75
|
||||||
|
vertex 155.16768 -101.600006 -63.5
|
||||||
|
vertex 155.16768 -0 -63.5
|
||||||
|
endloop
|
||||||
|
endfacet
|
||||||
|
facet normal 0.57357645 0 0.81915206
|
||||||
|
outer loop
|
||||||
|
vertex 87.15214 -0 -15.875
|
||||||
|
vertex 64.480286 -101.600006 0
|
||||||
|
vertex 87.15214 -101.600006 -15.875
|
||||||
|
endloop
|
||||||
|
endfacet
|
||||||
|
facet normal 0.5735765 0 0.81915194
|
||||||
|
outer loop
|
||||||
|
vertex 109.82398 -0 -31.75
|
||||||
|
vertex 87.15214 -0 -15.875
|
||||||
|
vertex 87.15214 -101.600006 -15.875
|
||||||
|
endloop
|
||||||
|
endfacet
|
||||||
|
facet normal 0.57357645 -0 0.819152
|
||||||
|
outer loop
|
||||||
|
vertex 109.82398 -101.600006 -31.75
|
||||||
|
vertex 155.16768 -0 -63.5
|
||||||
|
vertex 109.82398 -0 -31.75
|
||||||
|
endloop
|
||||||
|
endfacet
|
||||||
|
facet normal 0.57357645 -0 0.81915206
|
||||||
|
outer loop
|
||||||
|
vertex 64.480286 -101.600006 0
|
||||||
|
vertex 87.15214 -0 -15.875
|
||||||
|
vertex 64.480286 -0 0
|
||||||
|
endloop
|
||||||
|
endfacet
|
||||||
|
facet normal 0.4226182 0 -0.9063078
|
||||||
|
outer loop
|
||||||
|
vertex 84.906715 -101.600006 9.525
|
||||||
|
vertex 64.480286 -101.600006 0
|
||||||
|
vertex 64.480286 -0 0
|
||||||
|
endloop
|
||||||
|
endfacet
|
||||||
|
facet normal 0.42261833 0 -0.90630776
|
||||||
|
outer loop
|
||||||
|
vertex 105.33314 -101.600006 19.05
|
||||||
|
vertex 84.906715 -101.600006 9.525
|
||||||
|
vertex 84.906715 -0 9.525
|
||||||
|
endloop
|
||||||
|
endfacet
|
||||||
|
facet normal 0.4226182 0 -0.9063078
|
||||||
|
outer loop
|
||||||
|
vertex 84.906715 -0 9.525
|
||||||
|
vertex 84.906715 -101.600006 9.525
|
||||||
|
vertex 64.480286 -0 0
|
||||||
|
endloop
|
||||||
|
endfacet
|
||||||
|
facet normal 0.4226183 0 -0.9063078
|
||||||
|
outer loop
|
||||||
|
vertex 105.33314 -0 19.05
|
||||||
|
vertex 146.18599 -101.600006 38.1
|
||||||
|
vertex 105.33314 -101.600006 19.05
|
||||||
|
endloop
|
||||||
|
endfacet
|
||||||
|
facet normal 0.42261833 0 -0.90630776
|
||||||
|
outer loop
|
||||||
|
vertex 105.33314 -101.600006 19.05
|
||||||
|
vertex 84.906715 -0 9.525
|
||||||
|
vertex 105.33314 -0 19.05
|
||||||
|
endloop
|
||||||
|
endfacet
|
||||||
|
facet normal 0.4226183 0 -0.9063078
|
||||||
|
outer loop
|
||||||
|
vertex 146.18599 -101.600006 38.1
|
||||||
|
vertex 105.33314 -0 19.05
|
||||||
|
vertex 146.18599 -0 38.1
|
||||||
|
endloop
|
||||||
|
endfacet
|
||||||
|
facet normal 0 0 -1
|
||||||
|
outer loop
|
||||||
|
vertex 146.18599 -101.600006 38.1
|
||||||
|
vertex 146.18599 -0 38.1
|
||||||
|
vertex 241.3 -101.600006 38.1
|
||||||
|
endloop
|
||||||
|
endfacet
|
||||||
|
facet normal 0 0 -1
|
||||||
|
outer loop
|
||||||
|
vertex 241.3 -101.600006 38.1
|
||||||
|
vertex 146.18599 -0 38.1
|
||||||
|
vertex 241.3 -0 38.1
|
||||||
|
endloop
|
||||||
|
endfacet
|
||||||
|
facet normal 1 0 0
|
||||||
|
outer loop
|
||||||
|
vertex 241.3 -101.600006 38.1
|
||||||
|
vertex 241.3 -0 38.1
|
||||||
|
vertex 241.3 -101.600006 50.800003
|
||||||
|
endloop
|
||||||
|
endfacet
|
||||||
|
facet normal 1 -0 0
|
||||||
|
outer loop
|
||||||
|
vertex 241.3 -101.600006 50.800003
|
||||||
|
vertex 241.3 -0 38.1
|
||||||
|
vertex 241.3 -0 50.800003
|
||||||
endloop
|
endloop
|
||||||
endfacet
|
endfacet
|
||||||
facet normal 0 -0 0.99999994
|
facet normal 0 -0 0.99999994
|
||||||
outer loop
|
outer loop
|
||||||
vertex 9.5 -4 -2.5
|
vertex 241.3 -101.600006 50.800003
|
||||||
vertex 9.5 -0 -2.5
|
vertex 241.3 -0 50.800003
|
||||||
vertex 6.108964 -4 -2.5
|
vertex 143.37048 -101.600006 50.800003
|
||||||
endloop
|
endloop
|
||||||
endfacet
|
endfacet
|
||||||
facet normal 0 0 0.99999994
|
facet normal 0 0 0.99999994
|
||||||
outer loop
|
outer loop
|
||||||
vertex 6.108964 -4 -2.5
|
vertex 143.37048 -101.600006 50.800003
|
||||||
vertex 9.5 -0 -2.5
|
vertex 241.3 -0 50.800003
|
||||||
vertex 6.108964 -0 -2.5
|
vertex 143.37048 -0 50.800003
|
||||||
endloop
|
endloop
|
||||||
endfacet
|
endfacet
|
||||||
facet normal 0.5735763 0 0.8191522
|
facet normal -0.42261827 0 0.9063078
|
||||||
outer loop
|
outer loop
|
||||||
vertex 3.4311862 -4 -0.625
|
vertex 143.37048 -101.600006 50.800003
|
||||||
vertex 4.323779 -4 -1.25
|
vertex 143.37048 -0 50.800003
|
||||||
vertex 4.323779 -0 -1.25
|
vertex 88.9 -101.600006 25.400002
|
||||||
endloop
|
endloop
|
||||||
endfacet
|
endfacet
|
||||||
facet normal 0.57357645 0 0.819152
|
facet normal -0.42261827 0 0.9063078
|
||||||
outer loop
|
outer loop
|
||||||
vertex 4.323779 -4 -1.25
|
vertex 88.9 -101.600006 25.400002
|
||||||
vertex 6.108964 -4 -2.5
|
vertex 143.37048 -0 50.800003
|
||||||
vertex 6.108964 -0 -2.5
|
vertex 88.9 -0 25.400002
|
||||||
endloop
|
|
||||||
endfacet
|
|
||||||
facet normal 0.57357645 0 0.819152
|
|
||||||
outer loop
|
|
||||||
vertex 3.4311862 -0 -0.625
|
|
||||||
vertex 2.5385938 -0 0
|
|
||||||
vertex 2.5385938 -4 0
|
|
||||||
endloop
|
|
||||||
endfacet
|
|
||||||
facet normal 0.57357645 -0 0.819152
|
|
||||||
outer loop
|
|
||||||
vertex 3.4311862 -4 -0.625
|
|
||||||
vertex 3.4311862 -0 -0.625
|
|
||||||
vertex 2.5385938 -4 0
|
|
||||||
endloop
|
|
||||||
endfacet
|
|
||||||
facet normal 0.57357645 -0 0.819152
|
|
||||||
outer loop
|
|
||||||
vertex 4.323779 -4 -1.25
|
|
||||||
vertex 6.108964 -0 -2.5
|
|
||||||
vertex 4.323779 -0 -1.25
|
|
||||||
endloop
|
|
||||||
endfacet
|
|
||||||
facet normal 0.5735763 0 0.8191522
|
|
||||||
outer loop
|
|
||||||
vertex 3.4311862 -0 -0.625
|
|
||||||
vertex 3.4311862 -4 -0.625
|
|
||||||
vertex 4.323779 -0 -1.25
|
|
||||||
endloop
|
|
||||||
endfacet
|
|
||||||
facet normal 0.42261824 0 -0.9063078
|
|
||||||
outer loop
|
|
||||||
vertex 3.342784 -4 0.375
|
|
||||||
vertex 2.5385938 -4 0
|
|
||||||
vertex 2.5385938 -0 0
|
|
||||||
endloop
|
|
||||||
endfacet
|
|
||||||
facet normal 0.42261824 0 -0.9063078
|
|
||||||
outer loop
|
|
||||||
vertex 4.146974 -4 0.75
|
|
||||||
vertex 3.342784 -4 0.375
|
|
||||||
vertex 3.342784 -0 0.375
|
|
||||||
endloop
|
|
||||||
endfacet
|
|
||||||
facet normal 0.42261824 0 -0.9063078
|
|
||||||
outer loop
|
|
||||||
vertex 3.342784 -0 0.375
|
|
||||||
vertex 4.146974 -0 0.75
|
|
||||||
vertex 4.146974 -4 0.75
|
|
||||||
endloop
|
|
||||||
endfacet
|
|
||||||
facet normal 0.42261833 0 -0.90630776
|
|
||||||
outer loop
|
|
||||||
vertex 4.146974 -0 0.75
|
|
||||||
vertex 5.755354 -0 1.5
|
|
||||||
vertex 5.755354 -4 1.5
|
|
||||||
endloop
|
|
||||||
endfacet
|
|
||||||
facet normal 0.42261824 0 -0.9063078
|
|
||||||
outer loop
|
|
||||||
vertex 3.342784 -4 0.375
|
|
||||||
vertex 2.5385938 -0 0
|
|
||||||
vertex 3.342784 -0 0.375
|
|
||||||
endloop
|
|
||||||
endfacet
|
|
||||||
facet normal 0.42261833 0 -0.90630776
|
|
||||||
outer loop
|
|
||||||
vertex 5.755354 -4 1.5
|
|
||||||
vertex 4.146974 -4 0.75
|
|
||||||
vertex 4.146974 -0 0.75
|
|
||||||
endloop
|
|
||||||
endfacet
|
|
||||||
facet normal 0 0 -1
|
|
||||||
outer loop
|
|
||||||
vertex 5.755354 -4 1.5
|
|
||||||
vertex 5.755354 -0 1.5
|
|
||||||
vertex 9.5 -4 1.5
|
|
||||||
endloop
|
|
||||||
endfacet
|
|
||||||
facet normal 0 0 -1
|
|
||||||
outer loop
|
|
||||||
vertex 9.5 -4 1.5
|
|
||||||
vertex 5.755354 -0 1.5
|
|
||||||
vertex 9.5 -0 1.5
|
|
||||||
endloop
|
|
||||||
endfacet
|
|
||||||
facet normal 1 0 0
|
|
||||||
outer loop
|
|
||||||
vertex 9.5 -4 1.5
|
|
||||||
vertex 9.5 -0 1.5
|
|
||||||
vertex 9.5 -4 2
|
|
||||||
endloop
|
|
||||||
endfacet
|
|
||||||
facet normal 1 -0 0
|
|
||||||
outer loop
|
|
||||||
vertex 9.5 -4 2
|
|
||||||
vertex 9.5 -0 1.5
|
|
||||||
vertex 9.5 -0 2
|
|
||||||
endloop
|
endloop
|
||||||
endfacet
|
endfacet
|
||||||
facet normal 0 -0 1
|
facet normal 0 -0 1
|
||||||
outer loop
|
outer loop
|
||||||
vertex 9.5 -4 2
|
vertex 88.9 -101.600006 25.400002
|
||||||
vertex 9.5 -0 2
|
vertex 88.9 -0 25.400002
|
||||||
vertex 5.644507 -4 2
|
vertex 0 -101.600006 25.400002
|
||||||
endloop
|
endloop
|
||||||
endfacet
|
endfacet
|
||||||
facet normal 0 0 1
|
facet normal 0 0 1
|
||||||
outer loop
|
outer loop
|
||||||
vertex 5.644507 -4 2
|
vertex 0 -101.600006 25.400002
|
||||||
vertex 9.5 -0 2
|
vertex 88.9 -0 25.400002
|
||||||
vertex 5.644507 -0 2
|
vertex 0 -0 25.400002
|
||||||
endloop
|
|
||||||
endfacet
|
|
||||||
facet normal -0.42261824 0 0.90630776
|
|
||||||
outer loop
|
|
||||||
vertex 5.644507 -4 2
|
|
||||||
vertex 5.644507 -0 2
|
|
||||||
vertex 3.5 -4 1
|
|
||||||
endloop
|
|
||||||
endfacet
|
|
||||||
facet normal -0.42261824 0 0.90630776
|
|
||||||
outer loop
|
|
||||||
vertex 3.5 -4 1
|
|
||||||
vertex 5.644507 -0 2
|
|
||||||
vertex 3.5 -0 1
|
|
||||||
endloop
|
|
||||||
endfacet
|
|
||||||
facet normal 0 -0 1
|
|
||||||
outer loop
|
|
||||||
vertex 3.5 -4 1
|
|
||||||
vertex 3.5 -0 1
|
|
||||||
vertex 0 -4 1
|
|
||||||
endloop
|
|
||||||
endfacet
|
|
||||||
facet normal 0 0 1
|
|
||||||
outer loop
|
|
||||||
vertex 0 -4 1
|
|
||||||
vertex 3.5 -0 1
|
|
||||||
vertex 0 -0 1
|
|
||||||
endloop
|
endloop
|
||||||
endfacet
|
endfacet
|
||||||
facet normal -1 0 0
|
facet normal -1 0 0
|
||||||
outer loop
|
outer loop
|
||||||
vertex 0 -4 1
|
vertex 0 -101.600006 25.400002
|
||||||
vertex 0 -0 1
|
vertex 0 -0 25.400002
|
||||||
vertex 0 -4 0
|
vertex 0 -101.600006 0
|
||||||
endloop
|
endloop
|
||||||
endfacet
|
endfacet
|
||||||
facet normal -1 0 0
|
facet normal -1 0 0
|
||||||
outer loop
|
outer loop
|
||||||
vertex 0 -4 0
|
vertex 0 -101.600006 0
|
||||||
vertex 0 -0 1
|
vertex 0 -0 25.400002
|
||||||
vertex 0 -0 0
|
vertex 0 -0 0
|
||||||
endloop
|
endloop
|
||||||
endfacet
|
endfacet
|
||||||
facet normal 0 1 -0
|
facet normal 0 1 -0
|
||||||
outer loop
|
outer loop
|
||||||
vertex 3.342784 -0 0.375
|
vertex 84.906715 -0 9.525
|
||||||
vertex 2.5385938 -0 0
|
vertex 64.480286 -0 0
|
||||||
vertex 3.5 -0 1
|
vertex 88.9 -0 25.400002
|
||||||
endloop
|
endloop
|
||||||
endfacet
|
endfacet
|
||||||
facet normal 0 1 0
|
facet normal 0 1 0
|
||||||
outer loop
|
outer loop
|
||||||
vertex 3.4311862 -0 -0.625
|
vertex 105.33314 -0 19.05
|
||||||
vertex 4.323779 -0 -1.25
|
vertex 84.906715 -0 9.525
|
||||||
vertex 3.0950184 -0 -1
|
vertex 88.9 -0 25.400002
|
||||||
endloop
|
endloop
|
||||||
endfacet
|
endfacet
|
||||||
facet normal 0 1 0
|
facet normal 0 1 0
|
||||||
outer loop
|
outer loop
|
||||||
vertex 3.342784 -0 0.375
|
vertex 87.15214 -0 -15.875
|
||||||
vertex 3.5 -0 1
|
vertex 109.82398 -0 -31.75
|
||||||
vertex 4.146974 -0 0.75
|
vertex 78.613464 -0 -25.400002
|
||||||
|
endloop
|
||||||
|
endfacet
|
||||||
|
facet normal 0 1 0
|
||||||
|
outer loop
|
||||||
|
vertex 105.33314 -0 19.05
|
||||||
|
vertex 143.37048 -0 50.800003
|
||||||
|
vertex 146.18599 -0 38.1
|
||||||
|
endloop
|
||||||
|
endfacet
|
||||||
|
facet normal -0 1 0
|
||||||
|
outer loop
|
||||||
|
vertex 0 -0 25.400002
|
||||||
|
vertex 88.9 -0 25.400002
|
||||||
|
vertex 64.480286 -0 0
|
||||||
|
endloop
|
||||||
|
endfacet
|
||||||
|
facet normal 0 1 0
|
||||||
|
outer loop
|
||||||
|
vertex 0 -0 25.400002
|
||||||
|
vertex 64.480286 -0 0
|
||||||
|
vertex 0 -0 0
|
||||||
|
endloop
|
||||||
|
endfacet
|
||||||
|
facet normal -0 1 0
|
||||||
|
outer loop
|
||||||
|
vertex 143.37048 -0 50.800003
|
||||||
|
vertex 241.3 -0 50.800003
|
||||||
|
vertex 146.18599 -0 38.1
|
||||||
|
endloop
|
||||||
|
endfacet
|
||||||
|
facet normal 0 1 0
|
||||||
|
outer loop
|
||||||
|
vertex 241.3 -0 50.800003
|
||||||
|
vertex 241.3 -0 38.1
|
||||||
|
vertex 146.18599 -0 38.1
|
||||||
|
endloop
|
||||||
|
endfacet
|
||||||
|
facet normal 0 1 -0
|
||||||
|
outer loop
|
||||||
|
vertex 105.33314 -0 19.05
|
||||||
|
vertex 88.9 -0 25.400002
|
||||||
|
vertex 143.37048 -0 50.800003
|
||||||
endloop
|
endloop
|
||||||
endfacet
|
endfacet
|
||||||
facet normal 0 0.99999994 0
|
facet normal 0 0.99999994 0
|
||||||
outer loop
|
outer loop
|
||||||
vertex 4.323779 -0 -1.25
|
vertex 64.480286 -0 0
|
||||||
vertex 5.9513144 -0 -3
|
vertex 87.15214 -0 -15.875
|
||||||
vertex 3.0950184 -0 -1
|
vertex 78.613464 -0 -25.400002
|
||||||
endloop
|
endloop
|
||||||
endfacet
|
endfacet
|
||||||
facet normal 0 1 0
|
facet normal 0 1 0
|
||||||
outer loop
|
outer loop
|
||||||
vertex 0 -0 -1
|
vertex 109.82398 -0 -31.75
|
||||||
vertex 2.5385938 -0 0
|
vertex 151.16339 -0 -76.2
|
||||||
vertex 3.0950184 -0 -1
|
vertex 78.613464 -0 -25.400002
|
||||||
endloop
|
endloop
|
||||||
endfacet
|
endfacet
|
||||||
facet normal 0 1 0
|
facet normal 0 1 0
|
||||||
outer loop
|
outer loop
|
||||||
vertex 0 -0 -1
|
vertex 155.16768 -0 -63.5
|
||||||
|
vertex 151.16339 -0 -76.2
|
||||||
|
vertex 109.82398 -0 -31.75
|
||||||
|
endloop
|
||||||
|
endfacet
|
||||||
|
facet normal 0 1 0
|
||||||
|
outer loop
|
||||||
|
vertex 241.3 -0 -63.5
|
||||||
|
vertex 241.3 -0 -76.2
|
||||||
|
vertex 155.16768 -0 -63.5
|
||||||
|
endloop
|
||||||
|
endfacet
|
||||||
|
facet normal 0 1 0
|
||||||
|
outer loop
|
||||||
|
vertex 155.16768 -0 -63.5
|
||||||
|
vertex 241.3 -0 -76.2
|
||||||
|
vertex 151.16339 -0 -76.2
|
||||||
|
endloop
|
||||||
|
endfacet
|
||||||
|
facet normal 0 1 0
|
||||||
|
outer loop
|
||||||
|
vertex 64.480286 -0 0
|
||||||
|
vertex 78.613464 -0 -25.400002
|
||||||
|
vertex 0 -0 -25.400002
|
||||||
|
endloop
|
||||||
|
endfacet
|
||||||
|
facet normal 0 1 0
|
||||||
|
outer loop
|
||||||
|
vertex 0 -0 -25.400002
|
||||||
vertex 0 -0 0
|
vertex 0 -0 0
|
||||||
vertex 2.5385938 -0 0
|
vertex 64.480286 -0 0
|
||||||
endloop
|
|
||||||
endfacet
|
|
||||||
facet normal 0 0.99999994 -0
|
|
||||||
outer loop
|
|
||||||
vertex 9.5 -0 -3
|
|
||||||
vertex 6.108964 -0 -2.5
|
|
||||||
vertex 9.5 -0 -2.5
|
|
||||||
endloop
|
|
||||||
endfacet
|
|
||||||
facet normal 0 1 0
|
|
||||||
outer loop
|
|
||||||
vertex 9.5 -0 -3
|
|
||||||
vertex 5.9513144 -0 -3
|
|
||||||
vertex 6.108964 -0 -2.5
|
|
||||||
endloop
|
|
||||||
endfacet
|
|
||||||
facet normal 0 1 -0
|
|
||||||
outer loop
|
|
||||||
vertex 5.9513144 -0 -3
|
|
||||||
vertex 4.323779 -0 -1.25
|
|
||||||
vertex 6.108964 -0 -2.5
|
|
||||||
endloop
|
|
||||||
endfacet
|
|
||||||
facet normal 0 1 0
|
|
||||||
outer loop
|
|
||||||
vertex 5.644507 -0 2
|
|
||||||
vertex 5.755354 -0 1.5
|
|
||||||
vertex 4.146974 -0 0.75
|
|
||||||
endloop
|
|
||||||
endfacet
|
|
||||||
facet normal 0 0.99999994 -0
|
|
||||||
outer loop
|
|
||||||
vertex 3.0950184 -0 -1
|
|
||||||
vertex 2.5385938 -0 0
|
|
||||||
vertex 3.4311862 -0 -0.625
|
|
||||||
endloop
|
|
||||||
endfacet
|
|
||||||
facet normal 0 1 -0
|
|
||||||
outer loop
|
|
||||||
vertex 4.146974 -0 0.75
|
|
||||||
vertex 3.5 -0 1
|
|
||||||
vertex 5.644507 -0 2
|
|
||||||
endloop
|
|
||||||
endfacet
|
|
||||||
facet normal 0 1 -0
|
|
||||||
outer loop
|
|
||||||
vertex 9.5 -0 1.5
|
|
||||||
vertex 5.755354 -0 1.5
|
|
||||||
vertex 9.5 -0 2
|
|
||||||
endloop
|
|
||||||
endfacet
|
|
||||||
facet normal 0 1 -0
|
|
||||||
outer loop
|
|
||||||
vertex 5.755354 -0 1.5
|
|
||||||
vertex 5.644507 -0 2
|
|
||||||
vertex 9.5 -0 2
|
|
||||||
endloop
|
|
||||||
endfacet
|
|
||||||
facet normal 0 1 0
|
|
||||||
outer loop
|
|
||||||
vertex 2.5385938 -0 0
|
|
||||||
vertex 0 -0 0
|
|
||||||
vertex 0 -0 1
|
|
||||||
endloop
|
|
||||||
endfacet
|
|
||||||
facet normal 0 1 0
|
|
||||||
outer loop
|
|
||||||
vertex 3.5 -0 1
|
|
||||||
vertex 2.5385938 -0 0
|
|
||||||
vertex 0 -0 1
|
|
||||||
endloop
|
endloop
|
||||||
endfacet
|
endfacet
|
||||||
facet normal -0 -1 0
|
facet normal -0 -1 0
|
||||||
outer loop
|
outer loop
|
||||||
vertex 3.342784 -4 0.375
|
vertex 84.906715 -101.600006 9.525
|
||||||
vertex 3.5 -4 1
|
vertex 88.9 -101.600006 25.400002
|
||||||
vertex 2.5385938 -4 0
|
vertex 64.480286 -101.600006 0
|
||||||
endloop
|
endloop
|
||||||
endfacet
|
endfacet
|
||||||
facet normal -0 -1 0
|
facet normal -0 -1 0
|
||||||
outer loop
|
outer loop
|
||||||
vertex 4.146974 -4 0.75
|
vertex 105.33314 -101.600006 19.05
|
||||||
vertex 3.5 -4 1
|
vertex 88.9 -101.600006 25.400002
|
||||||
vertex 3.342784 -4 0.375
|
vertex 84.906715 -101.600006 9.525
|
||||||
endloop
|
endloop
|
||||||
endfacet
|
endfacet
|
||||||
facet normal 0 -1 -0
|
facet normal 0 -1 -0
|
||||||
outer loop
|
outer loop
|
||||||
vertex 3.4311862 -4 -0.625
|
vertex 87.15214 -101.600006 -15.875
|
||||||
vertex 3.0950184 -4 -1
|
vertex 78.613464 -101.600006 -25.400002
|
||||||
vertex 4.323779 -4 -1.25
|
vertex 109.82398 -101.600006 -31.75
|
||||||
|
endloop
|
||||||
|
endfacet
|
||||||
|
facet normal 0 -1 0
|
||||||
|
outer loop
|
||||||
|
vertex 105.33314 -101.600006 19.05
|
||||||
|
vertex 146.18599 -101.600006 38.1
|
||||||
|
vertex 143.37048 -101.600006 50.800003
|
||||||
|
endloop
|
||||||
|
endfacet
|
||||||
|
facet normal 0 -1 0
|
||||||
|
outer loop
|
||||||
|
vertex 0 -101.600006 25.400002
|
||||||
|
vertex 64.480286 -101.600006 0
|
||||||
|
vertex 88.9 -101.600006 25.400002
|
||||||
|
endloop
|
||||||
|
endfacet
|
||||||
|
facet normal 0 -1 0
|
||||||
|
outer loop
|
||||||
|
vertex 0 -101.600006 25.400002
|
||||||
|
vertex 0 -101.600006 0
|
||||||
|
vertex 64.480286 -101.600006 0
|
||||||
|
endloop
|
||||||
|
endfacet
|
||||||
|
facet normal 0 -1 0
|
||||||
|
outer loop
|
||||||
|
vertex 143.37048 -101.600006 50.800003
|
||||||
|
vertex 146.18599 -101.600006 38.1
|
||||||
|
vertex 241.3 -101.600006 50.800003
|
||||||
|
endloop
|
||||||
|
endfacet
|
||||||
|
facet normal 0 -1 -0
|
||||||
|
outer loop
|
||||||
|
vertex 241.3 -101.600006 50.800003
|
||||||
|
vertex 146.18599 -101.600006 38.1
|
||||||
|
vertex 241.3 -101.600006 38.1
|
||||||
|
endloop
|
||||||
|
endfacet
|
||||||
|
facet normal 0 -1 0
|
||||||
|
outer loop
|
||||||
|
vertex 105.33314 -101.600006 19.05
|
||||||
|
vertex 143.37048 -101.600006 50.800003
|
||||||
|
vertex 88.9 -101.600006 25.400002
|
||||||
endloop
|
endloop
|
||||||
endfacet
|
endfacet
|
||||||
facet normal 0 -0.99999994 0
|
facet normal 0 -0.99999994 0
|
||||||
outer loop
|
outer loop
|
||||||
vertex 4.146974 -4 0.75
|
vertex 64.480286 -101.600006 0
|
||||||
vertex 5.755354 -4 1.5
|
vertex 78.613464 -101.600006 -25.400002
|
||||||
vertex 5.644507 -4 2
|
vertex 87.15214 -101.600006 -15.875
|
||||||
endloop
|
endloop
|
||||||
endfacet
|
endfacet
|
||||||
facet normal 0 -1 0
|
facet normal -0 -1 -0
|
||||||
outer loop
|
outer loop
|
||||||
vertex 0 -4 1
|
vertex 109.82398 -101.600006 -31.75
|
||||||
vertex 2.5385938 -4 0
|
vertex 78.613464 -101.600006 -25.400002
|
||||||
vertex 3.5 -4 1
|
vertex 151.16339 -101.600006 -76.2
|
||||||
endloop
|
|
||||||
endfacet
|
|
||||||
facet normal 0 -1 0
|
|
||||||
outer loop
|
|
||||||
vertex 0 -4 1
|
|
||||||
vertex 0 -4 0
|
|
||||||
vertex 2.5385938 -4 0
|
|
||||||
endloop
|
|
||||||
endfacet
|
|
||||||
facet normal 0 -1 0
|
|
||||||
outer loop
|
|
||||||
vertex 5.644507 -4 2
|
|
||||||
vertex 5.755354 -4 1.5
|
|
||||||
vertex 9.5 -4 2
|
|
||||||
endloop
|
|
||||||
endfacet
|
|
||||||
facet normal 0 -1 -0
|
|
||||||
outer loop
|
|
||||||
vertex 9.5 -4 2
|
|
||||||
vertex 5.755354 -4 1.5
|
|
||||||
vertex 9.5 -4 1.5
|
|
||||||
endloop
|
|
||||||
endfacet
|
|
||||||
facet normal 0 -1 0
|
|
||||||
outer loop
|
|
||||||
vertex 4.146974 -4 0.75
|
|
||||||
vertex 5.644507 -4 2
|
|
||||||
vertex 3.5 -4 1
|
|
||||||
endloop
|
|
||||||
endfacet
|
|
||||||
facet normal 0 -0.99999994 0
|
|
||||||
outer loop
|
|
||||||
vertex 2.5385938 -4 0
|
|
||||||
vertex 3.0950184 -4 -1
|
|
||||||
vertex 3.4311862 -4 -0.625
|
|
||||||
endloop
|
|
||||||
endfacet
|
|
||||||
facet normal -0 -0.99999994 -0
|
|
||||||
outer loop
|
|
||||||
vertex 4.323779 -4 -1.25
|
|
||||||
vertex 3.0950184 -4 -1
|
|
||||||
vertex 5.9513144 -4 -3
|
|
||||||
endloop
|
endloop
|
||||||
endfacet
|
endfacet
|
||||||
facet normal -0 -1 0
|
facet normal -0 -1 0
|
||||||
outer loop
|
outer loop
|
||||||
vertex 6.108964 -4 -2.5
|
vertex 155.16768 -101.600006 -63.5
|
||||||
vertex 4.323779 -4 -1.25
|
vertex 109.82398 -101.600006 -31.75
|
||||||
vertex 5.9513144 -4 -3
|
vertex 151.16339 -101.600006 -76.2
|
||||||
endloop
|
endloop
|
||||||
endfacet
|
endfacet
|
||||||
facet normal -0 -0.99999994 -0
|
facet normal -0 -1 -0
|
||||||
outer loop
|
outer loop
|
||||||
vertex 9.5 -4 -2.5
|
vertex 241.3 -101.600006 -63.5
|
||||||
vertex 6.108964 -4 -2.5
|
vertex 155.16768 -101.600006 -63.5
|
||||||
vertex 9.5 -4 -3
|
vertex 241.3 -101.600006 -76.2
|
||||||
endloop
|
endloop
|
||||||
endfacet
|
endfacet
|
||||||
facet normal 0 -1 -0
|
facet normal 0 -1 -0
|
||||||
outer loop
|
outer loop
|
||||||
vertex 6.108964 -4 -2.5
|
vertex 155.16768 -101.600006 -63.5
|
||||||
vertex 5.9513144 -4 -3
|
vertex 151.16339 -101.600006 -76.2
|
||||||
vertex 9.5 -4 -3
|
vertex 241.3 -101.600006 -76.2
|
||||||
endloop
|
endloop
|
||||||
endfacet
|
endfacet
|
||||||
facet normal 0 -1 -0
|
facet normal 0 -1 -0
|
||||||
outer loop
|
outer loop
|
||||||
vertex 2.5385938 -4 0
|
vertex 64.480286 -101.600006 0
|
||||||
vertex 0 -4 -1
|
vertex 0 -101.600006 -25.400002
|
||||||
vertex 3.0950184 -4 -1
|
vertex 78.613464 -101.600006 -25.400002
|
||||||
endloop
|
endloop
|
||||||
endfacet
|
endfacet
|
||||||
facet normal 0 -1 0
|
facet normal 0 -1 0
|
||||||
outer loop
|
outer loop
|
||||||
vertex 0 -4 -1
|
vertex 0 -101.600006 -25.400002
|
||||||
vertex 2.5385938 -4 0
|
vertex 64.480286 -101.600006 0
|
||||||
vertex 0 -4 0
|
vertex 0 -101.600006 0
|
||||||
endloop
|
endloop
|
||||||
endfacet
|
endfacet
|
||||||
endsolid unnamed
|
endsolid unnamed
|
||||||
|
Before Width: | Height: | Size: 57 KiB After Width: | Height: | Size: 221 KiB |
@ -1,7 +1,5 @@
|
|||||||
import { test, expect } from '@playwright/test'
|
import { test, expect } from '@playwright/test'
|
||||||
import { secrets } from './secrets'
|
import { secrets } from './secrets'
|
||||||
import { EngineCommand } from '../../src/lang/std/engineConnection'
|
|
||||||
import { v4 as uuidv4 } from 'uuid'
|
|
||||||
import { getUtils } from './test-utils'
|
import { getUtils } from './test-utils'
|
||||||
import waitOn from 'wait-on'
|
import waitOn from 'wait-on'
|
||||||
import { Themes } from '../../src/lib/theme'
|
import { Themes } from '../../src/lib/theme'
|
||||||
@ -53,40 +51,38 @@ test('Basic sketch', async ({ page }) => {
|
|||||||
await page.goto('/')
|
await page.goto('/')
|
||||||
await u.waitForAuthSkipAppStart()
|
await u.waitForAuthSkipAppStart()
|
||||||
await u.openDebugPanel()
|
await u.openDebugPanel()
|
||||||
await u.waitForDefaultPlanesVisibilityChange()
|
|
||||||
|
|
||||||
await expect(page.getByRole('button', { name: 'Start Sketch' })).toBeVisible()
|
await expect(page.getByRole('button', { name: 'Start Sketch' })).toBeVisible()
|
||||||
|
|
||||||
// click on "Start Sketch" button
|
// click on "Start Sketch" button
|
||||||
await u.clearCommandLogs()
|
await u.clearCommandLogs()
|
||||||
await Promise.all([
|
await u.doAndWaitForImageDiff(
|
||||||
u.doAndWaitForImageDiff(
|
() => page.getByRole('button', { name: 'Start Sketch' }).click(),
|
||||||
() => page.getByRole('button', { name: 'Start Sketch' }).click(),
|
200
|
||||||
200
|
)
|
||||||
),
|
|
||||||
u.waitForDefaultPlanesVisibilityChange(),
|
|
||||||
])
|
|
||||||
|
|
||||||
// select a plane
|
// select a plane
|
||||||
await u.doAndWaitForCmd(() => page.mouse.click(700, 200), 'edit_mode_enter')
|
await page.mouse.click(700, 200)
|
||||||
await u.waitForCmdReceive('set_tool')
|
|
||||||
|
|
||||||
await u.doAndWaitForCmd(
|
await expect(page.locator('.cm-content')).toHaveText(
|
||||||
() => page.getByRole('button', { name: 'Line' }).click(),
|
`const part001 = startSketchOn('-XZ')`
|
||||||
'set_tool'
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
await page.waitForTimeout(300) // TODO detect animation ending, or disable animation
|
||||||
|
|
||||||
const startXPx = 600
|
const startXPx = 600
|
||||||
await u.doAndWaitForCmd(
|
await page.mouse.click(startXPx + PUR * 10, 500 - PUR * 10)
|
||||||
() => page.mouse.click(startXPx + PUR * 10, 500 - PUR * 10),
|
const startAt = '[23.74, -32.03]'
|
||||||
'mouse_click',
|
await expect(page.locator('.cm-content'))
|
||||||
false
|
.toHaveText(`const part001 = startSketchOn('-XZ')
|
||||||
)
|
|> startProfileAt(${startAt}, %)`)
|
||||||
|
await page.waitForTimeout(100)
|
||||||
|
|
||||||
|
await u.closeDebugPanel()
|
||||||
await page.mouse.click(startXPx + PUR * 20, 500 - PUR * 10)
|
await page.mouse.click(startXPx + PUR * 20, 500 - PUR * 10)
|
||||||
|
await page.waitForTimeout(100)
|
||||||
|
|
||||||
const startAt = '[18.26, -24.63]'
|
const num = 23.97
|
||||||
const num = '18.43'
|
|
||||||
await expect(page.locator('.cm-content'))
|
await expect(page.locator('.cm-content'))
|
||||||
.toHaveText(`const part001 = startSketchOn('-XZ')
|
.toHaveText(`const part001 = startSketchOn('-XZ')
|
||||||
|> startProfileAt(${startAt}, %)
|
|> startProfileAt(${startAt}, %)
|
||||||
@ -104,20 +100,15 @@ test('Basic sketch', async ({ page }) => {
|
|||||||
|> startProfileAt(${startAt}, %)
|
|> startProfileAt(${startAt}, %)
|
||||||
|> line([${num}, 0], %)
|
|> line([${num}, 0], %)
|
||||||
|> line([0, ${num}], %)
|
|> line([0, ${num}], %)
|
||||||
|> line([-36.69, 0], %)`)
|
|> line([-47.71, 0], %)`)
|
||||||
|
|
||||||
// deselect line tool
|
// deselect line tool
|
||||||
await u.doAndWaitForCmd(
|
await page.getByRole('button', { name: 'Line' }).click()
|
||||||
() => page.getByRole('button', { name: 'Line' }).click(),
|
await page.waitForTimeout(100)
|
||||||
'set_tool'
|
|
||||||
)
|
|
||||||
|
|
||||||
// click between first two clicks to get center of the line
|
// click between first two clicks to get center of the line
|
||||||
await u.doAndWaitForCmd(
|
await page.mouse.click(startXPx + PUR * 15, 500 - PUR * 10)
|
||||||
() => page.mouse.click(startXPx + PUR * 15, 500 - PUR * 10),
|
await page.waitForTimeout(100)
|
||||||
'select_with_point'
|
|
||||||
)
|
|
||||||
await u.closeDebugPanel()
|
|
||||||
|
|
||||||
// hold down shift
|
// hold down shift
|
||||||
await page.keyboard.down('Shift')
|
await page.keyboard.down('Shift')
|
||||||
@ -206,7 +197,7 @@ test('if you write invalid kcl you get inlined errors', async ({ page }) => {
|
|||||||
|
|
||||||
test('executes on load', async ({ page, context }) => {
|
test('executes on load', async ({ page, context }) => {
|
||||||
const u = getUtils(page)
|
const u = getUtils(page)
|
||||||
await context.addInitScript(async (token) => {
|
await context.addInitScript(async () => {
|
||||||
localStorage.setItem(
|
localStorage.setItem(
|
||||||
'persistCode',
|
'persistCode',
|
||||||
`const part001 = startSketchOn('-XZ')
|
`const part001 = startSketchOn('-XZ')
|
||||||
@ -271,59 +262,41 @@ test('Can create sketches on all planes and their back sides', async ({
|
|||||||
await page.goto('/')
|
await page.goto('/')
|
||||||
await u.waitForAuthSkipAppStart()
|
await u.waitForAuthSkipAppStart()
|
||||||
await u.openDebugPanel()
|
await u.openDebugPanel()
|
||||||
await u.waitForDefaultPlanesVisibilityChange()
|
|
||||||
|
|
||||||
const camCmd: EngineCommand = {
|
const camPos: [number, number, number] = [100, 100, 100]
|
||||||
type: 'modeling_cmd_req',
|
|
||||||
cmd_id: uuidv4(),
|
|
||||||
cmd: {
|
|
||||||
type: 'default_camera_look_at',
|
|
||||||
center: { x: 15, y: 0, z: 0 },
|
|
||||||
up: { x: 0, y: 0, z: 1 },
|
|
||||||
vantage: { x: 30, y: 30, z: 30 },
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
const TestSinglePlane = async ({
|
const TestSinglePlane = async ({
|
||||||
viewCmd,
|
viewCmd,
|
||||||
expectedCode,
|
expectedCode,
|
||||||
clickCoords,
|
clickCoords,
|
||||||
}: {
|
}: {
|
||||||
viewCmd: EngineCommand
|
viewCmd: [number, number, number]
|
||||||
expectedCode: string
|
expectedCode: string
|
||||||
clickCoords: { x: number; y: number }
|
clickCoords: { x: number; y: number }
|
||||||
}) => {
|
}) => {
|
||||||
await u.openDebugPanel()
|
await u.openDebugPanel()
|
||||||
await u.sendCustomCmd(viewCmd)
|
|
||||||
|
await u.updateCamPosition(viewCmd)
|
||||||
|
|
||||||
await u.clearCommandLogs()
|
await u.clearCommandLogs()
|
||||||
// await page.waitForTimeout(200)
|
|
||||||
await page.getByRole('button', { name: 'Start Sketch' }).click()
|
await page.getByRole('button', { name: 'Start Sketch' }).click()
|
||||||
await u.waitForDefaultPlanesVisibilityChange()
|
|
||||||
|
|
||||||
await u.closeDebugPanel()
|
await u.closeDebugPanel()
|
||||||
await page.mouse.click(clickCoords.x, clickCoords.y)
|
await page.mouse.click(clickCoords.x, clickCoords.y)
|
||||||
await u.openDebugPanel()
|
await page.waitForTimeout(300) // wait for animation
|
||||||
|
|
||||||
await expect(page.getByRole('button', { name: 'Line' })).toBeVisible()
|
await expect(page.getByRole('button', { name: 'Line' })).toBeVisible()
|
||||||
|
|
||||||
// draw a line
|
// draw a line
|
||||||
const startXPx = 600
|
const startXPx = 600
|
||||||
await u.clearCommandLogs()
|
|
||||||
await page.getByRole('button', { name: 'Line' }).click()
|
|
||||||
await u.waitForCmdReceive('set_tool')
|
|
||||||
await u.clearCommandLogs()
|
|
||||||
await u.closeDebugPanel()
|
await u.closeDebugPanel()
|
||||||
await page.mouse.click(startXPx + PUR * 10, 500 - PUR * 10)
|
await page.mouse.click(startXPx + PUR * 10, 500 - PUR * 10)
|
||||||
await u.openDebugPanel()
|
|
||||||
await u.waitForCmdReceive('mouse_click')
|
|
||||||
await u.closeDebugPanel()
|
|
||||||
await page.mouse.click(startXPx + PUR * 20, 500 - PUR * 10)
|
|
||||||
await u.openDebugPanel()
|
|
||||||
|
|
||||||
await expect(page.locator('.cm-content')).toHaveText(expectedCode)
|
await expect(page.locator('.cm-content')).toHaveText(expectedCode)
|
||||||
|
|
||||||
await page.getByRole('button', { name: 'Line' }).click()
|
await page.getByRole('button', { name: 'Line' }).click()
|
||||||
await u.clearCommandLogs()
|
await u.openAndClearDebugPanel()
|
||||||
await page.getByRole('button', { name: 'Exit Sketch' }).click()
|
await page.getByRole('button', { name: 'Exit Sketch' }).click()
|
||||||
await u.expectCmdLog('[data-message-type="execution-done"]')
|
await u.expectCmdLog('[data-message-type="execution-done"]')
|
||||||
|
|
||||||
@ -333,51 +306,41 @@ test('Can create sketches on all planes and their back sides', async ({
|
|||||||
|
|
||||||
const codeTemplate = (
|
const codeTemplate = (
|
||||||
plane = 'XY',
|
plane = 'XY',
|
||||||
sign = ''
|
rounded = false,
|
||||||
|
otherThing = '1'
|
||||||
) => `const part001 = startSketchOn('${plane}')
|
) => `const part001 = startSketchOn('${plane}')
|
||||||
|> startProfileAt([${sign}6.88, -9.29], %)
|
|> startProfileAt([28.9${otherThing}, -39${rounded ? '' : '.01'}], %)`
|
||||||
|> line([${sign}6.95, 0], %)`
|
|
||||||
await TestSinglePlane({
|
await TestSinglePlane({
|
||||||
viewCmd: camCmd,
|
viewCmd: camPos,
|
||||||
expectedCode: codeTemplate('XY'),
|
expectedCode: codeTemplate('XY'),
|
||||||
clickCoords: { x: 700, y: 350 }, // red plane
|
clickCoords: { x: 600, y: 388 }, // red plane
|
||||||
|
// clickCoords: { x: 600, y: 400 }, // red plane // clicks grid helper and that causes problems, should fix so that these coords work too.
|
||||||
})
|
})
|
||||||
await TestSinglePlane({
|
await TestSinglePlane({
|
||||||
viewCmd: camCmd,
|
viewCmd: camPos,
|
||||||
expectedCode: codeTemplate('YZ'),
|
expectedCode: codeTemplate('YZ', true),
|
||||||
clickCoords: { x: 1000, y: 200 }, // green plane
|
clickCoords: { x: 700, y: 300 }, // green plane
|
||||||
})
|
})
|
||||||
await TestSinglePlane({
|
await TestSinglePlane({
|
||||||
viewCmd: camCmd,
|
viewCmd: camPos,
|
||||||
expectedCode: codeTemplate('XZ', '-'),
|
expectedCode: codeTemplate('XZ'),
|
||||||
clickCoords: { x: 630, y: 130 }, // blue plane
|
clickCoords: { x: 700, y: 80 }, // blue plane
|
||||||
})
|
})
|
||||||
|
const camCmdBackSide: [number, number, number] = [-100, -100, -100]
|
||||||
// new camera angle to click the back side of all three planes
|
|
||||||
const camCmdBackSide: EngineCommand = {
|
|
||||||
type: 'modeling_cmd_req',
|
|
||||||
cmd_id: uuidv4(),
|
|
||||||
cmd: {
|
|
||||||
type: 'default_camera_look_at',
|
|
||||||
center: { x: -15, y: 0, z: 0 },
|
|
||||||
up: { x: 0, y: 0, z: 1 },
|
|
||||||
vantage: { x: -30, y: -30, z: -30 },
|
|
||||||
},
|
|
||||||
}
|
|
||||||
await TestSinglePlane({
|
await TestSinglePlane({
|
||||||
viewCmd: camCmdBackSide,
|
viewCmd: camCmdBackSide,
|
||||||
expectedCode: codeTemplate('-XY', '-'),
|
expectedCode: codeTemplate('-XY', false, '3'),
|
||||||
clickCoords: { x: 705, y: 136 }, // back of red plane
|
clickCoords: { x: 601, y: 118 }, // back of red plane
|
||||||
})
|
})
|
||||||
await TestSinglePlane({
|
await TestSinglePlane({
|
||||||
viewCmd: camCmdBackSide,
|
viewCmd: camCmdBackSide,
|
||||||
expectedCode: codeTemplate('-YZ', '-'),
|
expectedCode: codeTemplate('-YZ'),
|
||||||
clickCoords: { x: 1000, y: 350 }, // back of green plane
|
clickCoords: { x: 730, y: 219 }, // back of green plane
|
||||||
})
|
})
|
||||||
await TestSinglePlane({
|
await TestSinglePlane({
|
||||||
viewCmd: camCmdBackSide,
|
viewCmd: camCmdBackSide,
|
||||||
expectedCode: codeTemplate('-XZ'),
|
expectedCode: codeTemplate('-XZ', true),
|
||||||
clickCoords: { x: 600, y: 400 }, // back of blue plane
|
clickCoords: { x: 680, y: 427 }, // back of blue plane
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -387,7 +350,6 @@ test('Auto complete works', async ({ page }) => {
|
|||||||
await page.setViewportSize({ width: 1200, height: 500 })
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
await page.goto('/')
|
await page.goto('/')
|
||||||
await u.waitForAuthSkipAppStart()
|
await u.waitForAuthSkipAppStart()
|
||||||
await u.waitForDefaultPlanesVisibilityChange()
|
|
||||||
|
|
||||||
// this test might be brittle as we add and remove functions
|
// this test might be brittle as we add and remove functions
|
||||||
// but should also be easy to update.
|
// but should also be easy to update.
|
||||||
@ -405,6 +367,7 @@ test('Auto complete works', async ({ page }) => {
|
|||||||
await page.keyboard.type(' |> startProfi')
|
await page.keyboard.type(' |> startProfi')
|
||||||
// expect there be a single auto complete option that we can just hit enter on
|
// expect there be a single auto complete option that we can just hit enter on
|
||||||
await expect(page.locator('.cm-completionLabel')).toBeVisible()
|
await expect(page.locator('.cm-completionLabel')).toBeVisible()
|
||||||
|
await page.waitForTimeout(100)
|
||||||
await page.keyboard.press('Enter') // accepting the auto complete, not a new line
|
await page.keyboard.press('Enter') // accepting the auto complete, not a new line
|
||||||
|
|
||||||
await page.keyboard.type('([0,0], %)')
|
await page.keyboard.type('([0,0], %)')
|
||||||
@ -412,6 +375,7 @@ test('Auto complete works', async ({ page }) => {
|
|||||||
await page.keyboard.type(' |> lin')
|
await page.keyboard.type(' |> lin')
|
||||||
|
|
||||||
await expect(page.locator('.cm-tooltip-autocomplete')).toBeVisible()
|
await expect(page.locator('.cm-tooltip-autocomplete')).toBeVisible()
|
||||||
|
await page.waitForTimeout(100)
|
||||||
// press arrow down twice then enter to accept xLine
|
// press arrow down twice then enter to accept xLine
|
||||||
await page.keyboard.press('ArrowDown')
|
await page.keyboard.press('ArrowDown')
|
||||||
await page.keyboard.press('ArrowDown')
|
await page.keyboard.press('ArrowDown')
|
||||||
@ -478,38 +442,36 @@ test('Selections work on fresh and edited sketch', async ({ page }) => {
|
|||||||
await page.goto('/')
|
await page.goto('/')
|
||||||
await u.waitForAuthSkipAppStart()
|
await u.waitForAuthSkipAppStart()
|
||||||
await u.openDebugPanel()
|
await u.openDebugPanel()
|
||||||
await u.waitForDefaultPlanesVisibilityChange()
|
|
||||||
|
|
||||||
const xAxisClick = () => page.mouse.click(700, 250)
|
const xAxisClick = () =>
|
||||||
const emptySpaceClick = () => page.mouse.click(700, 300)
|
page.mouse.click(700, 250).then(() => page.waitForTimeout(100))
|
||||||
const topHorzSegmentClick = () => page.mouse.click(700, 285)
|
const emptySpaceClick = () =>
|
||||||
const bottomHorzSegmentClick = () => page.mouse.click(750, 393)
|
page.mouse.click(728, 343).then(() => page.waitForTimeout(100))
|
||||||
|
const topHorzSegmentClick = () =>
|
||||||
|
page.mouse.click(709, 289).then(() => page.waitForTimeout(100))
|
||||||
|
const bottomHorzSegmentClick = () =>
|
||||||
|
page.mouse.click(767, 396).then(() => page.waitForTimeout(100))
|
||||||
|
|
||||||
await u.clearCommandLogs()
|
await u.clearCommandLogs()
|
||||||
await page.getByRole('button', { name: 'Start Sketch' }).click()
|
await page.getByRole('button', { name: 'Start Sketch' }).click()
|
||||||
await u.waitForDefaultPlanesVisibilityChange()
|
|
||||||
|
|
||||||
// select a plane
|
// select a plane
|
||||||
await u.doAndWaitForCmd(() => page.mouse.click(700, 200), 'edit_mode_enter')
|
await page.mouse.click(700, 200)
|
||||||
await u.waitForCmdReceive('set_tool')
|
await page.waitForTimeout(700) // wait for animation
|
||||||
|
|
||||||
await u.doAndWaitForCmd(
|
|
||||||
() => page.getByRole('button', { name: 'Line' }).click(),
|
|
||||||
'set_tool'
|
|
||||||
)
|
|
||||||
|
|
||||||
const startXPx = 600
|
const startXPx = 600
|
||||||
await u.doAndWaitForCmd(
|
await page.mouse.click(startXPx + PUR * 10, 500 - PUR * 10)
|
||||||
() => page.mouse.click(startXPx + PUR * 10, 500 - PUR * 10),
|
const startAt = '[23.74, -32.03]'
|
||||||
'mouse_click',
|
await expect(page.locator('.cm-content'))
|
||||||
false
|
.toHaveText(`const part001 = startSketchOn('-XZ')
|
||||||
)
|
|> startProfileAt(${startAt}, %)`)
|
||||||
|
|
||||||
|
await u.closeDebugPanel()
|
||||||
|
|
||||||
await page.mouse.click(startXPx + PUR * 20, 500 - PUR * 10)
|
await page.mouse.click(startXPx + PUR * 20, 500 - PUR * 10)
|
||||||
|
|
||||||
const startAt = '[18.26, -24.63]'
|
const num = 23.97
|
||||||
const num = '18.43'
|
const num2 = '47.71'
|
||||||
const num2 = '36.69'
|
|
||||||
await expect(page.locator('.cm-content'))
|
await expect(page.locator('.cm-content'))
|
||||||
.toHaveText(`const part001 = startSketchOn('-XZ')
|
.toHaveText(`const part001 = startSketchOn('-XZ')
|
||||||
|> startProfileAt(${startAt}, %)
|
|> startProfileAt(${startAt}, %)
|
||||||
@ -530,10 +492,7 @@ test('Selections work on fresh and edited sketch', async ({ page }) => {
|
|||||||
|> line([-${num2}, 0], %)`)
|
|> line([-${num2}, 0], %)`)
|
||||||
|
|
||||||
// deselect line tool
|
// deselect line tool
|
||||||
await u.doAndWaitForCmd(
|
await page.getByRole('button', { name: 'Line' }).click()
|
||||||
() => page.getByRole('button', { name: 'Line' }).click(),
|
|
||||||
'set_tool'
|
|
||||||
)
|
|
||||||
|
|
||||||
await u.closeDebugPanel()
|
await u.closeDebugPanel()
|
||||||
const selectionSequence = async () => {
|
const selectionSequence = async () => {
|
||||||
@ -555,79 +514,73 @@ test('Selections work on fresh and edited sketch', async ({ page }) => {
|
|||||||
// now check clicking works including axis
|
// now check clicking works including axis
|
||||||
|
|
||||||
// click a segment hold shift and click an axis, see that a relevant constraint is enabled
|
// click a segment hold shift and click an axis, see that a relevant constraint is enabled
|
||||||
await u.doAndWaitForCmd(topHorzSegmentClick, 'select_with_point', false)
|
await topHorzSegmentClick()
|
||||||
await page.keyboard.down('Shift')
|
await page.keyboard.down('Shift')
|
||||||
const absYButton = page.getByRole('button', { name: 'ABS Y' })
|
const absYButton = page.getByRole('button', { name: 'ABS Y' })
|
||||||
await expect(absYButton).toBeDisabled()
|
await expect(absYButton).toBeDisabled()
|
||||||
await u.doAndWaitForCmd(xAxisClick, 'select_with_point', false)
|
await xAxisClick()
|
||||||
await page.keyboard.up('Shift')
|
await page.keyboard.up('Shift')
|
||||||
await absYButton.and(page.locator(':not([disabled])')).waitFor()
|
await absYButton.and(page.locator(':not([disabled])')).waitFor()
|
||||||
await expect(absYButton).not.toBeDisabled()
|
await expect(absYButton).not.toBeDisabled()
|
||||||
|
|
||||||
// clear selection by clicking on nothing
|
// clear selection by clicking on nothing
|
||||||
await u.doAndWaitForCmd(emptySpaceClick, 'select_clear', false)
|
await emptySpaceClick()
|
||||||
|
|
||||||
// same selection but click the axis first
|
// same selection but click the axis first
|
||||||
await u.doAndWaitForCmd(xAxisClick, 'select_with_point', false)
|
await xAxisClick()
|
||||||
await expect(absYButton).toBeDisabled()
|
await expect(absYButton).toBeDisabled()
|
||||||
await page.keyboard.down('Shift')
|
await page.keyboard.down('Shift')
|
||||||
await u.doAndWaitForCmd(topHorzSegmentClick, 'select_with_point', false)
|
await topHorzSegmentClick()
|
||||||
|
|
||||||
await page.keyboard.up('Shift')
|
await page.keyboard.up('Shift')
|
||||||
await expect(absYButton).not.toBeDisabled()
|
await expect(absYButton).not.toBeDisabled()
|
||||||
|
|
||||||
// clear selection by clicking on nothing
|
// clear selection by clicking on nothing
|
||||||
await u.doAndWaitForCmd(emptySpaceClick, 'select_clear', false)
|
await emptySpaceClick()
|
||||||
|
|
||||||
// check the same selection again by putting cursor in code first then selecting axis
|
// check the same selection again by putting cursor in code first then selecting axis
|
||||||
await u.doAndWaitForCmd(
|
await page.getByText(` |> line([-${num2}, 0], %)`).click()
|
||||||
() => page.getByText(` |> line([-${num2}, 0], %)`).click(),
|
|
||||||
'select_clear',
|
|
||||||
false
|
|
||||||
)
|
|
||||||
await page.keyboard.down('Shift')
|
await page.keyboard.down('Shift')
|
||||||
await expect(absYButton).toBeDisabled()
|
await expect(absYButton).toBeDisabled()
|
||||||
await u.doAndWaitForCmd(xAxisClick, 'select_with_point', false)
|
await xAxisClick()
|
||||||
await page.keyboard.up('Shift')
|
await page.keyboard.up('Shift')
|
||||||
await expect(absYButton).not.toBeDisabled()
|
await expect(absYButton).not.toBeDisabled()
|
||||||
|
|
||||||
// clear selection by clicking on nothing
|
// clear selection by clicking on nothing
|
||||||
await u.doAndWaitForCmd(emptySpaceClick, 'select_clear', false)
|
await emptySpaceClick()
|
||||||
|
|
||||||
// select segment in editor than another segment in scene and check there are two cursors
|
// select segment in editor than another segment in scene and check there are two cursors
|
||||||
await u.doAndWaitForCmd(
|
await page.getByText(` |> line([-${num2}, 0], %)`).click()
|
||||||
() => page.getByText(` |> line([-${num2}, 0], %)`).click(),
|
await page.waitForTimeout(300)
|
||||||
'select_clear',
|
|
||||||
false
|
|
||||||
)
|
|
||||||
await page.keyboard.down('Shift')
|
await page.keyboard.down('Shift')
|
||||||
await expect(page.locator('.cm-cursor')).toHaveCount(1)
|
await expect(page.locator('.cm-cursor')).toHaveCount(1)
|
||||||
await u.doAndWaitForCmd(bottomHorzSegmentClick, 'select_with_point', false) // another segment, bottom one
|
await bottomHorzSegmentClick()
|
||||||
await page.keyboard.up('Shift')
|
await page.keyboard.up('Shift')
|
||||||
await expect(page.locator('.cm-cursor')).toHaveCount(2)
|
await expect(page.locator('.cm-cursor')).toHaveCount(2)
|
||||||
|
|
||||||
// clear selection by clicking on nothing
|
// clear selection by clicking on nothing
|
||||||
await u.doAndWaitForCmd(emptySpaceClick, 'select_clear', false)
|
await emptySpaceClick()
|
||||||
}
|
}
|
||||||
|
|
||||||
await selectionSequence()
|
await selectionSequence()
|
||||||
|
|
||||||
// hovering in fresh sketch worked, lets try exiting and re-entering
|
// hovering in fresh sketch worked, lets try exiting and re-entering
|
||||||
await u.doAndWaitForCmd(
|
await u.openAndClearDebugPanel()
|
||||||
() => page.getByRole('button', { name: 'Exit Sketch' }).click(),
|
await page.getByRole('button', { name: 'Exit Sketch' }).click()
|
||||||
'edit_mode_exit'
|
await page.waitForTimeout(200)
|
||||||
)
|
|
||||||
// wait for execution done
|
// wait for execution done
|
||||||
|
|
||||||
await u.expectCmdLog('[data-message-type="execution-done"]')
|
await u.expectCmdLog('[data-message-type="execution-done"]')
|
||||||
|
await u.closeDebugPanel()
|
||||||
|
|
||||||
// select a line
|
// select a line
|
||||||
await u.doAndWaitForCmd(topHorzSegmentClick, 'select_clear', false)
|
// await topHorzSegmentClick()
|
||||||
|
await page.getByText(startAt).click() // TODO remove this and reinstate // await topHorzSegmentClick()
|
||||||
|
await page.waitForTimeout(100)
|
||||||
|
|
||||||
// enter sketch again
|
// enter sketch again
|
||||||
await u.doAndWaitForCmd(
|
await page.getByRole('button', { name: 'Edit Sketch' }).click()
|
||||||
() => page.getByRole('button', { name: 'Start Sketch' }).click(),
|
await page.waitForTimeout(300) // wait for animation
|
||||||
'edit_mode_enter',
|
|
||||||
false
|
|
||||||
)
|
|
||||||
|
|
||||||
// hover again and check it works
|
// hover again and check it works
|
||||||
await selectionSequence()
|
await selectionSequence()
|
||||||
@ -697,6 +650,8 @@ test('Can extrude from the command bar', async ({ page, context }) => {
|
|||||||
await page.setViewportSize({ width: 1200, height: 500 })
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
await page.goto('/')
|
await page.goto('/')
|
||||||
await u.waitForAuthSkipAppStart()
|
await u.waitForAuthSkipAppStart()
|
||||||
|
await u.openDebugPanel()
|
||||||
|
await u.expectCmdLog('[data-message-type="execution-done"]')
|
||||||
|
|
||||||
let cmdSearchBar = page.getByPlaceholder('Search commands')
|
let cmdSearchBar = page.getByPlaceholder('Search commands')
|
||||||
await page.keyboard.press('Meta+K')
|
await page.keyboard.press('Meta+K')
|
||||||
@ -710,10 +665,7 @@ test('Can extrude from the command bar', async ({ page, context }) => {
|
|||||||
await expect(page.getByRole('button', { name: 'selection' })).toBeDisabled()
|
await expect(page.getByRole('button', { name: 'selection' })).toBeDisabled()
|
||||||
|
|
||||||
// Click to select face and set distance
|
// Click to select face and set distance
|
||||||
await u.openAndClearDebugPanel()
|
|
||||||
await page.getByText('|> startProfileAt([-6.95, 4.98], %)').click()
|
await page.getByText('|> startProfileAt([-6.95, 4.98], %)').click()
|
||||||
await u.waitForCmdReceive('select_add')
|
|
||||||
await u.closeDebugPanel()
|
|
||||||
await page.getByRole('button', { name: 'Continue' }).click()
|
await page.getByRole('button', { name: 'Continue' }).click()
|
||||||
await expect(page.getByRole('button', { name: 'distance' })).toBeDisabled()
|
await expect(page.getByRole('button', { name: 'distance' })).toBeDisabled()
|
||||||
await page.keyboard.press('Enter')
|
await page.keyboard.press('Enter')
|
||||||
@ -735,3 +687,330 @@ test('Can extrude from the command bar', async ({ page, context }) => {
|
|||||||
|> extrude(5, %)`
|
|> extrude(5, %)`
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('Can add multiple sketches', async ({ page }) => {
|
||||||
|
const u = getUtils(page)
|
||||||
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
|
const PUR = 400 / 37.5 //pixeltoUnitRatio
|
||||||
|
await page.goto('/')
|
||||||
|
await u.waitForAuthSkipAppStart()
|
||||||
|
await u.openDebugPanel()
|
||||||
|
|
||||||
|
await expect(page.getByRole('button', { name: 'Start Sketch' })).toBeVisible()
|
||||||
|
|
||||||
|
// click on "Start Sketch" button
|
||||||
|
await u.clearCommandLogs()
|
||||||
|
await u.doAndWaitForImageDiff(
|
||||||
|
() => page.getByRole('button', { name: 'Start Sketch' }).click(),
|
||||||
|
200
|
||||||
|
)
|
||||||
|
|
||||||
|
// select a plane
|
||||||
|
await page.mouse.click(700, 200)
|
||||||
|
|
||||||
|
await expect(page.locator('.cm-content')).toHaveText(
|
||||||
|
`const part001 = startSketchOn('-XZ')`
|
||||||
|
)
|
||||||
|
|
||||||
|
await page.waitForTimeout(500) // TODO detect animation ending, or disable animation
|
||||||
|
|
||||||
|
const startXPx = 600
|
||||||
|
await page.mouse.click(startXPx + PUR * 10, 500 - PUR * 10)
|
||||||
|
const startAt = '[23.74, -32.03]'
|
||||||
|
await expect(page.locator('.cm-content'))
|
||||||
|
.toHaveText(`const part001 = startSketchOn('-XZ')
|
||||||
|
|> startProfileAt(${startAt}, %)`)
|
||||||
|
await page.waitForTimeout(100)
|
||||||
|
|
||||||
|
await u.closeDebugPanel()
|
||||||
|
await page.mouse.click(startXPx + PUR * 20, 500 - PUR * 10)
|
||||||
|
await page.waitForTimeout(100)
|
||||||
|
|
||||||
|
const num = 23.97
|
||||||
|
await expect(page.locator('.cm-content'))
|
||||||
|
.toHaveText(`const part001 = startSketchOn('-XZ')
|
||||||
|
|> startProfileAt(${startAt}, %)
|
||||||
|
|> line([${num}, 0], %)`)
|
||||||
|
|
||||||
|
await page.mouse.click(startXPx + PUR * 20, 500 - PUR * 20)
|
||||||
|
await expect(page.locator('.cm-content'))
|
||||||
|
.toHaveText(`const part001 = startSketchOn('-XZ')
|
||||||
|
|> startProfileAt(${startAt}, %)
|
||||||
|
|> line([${num}, 0], %)
|
||||||
|
|> line([0, ${num}], %)`)
|
||||||
|
await page.mouse.click(startXPx, 500 - PUR * 20)
|
||||||
|
const finalCodeFirstSketch = `const part001 = startSketchOn('-XZ')
|
||||||
|
|> startProfileAt(${startAt}, %)
|
||||||
|
|> line([${num}, 0], %)
|
||||||
|
|> line([0, ${num}], %)
|
||||||
|
|> line([-47.71, 0], %)`
|
||||||
|
await expect(page.locator('.cm-content')).toHaveText(finalCodeFirstSketch)
|
||||||
|
|
||||||
|
// exit the sketch
|
||||||
|
|
||||||
|
await u.openAndClearDebugPanel()
|
||||||
|
await page.getByRole('button', { name: 'Exit Sketch' }).click()
|
||||||
|
|
||||||
|
await u.expectCmdLog('[data-message-type="execution-done"]')
|
||||||
|
|
||||||
|
await u.updateCamPosition([0, 100, 100])
|
||||||
|
|
||||||
|
// start a new sketch
|
||||||
|
await u.clearCommandLogs()
|
||||||
|
await page.getByRole('button', { name: 'Start Sketch' }).click()
|
||||||
|
await page.waitForTimeout(100)
|
||||||
|
await page.mouse.click(673, 384)
|
||||||
|
|
||||||
|
await page.waitForTimeout(500) // TODO detect animation ending, or disable animation
|
||||||
|
await u.clearAndCloseDebugPanel()
|
||||||
|
|
||||||
|
await page.mouse.click(startXPx + PUR * 10, 500 - PUR * 10)
|
||||||
|
const startAt2 = '[23.61, -31.85]'
|
||||||
|
await expect(
|
||||||
|
(await page.locator('.cm-content').innerText()).replace(/\s/g, '')
|
||||||
|
).toBe(
|
||||||
|
`${finalCodeFirstSketch}
|
||||||
|
const part002 = startSketchOn('XY')
|
||||||
|
|> startProfileAt(${startAt2}, %)`.replace(/\s/g, '')
|
||||||
|
)
|
||||||
|
await page.waitForTimeout(100)
|
||||||
|
|
||||||
|
await u.closeDebugPanel()
|
||||||
|
await page.mouse.click(startXPx + PUR * 20, 500 - PUR * 10)
|
||||||
|
await page.waitForTimeout(100)
|
||||||
|
|
||||||
|
const num2 = 23.83
|
||||||
|
await expect(
|
||||||
|
(await page.locator('.cm-content').innerText()).replace(/\s/g, '')
|
||||||
|
).toBe(
|
||||||
|
`${finalCodeFirstSketch}
|
||||||
|
const part002 = startSketchOn('XY')
|
||||||
|
|> startProfileAt(${startAt2}, %)
|
||||||
|
|> line([${num2}, 0], %)`.replace(/\s/g, '')
|
||||||
|
)
|
||||||
|
|
||||||
|
await page.mouse.click(startXPx + PUR * 20, 500 - PUR * 20)
|
||||||
|
await expect(
|
||||||
|
(await page.locator('.cm-content').innerText()).replace(/\s/g, '')
|
||||||
|
).toBe(
|
||||||
|
`${finalCodeFirstSketch}
|
||||||
|
const part002 = startSketchOn('XY')
|
||||||
|
|> startProfileAt(${startAt2}, %)
|
||||||
|
|> line([${num2}, 0], %)
|
||||||
|
|> line([0, ${num2}], %)`.replace(/\s/g, '')
|
||||||
|
)
|
||||||
|
await page.mouse.click(startXPx, 500 - PUR * 20)
|
||||||
|
await expect(
|
||||||
|
(await page.locator('.cm-content').innerText()).replace(/\s/g, '')
|
||||||
|
).toBe(
|
||||||
|
`${finalCodeFirstSketch}
|
||||||
|
const part002 = startSketchOn('XY')
|
||||||
|
|> startProfileAt(${startAt2}, %)
|
||||||
|
|> line([${num2}, 0], %)
|
||||||
|
|> line([0, ${num2}], %)
|
||||||
|
|> line([-47.44, 0], %)`.replace(/\s/g, '')
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('ProgramMemory can be serialised', async ({ page, context }) => {
|
||||||
|
const u = getUtils(page)
|
||||||
|
await context.addInitScript(async () => {
|
||||||
|
localStorage.setItem(
|
||||||
|
'persistCode',
|
||||||
|
`const part = startSketchOn('XY')
|
||||||
|
|> startProfileAt([0, 0], %)
|
||||||
|
|> line([0, 1], %)
|
||||||
|
|> line([1, 0], %)
|
||||||
|
|> line([0, -1], %)
|
||||||
|
|> close(%)
|
||||||
|
|> extrude(1, %)
|
||||||
|
|> patternLinear({
|
||||||
|
axis: [1, 0, 1],
|
||||||
|
repetitions: 3,
|
||||||
|
distance: 6
|
||||||
|
}, %)`
|
||||||
|
)
|
||||||
|
})
|
||||||
|
await page.setViewportSize({ width: 1000, height: 500 })
|
||||||
|
await page.goto('/')
|
||||||
|
const messages: string[] = []
|
||||||
|
|
||||||
|
// Listen for all console events and push the message text to an array
|
||||||
|
page.on('console', (message) => messages.push(message.text()))
|
||||||
|
await u.waitForAuthSkipAppStart()
|
||||||
|
|
||||||
|
// wait for execution done
|
||||||
|
await u.openDebugPanel()
|
||||||
|
await u.expectCmdLog('[data-message-type="execution-done"]')
|
||||||
|
|
||||||
|
const forbiddenMessages = ['cannot serialize tagged newtype variant']
|
||||||
|
forbiddenMessages.forEach((forbiddenMessage) => {
|
||||||
|
messages.forEach((message) => {
|
||||||
|
expect(message).not.toContain(forbiddenMessage)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
test("Various pipe expressions should and shouldn't allow edit and or extrude", async ({
|
||||||
|
page,
|
||||||
|
context,
|
||||||
|
}) => {
|
||||||
|
const u = getUtils(page)
|
||||||
|
const selectionsSnippets = {
|
||||||
|
extrudeAndEditBlocked: '|> startProfileAt([10.81, 32.99], %)',
|
||||||
|
extrudeAndEditBlockedInFunction: '|> startProfileAt(pos, %)',
|
||||||
|
extrudeAndEditAllowed: '|> startProfileAt([15.72, 4.7], %)',
|
||||||
|
editOnly: '|> startProfileAt([15.79, -14.6], %)',
|
||||||
|
}
|
||||||
|
await context.addInitScript(
|
||||||
|
async ({
|
||||||
|
extrudeAndEditBlocked,
|
||||||
|
extrudeAndEditBlockedInFunction,
|
||||||
|
extrudeAndEditAllowed,
|
||||||
|
editOnly,
|
||||||
|
}: any) => {
|
||||||
|
localStorage.setItem(
|
||||||
|
'persistCode',
|
||||||
|
`const part001 = startSketchOn('-XZ')
|
||||||
|
${extrudeAndEditBlocked}
|
||||||
|
|> line([25.96, 2.93], %)
|
||||||
|
|> line([5.25, -5.72], %)
|
||||||
|
|> line([-2.01, -10.35], %)
|
||||||
|
|> line([-27.65, -2.78], %)
|
||||||
|
|> close(%)
|
||||||
|
|> extrude(5, %)
|
||||||
|
const part002 = startSketchOn('-XZ')
|
||||||
|
${extrudeAndEditAllowed}
|
||||||
|
|> line([10.32, 6.47], %)
|
||||||
|
|> line([9.71, -6.16], %)
|
||||||
|
|> line([-3.08, -9.86], %)
|
||||||
|
|> line([-12.02, -1.54], %)
|
||||||
|
|> close(%)
|
||||||
|
const part003 = startSketchOn('-XZ')
|
||||||
|
${editOnly}
|
||||||
|
|> line([27.55, -1.65], %)
|
||||||
|
|> line([4.95, -8], %)
|
||||||
|
|> line([-20.38, -10.12], %)
|
||||||
|
|> line([-15.79, 17.08], %)
|
||||||
|
|
||||||
|
fn yohey = (pos) => {
|
||||||
|
const part004 = startSketchOn('-XZ')
|
||||||
|
${extrudeAndEditBlockedInFunction}
|
||||||
|
|> line([27.55, -1.65], %)
|
||||||
|
|> line([4.95, -10.53], %)
|
||||||
|
|> line([-20.38, -8], %)
|
||||||
|
|> line([-15.79, 17.08], %)
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
|
||||||
|
yohey([15.79, -34.6])
|
||||||
|
`
|
||||||
|
)
|
||||||
|
},
|
||||||
|
selectionsSnippets
|
||||||
|
)
|
||||||
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
|
await page.goto('/')
|
||||||
|
await u.waitForAuthSkipAppStart()
|
||||||
|
|
||||||
|
// wait for execution done
|
||||||
|
await u.openDebugPanel()
|
||||||
|
await u.expectCmdLog('[data-message-type="execution-done"]')
|
||||||
|
await u.closeDebugPanel()
|
||||||
|
|
||||||
|
await page.getByText(selectionsSnippets.extrudeAndEditBlocked).click()
|
||||||
|
await expect(page.getByRole('button', { name: 'Extrude' })).toBeDisabled()
|
||||||
|
await expect(
|
||||||
|
page.getByRole('button', { name: 'Edit Sketch' })
|
||||||
|
).not.toBeVisible()
|
||||||
|
|
||||||
|
await page.getByText(selectionsSnippets.extrudeAndEditAllowed).click()
|
||||||
|
await expect(page.getByRole('button', { name: 'Extrude' })).not.toBeDisabled()
|
||||||
|
await expect(
|
||||||
|
page.getByRole('button', { name: 'Edit Sketch' })
|
||||||
|
).not.toBeDisabled()
|
||||||
|
|
||||||
|
await page.getByText(selectionsSnippets.editOnly).click()
|
||||||
|
await expect(page.getByRole('button', { name: 'Extrude' })).toBeDisabled()
|
||||||
|
await expect(
|
||||||
|
page.getByRole('button', { name: 'Edit Sketch' })
|
||||||
|
).not.toBeDisabled()
|
||||||
|
|
||||||
|
await page
|
||||||
|
.getByText(selectionsSnippets.extrudeAndEditBlockedInFunction)
|
||||||
|
.click()
|
||||||
|
await expect(page.getByRole('button', { name: 'Extrude' })).toBeDisabled()
|
||||||
|
await expect(
|
||||||
|
page.getByRole('button', { name: 'Edit Sketch' })
|
||||||
|
).not.toBeVisible()
|
||||||
|
|
||||||
|
// selecting an editable sketch but clicking "start sktech" should start a new sketch and not edit the existing one
|
||||||
|
await page.getByText(selectionsSnippets.extrudeAndEditAllowed).click()
|
||||||
|
await page.getByRole('button', { name: 'Start Sketch' }).click()
|
||||||
|
await page.mouse.click(700, 200)
|
||||||
|
// expect main content to contain `part005` i.e. started a new sketch
|
||||||
|
await expect(page.locator('.cm-content')).toHaveText(
|
||||||
|
/part005 = startSketchOn\('-XZ'\)/
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('Deselecting line tool should mean nothing happens on click', async ({
|
||||||
|
page,
|
||||||
|
}) => {
|
||||||
|
const u = getUtils(page)
|
||||||
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
|
const PUR = 400 / 37.5 //pixeltoUnitRatio
|
||||||
|
await page.goto('/')
|
||||||
|
await u.waitForAuthSkipAppStart()
|
||||||
|
await u.openDebugPanel()
|
||||||
|
|
||||||
|
await expect(page.getByRole('button', { name: 'Start Sketch' })).toBeVisible()
|
||||||
|
|
||||||
|
// click on "Start Sketch" button
|
||||||
|
await u.clearCommandLogs()
|
||||||
|
await u.doAndWaitForImageDiff(
|
||||||
|
() => page.getByRole('button', { name: 'Start Sketch' }).click(),
|
||||||
|
200
|
||||||
|
)
|
||||||
|
|
||||||
|
await page.mouse.click(700, 200)
|
||||||
|
|
||||||
|
await expect(page.locator('.cm-content')).toHaveText(
|
||||||
|
`const part001 = startSketchOn('-XZ')`
|
||||||
|
)
|
||||||
|
|
||||||
|
await page.waitForTimeout(300)
|
||||||
|
|
||||||
|
let previousCodeContent = await page.locator('.cm-content').innerText()
|
||||||
|
|
||||||
|
// deselect the line tool by clicking it
|
||||||
|
await page.getByRole('button', { name: 'Line' }).click()
|
||||||
|
|
||||||
|
await page.mouse.click(700, 200)
|
||||||
|
await page.waitForTimeout(100)
|
||||||
|
await page.mouse.click(700, 250)
|
||||||
|
await page.waitForTimeout(100)
|
||||||
|
await page.mouse.click(750, 200)
|
||||||
|
await page.waitForTimeout(100)
|
||||||
|
|
||||||
|
// expect no change
|
||||||
|
await expect(page.locator('.cm-content')).toHaveText(previousCodeContent)
|
||||||
|
|
||||||
|
// select line tool again
|
||||||
|
await page.getByRole('button', { name: 'Line' }).click()
|
||||||
|
|
||||||
|
await u.closeDebugPanel()
|
||||||
|
|
||||||
|
// line tool should work as expected again
|
||||||
|
await page.mouse.click(700, 200)
|
||||||
|
await expect(page.locator('.cm-content')).not.toHaveText(previousCodeContent)
|
||||||
|
previousCodeContent = await page.locator('.cm-content').innerText()
|
||||||
|
|
||||||
|
await page.mouse.click(700, 300)
|
||||||
|
await expect(page.locator('.cm-content')).not.toHaveText(previousCodeContent)
|
||||||
|
previousCodeContent = await page.locator('.cm-content').innerText()
|
||||||
|
|
||||||
|
await page.mouse.click(750, 300)
|
||||||
|
await expect(page.locator('.cm-content')).not.toHaveText(previousCodeContent)
|
||||||
|
previousCodeContent = await page.locator('.cm-content').innerText()
|
||||||
|
})
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
import { test, expect } from '@playwright/test'
|
import { test, expect } from '@playwright/test'
|
||||||
import { secrets } from './secrets'
|
import { secrets } from './secrets'
|
||||||
import { EngineCommand } from '../../src/lang/std/engineConnection'
|
|
||||||
import { v4 as uuidv4 } from 'uuid'
|
|
||||||
import { getUtils } from './test-utils'
|
import { getUtils } from './test-utils'
|
||||||
import { Models } from '@kittycad/lib'
|
import { Models } from '@kittycad/lib'
|
||||||
import fsp from 'fs/promises'
|
import fsp from 'fs/promises'
|
||||||
@ -40,19 +38,8 @@ test('change camera, show planes', async ({ page, context }) => {
|
|||||||
await u.waitForAuthSkipAppStart()
|
await u.waitForAuthSkipAppStart()
|
||||||
await u.openAndClearDebugPanel()
|
await u.openAndClearDebugPanel()
|
||||||
|
|
||||||
const camCmd: EngineCommand = {
|
const camPos: [number, number, number] = [0, 85, 85]
|
||||||
type: 'modeling_cmd_req',
|
await u.updateCamPosition(camPos)
|
||||||
cmd_id: uuidv4(),
|
|
||||||
cmd: {
|
|
||||||
type: 'default_camera_look_at',
|
|
||||||
center: { x: 0, y: 0, z: 0 },
|
|
||||||
up: { x: 0, y: 0, z: 1 },
|
|
||||||
vantage: { x: 0, y: 85, z: 85 },
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
await u.sendCustomCmd(camCmd)
|
|
||||||
await u.waitForCmdReceive('default_camera_look_at')
|
|
||||||
|
|
||||||
// rotate
|
// rotate
|
||||||
await u.closeDebugPanel()
|
await u.closeDebugPanel()
|
||||||
@ -62,13 +49,11 @@ test('change camera, show planes', async ({ page, context }) => {
|
|||||||
await page.mouse.up({ button: 'right' })
|
await page.mouse.up({ button: 'right' })
|
||||||
|
|
||||||
await u.openDebugPanel()
|
await u.openDebugPanel()
|
||||||
await u.waitForCmdReceive('camera_drag_end')
|
|
||||||
await page.waitForTimeout(500)
|
await page.waitForTimeout(500)
|
||||||
await u.clearCommandLogs()
|
await u.clearCommandLogs()
|
||||||
|
|
||||||
await page.getByRole('button', { name: 'Start Sketch' }).click()
|
await page.getByRole('button', { name: 'Start Sketch' }).click()
|
||||||
|
|
||||||
await u.waitForDefaultPlanesVisibilityChange()
|
|
||||||
await u.closeDebugPanel()
|
await u.closeDebugPanel()
|
||||||
|
|
||||||
await expect(page).toHaveScreenshot({
|
await expect(page).toHaveScreenshot({
|
||||||
@ -77,10 +62,8 @@ test('change camera, show planes', async ({ page, context }) => {
|
|||||||
|
|
||||||
await u.openAndClearDebugPanel()
|
await u.openAndClearDebugPanel()
|
||||||
await page.getByRole('button', { name: 'Exit Sketch' }).click()
|
await page.getByRole('button', { name: 'Exit Sketch' }).click()
|
||||||
await u.waitForDefaultPlanesVisibilityChange()
|
|
||||||
|
|
||||||
await u.sendCustomCmd(camCmd)
|
await u.updateCamPosition(camPos)
|
||||||
await u.waitForCmdReceive('default_camera_look_at')
|
|
||||||
|
|
||||||
await u.clearCommandLogs()
|
await u.clearCommandLogs()
|
||||||
await u.closeDebugPanel()
|
await u.closeDebugPanel()
|
||||||
@ -93,12 +76,10 @@ test('change camera, show planes', async ({ page, context }) => {
|
|||||||
await page.keyboard.up('Shift')
|
await page.keyboard.up('Shift')
|
||||||
|
|
||||||
await u.openDebugPanel()
|
await u.openDebugPanel()
|
||||||
await u.waitForCmdReceive('camera_drag_end')
|
|
||||||
await page.waitForTimeout(300)
|
await page.waitForTimeout(300)
|
||||||
await u.clearCommandLogs()
|
await u.clearCommandLogs()
|
||||||
|
|
||||||
await page.getByRole('button', { name: 'Start Sketch' }).click()
|
await page.getByRole('button', { name: 'Start Sketch' }).click()
|
||||||
await u.waitForDefaultPlanesVisibilityChange()
|
|
||||||
await u.closeDebugPanel()
|
await u.closeDebugPanel()
|
||||||
|
|
||||||
await expect(page).toHaveScreenshot({
|
await expect(page).toHaveScreenshot({
|
||||||
@ -107,10 +88,8 @@ test('change camera, show planes', async ({ page, context }) => {
|
|||||||
|
|
||||||
await u.openAndClearDebugPanel()
|
await u.openAndClearDebugPanel()
|
||||||
await page.getByRole('button', { name: 'Exit Sketch' }).click()
|
await page.getByRole('button', { name: 'Exit Sketch' }).click()
|
||||||
await u.waitForDefaultPlanesVisibilityChange()
|
|
||||||
|
|
||||||
await u.sendCustomCmd(camCmd)
|
await u.updateCamPosition(camPos)
|
||||||
await u.waitForCmdReceive('default_camera_look_at')
|
|
||||||
|
|
||||||
await u.clearCommandLogs()
|
await u.clearCommandLogs()
|
||||||
await u.closeDebugPanel()
|
await u.closeDebugPanel()
|
||||||
@ -119,17 +98,15 @@ test('change camera, show planes', async ({ page, context }) => {
|
|||||||
await page.keyboard.down('Control')
|
await page.keyboard.down('Control')
|
||||||
await page.mouse.move(700, 400)
|
await page.mouse.move(700, 400)
|
||||||
await page.mouse.down({ button: 'right' })
|
await page.mouse.down({ button: 'right' })
|
||||||
await page.mouse.move(700, 350)
|
await page.mouse.move(700, 300)
|
||||||
await page.mouse.up({ button: 'right' })
|
await page.mouse.up({ button: 'right' })
|
||||||
await page.keyboard.up('Control')
|
await page.keyboard.up('Control')
|
||||||
|
|
||||||
await u.openDebugPanel()
|
await u.openDebugPanel()
|
||||||
await u.waitForCmdReceive('camera_drag_end')
|
|
||||||
await page.waitForTimeout(300)
|
await page.waitForTimeout(300)
|
||||||
await u.clearCommandLogs()
|
await u.clearCommandLogs()
|
||||||
|
|
||||||
await page.getByRole('button', { name: 'Start Sketch' }).click()
|
await page.getByRole('button', { name: 'Start Sketch' }).click()
|
||||||
await u.waitForDefaultPlanesVisibilityChange()
|
|
||||||
await u.closeDebugPanel()
|
await u.closeDebugPanel()
|
||||||
|
|
||||||
await expect(page).toHaveScreenshot({
|
await expect(page).toHaveScreenshot({
|
||||||
@ -164,11 +141,11 @@ const part001 = startSketchOn('-XZ')
|
|||||||
|> xLineTo({ to: totalLen, tag: 'seg03' }, %)
|
|> xLineTo({ to: totalLen, tag: 'seg03' }, %)
|
||||||
|> yLine({ length: -armThick, tag: 'seg01' }, %)
|
|> yLine({ length: -armThick, tag: 'seg01' }, %)
|
||||||
|> angledLineThatIntersects({
|
|> angledLineThatIntersects({
|
||||||
angle: _180,
|
angle: HALF_TURN,
|
||||||
offset: -armThick,
|
offset: -armThick,
|
||||||
intersectTag: 'seg04'
|
intersectTag: 'seg04'
|
||||||
}, %)
|
}, %)
|
||||||
|> angledLineToY([segAng('seg04', %) + 180, _0], %)
|
|> angledLineToY([segAng('seg04', %) + 180, ZERO], %)
|
||||||
|> angledLineToY({
|
|> angledLineToY({
|
||||||
angle: -bottomAng,
|
angle: -bottomAng,
|
||||||
to: -totalHeightHalf - armThick,
|
to: -totalHeightHalf - armThick,
|
||||||
@ -177,12 +154,12 @@ const part001 = startSketchOn('-XZ')
|
|||||||
|> xLineTo(segEndX('seg03', %) + 0, %)
|
|> xLineTo(segEndX('seg03', %) + 0, %)
|
||||||
|> yLine(-segLen('seg01', %), %)
|
|> yLine(-segLen('seg01', %), %)
|
||||||
|> angledLineThatIntersects({
|
|> angledLineThatIntersects({
|
||||||
angle: _180,
|
angle: HALF_TURN,
|
||||||
offset: -armThick,
|
offset: -armThick,
|
||||||
intersectTag: 'seg02'
|
intersectTag: 'seg02'
|
||||||
}, %)
|
}, %)
|
||||||
|> angledLineToY([segAng('seg02', %) + 180, -baseHeight], %)
|
|> angledLineToY([segAng('seg02', %) + 180, -baseHeight], %)
|
||||||
|> xLineTo(_0, %)
|
|> xLineTo(ZERO, %)
|
||||||
|> close(%)
|
|> close(%)
|
||||||
|> extrude(4, %)`
|
|> extrude(4, %)`
|
||||||
)
|
)
|
||||||
@ -191,7 +168,6 @@ const part001 = startSketchOn('-XZ')
|
|||||||
await page.goto('/')
|
await page.goto('/')
|
||||||
await u.waitForAuthSkipAppStart()
|
await u.waitForAuthSkipAppStart()
|
||||||
await u.openDebugPanel()
|
await u.openDebugPanel()
|
||||||
await u.waitForDefaultPlanesVisibilityChange()
|
|
||||||
await u.expectCmdLog('[data-message-type="execution-done"]')
|
await u.expectCmdLog('[data-message-type="execution-done"]')
|
||||||
await u.waitForCmdReceive('extrude')
|
await u.waitForCmdReceive('extrude')
|
||||||
await page.waitForTimeout(1000)
|
await page.waitForTimeout(1000)
|
||||||
@ -386,3 +362,118 @@ const part001 = startSketchOn('-XZ')
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('extrude on each default plane should be stable', async ({
|
||||||
|
page,
|
||||||
|
context,
|
||||||
|
}) => {
|
||||||
|
const u = getUtils(page)
|
||||||
|
const makeCode = (plane = 'XY') => `const part001 = startSketchOn('${plane}')
|
||||||
|
|> startProfileAt([0.70, 0.44], %)
|
||||||
|
|> line([0.66, -0.02], %)
|
||||||
|
|> line([0.28, 0.50], %)
|
||||||
|
|> line([-0.56, 0.44], %)
|
||||||
|
|> line([-0.54, -0.38], %)
|
||||||
|
|> close(%)
|
||||||
|
|> extrude(1.00, %)
|
||||||
|
`
|
||||||
|
await context.addInitScript(async (code) => {
|
||||||
|
localStorage.setItem('persistCode', code)
|
||||||
|
}, makeCode('XY'))
|
||||||
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
|
await page.goto('/')
|
||||||
|
await u.waitForAuthSkipAppStart()
|
||||||
|
|
||||||
|
// wait for execution done
|
||||||
|
await u.openDebugPanel()
|
||||||
|
await u.expectCmdLog('[data-message-type="execution-done"]')
|
||||||
|
await u.clearAndCloseDebugPanel()
|
||||||
|
|
||||||
|
await page.getByText('Code').click()
|
||||||
|
await expect(page).toHaveScreenshot({
|
||||||
|
maxDiffPixels: 100,
|
||||||
|
})
|
||||||
|
await page.getByText('Code').click()
|
||||||
|
|
||||||
|
const runSnapshotsForOtherPlanes = async (plane = 'XY') => {
|
||||||
|
// clear code
|
||||||
|
await u.removeCurrentCode()
|
||||||
|
// add makeCode('XZ')
|
||||||
|
await page.locator('.cm-content').fill(makeCode(plane))
|
||||||
|
// wait for execution done
|
||||||
|
await u.openDebugPanel()
|
||||||
|
await u.expectCmdLog('[data-message-type="execution-done"]')
|
||||||
|
await u.clearAndCloseDebugPanel()
|
||||||
|
|
||||||
|
await page.getByText('Code').click()
|
||||||
|
await expect(page).toHaveScreenshot({
|
||||||
|
maxDiffPixels: 100,
|
||||||
|
})
|
||||||
|
await page.getByText('Code').click()
|
||||||
|
}
|
||||||
|
await runSnapshotsForOtherPlanes('-XY')
|
||||||
|
|
||||||
|
await runSnapshotsForOtherPlanes('XZ')
|
||||||
|
await runSnapshotsForOtherPlanes('-XZ')
|
||||||
|
|
||||||
|
await runSnapshotsForOtherPlanes('YZ')
|
||||||
|
await runSnapshotsForOtherPlanes('-YZ')
|
||||||
|
})
|
||||||
|
|
||||||
|
test('Draft segments should look right', async ({ page }) => {
|
||||||
|
const u = getUtils(page)
|
||||||
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
|
const PUR = 400 / 37.5 //pixeltoUnitRatio
|
||||||
|
await page.goto('/')
|
||||||
|
await u.waitForAuthSkipAppStart()
|
||||||
|
await u.openDebugPanel()
|
||||||
|
|
||||||
|
await expect(page.getByRole('button', { name: 'Start Sketch' })).toBeVisible()
|
||||||
|
|
||||||
|
// click on "Start Sketch" button
|
||||||
|
await u.clearCommandLogs()
|
||||||
|
await u.doAndWaitForImageDiff(
|
||||||
|
() => page.getByRole('button', { name: 'Start Sketch' }).click(),
|
||||||
|
200
|
||||||
|
)
|
||||||
|
|
||||||
|
// select a plane
|
||||||
|
await page.mouse.click(700, 200)
|
||||||
|
|
||||||
|
await expect(page.locator('.cm-content')).toHaveText(
|
||||||
|
`const part001 = startSketchOn('-XZ')`
|
||||||
|
)
|
||||||
|
|
||||||
|
await page.waitForTimeout(300) // TODO detect animation ending, or disable animation
|
||||||
|
|
||||||
|
const startXPx = 600
|
||||||
|
await page.mouse.click(startXPx + PUR * 10, 500 - PUR * 10)
|
||||||
|
const startAt = '[23.74, -32.03]'
|
||||||
|
await expect(page.locator('.cm-content'))
|
||||||
|
.toHaveText(`const part001 = startSketchOn('-XZ')
|
||||||
|
|> startProfileAt(${startAt}, %)`)
|
||||||
|
await page.waitForTimeout(100)
|
||||||
|
|
||||||
|
await u.closeDebugPanel()
|
||||||
|
await page.mouse.move(startXPx + PUR * 20, 500 - PUR * 10)
|
||||||
|
await expect(page).toHaveScreenshot({
|
||||||
|
maxDiffPixels: 100,
|
||||||
|
})
|
||||||
|
|
||||||
|
await page.mouse.click(startXPx + PUR * 20, 500 - PUR * 10)
|
||||||
|
await page.waitForTimeout(100)
|
||||||
|
|
||||||
|
const num = 23.97
|
||||||
|
await expect(page.locator('.cm-content'))
|
||||||
|
.toHaveText(`const part001 = startSketchOn('-XZ')
|
||||||
|
|> startProfileAt(${startAt}, %)
|
||||||
|
|> line([${num}, 0], %)`)
|
||||||
|
|
||||||
|
await page.getByRole('button', { name: 'Tangential Arc' }).click()
|
||||||
|
|
||||||
|
await page.mouse.move(startXPx + PUR * 30, 500 - PUR * 20, { steps: 10 })
|
||||||
|
|
||||||
|
await expect(page).toHaveScreenshot({
|
||||||
|
maxDiffPixels: 100,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
After Width: | Height: | Size: 41 KiB |
After Width: | Height: | Size: 44 KiB |
Before Width: | Height: | Size: 112 KiB After Width: | Height: | Size: 95 KiB |
Before Width: | Height: | Size: 79 KiB After Width: | Height: | Size: 79 KiB |
Before Width: | Height: | Size: 54 KiB After Width: | Height: | Size: 111 KiB |
After Width: | Height: | Size: 53 KiB |
After Width: | Height: | Size: 52 KiB |
After Width: | Height: | Size: 53 KiB |
After Width: | Height: | Size: 56 KiB |
After Width: | Height: | Size: 50 KiB |
After Width: | Height: | Size: 52 KiB |
@ -80,10 +80,21 @@ export function getUtils(page: Page) {
|
|||||||
waitForAuthSkipAppStart: () => waitForPageLoad(page),
|
waitForAuthSkipAppStart: () => waitForPageLoad(page),
|
||||||
removeCurrentCode: () => removeCurrentCode(page),
|
removeCurrentCode: () => removeCurrentCode(page),
|
||||||
sendCustomCmd: (cmd: EngineCommand) => sendCustomCmd(page, cmd),
|
sendCustomCmd: (cmd: EngineCommand) => sendCustomCmd(page, cmd),
|
||||||
|
updateCamPosition: async (xyz: [number, number, number]) => {
|
||||||
|
const fillInput = async () => {
|
||||||
|
await page.fill('[data-testid="cam-x-position"]', String(xyz[0]))
|
||||||
|
await page.fill('[data-testid="cam-y-position"]', String(xyz[1]))
|
||||||
|
await page.fill('[data-testid="cam-z-position"]', String(xyz[2]))
|
||||||
|
}
|
||||||
|
await fillInput()
|
||||||
|
await page.waitForTimeout(100)
|
||||||
|
await fillInput()
|
||||||
|
await page.waitForTimeout(100)
|
||||||
|
await fillInput()
|
||||||
|
await page.waitForTimeout(100)
|
||||||
|
},
|
||||||
clearCommandLogs: () => clearCommandLogs(page),
|
clearCommandLogs: () => clearCommandLogs(page),
|
||||||
expectCmdLog: (locatorStr: string) => expectCmdLog(page, locatorStr),
|
expectCmdLog: (locatorStr: string) => expectCmdLog(page, locatorStr),
|
||||||
waitForDefaultPlanesVisibilityChange: () =>
|
|
||||||
waitForDefaultPlanesToBeVisible(page),
|
|
||||||
openDebugPanel: () => openDebugPanel(page),
|
openDebugPanel: () => openDebugPanel(page),
|
||||||
closeDebugPanel: () => closeDebugPanel(page),
|
closeDebugPanel: () => closeDebugPanel(page),
|
||||||
openAndClearDebugPanel: async () => {
|
openAndClearDebugPanel: async () => {
|
||||||
|
@ -86,13 +86,11 @@ describe('ZMA (Tauri, Linux)', () => {
|
|||||||
expect(await homeSection.getText()).toContain('project-000')
|
expect(await homeSection.getText()).toContain('project-000')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('opens the new file and expects an error on Linux', async () => {
|
it('opens the new file and expects a loading stream', async () => {
|
||||||
const projectLink = await $('[data-testid="project-link"]')
|
const projectLink = await $('[data-testid="project-link"]')
|
||||||
await click(projectLink)
|
await click(projectLink)
|
||||||
const error = await $('h3')
|
const loadingText = await $('[data-testid="loading-stream"]')
|
||||||
expect(await error.getText()).toContain(
|
expect(await loadingText.getText()).toContain('Loading stream...')
|
||||||
"Can't find variable: RTCPeerConnection"
|
|
||||||
)
|
|
||||||
await browser.execute('window.location.href = "tauri://localhost/home"')
|
await browser.execute('window.location.href = "tauri://localhost/home"')
|
||||||
})
|
})
|
||||||
|
|
||||||
|
69
make-release.sh
Executable file
@ -0,0 +1,69 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
if ! git diff-index --quiet HEAD --; then
|
||||||
|
echo "Please stash uncommitted changes before running release script"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
git checkout main
|
||||||
|
git pull
|
||||||
|
git fetch --all
|
||||||
|
|
||||||
|
# Get the latest semver tag from git
|
||||||
|
latest_tag=$(jq -r '.version' package.json)
|
||||||
|
latest_tag="v$latest_tag"
|
||||||
|
|
||||||
|
# Print the latest semver tag
|
||||||
|
echo "Latest semver tag: $latest_tag"
|
||||||
|
|
||||||
|
# Function to bump version numbers
|
||||||
|
bump_version() {
|
||||||
|
local version=$1
|
||||||
|
local bump_type=$2
|
||||||
|
local major=$(echo $version | cut -d '.' -f 1 | sed 's/v//')
|
||||||
|
local minor=$(echo $version | cut -d '.' -f 2)
|
||||||
|
local patch=$(echo $version | cut -d '.' -f 3)
|
||||||
|
|
||||||
|
case "$bump_type" in
|
||||||
|
major)
|
||||||
|
major=$((major + 1))
|
||||||
|
minor=0
|
||||||
|
patch=0
|
||||||
|
;;
|
||||||
|
minor)
|
||||||
|
minor=$((minor + 1))
|
||||||
|
patch=0
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
patch=$((patch + 1))
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
echo "v${major}.${minor}.${patch}"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Determine the type of bump based on the argument
|
||||||
|
bump_type=${1:-patch}
|
||||||
|
|
||||||
|
# Bump the version
|
||||||
|
new_version=$(bump_version $latest_tag $bump_type)
|
||||||
|
|
||||||
|
# Print the new semver tag
|
||||||
|
echo "New semver tag: $new_version"
|
||||||
|
new_version_number=${new_version:1}
|
||||||
|
echo "New version number without 'v': $new_version_number"
|
||||||
|
|
||||||
|
|
||||||
|
git checkout -b "cut-release-$new_version"
|
||||||
|
|
||||||
|
echo "$(jq --arg v "$new_version_number" '.version=$v' package.json --indent 2)" > package.json
|
||||||
|
echo "$(jq --arg v "$new_version_number" '.package.version=$v' src-tauri/tauri.conf.json --indent 2)" > src-tauri/tauri.conf.json
|
||||||
|
|
||||||
|
git add package.json src-tauri/tauri.conf.json
|
||||||
|
git commit -m "Cut release $new_version"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "Versions has been bumped in relevant json files, a branch has been created and committed to."
|
||||||
|
echo ""
|
||||||
|
echo "What's left for you to do is, push the branch and make the release PR."
|
||||||
|
echo ""
|
28
package.json
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "untitled-app",
|
"name": "untitled-app",
|
||||||
"version": "0.14.0",
|
"version": "0.15.2",
|
||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@codemirror/autocomplete": "^6.10.2",
|
"@codemirror/autocomplete": "^6.10.2",
|
||||||
@ -10,7 +10,7 @@
|
|||||||
"@fortawesome/react-fontawesome": "^0.2.0",
|
"@fortawesome/react-fontawesome": "^0.2.0",
|
||||||
"@headlessui/react": "^1.7.17",
|
"@headlessui/react": "^1.7.17",
|
||||||
"@headlessui/tailwindcss": "^0.2.0",
|
"@headlessui/tailwindcss": "^0.2.0",
|
||||||
"@kittycad/lib": "^0.0.46",
|
"@kittycad/lib": "^0.0.53",
|
||||||
"@lezer/javascript": "^1.4.9",
|
"@lezer/javascript": "^1.4.9",
|
||||||
"@open-rpc/client-js": "^1.8.1",
|
"@open-rpc/client-js": "^1.8.1",
|
||||||
"@react-hook/resize-observer": "^1.2.6",
|
"@react-hook/resize-observer": "^1.2.6",
|
||||||
@ -21,6 +21,7 @@
|
|||||||
"@testing-library/react": "^14.0.0",
|
"@testing-library/react": "^14.0.0",
|
||||||
"@testing-library/user-event": "^14.5.1",
|
"@testing-library/user-event": "^14.5.1",
|
||||||
"@ts-stack/markdown": "^1.5.0",
|
"@ts-stack/markdown": "^1.5.0",
|
||||||
|
"@tweenjs/tween.js": "^23.1.1",
|
||||||
"@types/node": "^16.7.13",
|
"@types/node": "^16.7.13",
|
||||||
"@types/react": "^18.2.41",
|
"@types/react": "^18.2.41",
|
||||||
"@types/react-dom": "^18.0.0",
|
"@types/react-dom": "^18.0.0",
|
||||||
@ -33,6 +34,7 @@
|
|||||||
"fuse.js": "^7.0.0",
|
"fuse.js": "^7.0.0",
|
||||||
"http-server": "^14.1.1",
|
"http-server": "^14.1.1",
|
||||||
"json-rpc-2.0": "^1.6.0",
|
"json-rpc-2.0": "^1.6.0",
|
||||||
|
"node-fetch": "^3.3.2",
|
||||||
"re-resizable": "^6.9.11",
|
"re-resizable": "^6.9.11",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
@ -45,11 +47,12 @@
|
|||||||
"sketch-helpers": "^0.0.4",
|
"sketch-helpers": "^0.0.4",
|
||||||
"swr": "^2.2.2",
|
"swr": "^2.2.2",
|
||||||
"tauri-plugin-fs-extra-api": "https://github.com/tauri-apps/tauri-plugin-fs-extra#v1",
|
"tauri-plugin-fs-extra-api": "https://github.com/tauri-apps/tauri-plugin-fs-extra#v1",
|
||||||
|
"three": "^0.160.0",
|
||||||
"toml": "^3.0.0",
|
"toml": "^3.0.0",
|
||||||
"ts-node": "^10.9.1",
|
"ts-node": "^10.9.1",
|
||||||
"typescript": "^5.2.2",
|
"typescript": "^5.2.2",
|
||||||
"uuid": "^9.0.1",
|
"uuid": "^9.0.1",
|
||||||
"vitest": "^0.34.6",
|
"vitest": "^1.3.1",
|
||||||
"vscode-jsonrpc": "^8.1.0",
|
"vscode-jsonrpc": "^8.1.0",
|
||||||
"vscode-languageserver-protocol": "^3.17.5",
|
"vscode-languageserver-protocol": "^3.17.5",
|
||||||
"wasm-pack": "^0.12.1",
|
"wasm-pack": "^0.12.1",
|
||||||
@ -70,7 +73,6 @@
|
|||||||
"test": "vitest --mode development",
|
"test": "vitest --mode development",
|
||||||
"test:nowatch": "vitest run --mode development",
|
"test:nowatch": "vitest run --mode development",
|
||||||
"test:rust": "(cd src/wasm-lib && cargo test --all && cargo clippy --all --tests --benches)",
|
"test:rust": "(cd src/wasm-lib && cargo test --all && cargo clippy --all --tests --benches)",
|
||||||
"test:cov": "vitest run --coverage --mode development",
|
|
||||||
"test:e2e:tauri": "E2E_TAURI_ENABLED=true TS_NODE_COMPILER_OPTIONS='{\"module\": \"commonjs\"}' wdio run wdio.conf.ts",
|
"test:e2e:tauri": "E2E_TAURI_ENABLED=true TS_NODE_COMPILER_OPTIONS='{\"module\": \"commonjs\"}' wdio run wdio.conf.ts",
|
||||||
"simpleserver:ci": "yarn pretest && http-server ./public --cors -p 3000 &",
|
"simpleserver:ci": "yarn pretest && http-server ./public --cors -p 3000 &",
|
||||||
"simpleserver": "yarn pretest && http-server ./public --cors -p 3000",
|
"simpleserver": "yarn pretest && http-server ./public --cors -p 3000",
|
||||||
@ -82,7 +84,9 @@
|
|||||||
"remove-importmeta": "sed -i 's/import.meta.url/window.location.origin/g' \"./src/wasm-lib/pkg/wasm_lib.js\"; sed -i '' 's/import.meta.url/window.location.origin/g' \"./src/wasm-lib/pkg/wasm_lib.js\" || echo \"sed for both mac and linux\"",
|
"remove-importmeta": "sed -i 's/import.meta.url/window.location.origin/g' \"./src/wasm-lib/pkg/wasm_lib.js\"; sed -i '' 's/import.meta.url/window.location.origin/g' \"./src/wasm-lib/pkg/wasm_lib.js\" || echo \"sed for both mac and linux\"",
|
||||||
"wasm-prep": "rm -rf src/wasm-lib/pkg && mkdir src/wasm-lib/pkg && rm -rf src/wasm-lib/kcl/bindings",
|
"wasm-prep": "rm -rf src/wasm-lib/pkg && mkdir src/wasm-lib/pkg && rm -rf src/wasm-lib/kcl/bindings",
|
||||||
"lint": "eslint --fix src",
|
"lint": "eslint --fix src",
|
||||||
"bump-jsons": "echo \"$(jq --arg v \"$VERSION\" '.version=$v' package.json --indent 2)\" > package.json && echo \"$(jq --arg v \"$VERSION\" '.package.version=$v' src-tauri/tauri.conf.json --indent 2)\" > src-tauri/tauri.conf.json"
|
"bump-jsons": "echo \"$(jq --arg v \"$VERSION\" '.version=$v' package.json --indent 2)\" > package.json && echo \"$(jq --arg v \"$VERSION\" '.package.version=$v' src-tauri/tauri.conf.json --indent 2)\" > src-tauri/tauri.conf.json",
|
||||||
|
"postinstall": "patch-package && yarn xstate:typegen",
|
||||||
|
"xstate:typegen": "yarn xstate typegen \"src/**/*.ts?(x)\""
|
||||||
},
|
},
|
||||||
"prettier": {
|
"prettier": {
|
||||||
"trailingComma": "es5",
|
"trailingComma": "es5",
|
||||||
@ -109,36 +113,40 @@
|
|||||||
"@tauri-apps/cli": "^1.5.6",
|
"@tauri-apps/cli": "^1.5.6",
|
||||||
"@types/crypto-js": "^4.1.1",
|
"@types/crypto-js": "^4.1.1",
|
||||||
"@types/debounce-promise": "^3.1.8",
|
"@types/debounce-promise": "^3.1.8",
|
||||||
"@types/isomorphic-fetch": "^0.0.36",
|
|
||||||
"@types/pixelmatch": "^5.2.6",
|
"@types/pixelmatch": "^5.2.6",
|
||||||
"@types/pngjs": "^6.0.4",
|
"@types/pngjs": "^6.0.4",
|
||||||
"@types/react-modal": "^3.16.3",
|
"@types/react-modal": "^3.16.3",
|
||||||
|
"@types/three": "^0.160.0",
|
||||||
"@types/uuid": "^9.0.4",
|
"@types/uuid": "^9.0.4",
|
||||||
"@types/wait-on": "^5.3.4",
|
"@types/wait-on": "^5.3.4",
|
||||||
"@types/wicg-file-system-access": "^2020.9.6",
|
"@types/wicg-file-system-access": "^2020.9.6",
|
||||||
"@types/ws": "^8.5.5",
|
"@types/ws": "^8.5.5",
|
||||||
"@vitejs/plugin-react": "^4.1.1",
|
"@vitejs/plugin-react": "^4.2.1",
|
||||||
"@vitest/coverage-istanbul": "^0.34.6",
|
|
||||||
"@wdio/cli": "^8.24.3",
|
"@wdio/cli": "^8.24.3",
|
||||||
"@wdio/globals": "^8.24.3",
|
"@wdio/globals": "^8.24.3",
|
||||||
"@wdio/local-runner": "^8.24.3",
|
"@wdio/local-runner": "^8.24.3",
|
||||||
"@wdio/mocha-framework": "^8.24.3",
|
"@wdio/mocha-framework": "^8.24.3",
|
||||||
"@wdio/spec-reporter": "^8.24.2",
|
"@wdio/spec-reporter": "^8.24.2",
|
||||||
|
"@xstate/cli": "^0.5.17",
|
||||||
"autoprefixer": "^10.4.13",
|
"autoprefixer": "^10.4.13",
|
||||||
"eslint": "^8.53.0",
|
"eslint": "^8.53.0",
|
||||||
"eslint-config-react-app": "^7.0.1",
|
"eslint-config-react-app": "^7.0.1",
|
||||||
"eslint-plugin-css-modules": "^2.12.0",
|
"eslint-plugin-css-modules": "^2.12.0",
|
||||||
"happy-dom": "^10.8.0",
|
"happy-dom": "^10.8.0",
|
||||||
"husky": "^8.0.3",
|
"husky": "^8.0.3",
|
||||||
|
"patch-package": "^8.0.0",
|
||||||
"pixelmatch": "^5.3.0",
|
"pixelmatch": "^5.3.0",
|
||||||
"pngjs": "^7.0.0",
|
"pngjs": "^7.0.0",
|
||||||
"postcss": "^8.4.31",
|
"postcss": "^8.4.31",
|
||||||
|
"postinstall-postinstall": "^2.1.0",
|
||||||
"prettier": "^2.8.0",
|
"prettier": "^2.8.0",
|
||||||
"setimmediate": "^1.0.5",
|
"setimmediate": "^1.0.5",
|
||||||
"tailwindcss": "^3.3.6",
|
"tailwindcss": "^3.3.6",
|
||||||
"vite": "^4.5.2",
|
"vite": "^5.1.3",
|
||||||
"vite-plugin-eslint": "^1.8.1",
|
"vite-plugin-eslint": "^1.8.1",
|
||||||
"vite-tsconfig-paths": "^4.2.1",
|
"vite-plugin-package-version": "^1.1.0",
|
||||||
|
"vite-tsconfig-paths": "^4.3.1",
|
||||||
|
"vitest-webgl-canvas-mock": "^1.1.0",
|
||||||
"wait-on": "^7.2.0",
|
"wait-on": "^7.2.0",
|
||||||
"yarn": "^1.22.19"
|
"yarn": "^1.22.19"
|
||||||
}
|
}
|
||||||
|
138
patches/three+0.160.0.patch
Normal file
@ -0,0 +1,138 @@
|
|||||||
|
diff --git a/node_modules/three/examples/jsm/controls/OrbitControls.js b/node_modules/three/examples/jsm/controls/OrbitControls.js
|
||||||
|
index f29e7fe..0ef636b 100644
|
||||||
|
--- a/node_modules/three/examples/jsm/controls/OrbitControls.js
|
||||||
|
+++ b/node_modules/three/examples/jsm/controls/OrbitControls.js
|
||||||
|
@@ -113,6 +113,25 @@ class OrbitControls extends EventDispatcher {
|
||||||
|
// public methods
|
||||||
|
//
|
||||||
|
|
||||||
|
+ this.interactionGuards = {
|
||||||
|
+ pan: {
|
||||||
|
+ description: 'Right click + Shift + drag or middle click + drag',
|
||||||
|
+ callback: (e) => e.button === 2 && !e.ctrlKey,
|
||||||
|
+ },
|
||||||
|
+ zoom: {
|
||||||
|
+ description: 'Scroll wheel or Right click + Ctrl + drag',
|
||||||
|
+ dragCallback: (e) => e.button === 2 && e.ctrlKey,
|
||||||
|
+ scrollCallback: () => true,
|
||||||
|
+ },
|
||||||
|
+ rotate: {
|
||||||
|
+ description: 'Right click + drag',
|
||||||
|
+ callback: (e) => e.button === 0,
|
||||||
|
+ },
|
||||||
|
+ }
|
||||||
|
+ this.setMouseGuards = (interactionGuards) => {
|
||||||
|
+ this.interactionGuards = interactionGuards
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
this.getPolarAngle = function () {
|
||||||
|
|
||||||
|
return spherical.phi;
|
||||||
|
@@ -1057,92 +1076,21 @@ class OrbitControls extends EventDispatcher {
|
||||||
|
|
||||||
|
function onMouseDown( event ) {
|
||||||
|
|
||||||
|
- let mouseAction;
|
||||||
|
-
|
||||||
|
- switch ( event.button ) {
|
||||||
|
-
|
||||||
|
- case 0:
|
||||||
|
-
|
||||||
|
- mouseAction = scope.mouseButtons.LEFT;
|
||||||
|
- break;
|
||||||
|
-
|
||||||
|
- case 1:
|
||||||
|
-
|
||||||
|
- mouseAction = scope.mouseButtons.MIDDLE;
|
||||||
|
- break;
|
||||||
|
-
|
||||||
|
- case 2:
|
||||||
|
-
|
||||||
|
- mouseAction = scope.mouseButtons.RIGHT;
|
||||||
|
- break;
|
||||||
|
-
|
||||||
|
- default:
|
||||||
|
-
|
||||||
|
- mouseAction = - 1;
|
||||||
|
-
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- switch ( mouseAction ) {
|
||||||
|
-
|
||||||
|
- case MOUSE.DOLLY:
|
||||||
|
-
|
||||||
|
- if ( scope.enableZoom === false ) return;
|
||||||
|
-
|
||||||
|
- handleMouseDownDolly( event );
|
||||||
|
-
|
||||||
|
- state = STATE.DOLLY;
|
||||||
|
-
|
||||||
|
- break;
|
||||||
|
-
|
||||||
|
- case MOUSE.ROTATE:
|
||||||
|
-
|
||||||
|
- if ( event.ctrlKey || event.metaKey || event.shiftKey ) {
|
||||||
|
-
|
||||||
|
- if ( scope.enablePan === false ) return;
|
||||||
|
-
|
||||||
|
- handleMouseDownPan( event );
|
||||||
|
-
|
||||||
|
- state = STATE.PAN;
|
||||||
|
-
|
||||||
|
- } else {
|
||||||
|
-
|
||||||
|
- if ( scope.enableRotate === false ) return;
|
||||||
|
-
|
||||||
|
- handleMouseDownRotate( event );
|
||||||
|
-
|
||||||
|
- state = STATE.ROTATE;
|
||||||
|
-
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- break;
|
||||||
|
-
|
||||||
|
- case MOUSE.PAN:
|
||||||
|
-
|
||||||
|
- if ( event.ctrlKey || event.metaKey || event.shiftKey ) {
|
||||||
|
-
|
||||||
|
- if ( scope.enableRotate === false ) return;
|
||||||
|
-
|
||||||
|
- handleMouseDownRotate( event );
|
||||||
|
-
|
||||||
|
- state = STATE.ROTATE;
|
||||||
|
-
|
||||||
|
- } else {
|
||||||
|
-
|
||||||
|
- if ( scope.enablePan === false ) return;
|
||||||
|
-
|
||||||
|
- handleMouseDownPan( event );
|
||||||
|
-
|
||||||
|
- state = STATE.PAN;
|
||||||
|
-
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- break;
|
||||||
|
-
|
||||||
|
- default:
|
||||||
|
-
|
||||||
|
- state = STATE.NONE;
|
||||||
|
-
|
||||||
|
- }
|
||||||
|
+ if (scope.interactionGuards.pan.callback(event)) {
|
||||||
|
+ if (scope.enablePan === false) return
|
||||||
|
+ handleMouseDownPan(event)
|
||||||
|
+ state = STATE.PAN
|
||||||
|
+ } else if (scope.interactionGuards.rotate.callback(event)) {
|
||||||
|
+ if (scope.enableRotate === false) return
|
||||||
|
+ handleMouseDownRotate(event)
|
||||||
|
+ state = STATE.ROTATE
|
||||||
|
+ } else if (scope.interactionGuards.zoom.dragCallback(event)) {
|
||||||
|
+ if (scope.enableZoom === false) return
|
||||||
|
+ handleMouseDownDolly(event)
|
||||||
|
+ state = STATE.DOLLY
|
||||||
|
+ } else {
|
||||||
|
+ return
|
||||||
|
+ }
|
||||||
|
|
||||||
|
if ( state !== STATE.NONE ) {
|
||||||
|
|
@ -1,4 +1,4 @@
|
|||||||
import { defineConfig, devices } from '@playwright/test';
|
import { defineConfig, devices } from '@playwright/test'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Read environment variables from file.
|
* Read environment variables from file.
|
||||||
@ -78,5 +78,4 @@ export default defineConfig({
|
|||||||
// url: 'http://127.0.0.1:3000',
|
// url: 'http://127.0.0.1:3000',
|
||||||
reuseExistingServer: !process.env.CI,
|
reuseExistingServer: !process.env.CI,
|
||||||
},
|
},
|
||||||
});
|
})
|
||||||
|
|
26
public/announce_release.py
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
import requests
|
||||||
|
import os
|
||||||
|
|
||||||
|
webhook_url = os.getenv('DISCORD_WEBHOOK_URL')
|
||||||
|
release_version = os.getenv('RELEASE_VERSION')
|
||||||
|
release_body = os.getenv('RELEASE_BODY')
|
||||||
|
|
||||||
|
# message to send to Discord
|
||||||
|
data = {
|
||||||
|
"content":
|
||||||
|
f'''
|
||||||
|
**{release_version}** is now available! Check out the latest features and improvements here: https://zoo.dev/modeling-app/download
|
||||||
|
{release_body}
|
||||||
|
''',
|
||||||
|
"username": "Modeling App Release Updates",
|
||||||
|
"avatar_url": "https://raw.githubusercontent.com/KittyCAD/modeling-app/main/public/discord-avatar.png"
|
||||||
|
}
|
||||||
|
|
||||||
|
# POST request to the Discord webhook
|
||||||
|
response = requests.post(webhook_url, json=data)
|
||||||
|
|
||||||
|
# Check for success
|
||||||
|
if response.status_code == 204:
|
||||||
|
print("Successfully sent the message to Discord.")
|
||||||
|
else:
|
||||||
|
print("Failed to send the message to Discord.")
|
BIN
public/discord-avatar.png
Normal file
After Width: | Height: | Size: 11 KiB |
104
src-tauri/Cargo.lock
generated
@ -67,9 +67,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "anyhow"
|
name = "anyhow"
|
||||||
version = "1.0.75"
|
version = "1.0.79"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6"
|
checksum = "080e9890a082662b09c1ad45f567faeeb47f22b5fb23895fbe1e651e718e25ca"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "app"
|
name = "app"
|
||||||
@ -95,7 +95,7 @@ checksum = "bc00ceb34980c03614e35a3a4e218276a0a824e911d07651cd0d858a51e8c0f0"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.33",
|
"syn 2.0.48",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -542,7 +542,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "13b588ba4ac1a99f7f2964d24b3d896ddc6bf847ee3855dbd4366f058cfcd331"
|
checksum = "13b588ba4ac1a99f7f2964d24b3d896ddc6bf847ee3855dbd4366f058cfcd331"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.33",
|
"syn 2.0.48",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -552,7 +552,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "30d2b3721e861707777e3195b0158f950ae6dc4a27e4d02ff9f67e3eb3de199e"
|
checksum = "30d2b3721e861707777e3195b0158f950ae6dc4a27e4d02ff9f67e3eb3de199e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.33",
|
"syn 2.0.48",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -582,7 +582,7 @@ dependencies = [
|
|||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"strsim",
|
"strsim",
|
||||||
"syn 2.0.33",
|
"syn 2.0.48",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -593,7 +593,7 @@ checksum = "29a358ff9f12ec09c3e61fef9b5a9902623a695a46a917b07f269bff1445611a"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"darling_core",
|
"darling_core",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.33",
|
"syn 2.0.48",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -899,7 +899,7 @@ checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.33",
|
"syn 2.0.48",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -1664,9 +1664,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "kittycad"
|
name = "kittycad"
|
||||||
version = "0.2.42"
|
version = "0.2.53"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6aa554d86b6dbbd976a659c912ae25ce817b4378eb12a5684907e263410f0a7b"
|
checksum = "a086e1a1bbddb3b38959c0f0ce6de6b3a3b7566e38e0b7d5fb101e91911beed4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
@ -2215,7 +2215,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.33",
|
"syn 2.0.48",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -2368,7 +2368,7 @@ dependencies = [
|
|||||||
"regex",
|
"regex",
|
||||||
"regex-syntax 0.7.5",
|
"regex-syntax 0.7.5",
|
||||||
"structmeta",
|
"structmeta",
|
||||||
"syn 2.0.33",
|
"syn 2.0.48",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -2487,7 +2487,7 @@ dependencies = [
|
|||||||
"phf_shared 0.11.2",
|
"phf_shared 0.11.2",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.33",
|
"syn 2.0.48",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -2555,7 +2555,7 @@ checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.33",
|
"syn 2.0.48",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -2657,9 +2657,9 @@ checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro2"
|
name = "proc-macro2"
|
||||||
version = "1.0.67"
|
version = "1.0.78"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3d433d9f1a3e8c1263d9456598b16fec66f4acc9a74dacffd35c7bb09b3a1328"
|
checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
@ -2675,9 +2675,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "quote"
|
name = "quote"
|
||||||
version = "1.0.33"
|
version = "1.0.35"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae"
|
checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
]
|
]
|
||||||
@ -3235,9 +3235,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde"
|
name = "serde"
|
||||||
version = "1.0.193"
|
version = "1.0.196"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "25dd9975e68d0cb5aa1120c288333fc98731bd1dd12f561e468ea4728c042b89"
|
checksum = "870026e60fa08c69f064aa766c10f10b1d62db9ccd4d0abb206472bee0ce3b32"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde_derive",
|
"serde_derive",
|
||||||
]
|
]
|
||||||
@ -3253,13 +3253,13 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_derive"
|
name = "serde_derive"
|
||||||
version = "1.0.193"
|
version = "1.0.196"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3"
|
checksum = "33c85360c95e7d137454dc81d9a4ed2b8efd8fbe19cee57357b32b9771fccb67"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.33",
|
"syn 2.0.48",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -3275,9 +3275,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_json"
|
name = "serde_json"
|
||||||
version = "1.0.108"
|
version = "1.0.113"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b"
|
checksum = "69801b70b1c3dac963ecb03a364ba0ceda9cf60c71cfe475e99864759c8b8a79"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"itoa 1.0.6",
|
"itoa 1.0.6",
|
||||||
"ryu",
|
"ryu",
|
||||||
@ -3302,7 +3302,7 @@ checksum = "bcec881020c684085e55a25f7fd888954d56609ef363479dc5a1305eb0d40cab"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.33",
|
"syn 2.0.48",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -3352,7 +3352,7 @@ dependencies = [
|
|||||||
"darling",
|
"darling",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.33",
|
"syn 2.0.48",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -3556,7 +3556,7 @@ dependencies = [
|
|||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"structmeta-derive",
|
"structmeta-derive",
|
||||||
"syn 2.0.33",
|
"syn 2.0.48",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -3567,7 +3567,7 @@ checksum = "a60bcaff7397072dca0017d1db428e30d5002e00b6847703e2e42005c95fbe00"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.33",
|
"syn 2.0.48",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -3605,9 +3605,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "2.0.33"
|
version = "2.0.48"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9caece70c63bfba29ec2fed841a09851b14a235c60010fa4de58089b6c025668"
|
checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@ -3760,9 +3760,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tauri"
|
name = "tauri"
|
||||||
version = "1.5.3"
|
version = "1.5.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "32d563b672acde8d0cc4c1b1f5b855976923f67e8d6fe1eba51df0211e197be2"
|
checksum = "fd27c04b9543776a972c86ccf70660b517ecabbeced9fb58d8b961a13ad129af"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"bytes",
|
"bytes",
|
||||||
@ -3812,9 +3812,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tauri-build"
|
name = "tauri-build"
|
||||||
version = "1.5.0"
|
version = "1.5.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "defbfc551bd38ab997e5f8e458f87396d2559d05ce32095076ad6c30f7fc5f9c"
|
checksum = "e9914a4715e0b75d9f387a285c7e26b5bbfeb1249ad9f842675a82481565c532"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"cargo_toml",
|
"cargo_toml",
|
||||||
@ -3831,9 +3831,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tauri-codegen"
|
name = "tauri-codegen"
|
||||||
version = "1.4.1"
|
version = "1.4.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7b3475e55acec0b4a50fb96435f19631fb58cbcd31923e1a213de5c382536bbb"
|
checksum = "a1554c5857f65dbc377cefb6b97c8ac77b1cb2a90d30d3448114d5d6b48a77fc"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"base64 0.21.2",
|
"base64 0.21.2",
|
||||||
"brotli",
|
"brotli",
|
||||||
@ -3857,9 +3857,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tauri-macros"
|
name = "tauri-macros"
|
||||||
version = "1.4.2"
|
version = "1.4.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "acea6445eececebd72ed7720cfcca46eee3b5bad8eb408be8f7ef2e3f7411500"
|
checksum = "277abf361a3a6993ec16bcbb179de0d6518009b851090a01adfea12ac89fa875"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"heck 0.4.1",
|
"heck 0.4.1",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
@ -3872,7 +3872,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "tauri-plugin-fs-extra"
|
name = "tauri-plugin-fs-extra"
|
||||||
version = "0.0.0"
|
version = "0.0.0"
|
||||||
source = "git+https://github.com/tauri-apps/plugins-workspace?branch=v1#537053d3171a7374a1a86fed422523e7b45a4fb8"
|
source = "git+https://github.com/tauri-apps/plugins-workspace?branch=v1#01211ff0759d578e0e9ac8c98c31fdf09077eb34"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"log",
|
"log",
|
||||||
"serde",
|
"serde",
|
||||||
@ -3883,9 +3883,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tauri-runtime"
|
name = "tauri-runtime"
|
||||||
version = "0.14.1"
|
version = "0.14.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "07f8e9e53e00e9f41212c115749e87d5cd2a9eebccafca77a19722eeecd56d43"
|
checksum = "cf2d0652aa2891ff3e9caa2401405257ea29ab8372cce01f186a5825f1bd0e76"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"gtk",
|
"gtk",
|
||||||
"http",
|
"http",
|
||||||
@ -3904,9 +3904,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tauri-runtime-wry"
|
name = "tauri-runtime-wry"
|
||||||
version = "0.14.2"
|
version = "0.14.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "803a01101bc611ba03e13329951a1bde44287a54234189b9024b78619c1bc206"
|
checksum = "6cae61fbc731f690a4899681c9052dde6d05b159b44563ace8186fc1bfb7d158"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cocoa",
|
"cocoa",
|
||||||
"gtk",
|
"gtk",
|
||||||
@ -3924,9 +3924,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tauri-utils"
|
name = "tauri-utils"
|
||||||
version = "1.5.1"
|
version = "1.5.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a52165bb340e6f6a75f1f5eeeab1bb49f861c12abe3a176067d53642b5454986"
|
checksum = "ece74810b1d3d44f29f732a7ae09a63183d63949bbdd59c61f8ed2a1b70150db"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"brotli",
|
"brotli",
|
||||||
"ctor",
|
"ctor",
|
||||||
@ -4009,7 +4009,7 @@ checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.33",
|
"syn 2.0.48",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -4051,9 +4051,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tokio"
|
name = "tokio"
|
||||||
version = "1.34.0"
|
version = "1.36.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d0c014766411e834f7af5b8f4cf46257aab4036ca95e9d2c144a10f59ad6f5b9"
|
checksum = "61285f6515fa018fb2d1e46eb21223fff441ee8db5d0f1435e8ab4f5cdb80931"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"backtrace",
|
"backtrace",
|
||||||
"bytes",
|
"bytes",
|
||||||
@ -4193,7 +4193,7 @@ checksum = "0f57e3ca2a01450b1a921183a9c9cbfda207fd822cef4ccb00a65402cbba7a74"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.33",
|
"syn 2.0.48",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -4443,7 +4443,7 @@ dependencies = [
|
|||||||
"once_cell",
|
"once_cell",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.33",
|
"syn 2.0.48",
|
||||||
"wasm-bindgen-shared",
|
"wasm-bindgen-shared",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -4477,7 +4477,7 @@ checksum = "e128beba882dd1eb6200e1dc92ae6c5dbaa4311aa7bb211ca035779e5efc39f8"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.33",
|
"syn 2.0.48",
|
||||||
"wasm-bindgen-backend",
|
"wasm-bindgen-backend",
|
||||||
"wasm-bindgen-shared",
|
"wasm-bindgen-shared",
|
||||||
]
|
]
|
||||||
|
@ -12,17 +12,17 @@ rust-version = "1.60"
|
|||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
tauri-build = { version = "1.5.0", features = [] }
|
tauri-build = { version = "1.5.1", features = [] }
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow = "1"
|
anyhow = "1"
|
||||||
kittycad = "0.2.42"
|
kittycad = "0.2.53"
|
||||||
oauth2 = "4.4.2"
|
oauth2 = "4.4.2"
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
serde_json = "1.0"
|
serde_json = "1.0"
|
||||||
tauri = { version = "1.5.3", features = [ "os-all", "dialog-all", "fs-all", "http-request", "path-all", "shell-open", "shell-open-api", "devtools"] }
|
tauri = { version = "1.5.4", features = [ "os-all", "dialog-all", "fs-all", "http-request", "path-all", "shell-open", "shell-open-api", "devtools"] }
|
||||||
tauri-plugin-fs-extra = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "v1" }
|
tauri-plugin-fs-extra = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "v1" }
|
||||||
tokio = { version = "1.34.0", features = ["time"] }
|
tokio = { version = "1.36.0", features = ["time"] }
|
||||||
toml = "0.8.2"
|
toml = "0.8.2"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
|
@ -7,6 +7,7 @@ use std::io::Read;
|
|||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use oauth2::TokenResponse;
|
use oauth2::TokenResponse;
|
||||||
|
use std::process::Command;
|
||||||
use tauri::{InvokeError, Manager};
|
use tauri::{InvokeError, Manager};
|
||||||
const DEFAULT_HOST: &str = "https://api.kittycad.io";
|
const DEFAULT_HOST: &str = "https://api.kittycad.io";
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
},
|
},
|
||||||
"package": {
|
"package": {
|
||||||
"productName": "zoo-modeling-app",
|
"productName": "zoo-modeling-app",
|
||||||
"version": "0.14.0"
|
"version": "0.15.2"
|
||||||
},
|
},
|
||||||
"tauri": {
|
"tauri": {
|
||||||
"allowlist": {
|
"allowlist": {
|
||||||
|
@ -1,73 +0,0 @@
|
|||||||
import { render, screen } from '@testing-library/react'
|
|
||||||
import { App } from './App'
|
|
||||||
import { describe, test, vi } from 'vitest'
|
|
||||||
import {
|
|
||||||
Route,
|
|
||||||
RouterProvider,
|
|
||||||
createMemoryRouter,
|
|
||||||
createRoutesFromElements,
|
|
||||||
} from 'react-router-dom'
|
|
||||||
import { GlobalStateProvider } from './components/GlobalStateProvider'
|
|
||||||
import CommandBarProvider from 'components/CommandBar/CommandBar'
|
|
||||||
import ModelingMachineProvider from 'components/ModelingMachineProvider'
|
|
||||||
import { BROWSER_FILE_NAME } from 'Router'
|
|
||||||
|
|
||||||
let listener: ((rect: any) => void) | undefined = undefined
|
|
||||||
;(global as any).ResizeObserver = class ResizeObserver {
|
|
||||||
constructor(ls: ((rect: any) => void) | undefined) {
|
|
||||||
listener = ls
|
|
||||||
}
|
|
||||||
observe() {}
|
|
||||||
unobserve() {}
|
|
||||||
disconnect() {}
|
|
||||||
}
|
|
||||||
|
|
||||||
describe('App tests', () => {
|
|
||||||
test('Renders the modeling app screen, including "Variables" pane.', () => {
|
|
||||||
vi.mock('react-router-dom', async () => {
|
|
||||||
const actual = (await vi.importActual('react-router-dom')) as Record<
|
|
||||||
string,
|
|
||||||
any
|
|
||||||
>
|
|
||||||
return {
|
|
||||||
...actual,
|
|
||||||
useParams: () => ({ id: BROWSER_FILE_NAME }),
|
|
||||||
useLoaderData: () => ({ code: null }),
|
|
||||||
}
|
|
||||||
})
|
|
||||||
render(
|
|
||||||
<TestWrap>
|
|
||||||
<App />
|
|
||||||
</TestWrap>
|
|
||||||
)
|
|
||||||
const linkElement = screen.getByText(/Variables/i)
|
|
||||||
expect(linkElement).toBeInTheDocument()
|
|
||||||
|
|
||||||
vi.restoreAllMocks()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
function TestWrap({ children }: { children: React.ReactNode }) {
|
|
||||||
// We have to use a memory router in the testing environment,
|
|
||||||
// and we have to use the createMemoryRouter function instead of <MemoryRouter /> as of react-router v6.4:
|
|
||||||
// https://reactrouter.com/en/6.16.0/routers/picking-a-router#using-v64-data-apis
|
|
||||||
const router = createMemoryRouter(
|
|
||||||
createRoutesFromElements(
|
|
||||||
<Route
|
|
||||||
path="/file/:id"
|
|
||||||
element={
|
|
||||||
<CommandBarProvider>
|
|
||||||
<GlobalStateProvider>
|
|
||||||
<ModelingMachineProvider>{children}</ModelingMachineProvider>
|
|
||||||
</GlobalStateProvider>
|
|
||||||
</CommandBarProvider>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
),
|
|
||||||
{
|
|
||||||
initialEntries: ['/file/new'],
|
|
||||||
initialIndex: 0,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
return <RouterProvider router={router} />
|
|
||||||
}
|
|
203
src/App.tsx
@ -19,21 +19,24 @@ import {
|
|||||||
} from '@fortawesome/free-solid-svg-icons'
|
} from '@fortawesome/free-solid-svg-icons'
|
||||||
import { useHotkeys } from 'react-hotkeys-hook'
|
import { useHotkeys } from 'react-hotkeys-hook'
|
||||||
import { getNormalisedCoordinates } from './lib/utils'
|
import { getNormalisedCoordinates } from './lib/utils'
|
||||||
import { useLoaderData } from 'react-router-dom'
|
import { useLoaderData, useNavigate } from 'react-router-dom'
|
||||||
import { IndexLoaderData } from './Router'
|
import { type IndexLoaderData } from 'lib/types'
|
||||||
|
import { paths } from 'lib/paths'
|
||||||
import { useGlobalStateContext } from 'hooks/useGlobalStateContext'
|
import { useGlobalStateContext } from 'hooks/useGlobalStateContext'
|
||||||
import { onboardingPaths } from 'routes/Onboarding'
|
import { onboardingPaths } from 'routes/Onboarding/paths'
|
||||||
import { cameraMouseDragGuards } from 'lib/cameraControls'
|
|
||||||
import { CameraDragInteractionType_type } from '@kittycad/lib/dist/types/src/models'
|
|
||||||
import { CodeMenu } from 'components/CodeMenu'
|
import { CodeMenu } from 'components/CodeMenu'
|
||||||
import { TextEditor } from 'components/TextEditor'
|
import { TextEditor } from 'components/TextEditor'
|
||||||
import { Themes, getSystemTheme } from 'lib/theme'
|
import { Themes, getSystemTheme } from 'lib/theme'
|
||||||
import { useEngineConnectionSubscriptions } from 'hooks/useEngineConnectionSubscriptions'
|
import { useEngineConnectionSubscriptions } from 'hooks/useEngineConnectionSubscriptions'
|
||||||
import { engineCommandManager } from './lang/std/engineConnection'
|
import { engineCommandManager } from './lang/std/engineConnection'
|
||||||
import { useModelingContext } from 'hooks/useModelingContext'
|
import { useModelingContext } from 'hooks/useModelingContext'
|
||||||
|
import { useAbsoluteFilePath } from 'hooks/useAbsoluteFilePath'
|
||||||
|
import { isTauri } from 'lib/isTauri'
|
||||||
|
|
||||||
export function App() {
|
export function App() {
|
||||||
const { project, file } = useLoaderData() as IndexLoaderData
|
const { project, file } = useLoaderData() as IndexLoaderData
|
||||||
|
const navigate = useNavigate()
|
||||||
|
const filePath = useAbsoluteFilePath()
|
||||||
|
|
||||||
useHotKeyListener()
|
useHotKeyListener()
|
||||||
const {
|
const {
|
||||||
@ -51,8 +54,7 @@ export function App() {
|
|||||||
}))
|
}))
|
||||||
|
|
||||||
const { settings } = useGlobalStateContext()
|
const { settings } = useGlobalStateContext()
|
||||||
const { showDebugPanel, onboardingStatus, cameraControls, theme } =
|
const { showDebugPanel, onboardingStatus, theme } = settings?.context || {}
|
||||||
settings?.context || {}
|
|
||||||
const { state, send } = useModelingContext()
|
const { state, send } = useModelingContext()
|
||||||
|
|
||||||
const editorTheme = theme === Themes.System ? getSystemTheme() : theme
|
const editorTheme = theme === Themes.System ? getSystemTheme() : theme
|
||||||
@ -71,6 +73,16 @@ export function App() {
|
|||||||
useHotkeys('shift + e', () => togglePane('kclErrors'))
|
useHotkeys('shift + e', () => togglePane('kclErrors'))
|
||||||
useHotkeys('shift + d', () => togglePane('debug'))
|
useHotkeys('shift + d', () => togglePane('debug'))
|
||||||
useHotkeys('esc', () => send('Cancel'))
|
useHotkeys('esc', () => send('Cancel'))
|
||||||
|
useHotkeys('backspace', (e) => {
|
||||||
|
e.preventDefault()
|
||||||
|
})
|
||||||
|
useHotkeys(
|
||||||
|
isTauri() ? 'mod + ,' : 'shift + mod + ,',
|
||||||
|
() => navigate(filePath + paths.SETTINGS),
|
||||||
|
{
|
||||||
|
splitKey: '|',
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
const paneOpacity = [onboardingPaths.CAMERA, onboardingPaths.STREAMING].some(
|
const paneOpacity = [onboardingPaths.CAMERA, onboardingPaths.STREAMING].some(
|
||||||
(p) => p === onboardingStatus
|
(p) => p === onboardingStatus
|
||||||
@ -84,9 +96,11 @@ export function App() {
|
|||||||
|
|
||||||
const debounceSocketSend = throttle<EngineCommand>((message) => {
|
const debounceSocketSend = throttle<EngineCommand>((message) => {
|
||||||
engineCommandManager.sendSceneCommand(message)
|
engineCommandManager.sendSceneCommand(message)
|
||||||
}, 16)
|
}, 1000 / 15)
|
||||||
const handleMouseMove: MouseEventHandler<HTMLDivElement> = (e) => {
|
const handleMouseMove: MouseEventHandler<HTMLDivElement> = (e) => {
|
||||||
e.nativeEvent.preventDefault()
|
if (state.matches('Sketch')) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
const { x, y } = getNormalisedCoordinates({
|
const { x, y } = getNormalisedCoordinates({
|
||||||
clientX: e.clientX,
|
clientX: e.clientX,
|
||||||
@ -97,58 +111,11 @@ export function App() {
|
|||||||
|
|
||||||
const newCmdId = uuidv4()
|
const newCmdId = uuidv4()
|
||||||
if (buttonDownInStream === undefined) {
|
if (buttonDownInStream === undefined) {
|
||||||
if (state.matches('Sketch.Line Tool')) {
|
|
||||||
debounceSocketSend({
|
|
||||||
type: 'modeling_cmd_req',
|
|
||||||
cmd_id: newCmdId,
|
|
||||||
cmd: {
|
|
||||||
type: 'mouse_move',
|
|
||||||
window: { x, y },
|
|
||||||
},
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
debounceSocketSend({
|
|
||||||
type: 'modeling_cmd_req',
|
|
||||||
cmd: {
|
|
||||||
type: 'highlight_set_entity',
|
|
||||||
selected_at_window: { x, y },
|
|
||||||
},
|
|
||||||
cmd_id: newCmdId,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (state.matches('Sketch.Move Tool')) {
|
|
||||||
debounceSocketSend({
|
|
||||||
type: 'modeling_cmd_req',
|
|
||||||
cmd_id: newCmdId,
|
|
||||||
cmd: {
|
|
||||||
type: 'handle_mouse_drag_move',
|
|
||||||
window: { x, y },
|
|
||||||
},
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
const interactionGuards = cameraMouseDragGuards[cameraControls]
|
|
||||||
let interaction: CameraDragInteractionType_type
|
|
||||||
|
|
||||||
const eWithButton = { ...e, button: buttonDownInStream }
|
|
||||||
|
|
||||||
if (interactionGuards.pan.callback(eWithButton)) {
|
|
||||||
interaction = 'pan'
|
|
||||||
} else if (interactionGuards.rotate.callback(eWithButton)) {
|
|
||||||
interaction = 'rotate'
|
|
||||||
} else if (interactionGuards.zoom.dragCallback(eWithButton)) {
|
|
||||||
interaction = 'zoom'
|
|
||||||
} else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
debounceSocketSend({
|
debounceSocketSend({
|
||||||
type: 'modeling_cmd_req',
|
type: 'modeling_cmd_req',
|
||||||
cmd: {
|
cmd: {
|
||||||
type: 'camera_drag_move',
|
type: 'highlight_set_entity',
|
||||||
interaction,
|
selected_at_window: { x, y },
|
||||||
window: { x, y },
|
|
||||||
},
|
},
|
||||||
cmd_id: newCmdId,
|
cmd_id: newCmdId,
|
||||||
})
|
})
|
||||||
@ -170,62 +137,71 @@ export function App() {
|
|||||||
enableMenu={true}
|
enableMenu={true}
|
||||||
/>
|
/>
|
||||||
<ModalContainer />
|
<ModalContainer />
|
||||||
<Resizable
|
<div className="h-full">
|
||||||
className={
|
<div className="h-full flex">
|
||||||
'pointer-events-none h-full flex flex-col flex-1 z-10 my-5 ml-5 pr-1 transition-opacity transition-duration-75 ' +
|
<div className="h-full w-5 z-10 pointer-events-auto"></div>
|
||||||
+paneOpacity
|
<div className="flex flex-col">
|
||||||
}
|
<div className="h-5 z-10 pointer-events-auto"></div>
|
||||||
defaultSize={{
|
<Resizable
|
||||||
width: '550px',
|
className={
|
||||||
height: 'auto',
|
'pointer-events-none h-full flex flex-col flex-1 z-10 pr-1 transition-opacity transition-duration-75 ' +
|
||||||
}}
|
+paneOpacity
|
||||||
minWidth={200}
|
}
|
||||||
maxWidth={800}
|
defaultSize={{
|
||||||
minHeight={'auto'}
|
width: '550px',
|
||||||
maxHeight={'auto'}
|
height: 'auto',
|
||||||
handleClasses={{
|
}}
|
||||||
right:
|
minWidth={200}
|
||||||
'hover:bg-chalkboard-10/50 bg-transparent transition-colors duration-75 transition-ease-out delay-100 ' +
|
maxWidth={800}
|
||||||
(buttonDownInStream || onboardingStatus === 'camera'
|
minHeight={'auto'}
|
||||||
? 'pointer-events-none '
|
maxHeight={'auto'}
|
||||||
: 'pointer-events-auto'),
|
handleClasses={{
|
||||||
}}
|
right:
|
||||||
>
|
'hover:bg-chalkboard-10/50 bg-transparent transition-colors duration-75 transition-ease-out delay-100 ' +
|
||||||
<div
|
(buttonDownInStream || onboardingStatus === 'camera'
|
||||||
id="code-pane"
|
? 'pointer-events-none '
|
||||||
className="h-full flex flex-col justify-between pointer-events-none"
|
: 'pointer-events-auto'),
|
||||||
>
|
}}
|
||||||
<CollapsiblePanel
|
>
|
||||||
title="Code"
|
<div
|
||||||
icon={faCode}
|
id="code-pane"
|
||||||
className="open:!mb-2"
|
className="h-full flex flex-col justify-between pointer-events-none"
|
||||||
open={openPanes.includes('code')}
|
>
|
||||||
menu={<CodeMenu />}
|
<CollapsiblePanel
|
||||||
>
|
title="Code"
|
||||||
<TextEditor theme={editorTheme} />
|
icon={faCode}
|
||||||
</CollapsiblePanel>
|
className="open:!mb-2"
|
||||||
<section className="flex flex-col">
|
open={openPanes.includes('code')}
|
||||||
<MemoryPanel
|
menu={<CodeMenu />}
|
||||||
theme={editorTheme}
|
>
|
||||||
open={openPanes.includes('variables')}
|
<TextEditor theme={editorTheme} />
|
||||||
title="Variables"
|
</CollapsiblePanel>
|
||||||
icon={faSquareRootVariable}
|
<section className="flex flex-col">
|
||||||
/>
|
<MemoryPanel
|
||||||
<Logs
|
theme={editorTheme}
|
||||||
theme={editorTheme}
|
open={openPanes.includes('variables')}
|
||||||
open={openPanes.includes('logs')}
|
title="Variables"
|
||||||
title="Logs"
|
icon={faSquareRootVariable}
|
||||||
icon={faCodeCommit}
|
/>
|
||||||
/>
|
<Logs
|
||||||
<KCLErrors
|
theme={editorTheme}
|
||||||
theme={editorTheme}
|
open={openPanes.includes('logs')}
|
||||||
open={openPanes.includes('kclErrors')}
|
title="Logs"
|
||||||
title="KCL Errors"
|
icon={faCodeCommit}
|
||||||
iconClassNames={{ icon: 'group-open:text-destroy-30' }}
|
/>
|
||||||
/>
|
<KCLErrors
|
||||||
</section>
|
theme={editorTheme}
|
||||||
|
open={openPanes.includes('kclErrors')}
|
||||||
|
title="KCL Errors"
|
||||||
|
iconClassNames={{ icon: 'group-open:text-destroy-30' }}
|
||||||
|
/>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
</Resizable>
|
||||||
|
<div className="h-5 z-10 pointer-events-auto"></div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Resizable>
|
</div>
|
||||||
<Stream className="absolute inset-0 z-0" />
|
<Stream className="absolute inset-0 z-0" />
|
||||||
{showDebugPanel && (
|
{showDebugPanel && (
|
||||||
<DebugPanel
|
<DebugPanel
|
||||||
@ -238,6 +214,7 @@ export function App() {
|
|||||||
open={openPanes.includes('debug')}
|
open={openPanes.includes('debug')}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
{/* <CamToggle /> */}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -14,10 +14,7 @@ import {
|
|||||||
import { useEffect } from 'react'
|
import { useEffect } from 'react'
|
||||||
import { ErrorPage } from './components/ErrorPage'
|
import { ErrorPage } from './components/ErrorPage'
|
||||||
import { Settings } from './routes/Settings'
|
import { Settings } from './routes/Settings'
|
||||||
import Onboarding, {
|
import Onboarding, { onboardingRoutes } from './routes/Onboarding'
|
||||||
onboardingRoutes,
|
|
||||||
onboardingPaths,
|
|
||||||
} from './routes/Onboarding'
|
|
||||||
import SignIn from './routes/SignIn'
|
import SignIn from './routes/SignIn'
|
||||||
import { Auth } from './Auth'
|
import { Auth } from './Auth'
|
||||||
import { isTauri } from './lib/isTauri'
|
import { isTauri } from './lib/isTauri'
|
||||||
@ -29,7 +26,7 @@ import {
|
|||||||
isProjectDirectory,
|
isProjectDirectory,
|
||||||
PROJECT_ENTRYPOINT,
|
PROJECT_ENTRYPOINT,
|
||||||
} from './lib/tauriFS'
|
} from './lib/tauriFS'
|
||||||
import { metadata, type Metadata } from 'tauri-plugin-fs-extra-api'
|
import { metadata } from 'tauri-plugin-fs-extra-api'
|
||||||
import DownloadAppBanner from './components/DownloadAppBanner'
|
import DownloadAppBanner from './components/DownloadAppBanner'
|
||||||
import { WasmErrBanner } from './components/WasmErrBanner'
|
import { WasmErrBanner } from './components/WasmErrBanner'
|
||||||
import { GlobalStateProvider } from './components/GlobalStateProvider'
|
import { GlobalStateProvider } from './components/GlobalStateProvider'
|
||||||
@ -42,9 +39,12 @@ import CommandBarProvider from 'components/CommandBar/CommandBar'
|
|||||||
import { TEST, VITE_KC_SENTRY_DSN } from './env'
|
import { TEST, VITE_KC_SENTRY_DSN } from './env'
|
||||||
import * as Sentry from '@sentry/react'
|
import * as Sentry from '@sentry/react'
|
||||||
import ModelingMachineProvider from 'components/ModelingMachineProvider'
|
import ModelingMachineProvider from 'components/ModelingMachineProvider'
|
||||||
import { KclContextProvider, kclManager } from 'lang/KclSinglton'
|
import { KclContextProvider, kclManager } from 'lang/KclSingleton'
|
||||||
import FileMachineProvider from 'components/FileMachineProvider'
|
import FileMachineProvider from 'components/FileMachineProvider'
|
||||||
import { sep } from '@tauri-apps/api/path'
|
import { sep } from '@tauri-apps/api/path'
|
||||||
|
import { paths } from 'lib/paths'
|
||||||
|
import { IndexLoaderData, HomeLoaderData } from 'lib/types'
|
||||||
|
import { fileSystemManager } from 'lang/std/fileSystemManager'
|
||||||
|
|
||||||
if (VITE_KC_SENTRY_DSN && !TEST) {
|
if (VITE_KC_SENTRY_DSN && !TEST) {
|
||||||
Sentry.init({
|
Sentry.init({
|
||||||
@ -78,43 +78,8 @@ if (VITE_KC_SENTRY_DSN && !TEST) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const prependRoutes =
|
|
||||||
(routesObject: Record<string, string>) => (prepend: string) => {
|
|
||||||
return Object.fromEntries(
|
|
||||||
Object.entries(routesObject).map(([constName, path]) => [
|
|
||||||
constName,
|
|
||||||
prepend + path,
|
|
||||||
])
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export const paths = {
|
|
||||||
INDEX: '/',
|
|
||||||
HOME: '/home',
|
|
||||||
FILE: '/file',
|
|
||||||
SETTINGS: '/settings',
|
|
||||||
SIGN_IN: '/signin',
|
|
||||||
ONBOARDING: prependRoutes(onboardingPaths)(
|
|
||||||
'/onboarding'
|
|
||||||
) as typeof onboardingPaths,
|
|
||||||
}
|
|
||||||
|
|
||||||
export const BROWSER_FILE_NAME = 'new'
|
export const BROWSER_FILE_NAME = 'new'
|
||||||
|
|
||||||
export type IndexLoaderData = {
|
|
||||||
code: string | null
|
|
||||||
project?: ProjectWithEntryPointMetadata
|
|
||||||
file?: FileEntry
|
|
||||||
}
|
|
||||||
|
|
||||||
export type ProjectWithEntryPointMetadata = FileEntry & {
|
|
||||||
entrypointMetadata: Metadata
|
|
||||||
}
|
|
||||||
export type HomeLoaderData = {
|
|
||||||
projects: ProjectWithEntryPointMetadata[]
|
|
||||||
newDefaultDirectory?: string
|
|
||||||
}
|
|
||||||
|
|
||||||
type CreateBrowserRouterArg = Parameters<typeof createBrowserRouter>[0]
|
type CreateBrowserRouterArg = Parameters<typeof createBrowserRouter>[0]
|
||||||
|
|
||||||
const addGlobalContextToElements = (
|
const addGlobalContextToElements = (
|
||||||
@ -146,18 +111,18 @@ const router = createBrowserRouter(
|
|||||||
{
|
{
|
||||||
path: paths.FILE + '/:id',
|
path: paths.FILE + '/:id',
|
||||||
element: (
|
element: (
|
||||||
<Auth>
|
<KclContextProvider>
|
||||||
<FileMachineProvider>
|
<Auth>
|
||||||
<KclContextProvider>
|
<FileMachineProvider>
|
||||||
<ModelingMachineProvider>
|
<ModelingMachineProvider>
|
||||||
<Outlet />
|
<Outlet />
|
||||||
<App />
|
<App />
|
||||||
</ModelingMachineProvider>
|
</ModelingMachineProvider>
|
||||||
<WasmErrBanner />
|
<WasmErrBanner />
|
||||||
</KclContextProvider>
|
</FileMachineProvider>
|
||||||
</FileMachineProvider>
|
{!isTauri() && import.meta.env.PROD && <DownloadAppBanner />}
|
||||||
{!isTauri() && import.meta.env.PROD && <DownloadAppBanner />}
|
</Auth>
|
||||||
</Auth>
|
</KclContextProvider>
|
||||||
),
|
),
|
||||||
id: paths.FILE,
|
id: paths.FILE,
|
||||||
loader: async ({
|
loader: async ({
|
||||||
@ -210,6 +175,10 @@ const router = createBrowserRouter(
|
|||||||
const children = await readDir(projectPath, { recursive: true })
|
const children = await readDir(projectPath, { recursive: true })
|
||||||
kclManager.setCodeAndExecute(code, false)
|
kclManager.setCodeAndExecute(code, false)
|
||||||
|
|
||||||
|
// Set the file system manager to the project path
|
||||||
|
// So that WASM gets an updated path for operations
|
||||||
|
fileSystemManager.dir = projectPath
|
||||||
|
|
||||||
return {
|
return {
|
||||||
code,
|
code,
|
||||||
project: {
|
project: {
|
||||||
@ -249,7 +218,7 @@ const router = createBrowserRouter(
|
|||||||
<Home />
|
<Home />
|
||||||
</Auth>
|
</Auth>
|
||||||
),
|
),
|
||||||
loader: async () => {
|
loader: async (): Promise<HomeLoaderData | Response> => {
|
||||||
if (!isTauri()) {
|
if (!isTauri()) {
|
||||||
return redirect(paths.FILE + '/' + BROWSER_FILE_NAME)
|
return redirect(paths.FILE + '/' + BROWSER_FILE_NAME)
|
||||||
}
|
}
|
||||||
|
105
src/Toolbar.tsx
@ -5,6 +5,8 @@ import { useModelingContext } from 'hooks/useModelingContext'
|
|||||||
import { useCommandsContext } from 'hooks/useCommandsContext'
|
import { useCommandsContext } from 'hooks/useCommandsContext'
|
||||||
import { ActionButton } from 'components/ActionButton'
|
import { ActionButton } from 'components/ActionButton'
|
||||||
import usePlatform from 'hooks/usePlatform'
|
import usePlatform from 'hooks/usePlatform'
|
||||||
|
import { isSingleCursorInPipe } from 'lang/queryAst'
|
||||||
|
import { kclManager } from 'lang/KclSingleton'
|
||||||
|
|
||||||
export const Toolbar = () => {
|
export const Toolbar = () => {
|
||||||
const platform = usePlatform()
|
const platform = usePlatform()
|
||||||
@ -13,14 +15,15 @@ export const Toolbar = () => {
|
|||||||
const toolbarButtonsRef = useRef<HTMLUListElement>(null)
|
const toolbarButtonsRef = useRef<HTMLUListElement>(null)
|
||||||
const bgClassName =
|
const bgClassName =
|
||||||
'group-enabled:group-hover:bg-energy-10 group-pressed:bg-energy-10 dark:group-enabled:group-hover:bg-chalkboard-80 dark:group-pressed:bg-chalkboard-80'
|
'group-enabled:group-hover:bg-energy-10 group-pressed:bg-energy-10 dark:group-enabled:group-hover:bg-chalkboard-80 dark:group-pressed:bg-chalkboard-80'
|
||||||
const pathId = useMemo(
|
const pathId = useMemo(() => {
|
||||||
() =>
|
if (!isSingleCursorInPipe(context.selectionRanges, kclManager.ast)) {
|
||||||
isCursorInSketchCommandRange(
|
return false
|
||||||
engineCommandManager.artifactMap,
|
}
|
||||||
context.selectionRanges
|
return isCursorInSketchCommandRange(
|
||||||
),
|
engineCommandManager.artifactMap,
|
||||||
[engineCommandManager.artifactMap, context.selectionRanges]
|
context.selectionRanges
|
||||||
)
|
)
|
||||||
|
}, [engineCommandManager.artifactMap, context.selectionRanges])
|
||||||
|
|
||||||
function handleToolbarButtonsWheelEvent(ev: WheelEvent<HTMLSpanElement>) {
|
function handleToolbarButtonsWheelEvent(ev: WheelEvent<HTMLSpanElement>) {
|
||||||
const span = toolbarButtonsRef.current
|
const span = toolbarButtonsRef.current
|
||||||
@ -50,7 +53,9 @@ export const Toolbar = () => {
|
|||||||
<li className="contents">
|
<li className="contents">
|
||||||
<ActionButton
|
<ActionButton
|
||||||
Element="button"
|
Element="button"
|
||||||
onClick={() => send({ type: 'Enter sketch' })}
|
onClick={() =>
|
||||||
|
send({ type: 'Enter sketch', data: { forceNewSketch: true } })
|
||||||
|
}
|
||||||
icon={{
|
icon={{
|
||||||
icon: 'sketch',
|
icon: 'sketch',
|
||||||
bgClassName,
|
bgClassName,
|
||||||
@ -89,44 +94,48 @@ export const Toolbar = () => {
|
|||||||
</li>
|
</li>
|
||||||
)}
|
)}
|
||||||
{state.matches('Sketch') && !state.matches('idle') && (
|
{state.matches('Sketch') && !state.matches('idle') && (
|
||||||
<li className="contents">
|
<>
|
||||||
<ActionButton
|
<li className="contents" key="line-button">
|
||||||
Element="button"
|
<ActionButton
|
||||||
onClick={() =>
|
Element="button"
|
||||||
state.matches('Sketch.Line Tool')
|
onClick={() =>
|
||||||
? send('CancelSketch')
|
state?.matches('Sketch.Line tool')
|
||||||
: send('Equip tool')
|
? send('CancelSketch')
|
||||||
}
|
: send('Equip Line tool')
|
||||||
aria-pressed={state.matches('Sketch.Line Tool')}
|
}
|
||||||
className="pressed:bg-energy-10/20 dark:pressed:bg-energy-80"
|
aria-pressed={state?.matches('Sketch.Line tool')}
|
||||||
icon={{
|
className="pressed:bg-energy-10/20 dark:pressed:bg-energy-80"
|
||||||
icon: 'line',
|
icon={{
|
||||||
bgClassName,
|
icon: 'line',
|
||||||
}}
|
bgClassName,
|
||||||
>
|
}}
|
||||||
Line
|
>
|
||||||
</ActionButton>
|
Line
|
||||||
</li>
|
</ActionButton>
|
||||||
)}
|
</li>
|
||||||
{state.matches('Sketch') && (
|
<li className="contents" key="tangential-arc-button">
|
||||||
<li className="contents">
|
<ActionButton
|
||||||
<ActionButton
|
Element="button"
|
||||||
Element="button"
|
onClick={() =>
|
||||||
onClick={() =>
|
state.matches('Sketch.Tangential arc to')
|
||||||
state.matches('Sketch.Move Tool')
|
? send('CancelSketch')
|
||||||
? send('CancelSketch')
|
: send('Equip tangential arc to')
|
||||||
: send('Equip move tool')
|
}
|
||||||
}
|
aria-pressed={state.matches('Sketch.Tangential arc to')}
|
||||||
aria-pressed={state.matches('Sketch.Move Tool')}
|
className="pressed:bg-energy-10/20 dark:pressed:bg-energy-80"
|
||||||
className="pressed:bg-energy-10/20 dark:pressed:bg-energy-80"
|
icon={{
|
||||||
icon={{
|
icon: 'arc',
|
||||||
icon: 'move',
|
bgClassName,
|
||||||
bgClassName,
|
}}
|
||||||
}}
|
disabled={
|
||||||
>
|
!state.can('Equip tangential arc to') &&
|
||||||
Move
|
!state.matches('Sketch.Tangential arc to')
|
||||||
</ActionButton>
|
}
|
||||||
</li>
|
>
|
||||||
|
Tangential Arc
|
||||||
|
</ActionButton>
|
||||||
|
</li>
|
||||||
|
</>
|
||||||
)}
|
)}
|
||||||
{state.matches('Sketch.SketchIdle') &&
|
{state.matches('Sketch.SketchIdle') &&
|
||||||
state.nextEvents
|
state.nextEvents
|
||||||
@ -151,7 +160,7 @@ export const Toolbar = () => {
|
|||||||
return 0
|
return 0
|
||||||
})
|
})
|
||||||
.map((eventName) => (
|
.map((eventName) => (
|
||||||
<li className="contents">
|
<li className="contents" key={eventName}>
|
||||||
<ActionButton
|
<ActionButton
|
||||||
Element="button"
|
Element="button"
|
||||||
className="text-sm"
|
className="text-sm"
|
||||||
|
252
src/clientSideScene/ClientSideSceneComp.tsx
Normal file
@ -0,0 +1,252 @@
|
|||||||
|
import { useRef, useEffect, useState } from 'react'
|
||||||
|
import { useModelingContext } from 'hooks/useModelingContext'
|
||||||
|
|
||||||
|
import { cameraMouseDragGuards } from 'lib/cameraControls'
|
||||||
|
import { useGlobalStateContext } from 'hooks/useGlobalStateContext'
|
||||||
|
import { useStore } from 'useStore'
|
||||||
|
import {
|
||||||
|
DEBUG_SHOW_BOTH_SCENES,
|
||||||
|
ReactCameraProperties,
|
||||||
|
sceneInfra,
|
||||||
|
} from './sceneInfra'
|
||||||
|
import { throttle } from 'lib/utils'
|
||||||
|
|
||||||
|
function useShouldHideScene(): { hideClient: boolean; hideServer: boolean } {
|
||||||
|
const [isCamMoving, setIsCamMoving] = useState(false)
|
||||||
|
const [isTween, setIsTween] = useState(false)
|
||||||
|
|
||||||
|
const { state } = useModelingContext()
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
sceneInfra.setIsCamMovingCallback((isMoving, isTween) => {
|
||||||
|
setIsCamMoving(isMoving)
|
||||||
|
setIsTween(isTween)
|
||||||
|
})
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
if (DEBUG_SHOW_BOTH_SCENES || !isCamMoving)
|
||||||
|
return { hideClient: false, hideServer: false }
|
||||||
|
let hideServer = state.matches('Sketch')
|
||||||
|
if (isTween) {
|
||||||
|
hideServer = false
|
||||||
|
}
|
||||||
|
|
||||||
|
return { hideClient: !hideServer, hideServer }
|
||||||
|
}
|
||||||
|
|
||||||
|
export const ClientSideScene = ({
|
||||||
|
cameraControls,
|
||||||
|
}: {
|
||||||
|
cameraControls: ReturnType<
|
||||||
|
typeof useGlobalStateContext
|
||||||
|
>['settings']['context']['cameraControls']
|
||||||
|
}) => {
|
||||||
|
const canvasRef = useRef<HTMLDivElement>(null)
|
||||||
|
const { state, send } = useModelingContext()
|
||||||
|
const { hideClient, hideServer } = useShouldHideScene()
|
||||||
|
const { setHighlightRange } = useStore((s) => ({
|
||||||
|
setHighlightRange: s.setHighlightRange,
|
||||||
|
highlightRange: s.highlightRange,
|
||||||
|
}))
|
||||||
|
|
||||||
|
// Listen for changes to the camera controls setting
|
||||||
|
// and update the client-side scene's controls accordingly.
|
||||||
|
useEffect(() => {
|
||||||
|
sceneInfra.setInteractionGuards(cameraMouseDragGuards[cameraControls])
|
||||||
|
}, [cameraControls])
|
||||||
|
useEffect(() => {
|
||||||
|
sceneInfra.updateOtherSelectionColors(
|
||||||
|
state?.context?.selectionRanges?.otherSelections || []
|
||||||
|
)
|
||||||
|
}, [state?.context?.selectionRanges?.otherSelections])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!canvasRef.current) return
|
||||||
|
const canvas = canvasRef.current
|
||||||
|
canvas.appendChild(sceneInfra.renderer.domElement)
|
||||||
|
sceneInfra.animate()
|
||||||
|
sceneInfra.setHighlightCallback(setHighlightRange)
|
||||||
|
canvas.addEventListener('mousemove', sceneInfra.onMouseMove, false)
|
||||||
|
canvas.addEventListener('mousedown', sceneInfra.onMouseDown, false)
|
||||||
|
canvas.addEventListener('mouseup', sceneInfra.onMouseUp, false)
|
||||||
|
sceneInfra.setSend(send)
|
||||||
|
return () => {
|
||||||
|
canvas?.removeEventListener('mousemove', sceneInfra.onMouseMove)
|
||||||
|
canvas?.removeEventListener('mousedown', sceneInfra.onMouseDown)
|
||||||
|
canvas?.removeEventListener('mouseup', sceneInfra.onMouseUp)
|
||||||
|
}
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
ref={canvasRef}
|
||||||
|
className={`absolute inset-0 h-full w-full transition-all duration-300 ${
|
||||||
|
hideClient ? 'opacity-0' : 'opacity-100'
|
||||||
|
} ${hideServer ? 'bg-black' : ''} ${
|
||||||
|
!hideClient && !hideServer && state.matches('Sketch')
|
||||||
|
? 'bg-black/80'
|
||||||
|
: ''
|
||||||
|
}`}
|
||||||
|
></div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const throttled = throttle((a: ReactCameraProperties) => {
|
||||||
|
if (a.type === 'perspective' && a.fov) {
|
||||||
|
sceneInfra.dollyZoom(a.fov)
|
||||||
|
}
|
||||||
|
}, 1000 / 15)
|
||||||
|
|
||||||
|
export const CamDebugSettings = () => {
|
||||||
|
const [camSettings, setCamSettings] = useState<ReactCameraProperties>({
|
||||||
|
type: 'perspective',
|
||||||
|
fov: 12,
|
||||||
|
position: [0, 0, 0],
|
||||||
|
quaternion: [0, 0, 0, 1],
|
||||||
|
})
|
||||||
|
const [fov, setFov] = useState(12)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
sceneInfra.setReactCameraPropertiesCallback(setCamSettings)
|
||||||
|
}, [sceneInfra])
|
||||||
|
useEffect(() => {
|
||||||
|
if (camSettings.type === 'perspective' && camSettings.fov) {
|
||||||
|
setFov(camSettings.fov)
|
||||||
|
}
|
||||||
|
}, [(camSettings as any)?.fov])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<h3>cam settings</h3>
|
||||||
|
perspective cam
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
checked={camSettings.type === 'perspective'}
|
||||||
|
onChange={(e) => {
|
||||||
|
if (camSettings.type === 'perspective') {
|
||||||
|
sceneInfra.useOrthographicCamera()
|
||||||
|
} else {
|
||||||
|
sceneInfra.usePerspectiveCamera()
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
{camSettings.type === 'perspective' && (
|
||||||
|
<input
|
||||||
|
type="range"
|
||||||
|
min="4"
|
||||||
|
max="90"
|
||||||
|
step={0.5}
|
||||||
|
value={fov}
|
||||||
|
onChange={(e) => {
|
||||||
|
setFov(parseFloat(e.target.value))
|
||||||
|
|
||||||
|
throttled({
|
||||||
|
...camSettings,
|
||||||
|
fov: parseFloat(e.target.value),
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
className="w-full cursor-pointer pointer-events-auto"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{camSettings.type === 'perspective' && (
|
||||||
|
<div>
|
||||||
|
<span>fov</span>
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
value={camSettings.fov}
|
||||||
|
className="text-black w-16"
|
||||||
|
onChange={(e) => {
|
||||||
|
sceneInfra.setCam({
|
||||||
|
...camSettings,
|
||||||
|
fov: parseFloat(e.target.value),
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{camSettings.type === 'orthographic' && (
|
||||||
|
<>
|
||||||
|
<div>
|
||||||
|
<span>fov</span>
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
value={camSettings.zoom}
|
||||||
|
className="text-black w-16"
|
||||||
|
onChange={(e) => {
|
||||||
|
sceneInfra.setCam({
|
||||||
|
...camSettings,
|
||||||
|
zoom: parseFloat(e.target.value),
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
<div>
|
||||||
|
Position
|
||||||
|
<ul className="flex">
|
||||||
|
<li>
|
||||||
|
<span className="pl-2 pr-1">x:</span>
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
step={5}
|
||||||
|
data-testid="cam-x-position"
|
||||||
|
value={camSettings.position[0]}
|
||||||
|
className="text-black w-16"
|
||||||
|
onChange={(e) => {
|
||||||
|
sceneInfra.setCam({
|
||||||
|
...camSettings,
|
||||||
|
position: [
|
||||||
|
parseFloat(e.target.value),
|
||||||
|
camSettings.position[1],
|
||||||
|
camSettings.position[2],
|
||||||
|
],
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<span className="pl-2 pr-1">y:</span>
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
step={5}
|
||||||
|
data-testid="cam-y-position"
|
||||||
|
value={camSettings.position[1]}
|
||||||
|
className="text-black w-16"
|
||||||
|
onChange={(e) => {
|
||||||
|
sceneInfra.setCam({
|
||||||
|
...camSettings,
|
||||||
|
position: [
|
||||||
|
camSettings.position[0],
|
||||||
|
parseFloat(e.target.value),
|
||||||
|
camSettings.position[2],
|
||||||
|
],
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<span className="pl-2 pr-1">z:</span>
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
step={5}
|
||||||
|
data-testid="cam-z-position"
|
||||||
|
value={camSettings.position[2]}
|
||||||
|
className="text-black w-16"
|
||||||
|
onChange={(e) => {
|
||||||
|
sceneInfra.setCam({
|
||||||
|
...camSettings,
|
||||||
|
position: [
|
||||||
|
camSettings.position[0],
|
||||||
|
camSettings.position[1],
|
||||||
|
parseFloat(e.target.value),
|
||||||
|
],
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
33
src/clientSideScene/helpers.ts
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
import {
|
||||||
|
GridHelper,
|
||||||
|
LineBasicMaterial,
|
||||||
|
OrthographicCamera,
|
||||||
|
PerspectiveCamera,
|
||||||
|
Group,
|
||||||
|
Mesh,
|
||||||
|
} from 'three'
|
||||||
|
|
||||||
|
export function createGridHelper({
|
||||||
|
size,
|
||||||
|
divisions,
|
||||||
|
}: {
|
||||||
|
size: number
|
||||||
|
divisions: number
|
||||||
|
}) {
|
||||||
|
const gridHelperMaterial = new LineBasicMaterial({
|
||||||
|
color: 0xaaaaaa,
|
||||||
|
transparent: true,
|
||||||
|
opacity: 0.5,
|
||||||
|
depthTest: false,
|
||||||
|
})
|
||||||
|
const gridHelper = new GridHelper(size, divisions, 0x0000ff, 0xffffff)
|
||||||
|
gridHelper.material = gridHelperMaterial
|
||||||
|
gridHelper.rotation.x = Math.PI / 2
|
||||||
|
return gridHelper
|
||||||
|
}
|
||||||
|
|
||||||
|
export const orthoScale = (cam: OrthographicCamera | PerspectiveCamera) =>
|
||||||
|
0.55 / cam.zoom
|
||||||
|
|
||||||
|
export const perspScale = (cam: PerspectiveCamera, group: Group | Mesh) =>
|
||||||
|
(group.position.distanceTo(cam.position) * cam.fov) / 4000
|
1032
src/clientSideScene/sceneEntities.ts
Normal file
28
src/clientSideScene/sceneInfra.test.ts
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
import { Quaternion } from 'three'
|
||||||
|
import { isQuaternionVertical } from './sceneInfra'
|
||||||
|
|
||||||
|
describe('isQuaternionVertical', () => {
|
||||||
|
it('should identify vertical quaternions', () => {
|
||||||
|
const verticalQuaternions = [
|
||||||
|
new Quaternion(1, 0, 0, 0).normalize(), // bottom
|
||||||
|
new Quaternion(-0.7, 0.7, 0, 0).normalize(), // bottom 2
|
||||||
|
new Quaternion(0, 1, 0, 0).normalize(), // bottom 3
|
||||||
|
new Quaternion(0, 0, 0, 1).normalize(), // look from top
|
||||||
|
]
|
||||||
|
verticalQuaternions.forEach((quaternion) => {
|
||||||
|
expect(isQuaternionVertical(quaternion)).toBe(true)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should identify non-vertical quaternions', () => {
|
||||||
|
const nonVerticalQuaternions = [
|
||||||
|
new Quaternion(0.7, 0, 0, 0.7).normalize(), // front
|
||||||
|
new Quaternion(0, 0.7, 0.7, 0).normalize(), // back
|
||||||
|
new Quaternion(-0.5, 0.5, 0.5, -0.5).normalize(), // left side
|
||||||
|
new Quaternion(0.5, 0.5, 0.5, 0.5).normalize(), // right side
|
||||||
|
]
|
||||||
|
nonVerticalQuaternions.forEach((quaternion) => {
|
||||||
|
expect(isQuaternionVertical(quaternion)).toBe(false)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
1163
src/clientSideScene/sceneInfra.ts
Normal file
358
src/clientSideScene/segments.ts
Normal file
@ -0,0 +1,358 @@
|
|||||||
|
import { Coords2d } from 'lang/std/sketch'
|
||||||
|
import {
|
||||||
|
BufferGeometry,
|
||||||
|
CatmullRomCurve3,
|
||||||
|
ConeGeometry,
|
||||||
|
CurvePath,
|
||||||
|
EllipseCurve,
|
||||||
|
ExtrudeGeometry,
|
||||||
|
Group,
|
||||||
|
LineCurve3,
|
||||||
|
Mesh,
|
||||||
|
MeshBasicMaterial,
|
||||||
|
NormalBufferAttributes,
|
||||||
|
Shape,
|
||||||
|
SphereGeometry,
|
||||||
|
Vector2,
|
||||||
|
Vector3,
|
||||||
|
} from 'three'
|
||||||
|
import { mergeGeometries } from 'three/examples/jsm/utils/BufferGeometryUtils.js'
|
||||||
|
import { PathToNode, SketchGroup, getTangentialArcToInfo } from 'lang/wasm'
|
||||||
|
import {
|
||||||
|
STRAIGHT_SEGMENT,
|
||||||
|
STRAIGHT_SEGMENT_BODY,
|
||||||
|
STRAIGHT_SEGMENT_DASH,
|
||||||
|
TANGENTIAL_ARC_TO_SEGMENT,
|
||||||
|
TANGENTIAL_ARC_TO_SEGMENT_BODY,
|
||||||
|
TANGENTIAL_ARC_TO__SEGMENT_DASH,
|
||||||
|
} from './sceneEntities'
|
||||||
|
import { getTangentPointFromPreviousArc } from 'lib/utils2d'
|
||||||
|
import { ARROWHEAD } from './sceneInfra'
|
||||||
|
|
||||||
|
export function straightSegment({
|
||||||
|
from,
|
||||||
|
to,
|
||||||
|
id,
|
||||||
|
pathToNode,
|
||||||
|
isDraftSegment,
|
||||||
|
scale = 1,
|
||||||
|
}: {
|
||||||
|
from: Coords2d
|
||||||
|
to: Coords2d
|
||||||
|
id: string
|
||||||
|
pathToNode: PathToNode
|
||||||
|
isDraftSegment?: boolean
|
||||||
|
scale?: number
|
||||||
|
}): Group {
|
||||||
|
const group = new Group()
|
||||||
|
|
||||||
|
const shape = new Shape()
|
||||||
|
shape.moveTo(0, -0.08 * scale)
|
||||||
|
shape.lineTo(0, 0.08 * scale) // The width of the line
|
||||||
|
|
||||||
|
let geometry
|
||||||
|
if (isDraftSegment) {
|
||||||
|
geometry = dashedStraight(from, to, shape, scale)
|
||||||
|
} else {
|
||||||
|
const line = new LineCurve3(
|
||||||
|
new Vector3(from[0], from[1], 0),
|
||||||
|
new Vector3(to[0], to[1], 0)
|
||||||
|
)
|
||||||
|
|
||||||
|
geometry = new ExtrudeGeometry(shape, {
|
||||||
|
steps: 2,
|
||||||
|
bevelEnabled: false,
|
||||||
|
extrudePath: line,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const body = new MeshBasicMaterial({ color: 0xffffff })
|
||||||
|
const mesh = new Mesh(geometry, body)
|
||||||
|
mesh.userData.type = isDraftSegment
|
||||||
|
? STRAIGHT_SEGMENT_DASH
|
||||||
|
: STRAIGHT_SEGMENT_BODY
|
||||||
|
mesh.name = STRAIGHT_SEGMENT_BODY
|
||||||
|
|
||||||
|
group.userData = {
|
||||||
|
type: STRAIGHT_SEGMENT,
|
||||||
|
id,
|
||||||
|
from,
|
||||||
|
to,
|
||||||
|
pathToNode,
|
||||||
|
isSelected: false,
|
||||||
|
}
|
||||||
|
|
||||||
|
const arrowGroup = createArrowhead(scale)
|
||||||
|
arrowGroup.position.set(to[0], to[1], 0)
|
||||||
|
const dir = new Vector3()
|
||||||
|
.subVectors(new Vector3(to[0], to[1], 0), new Vector3(from[0], from[1], 0))
|
||||||
|
.normalize()
|
||||||
|
arrowGroup.quaternion.setFromUnitVectors(new Vector3(0, 1, 0), dir)
|
||||||
|
|
||||||
|
group.add(mesh, arrowGroup)
|
||||||
|
|
||||||
|
return group
|
||||||
|
}
|
||||||
|
|
||||||
|
function createArrowhead(scale = 1): Group {
|
||||||
|
const arrowMaterial = new MeshBasicMaterial({ color: 0xffffff })
|
||||||
|
const arrowheadMesh = new Mesh(new ConeGeometry(0.31, 1.5, 12), arrowMaterial)
|
||||||
|
arrowheadMesh.position.set(0, -0.6, 0)
|
||||||
|
const sphereMesh = new Mesh(new SphereGeometry(0.27, 12, 12), arrowMaterial)
|
||||||
|
|
||||||
|
const arrowGroup = new Group()
|
||||||
|
arrowGroup.userData.type = ARROWHEAD
|
||||||
|
arrowGroup.name = ARROWHEAD
|
||||||
|
arrowGroup.add(arrowheadMesh, sphereMesh)
|
||||||
|
arrowGroup.lookAt(new Vector3(0, 1, 0))
|
||||||
|
arrowGroup.scale.set(scale, scale, scale)
|
||||||
|
return arrowGroup
|
||||||
|
}
|
||||||
|
|
||||||
|
export function tangentialArcToSegment({
|
||||||
|
prevSegment,
|
||||||
|
from,
|
||||||
|
to,
|
||||||
|
id,
|
||||||
|
pathToNode,
|
||||||
|
isDraftSegment,
|
||||||
|
scale = 1,
|
||||||
|
}: {
|
||||||
|
prevSegment: SketchGroup['value'][number]
|
||||||
|
from: Coords2d
|
||||||
|
to: Coords2d
|
||||||
|
id: string
|
||||||
|
pathToNode: PathToNode
|
||||||
|
isDraftSegment?: boolean
|
||||||
|
scale?: number
|
||||||
|
}): Group {
|
||||||
|
const group = new Group()
|
||||||
|
|
||||||
|
const previousPoint =
|
||||||
|
prevSegment?.type === 'TangentialArcTo'
|
||||||
|
? getTangentPointFromPreviousArc(
|
||||||
|
prevSegment.center,
|
||||||
|
prevSegment.ccw,
|
||||||
|
prevSegment.to
|
||||||
|
)
|
||||||
|
: prevSegment.from
|
||||||
|
|
||||||
|
const { center, radius, startAngle, endAngle, ccw } = getTangentialArcToInfo({
|
||||||
|
arcStartPoint: from,
|
||||||
|
arcEndPoint: to,
|
||||||
|
tanPreviousPoint: previousPoint,
|
||||||
|
obtuse: true,
|
||||||
|
})
|
||||||
|
|
||||||
|
const geometry = createArcGeometry({
|
||||||
|
center,
|
||||||
|
radius,
|
||||||
|
startAngle,
|
||||||
|
endAngle,
|
||||||
|
ccw,
|
||||||
|
isDashed: isDraftSegment,
|
||||||
|
scale,
|
||||||
|
})
|
||||||
|
|
||||||
|
const body = new MeshBasicMaterial({ color: 0xffffff })
|
||||||
|
const mesh = new Mesh(geometry, body)
|
||||||
|
mesh.userData.type = isDraftSegment
|
||||||
|
? TANGENTIAL_ARC_TO__SEGMENT_DASH
|
||||||
|
: TANGENTIAL_ARC_TO_SEGMENT_BODY
|
||||||
|
|
||||||
|
group.userData = {
|
||||||
|
type: TANGENTIAL_ARC_TO_SEGMENT,
|
||||||
|
id,
|
||||||
|
from,
|
||||||
|
to,
|
||||||
|
prevSegment,
|
||||||
|
pathToNode,
|
||||||
|
isSelected: false,
|
||||||
|
}
|
||||||
|
|
||||||
|
const arrowGroup = createArrowhead(scale)
|
||||||
|
arrowGroup.position.set(to[0], to[1], 0)
|
||||||
|
const arrowheadAngle = endAngle + (Math.PI / 2) * (ccw ? 1 : -1)
|
||||||
|
arrowGroup.quaternion.setFromUnitVectors(
|
||||||
|
new Vector3(0, 1, 0),
|
||||||
|
new Vector3(Math.cos(arrowheadAngle), Math.sin(arrowheadAngle), 0)
|
||||||
|
)
|
||||||
|
|
||||||
|
group.add(mesh, arrowGroup)
|
||||||
|
|
||||||
|
return group
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createArcGeometry({
|
||||||
|
center,
|
||||||
|
radius,
|
||||||
|
startAngle,
|
||||||
|
endAngle,
|
||||||
|
ccw,
|
||||||
|
isDashed = false,
|
||||||
|
scale = 1,
|
||||||
|
}: {
|
||||||
|
center: Coords2d
|
||||||
|
radius: number
|
||||||
|
startAngle: number
|
||||||
|
endAngle: number
|
||||||
|
ccw: boolean
|
||||||
|
isDashed?: boolean
|
||||||
|
scale?: number
|
||||||
|
}): BufferGeometry {
|
||||||
|
const dashSize = 1.2 * scale
|
||||||
|
const gapSize = 1.2 * scale
|
||||||
|
const arcStart = new EllipseCurve(
|
||||||
|
center[0],
|
||||||
|
center[1],
|
||||||
|
radius,
|
||||||
|
radius,
|
||||||
|
startAngle,
|
||||||
|
endAngle,
|
||||||
|
!ccw,
|
||||||
|
0
|
||||||
|
)
|
||||||
|
const arcEnd = new EllipseCurve(
|
||||||
|
center[0],
|
||||||
|
center[1],
|
||||||
|
radius,
|
||||||
|
radius,
|
||||||
|
endAngle,
|
||||||
|
startAngle,
|
||||||
|
ccw,
|
||||||
|
0
|
||||||
|
)
|
||||||
|
const shape = new Shape()
|
||||||
|
shape.moveTo(0, -0.08 * scale)
|
||||||
|
shape.lineTo(0, 0.08 * scale) // The width of the line
|
||||||
|
|
||||||
|
if (!isDashed) {
|
||||||
|
const points = arcStart.getPoints(50)
|
||||||
|
const path = new CurvePath<Vector3>()
|
||||||
|
path.add(new CatmullRomCurve3(points.map((p) => new Vector3(p.x, p.y, 0))))
|
||||||
|
|
||||||
|
return new ExtrudeGeometry(shape, {
|
||||||
|
steps: 100,
|
||||||
|
bevelEnabled: false,
|
||||||
|
extrudePath: path,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const length = arcStart.getLength()
|
||||||
|
const totalDashes = length / (dashSize + gapSize) // rounding makes the dashes jittery since the new dash is suddenly appears instead of growing into place
|
||||||
|
const dashesAtEachEnd = Math.min(100, totalDashes / 2) // Assuming we want 50 dashes total, 25 at each end
|
||||||
|
|
||||||
|
const dashGeometries = []
|
||||||
|
|
||||||
|
// Function to create a dash at a specific t value (0 to 1 along the curve)
|
||||||
|
const createDashAt = (t: number, curve: EllipseCurve) => {
|
||||||
|
const startVec = curve.getPoint(t)
|
||||||
|
const endVec = curve.getPoint(Math.min(0.5, t + dashSize / length))
|
||||||
|
const midVec = curve.getPoint(Math.min(0.5, t + dashSize / length / 2))
|
||||||
|
const dashCurve = new CurvePath<Vector3>()
|
||||||
|
dashCurve.add(
|
||||||
|
new CatmullRomCurve3([
|
||||||
|
new Vector3(startVec.x, startVec.y, 0),
|
||||||
|
new Vector3(midVec.x, midVec.y, 0),
|
||||||
|
new Vector3(endVec.x, endVec.y, 0),
|
||||||
|
])
|
||||||
|
)
|
||||||
|
return new ExtrudeGeometry(shape, {
|
||||||
|
steps: 3,
|
||||||
|
bevelEnabled: false,
|
||||||
|
extrudePath: dashCurve,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create dashes at the start of the arc
|
||||||
|
for (let i = 0; i < dashesAtEachEnd; i++) {
|
||||||
|
const t = i / totalDashes
|
||||||
|
dashGeometries.push(createDashAt(t, arcStart))
|
||||||
|
dashGeometries.push(createDashAt(t, arcEnd))
|
||||||
|
}
|
||||||
|
|
||||||
|
// fill in the remaining arc
|
||||||
|
const remainingArcLength = length - dashesAtEachEnd * 2 * (dashSize + gapSize)
|
||||||
|
if (remainingArcLength > 0) {
|
||||||
|
const remainingArcStartT = dashesAtEachEnd / totalDashes
|
||||||
|
const remainingArcEndT = 1 - remainingArcStartT
|
||||||
|
const centerVec = new Vector2(center[0], center[1])
|
||||||
|
const remainingArcStartVec = arcStart.getPoint(remainingArcStartT)
|
||||||
|
const remainingArcEndVec = arcStart.getPoint(remainingArcEndT)
|
||||||
|
const remainingArcCurve = new EllipseCurve(
|
||||||
|
arcStart.aX,
|
||||||
|
arcStart.aY,
|
||||||
|
arcStart.xRadius,
|
||||||
|
arcStart.yRadius,
|
||||||
|
new Vector2().subVectors(centerVec, remainingArcStartVec).angle() +
|
||||||
|
Math.PI,
|
||||||
|
new Vector2().subVectors(centerVec, remainingArcEndVec).angle() + Math.PI,
|
||||||
|
!ccw
|
||||||
|
)
|
||||||
|
const remainingArcPoints = remainingArcCurve.getPoints(50)
|
||||||
|
const remainingArcPath = new CurvePath<Vector3>()
|
||||||
|
remainingArcPath.add(
|
||||||
|
new CatmullRomCurve3(
|
||||||
|
remainingArcPoints.map((p) => new Vector3(p.x, p.y, 0))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
const remainingArcGeometry = new ExtrudeGeometry(shape, {
|
||||||
|
steps: 50,
|
||||||
|
bevelEnabled: false,
|
||||||
|
extrudePath: remainingArcPath,
|
||||||
|
})
|
||||||
|
dashGeometries.push(remainingArcGeometry)
|
||||||
|
}
|
||||||
|
|
||||||
|
const geo = dashGeometries.length
|
||||||
|
? mergeGeometries(dashGeometries)
|
||||||
|
: new BufferGeometry()
|
||||||
|
geo.userData.type = 'dashed'
|
||||||
|
return geo
|
||||||
|
}
|
||||||
|
|
||||||
|
export function dashedStraight(
|
||||||
|
from: Coords2d,
|
||||||
|
to: Coords2d,
|
||||||
|
shape: Shape,
|
||||||
|
scale = 1
|
||||||
|
): BufferGeometry<NormalBufferAttributes> {
|
||||||
|
const dashSize = 1.2 * scale
|
||||||
|
const gapSize = 1.2 * scale // todo: gabSize is not respected
|
||||||
|
const dashLine = new LineCurve3(
|
||||||
|
new Vector3(from[0], from[1], 0),
|
||||||
|
new Vector3(to[0], to[1], 0)
|
||||||
|
)
|
||||||
|
const length = dashLine.getLength()
|
||||||
|
const numberOfPoints = (length / (dashSize + gapSize)) * 2
|
||||||
|
const startOfLine = new Vector3(from[0], from[1], 0)
|
||||||
|
const endOfLine = new Vector3(to[0], to[1], 0)
|
||||||
|
const dashGeometries = []
|
||||||
|
const dashComponent = (xOrY: number, pointIndex: number) =>
|
||||||
|
((to[xOrY] - from[xOrY]) / numberOfPoints) * pointIndex + from[xOrY]
|
||||||
|
for (let i = 0; i < numberOfPoints; i += 2) {
|
||||||
|
const dashStart = new Vector3(dashComponent(0, i), dashComponent(1, i), 0)
|
||||||
|
let dashEnd = new Vector3(
|
||||||
|
dashComponent(0, i + 1),
|
||||||
|
dashComponent(1, i + 1),
|
||||||
|
0
|
||||||
|
)
|
||||||
|
if (startOfLine.distanceTo(dashEnd) > startOfLine.distanceTo(endOfLine))
|
||||||
|
dashEnd = endOfLine
|
||||||
|
|
||||||
|
if (dashEnd) {
|
||||||
|
const dashCurve = new LineCurve3(dashStart, dashEnd)
|
||||||
|
const dashGeometry = new ExtrudeGeometry(shape, {
|
||||||
|
steps: 1,
|
||||||
|
bevelEnabled: false,
|
||||||
|
extrudePath: dashCurve,
|
||||||
|
})
|
||||||
|
dashGeometries.push(dashGeometry)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const geo = dashGeometries.length
|
||||||
|
? mergeGeometries(dashGeometries)
|
||||||
|
: new BufferGeometry()
|
||||||
|
geo.userData.type = 'dashed'
|
||||||
|
return geo
|
||||||
|
}
|
@ -1,6 +1,6 @@
|
|||||||
import { ActionIcon, ActionIconProps } from './ActionIcon'
|
import { ActionIcon, ActionIconProps } from './ActionIcon'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { paths } from '../Router'
|
import { paths } from 'lib/paths'
|
||||||
import { Link } from 'react-router-dom'
|
import { Link } from 'react-router-dom'
|
||||||
import type { LinkProps } from 'react-router-dom'
|
import type { LinkProps } from 'react-router-dom'
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { Toolbar } from '../Toolbar'
|
import { Toolbar } from '../Toolbar'
|
||||||
import UserSidebarMenu from './UserSidebarMenu'
|
import UserSidebarMenu from './UserSidebarMenu'
|
||||||
import { IndexLoaderData } from '../Router'
|
import { type IndexLoaderData } from 'lib/types'
|
||||||
import ProjectSidebarMenu from './ProjectSidebarMenu'
|
import ProjectSidebarMenu from './ProjectSidebarMenu'
|
||||||
import { useGlobalStateContext } from 'hooks/useGlobalStateContext'
|
import { useGlobalStateContext } from 'hooks/useGlobalStateContext'
|
||||||
import styles from './AppHeader.module.css'
|
import styles from './AppHeader.module.css'
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { useModelingContext } from 'hooks/useModelingContext'
|
import { useModelingContext } from 'hooks/useModelingContext'
|
||||||
import { kclManager } from 'lang/KclSinglton'
|
import { kclManager } from 'lang/KclSingleton'
|
||||||
import { getNodeFromPath, getNodePathFromSourceRange } from 'lang/queryAst'
|
import { getNodeFromPath, getNodePathFromSourceRange } from 'lang/queryAst'
|
||||||
import { useEffect, useRef, useState } from 'react'
|
import { useEffect, useRef, useState } from 'react'
|
||||||
import { useStore } from 'useStore'
|
import { useStore } from 'useStore'
|
||||||
|
@ -8,7 +8,7 @@ import {
|
|||||||
} from '../lang/modifyAst'
|
} from '../lang/modifyAst'
|
||||||
import { findAllPreviousVariables, PrevVariable } from '../lang/queryAst'
|
import { findAllPreviousVariables, PrevVariable } from '../lang/queryAst'
|
||||||
import { engineCommandManager } from '../lang/std/engineConnection'
|
import { engineCommandManager } from '../lang/std/engineConnection'
|
||||||
import { kclManager, useKclContext } from 'lang/KclSinglton'
|
import { kclManager, useKclContext } from 'lang/KclSingleton'
|
||||||
import { useModelingContext } from 'hooks/useModelingContext'
|
import { useModelingContext } from 'hooks/useModelingContext'
|
||||||
import { executeAst } from 'useStore'
|
import { executeAst } from 'useStore'
|
||||||
|
|
||||||
@ -148,7 +148,6 @@ export function useCalc({
|
|||||||
executeAst({
|
executeAst({
|
||||||
ast,
|
ast,
|
||||||
engineCommandManager,
|
engineCommandManager,
|
||||||
defaultPlanes: kclManager.defaultPlanes,
|
|
||||||
useFakeExecutor: true,
|
useFakeExecutor: true,
|
||||||
programMemoryOverride: JSON.parse(
|
programMemoryOverride: JSON.parse(
|
||||||
JSON.stringify(kclManager.programMemory)
|
JSON.stringify(kclManager.programMemory)
|
||||||
|
75
src/components/CamToggle.tsx
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
import { useState, useEffect } from 'react'
|
||||||
|
import { sceneInfra } from '../clientSideScene/sceneInfra'
|
||||||
|
import { engineCommandManager } from 'lang/std/engineConnection'
|
||||||
|
import { throttle, isReducedMotion } from 'lib/utils'
|
||||||
|
|
||||||
|
const updateDollyZoom = throttle(
|
||||||
|
(newFov: number) => sceneInfra.dollyZoom(newFov),
|
||||||
|
1000 / 15
|
||||||
|
)
|
||||||
|
|
||||||
|
export const CamToggle = () => {
|
||||||
|
const [isPerspective, setIsPerspective] = useState(true)
|
||||||
|
const [fov, setFov] = useState(40)
|
||||||
|
const [enableRotate, setEnableRotate] = useState(true)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
engineCommandManager.waitForReady.then(async () => {
|
||||||
|
sceneInfra.dollyZoom(fov)
|
||||||
|
})
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
const toggleCamera = () => {
|
||||||
|
if (isPerspective) {
|
||||||
|
isReducedMotion()
|
||||||
|
? sceneInfra.useOrthographicCamera()
|
||||||
|
: sceneInfra.animateToOrthographic()
|
||||||
|
} else {
|
||||||
|
isReducedMotion()
|
||||||
|
? sceneInfra.usePerspectiveCamera()
|
||||||
|
: sceneInfra.animateToPerspective()
|
||||||
|
}
|
||||||
|
setIsPerspective(!isPerspective)
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleFovChange = (newFov: number) => {
|
||||||
|
setFov(newFov)
|
||||||
|
updateDollyZoom(newFov)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="absolute right-14 bottom-3">
|
||||||
|
{isPerspective && (
|
||||||
|
<div className="">
|
||||||
|
<input
|
||||||
|
type="range"
|
||||||
|
min="4"
|
||||||
|
max="90"
|
||||||
|
step={0.5}
|
||||||
|
value={fov}
|
||||||
|
onChange={(e) => handleFovChange(Number(e.target.value))}
|
||||||
|
className="w-full cursor-pointer pointer-events-auto"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<button onClick={toggleCamera} className="">
|
||||||
|
{isPerspective
|
||||||
|
? 'Switch to Orthographic Camera'
|
||||||
|
: 'Switch to Perspective Camera'}
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={() => {
|
||||||
|
if (enableRotate) {
|
||||||
|
sceneInfra.controls.enableRotate = false
|
||||||
|
} else {
|
||||||
|
sceneInfra.controls.enableRotate = true
|
||||||
|
}
|
||||||
|
setEnableRotate(!enableRotate)
|
||||||
|
}}
|
||||||
|
className=""
|
||||||
|
>
|
||||||
|
{enableRotate ? 'Disable Rotation' : 'Enable Rotation'}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
@ -9,7 +9,7 @@ import styles from './CodeMenu.module.css'
|
|||||||
import { useConvertToVariable } from 'hooks/useToolbarGuards'
|
import { useConvertToVariable } from 'hooks/useToolbarGuards'
|
||||||
import { editorShortcutMeta } from './TextEditor'
|
import { editorShortcutMeta } from './TextEditor'
|
||||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
||||||
import { kclManager } from 'lang/KclSinglton'
|
import { kclManager } from 'lang/KclSingleton'
|
||||||
|
|
||||||
export const CodeMenu = ({ children }: PropsWithChildren) => {
|
export const CodeMenu = ({ children }: PropsWithChildren) => {
|
||||||
const { enable: convertToVarEnabled, handleClick: handleConvertToVarClick } =
|
const { enable: convertToVarEnabled, handleClick: handleConvertToVarClick } =
|
||||||
@ -77,6 +77,24 @@ export const CodeMenu = ({ children }: PropsWithChildren) => {
|
|||||||
</small>
|
</small>
|
||||||
</a>
|
</a>
|
||||||
</Menu.Item>
|
</Menu.Item>
|
||||||
|
<Menu.Item>
|
||||||
|
<a
|
||||||
|
className={styles.button}
|
||||||
|
href="https://github.com/KittyCAD/kcl-samples"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
>
|
||||||
|
<span>KCL samples</span>
|
||||||
|
<small>
|
||||||
|
On GitHub
|
||||||
|
<FontAwesomeIcon
|
||||||
|
icon={faArrowUpRightFromSquare}
|
||||||
|
className="ml-1 align-text-top"
|
||||||
|
width={12}
|
||||||
|
/>
|
||||||
|
</small>
|
||||||
|
</a>
|
||||||
|
</Menu.Item>
|
||||||
</Menu.Items>
|
</Menu.Items>
|
||||||
</div>
|
</div>
|
||||||
</Menu>
|
</Menu>
|
||||||
|
@ -27,6 +27,7 @@ export const CommandBarProvider = ({
|
|||||||
}) => {
|
}) => {
|
||||||
const { pathname } = useLocation()
|
const { pathname } = useLocation()
|
||||||
const [commandBarState, commandBarSend] = useMachine(commandBarMachine, {
|
const [commandBarState, commandBarSend] = useMachine(commandBarMachine, {
|
||||||
|
devTools: true,
|
||||||
guards: {
|
guards: {
|
||||||
'Arguments are ready': (context, _) => {
|
'Arguments are ready': (context, _) => {
|
||||||
return context.selectedCommand?.args
|
return context.selectedCommand?.args
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { useCommandsContext } from 'hooks/useCommandsContext'
|
import { useCommandsContext } from 'hooks/useCommandsContext'
|
||||||
import { CustomIcon } from '../CustomIcon'
|
import { CustomIcon } from '../CustomIcon'
|
||||||
import React, { useState } from 'react'
|
import React, { ReactNode, useState } from 'react'
|
||||||
import { ActionButton } from '../ActionButton'
|
import { ActionButton } from '../ActionButton'
|
||||||
import { Selections, getSelectionTypeDisplayText } from 'lib/selections'
|
import { Selections, getSelectionTypeDisplayText } from 'lib/selections'
|
||||||
import { useHotkeys } from 'react-hotkeys-hook'
|
import { useHotkeys } from 'react-hotkeys-hook'
|
||||||
@ -99,15 +99,7 @@ function CommandBarHeader({ children }: React.PropsWithChildren<{}>) {
|
|||||||
) : typeof argumentsToSubmit[argName] === 'object' ? (
|
) : typeof argumentsToSubmit[argName] === 'object' ? (
|
||||||
JSON.stringify(argumentsToSubmit[argName])
|
JSON.stringify(argumentsToSubmit[argName])
|
||||||
) : (
|
) : (
|
||||||
argumentsToSubmit[argName]
|
<em>{argumentsToSubmit[argName] as ReactNode}</em>
|
||||||
)
|
|
||||||
) : arg.payload ? (
|
|
||||||
arg.inputType === 'selection' ? (
|
|
||||||
getSelectionTypeDisplayText(arg.payload as Selections)
|
|
||||||
) : typeof arg.payload === 'object' ? (
|
|
||||||
JSON.stringify(arg.payload)
|
|
||||||
) : (
|
|
||||||
arg.payload
|
|
||||||
)
|
)
|
||||||
) : (
|
) : (
|
||||||
<em>{argName}</em>
|
<em>{argName}</em>
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { useSelector } from '@xstate/react'
|
import { useSelector } from '@xstate/react'
|
||||||
import { useCommandsContext } from 'hooks/useCommandsContext'
|
import { useCommandsContext } from 'hooks/useCommandsContext'
|
||||||
import { useKclContext } from 'lang/KclSinglton'
|
import { useKclContext } from 'lang/KclSingleton'
|
||||||
import { CommandArgument } from 'lib/commandTypes'
|
import { CommandArgument } from 'lib/commandTypes'
|
||||||
import {
|
import {
|
||||||
ResolvedSelectionType,
|
ResolvedSelectionType,
|
||||||
@ -27,7 +27,7 @@ function CommandBarSelectionInput({
|
|||||||
}) {
|
}) {
|
||||||
const { code } = useKclContext()
|
const { code } = useKclContext()
|
||||||
const inputRef = useRef<HTMLInputElement>(null)
|
const inputRef = useRef<HTMLInputElement>(null)
|
||||||
const { commandBarSend } = useCommandsContext()
|
const { commandBarState, commandBarSend } = useCommandsContext()
|
||||||
const [hasSubmitted, setHasSubmitted] = useState(false)
|
const [hasSubmitted, setHasSubmitted] = useState(false)
|
||||||
const selection = useSelector(arg.actor, selectionSelector)
|
const selection = useSelector(arg.actor, selectionSelector)
|
||||||
const [selectionsByType, setSelectionsByType] = useState<
|
const [selectionsByType, setSelectionsByType] = useState<
|
||||||
@ -59,8 +59,16 @@ function CommandBarSelectionInput({
|
|||||||
)
|
)
|
||||||
}, [selection])
|
}, [selection])
|
||||||
|
|
||||||
|
// Fast-forward through this arg if it's marked as skippable
|
||||||
|
// and we have a valid selection already
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setCanSubmitSelection(canSubmitSelectionArg(selectionsByType, arg))
|
setCanSubmitSelection(canSubmitSelectionArg(selectionsByType, arg))
|
||||||
|
const argValue = commandBarState.context.argumentsToSubmit[arg.name]
|
||||||
|
if (canSubmitSelection && arg.skip && argValue === undefined) {
|
||||||
|
handleSubmit({
|
||||||
|
preventDefault: () => {},
|
||||||
|
} as React.FormEvent<HTMLFormElement>)
|
||||||
|
}
|
||||||
}, [selectionsByType, arg])
|
}, [selectionsByType, arg])
|
||||||
|
|
||||||
function handleChange() {
|
function handleChange() {
|
||||||
|
@ -48,6 +48,7 @@ function CommandComboBox({
|
|||||||
(event.metaKey && event.key === 'k') ||
|
(event.metaKey && event.key === 'k') ||
|
||||||
(event.key === 'Backspace' && !event.currentTarget.value)
|
(event.key === 'Backspace' && !event.currentTarget.value)
|
||||||
) {
|
) {
|
||||||
|
event.preventDefault()
|
||||||
commandBarSend({ type: 'Close' })
|
commandBarSend({ type: 'Close' })
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
|
@ -1,9 +1,12 @@
|
|||||||
export type CustomIconName =
|
export type CustomIconName =
|
||||||
|
| 'arc'
|
||||||
| 'arrowDown'
|
| 'arrowDown'
|
||||||
| 'arrowLeft'
|
| 'arrowLeft'
|
||||||
| 'arrowRight'
|
| 'arrowRight'
|
||||||
| 'arrowUp'
|
| 'arrowUp'
|
||||||
| 'checkmark'
|
| 'checkmark'
|
||||||
|
| 'clipboardPlus'
|
||||||
|
| 'clipboardCheckmark'
|
||||||
| 'close'
|
| 'close'
|
||||||
| 'equal'
|
| 'equal'
|
||||||
| 'extrude'
|
| 'extrude'
|
||||||
@ -13,10 +16,14 @@ export type CustomIconName =
|
|||||||
| 'folderPlus'
|
| 'folderPlus'
|
||||||
| 'gear'
|
| 'gear'
|
||||||
| 'horizontal'
|
| 'horizontal'
|
||||||
|
| 'horizontalDash'
|
||||||
| 'line'
|
| 'line'
|
||||||
| 'move'
|
| 'move'
|
||||||
|
| 'network'
|
||||||
|
| 'networkCrossedOut'
|
||||||
| 'parallel'
|
| 'parallel'
|
||||||
| 'search'
|
| 'search'
|
||||||
|
| 'settings'
|
||||||
| 'sketch'
|
| 'sketch'
|
||||||
| 'vertical'
|
| 'vertical'
|
||||||
|
|
||||||
@ -27,6 +34,22 @@ export const CustomIcon = ({
|
|||||||
name: CustomIconName
|
name: CustomIconName
|
||||||
} & React.SVGProps<SVGSVGElement>) => {
|
} & React.SVGProps<SVGSVGElement>) => {
|
||||||
switch (name) {
|
switch (name) {
|
||||||
|
case 'arc':
|
||||||
|
return (
|
||||||
|
<svg
|
||||||
|
{...props}
|
||||||
|
viewBox="0 0 20 20"
|
||||||
|
fill="none"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
fillRule="evenodd"
|
||||||
|
clipRule="evenodd"
|
||||||
|
d="M10 1.5C8.60217 1.5 7.22591 1.84474 5.99313 2.50367C4.76035 3.1626 3.70911 4.1154 2.93251 5.27765C2.15592 6.43991 1.67794 7.77575 1.54093 9.16685C1.40392 10.558 1.6121 11.9614 2.14703 13.2528C2.68195 14.5442 3.52712 15.6838 4.60766 16.5706C5.6882 17.4574 6.97076 18.064 8.34173 18.3367C9.71271 18.6094 11.1298 18.5398 12.4674 18.134C13.8051 17.7282 15.022 16.9988 16.0104 16.0104C16.3068 15.714 16.5796 15.3974 16.8273 15.0634L16.0241 14.4677C15.8055 14.7624 15.5648 15.0418 15.3033 15.3033C14.4312 16.1754 13.3574 16.819 12.1771 17.1771C10.9969 17.5351 9.74651 17.5965 8.53682 17.3559C7.32714 17.1153 6.19547 16.58 5.24205 15.7976C4.28863 15.0151 3.5429 14.0096 3.07091 12.8701C2.59891 11.7306 2.41522 10.4923 2.53612 9.26487C2.65701 8.03743 3.07875 6.85874 3.76398 5.83322C4.44921 4.8077 5.37678 3.967 6.46453 3.38559C7.55227 2.80418 8.76662 2.5 10 2.5C10.3699 2.5 10.7376 2.52734 11.1005 2.58117L11.2472 1.59199C10.836 1.53099 10.4192 1.5 10 1.5ZM13.2067 3.22008C13.5383 3.37691 13.8593 3.5585 14.1668 3.76398C14.4743 3.96946 14.7649 4.19652 15.0367 4.44286L15.7083 3.70191C15.4002 3.42271 15.0709 3.16538 14.7223 2.93251C14.3738 2.69964 14.0101 2.49384 13.6342 2.31609L13.2067 3.22008ZM16.433 6.14423C16.6216 6.45886 16.7876 6.78818 16.9291 7.12987C17.0706 7.47157 17.1861 7.82181 17.2752 8.17765L18.2453 7.93467C18.1443 7.53138 18.0134 7.13444 17.853 6.74719C17.6926 6.35995 17.5044 5.98672 17.2907 5.63012L16.433 6.14423ZM17.491 10.368C17.473 10.7344 17.428 11.1004 17.3559 11.4632C17.2837 11.8259 17.1852 12.1813 17.0616 12.5267L18.0031 12.8636C18.1432 12.4721 18.2549 12.0694 18.3367 11.6583C18.4184 11.2472 18.4694 10.8323 18.4898 10.4171L17.491 10.368Z"
|
||||||
|
fill="currentColor"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
)
|
||||||
case 'arrowDown':
|
case 'arrowDown':
|
||||||
return (
|
return (
|
||||||
<svg
|
<svg
|
||||||
@ -107,6 +130,38 @@ export const CustomIcon = ({
|
|||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
)
|
)
|
||||||
|
case 'clipboardCheckmark':
|
||||||
|
return (
|
||||||
|
<svg
|
||||||
|
{...props}
|
||||||
|
viewBox="0 0 20 20"
|
||||||
|
fill="none"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
fillRule="evenodd"
|
||||||
|
clipRule="evenodd"
|
||||||
|
d="M6.5 3H7L13 3L13.5 3V3.5V4.00001L15.5 4.00002L16 4.00002V4.50002V10.0351C15.6905 9.85609 15.3548 9.71733 15 9.62602V5.00002L13.5 5.00001V6.50001V7.00001L13 7.00001L7 7.00001L6.5 7.00001V6.50001V5.00001L5 5.00001V16H10.8773C11.2024 16.4055 11.6047 16.7463 12.062 17H4.5H4V16.5V4.50001V4.00001L4.5 4.00001L6.5 4.00001V3.5V3ZM15.938 17C15.9588 16.9885 15.9794 16.9768 16 16.9649V17H15.938ZM7.5 4V4.50001V6.00001L12.5 6.00001V4.50001V4L7.5 4ZM13 9H7V8H13V9ZM15.6855 11.5L13.2101 14.8005L12.2071 13.7975L11.5 14.5046L12.9107 15.9153L13.6642 15.8617L16.4855 12.1L15.6855 11.5Z"
|
||||||
|
fill="currentColor"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
)
|
||||||
|
case 'clipboardPlus':
|
||||||
|
return (
|
||||||
|
<svg
|
||||||
|
{...props}
|
||||||
|
viewBox="0 0 20 20"
|
||||||
|
fill="none"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
fillRule="evenodd"
|
||||||
|
clipRule="evenodd"
|
||||||
|
d="M6.5 3H7L13 3L13.5 3V3.5V4.00001L15.5 4.00002L16 4.00002V4.50002V10.0351C15.6905 9.85609 15.3548 9.71733 15 9.62602V5.00002L13.5 5.00001V6.50001V7.00001L13 7.00001L7 7.00001L6.5 7.00001V6.50001V5.00001L5 5.00001V16H10.8773C11.2024 16.4055 11.6047 16.7463 12.062 17H4.5H4V16.5V4.50001V4.00001L4.5 4.00001L6.5 4.00001V3.5V3ZM15.938 17C15.9588 16.9885 15.9794 16.9768 16 16.9649V17H15.938ZM7.5 4V4.50001V6.00001L12.5 6.00001V4.50001V4L7.5 4ZM13 9H7V8H13V9ZM13.5 11V13H11.5V14H13.5V16H14.5V14H16.5V13H14.5V11H13.5Z"
|
||||||
|
fill="currentColor"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
)
|
||||||
case 'close':
|
case 'close':
|
||||||
return (
|
return (
|
||||||
<svg
|
<svg
|
||||||
@ -249,6 +304,22 @@ export const CustomIcon = ({
|
|||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
)
|
)
|
||||||
|
case 'horizontalDash':
|
||||||
|
return (
|
||||||
|
<svg
|
||||||
|
{...props}
|
||||||
|
viewBox="0 0 20 20"
|
||||||
|
fill="none"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
fillRule="evenodd"
|
||||||
|
clipRule="evenodd"
|
||||||
|
d="M14 10.5H6V9.5H14V10.5Z"
|
||||||
|
fill="currentColor"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
)
|
||||||
case 'line':
|
case 'line':
|
||||||
return (
|
return (
|
||||||
<svg
|
<svg
|
||||||
@ -281,6 +352,38 @@ export const CustomIcon = ({
|
|||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
)
|
)
|
||||||
|
case 'network':
|
||||||
|
return (
|
||||||
|
<svg
|
||||||
|
{...props}
|
||||||
|
viewBox="0 0 20 20"
|
||||||
|
fill="none"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
fillRule="evenodd"
|
||||||
|
clipRule="evenodd"
|
||||||
|
d="M18 9.64741C17.1925 8.24871 16.0344 7.08457 14.6399 6.26971C13.2455 5.45486 11.6628 5.01742 10.0478 5.00051C8.4328 4.9836 6.84127 5.38779 5.43006 6.17326C4.01884 6.95873 2.83666 8.09837 2 9.47985L2.76881 9.94546C3.52456 8.69756 4.59243 7.66813 5.86718 6.95862C7.14193 6.2491 8.57955 5.88399 10.0384 5.89927C11.4972 5.91455 12.9269 6.30968 14.1865 7.04574C15.4461 7.7818 16.4922 8.83337 17.2216 10.0968L18 9.64741ZM15.2155 11.0953C14.6772 10.1628 13.9051 9.3867 12.9755 8.84347C12.0459 8.30023 10.9907 8.00861 9.91406 7.99733C8.8374 7.98606 7.77638 8.25552 6.83557 8.77917C5.89476 9.30281 5.10664 10.0626 4.54887 10.9836L5.34391 11.4651C5.81802 10.6822 6.48792 10.0364 7.28761 9.59132C8.0873 9.14622 8.98916 8.91718 9.90432 8.92676C10.8195 8.93635 11.7164 9.18423 12.5065 9.64598C13.2967 10.1077 13.953 10.7674 14.4106 11.56L15.2155 11.0953ZM10 14C10.8284 14 11.5 13.3284 11.5 12.5C11.5 11.6716 10.8284 11 10 11C9.17157 11 8.5 11.6716 8.5 12.5C8.5 13.3284 9.17157 14 10 14Z"
|
||||||
|
fill="currentColor"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
)
|
||||||
|
case 'networkCrossedOut':
|
||||||
|
return (
|
||||||
|
<svg
|
||||||
|
{...props}
|
||||||
|
viewBox="0 0 20 20"
|
||||||
|
fill="none"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
fillRule="evenodd"
|
||||||
|
clipRule="evenodd"
|
||||||
|
d="M4.35352 5.39647L14.253 15.296L14.9601 14.5889L5.06062 4.68936L4.35352 5.39647ZM12.5065 9.64599C11.9609 9.32713 11.3643 9.11025 10.746 9.00341L9.74058 7.99796C9.79835 7.99694 9.85618 7.99674 9.91406 7.99735C10.9907 8.00862 12.0459 8.30025 12.9755 8.84348C13.9051 9.38672 14.6772 10.1628 15.2155 11.0953L14.4106 11.56C13.953 10.7674 13.2967 10.1077 12.5065 9.64599ZM6.48788 8.98789L7.16295 9.66297C6.41824 10.1045 5.79317 10.7233 5.34391 11.4651L4.54887 10.9836C5.03646 10.1785 5.70009 9.49656 6.48788 8.98789ZM10.0384 5.89928C9.3134 5.89169 8.59366 5.97804 7.89655 6.15392L7.16867 5.42605C8.09637 5.13507 9.06776 4.99026 10.0478 5.00052C11.6628 5.01744 13.2455 5.45488 14.6399 6.26973C16.0344 7.08458 17.1925 8.24872 18 9.64742L17.2216 10.0968C16.4922 8.83338 15.4461 7.78181 14.1865 7.04575C12.9269 6.3097 11.4972 5.91456 10.0384 5.89928ZM5.00782 7.50783L4.36522 6.86524C3.42033 7.57557 2.61639 8.46208 2 9.47986L2.76881 9.94547C3.34775 8.98952 4.10986 8.16177 5.00782 7.50783ZM10 14C10.4142 14 10.7892 13.8321 11.0607 13.5607L8.93934 11.4394C8.66789 11.7108 8.5 12.0858 8.5 12.5C8.5 13.3284 9.17157 14 10 14Z"
|
||||||
|
fill="currentColor"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
)
|
||||||
case 'parallel':
|
case 'parallel':
|
||||||
return (
|
return (
|
||||||
<svg
|
<svg
|
||||||
@ -313,6 +416,22 @@ export const CustomIcon = ({
|
|||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
)
|
)
|
||||||
|
case 'settings':
|
||||||
|
return (
|
||||||
|
<svg
|
||||||
|
{...props}
|
||||||
|
viewBox="0 0 20 20"
|
||||||
|
fill="none"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
fillRule="evenodd"
|
||||||
|
clipRule="evenodd"
|
||||||
|
d="M8 5.5C8 5.77614 7.77614 6 7.5 6C7.22386 6 7 5.77614 7 5.5C7 5.22386 7.22386 5 7.5 5C7.77614 5 8 5.22386 8 5.5ZM6.08535 6C6.29127 6.5826 6.84689 7 7.5 7C8.32843 7 9 6.32843 9 5.5C9 4.67157 8.32843 4 7.5 4C6.84689 4 6.29127 4.4174 6.08535 5H5V6H6.08535ZM15 6H9.94999C9.98278 5.83844 10 5.67123 10 5.5C10 5.32877 9.98278 5.16155 9.94999 5H15V6ZM11 14.5C11 14.7761 10.7761 15 10.5 15C10.2239 15 10 14.7761 10 14.5C10 14.2239 10.2239 14 10.5 14C10.7761 14 11 14.2239 11 14.5ZM9.08535 15C9.29127 15.5826 9.84689 16 10.5 16C11.3284 16 12 15.3284 12 14.5C12 13.6716 11.3284 13 10.5 13C9.84689 13 9.29127 13.4174 9.08535 14H5V15H9.08535ZM15 15H12.95C12.9828 14.8384 13 14.6712 13 14.5C13 14.3288 12.9828 14.1616 12.95 14H15V15ZM11.5 10.5C11.7761 10.5 12 10.2761 12 10C12 9.72386 11.7761 9.5 11.5 9.5C11.2239 9.5 11 9.72386 11 10C11 10.2761 11.2239 10.5 11.5 10.5ZM11.5 8.5C12.1531 8.5 12.7087 8.9174 12.9146 9.5H15V10.5H12.9146C12.7087 11.0826 12.1531 11.5 11.5 11.5C10.6716 11.5 10 10.8284 10 10C10 9.17157 10.6716 8.5 11.5 8.5ZM9.05001 10.5C9.01722 10.3384 9 10.1712 9 10C9 9.82877 9.01722 9.66155 9.05001 9.5H5V10.5H9.05001Z"
|
||||||
|
fill="currentColor"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
)
|
||||||
case 'sketch':
|
case 'sketch':
|
||||||
return (
|
return (
|
||||||
<svg
|
<svg
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import { CollapsiblePanel, CollapsiblePanelProps } from './CollapsiblePanel'
|
import { CollapsiblePanel, CollapsiblePanelProps } from './CollapsiblePanel'
|
||||||
import { AstExplorer } from './AstExplorer'
|
import { AstExplorer } from './AstExplorer'
|
||||||
import { EngineCommands } from './EngineCommands'
|
import { EngineCommands } from './EngineCommands'
|
||||||
|
import { CamDebugSettings } from 'clientSideScene/ClientSideSceneComp'
|
||||||
|
|
||||||
export const DebugPanel = ({ className, ...props }: CollapsiblePanelProps) => {
|
export const DebugPanel = ({ className, ...props }: CollapsiblePanelProps) => {
|
||||||
return (
|
return (
|
||||||
@ -15,6 +16,7 @@ export const DebugPanel = ({ className, ...props }: CollapsiblePanelProps) => {
|
|||||||
>
|
>
|
||||||
<section className="p-4 flex flex-col gap-4">
|
<section className="p-4 flex flex-col gap-4">
|
||||||
<EngineCommands />
|
<EngineCommands />
|
||||||
|
<CamDebugSettings />
|
||||||
<div style={{ height: '400px' }} className="overflow-y-auto">
|
<div style={{ height: '400px' }} className="overflow-y-auto">
|
||||||
<AstExplorer />
|
<AstExplorer />
|
||||||
</div>
|
</div>
|
||||||
|
43
src/components/DragWarningToast.tsx
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
import { MoveDesc } from 'machines/modelingMachine'
|
||||||
|
|
||||||
|
export const DragWarningToast = (moveDescs: MoveDesc[]) => {
|
||||||
|
if (moveDescs.length === 1) {
|
||||||
|
return (
|
||||||
|
<div className="flex items-center">
|
||||||
|
<div>🔒</div>
|
||||||
|
<div className="dark:bg-slate-950/50 bg-slate-400/50 p-1 px-3 rounded-xl text-sm">
|
||||||
|
move disabled: line{' '}
|
||||||
|
<span className="dark:text-energy-20 text-lime-600">
|
||||||
|
{moveDescs[0].line}
|
||||||
|
</span>
|
||||||
|
:{' '}
|
||||||
|
<pre>
|
||||||
|
<code className="dark:text-energy-20 text-lime-600">
|
||||||
|
{moveDescs[0].snippet}
|
||||||
|
</code>
|
||||||
|
</pre>{' '}
|
||||||
|
is fully constrained
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
} else if (moveDescs.length > 1) {
|
||||||
|
return (
|
||||||
|
<div className="dark:bg-slate-950/50 bg-slate-400/50 p-1 px-3 rounded-xl text-sm">
|
||||||
|
<div>Move disabled as The following lines are constrained</div>
|
||||||
|
{moveDescs.map((desc, i) => {
|
||||||
|
return (
|
||||||
|
<div key={i}>
|
||||||
|
line {desc.line}:{' '}
|
||||||
|
<pre className="inline-block">
|
||||||
|
<code className="dark:text-energy-20 text-lime-600">
|
||||||
|
{moveDescs[0].snippet}
|
||||||
|
</code>
|
||||||
|
</pre>{' '}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
@ -1,6 +1,7 @@
|
|||||||
import { useMachine } from '@xstate/react'
|
import { useMachine } from '@xstate/react'
|
||||||
import { useNavigate, useRouteLoaderData } from 'react-router-dom'
|
import { useNavigate, useRouteLoaderData } from 'react-router-dom'
|
||||||
import { IndexLoaderData, paths } from '../Router'
|
import { type IndexLoaderData } from 'lib/types'
|
||||||
|
import { paths } from 'lib/paths'
|
||||||
import React, { createContext } from 'react'
|
import React, { createContext } from 'react'
|
||||||
import { toast } from 'react-hot-toast'
|
import { toast } from 'react-hot-toast'
|
||||||
import {
|
import {
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { IndexLoaderData, paths } from 'Router'
|
import { type IndexLoaderData } from 'lib/types'
|
||||||
|
import { paths } from 'lib/paths'
|
||||||
import { ActionButton } from './ActionButton'
|
import { ActionButton } from './ActionButton'
|
||||||
import Tooltip from './Tooltip'
|
import Tooltip from './Tooltip'
|
||||||
import { FileEntry } from '@tauri-apps/api/fs'
|
import { FileEntry } from '@tauri-apps/api/fs'
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { useMachine } from '@xstate/react'
|
import { useMachine } from '@xstate/react'
|
||||||
import { useNavigate } from 'react-router-dom'
|
import { useNavigate } from 'react-router-dom'
|
||||||
import { paths } from '../Router'
|
import { paths } from 'lib/paths'
|
||||||
import { authMachine, TOKEN_PERSIST_KEY } from '../machines/authMachine'
|
import { authMachine, TOKEN_PERSIST_KEY } from '../machines/authMachine'
|
||||||
import withBaseUrl from '../lib/withBaseURL'
|
import withBaseUrl from '../lib/withBaseURL'
|
||||||
import React, { createContext, useEffect, useRef } from 'react'
|
import React, { createContext, useEffect, useRef } from 'react'
|
||||||
@ -30,6 +30,12 @@ type GlobalContext = {
|
|||||||
settings: MachineContext<typeof settingsMachine>
|
settings: MachineContext<typeof settingsMachine>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// a little hacky for sure, open to changing it
|
||||||
|
// this implies that we should only even have one instance of this provider mounted at any one time
|
||||||
|
// but I think that's a safe assumption
|
||||||
|
let settingsStateRef: (typeof settingsMachine)['context'] | undefined
|
||||||
|
export const getSettingsState = () => settingsStateRef
|
||||||
|
|
||||||
export const GlobalStateContext = createContext({} as GlobalContext)
|
export const GlobalStateContext = createContext({} as GlobalContext)
|
||||||
|
|
||||||
export const GlobalStateProvider = ({
|
export const GlobalStateProvider = ({
|
||||||
@ -71,6 +77,7 @@ export const GlobalStateProvider = ({
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
settingsStateRef = settingsState.context
|
||||||
|
|
||||||
useStateMachineCommands({
|
useStateMachineCommands({
|
||||||
machineId: 'settings',
|
machineId: 'settings',
|
||||||
|
@ -2,7 +2,7 @@ import ReactJson from 'react-json-view'
|
|||||||
import { useEffect } from 'react'
|
import { useEffect } from 'react'
|
||||||
import { CollapsiblePanel, CollapsiblePanelProps } from './CollapsiblePanel'
|
import { CollapsiblePanel, CollapsiblePanelProps } from './CollapsiblePanel'
|
||||||
import { Themes } from '../lib/theme'
|
import { Themes } from '../lib/theme'
|
||||||
import { useKclContext } from 'lang/KclSinglton'
|
import { useKclContext } from 'lang/KclSingleton'
|
||||||
|
|
||||||
const ReactJsonTypeHack = ReactJson as any
|
const ReactJsonTypeHack = ReactJson as any
|
||||||
|
|
||||||
|
@ -41,9 +41,9 @@ describe('processMemory', () => {
|
|||||||
otherVar: 3,
|
otherVar: 3,
|
||||||
theExtrude: [],
|
theExtrude: [],
|
||||||
theSketch: [
|
theSketch: [
|
||||||
{ type: 'toPoint', to: [-3.35, 0.17], from: [0, 0], name: '' },
|
{ type: 'ToPoint', to: [-3.35, 0.17], from: [0, 0], name: '' },
|
||||||
{ type: 'toPoint', to: [0.98, 5.16], from: [-3.35, 0.17], name: '' },
|
{ type: 'ToPoint', to: [0.98, 5.16], from: [-3.35, 0.17], name: '' },
|
||||||
{ type: 'toPoint', to: [2.15, 4.32], from: [0.98, 5.16], name: '' },
|
{ type: 'ToPoint', to: [2.15, 4.32], from: [0.98, 5.16], name: '' },
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -3,7 +3,7 @@ import { CollapsiblePanel, CollapsiblePanelProps } from './CollapsiblePanel'
|
|||||||
import { useMemo } from 'react'
|
import { useMemo } from 'react'
|
||||||
import { ProgramMemory, Path, ExtrudeSurface } from '../lang/wasm'
|
import { ProgramMemory, Path, ExtrudeSurface } from '../lang/wasm'
|
||||||
import { Themes } from '../lib/theme'
|
import { Themes } from '../lib/theme'
|
||||||
import { useKclContext } from 'lang/KclSinglton'
|
import { useKclContext } from 'lang/KclSingleton'
|
||||||
|
|
||||||
interface MemoryPanelProps extends CollapsiblePanelProps {
|
interface MemoryPanelProps extends CollapsiblePanelProps {
|
||||||
theme?: Exclude<Themes, Themes.System>
|
theme?: Exclude<Themes, Themes.System>
|
||||||
|
@ -13,23 +13,7 @@ import { useSetupEngineManager } from 'hooks/useSetupEngineManager'
|
|||||||
import { useGlobalStateContext } from 'hooks/useGlobalStateContext'
|
import { useGlobalStateContext } from 'hooks/useGlobalStateContext'
|
||||||
import { isCursorInSketchCommandRange } from 'lang/util'
|
import { isCursorInSketchCommandRange } from 'lang/util'
|
||||||
import { engineCommandManager } from 'lang/std/engineConnection'
|
import { engineCommandManager } from 'lang/std/engineConnection'
|
||||||
import { v4 as uuidv4 } from 'uuid'
|
import { kclManager, useKclContext } from 'lang/KclSingleton'
|
||||||
import { addStartSketch } from 'lang/modifyAst'
|
|
||||||
import { roundOff } from 'lib/utils'
|
|
||||||
import {
|
|
||||||
recast,
|
|
||||||
parse,
|
|
||||||
Program,
|
|
||||||
PipeExpression,
|
|
||||||
CallExpression,
|
|
||||||
} from 'lang/wasm'
|
|
||||||
import { getNodeFromPath } from 'lang/queryAst'
|
|
||||||
import {
|
|
||||||
addCloseToPipe,
|
|
||||||
addNewSketchLn,
|
|
||||||
compareVec2Epsilon,
|
|
||||||
} from 'lang/std/sketch'
|
|
||||||
import { kclManager, useKclContext } from 'lang/KclSinglton'
|
|
||||||
import { applyConstraintHorzVertDistance } from './Toolbar/SetHorzVertDistance'
|
import { applyConstraintHorzVertDistance } from './Toolbar/SetHorzVertDistance'
|
||||||
import {
|
import {
|
||||||
angleBetweenInfo,
|
angleBetweenInfo,
|
||||||
@ -49,6 +33,11 @@ import { applyConstraintIntersect } from './Toolbar/Intersect'
|
|||||||
import { applyConstraintAbsDistance } from './Toolbar/SetAbsDistance'
|
import { applyConstraintAbsDistance } from './Toolbar/SetAbsDistance'
|
||||||
import useStateMachineCommands from 'hooks/useStateMachineCommands'
|
import useStateMachineCommands from 'hooks/useStateMachineCommands'
|
||||||
import { modelingMachineConfig } from 'lib/commandBarConfigs/modelingCommandConfig'
|
import { modelingMachineConfig } from 'lib/commandBarConfigs/modelingCommandConfig'
|
||||||
|
import { sceneInfra } from 'clientSideScene/sceneInfra'
|
||||||
|
import { getSketchQuaternion } from 'clientSideScene/sceneEntities'
|
||||||
|
import { startSketchOnDefault } from 'lang/modifyAst'
|
||||||
|
import { Program } from 'lang/wasm'
|
||||||
|
import { isSingleCursorInPipe } from 'lang/queryAst'
|
||||||
|
|
||||||
type MachineContext<T extends AnyStateMachine> = {
|
type MachineContext<T extends AnyStateMachine> = {
|
||||||
state: StateFrom<T>
|
state: StateFrom<T>
|
||||||
@ -92,181 +81,10 @@ export const ModelingMachineProvider = ({
|
|||||||
const [modelingState, modelingSend, modelingActor] = useMachine(
|
const [modelingState, modelingSend, modelingActor] = useMachine(
|
||||||
modelingMachine,
|
modelingMachine,
|
||||||
{
|
{
|
||||||
// context: persistedSettings,
|
|
||||||
actions: {
|
actions: {
|
||||||
'Modify AST': () => {},
|
|
||||||
'Update code selection cursors': () => {},
|
|
||||||
'show default planes': () => {
|
|
||||||
kclManager.showPlanes()
|
|
||||||
},
|
|
||||||
'create path': assign({
|
|
||||||
sketchEnginePathId: () => {
|
|
||||||
const sketchUuid = uuidv4()
|
|
||||||
engineCommandManager.sendSceneCommand({
|
|
||||||
type: 'modeling_cmd_req',
|
|
||||||
cmd_id: sketchUuid,
|
|
||||||
cmd: {
|
|
||||||
type: 'start_path',
|
|
||||||
},
|
|
||||||
})
|
|
||||||
engineCommandManager.sendSceneCommand({
|
|
||||||
type: 'modeling_cmd_req',
|
|
||||||
cmd_id: uuidv4(),
|
|
||||||
cmd: {
|
|
||||||
type: 'edit_mode_enter',
|
|
||||||
target: sketchUuid,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
return sketchUuid
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
'AST start new sketch': assign(
|
|
||||||
({ sketchEnginePathId }, { data: { coords, axis, segmentId } }) => {
|
|
||||||
if (!axis) {
|
|
||||||
// Something really weird must have happened for this to happen.
|
|
||||||
console.error('axis is undefined for starting a new sketch')
|
|
||||||
return {}
|
|
||||||
}
|
|
||||||
if (!segmentId) {
|
|
||||||
// Something really weird must have happened for this to happen.
|
|
||||||
console.error('segmentId is undefined for starting a new sketch')
|
|
||||||
return {}
|
|
||||||
}
|
|
||||||
|
|
||||||
const _addStartSketch = addStartSketch(
|
|
||||||
kclManager.ast,
|
|
||||||
axis,
|
|
||||||
[roundOff(coords[0].x), roundOff(coords[0].y)],
|
|
||||||
[
|
|
||||||
roundOff(coords[1].x - coords[0].x),
|
|
||||||
roundOff(coords[1].y - coords[0].y),
|
|
||||||
]
|
|
||||||
)
|
|
||||||
const _modifiedAst = _addStartSketch.modifiedAst
|
|
||||||
const _pathToNode = _addStartSketch.pathToNode
|
|
||||||
const newCode = recast(_modifiedAst)
|
|
||||||
const astWithUpdatedSource = parse(newCode)
|
|
||||||
const updatedPipeNode = getNodeFromPath<PipeExpression>(
|
|
||||||
astWithUpdatedSource,
|
|
||||||
_pathToNode
|
|
||||||
).node
|
|
||||||
const startProfileAtCallExp = updatedPipeNode.body.find(
|
|
||||||
(exp) =>
|
|
||||||
exp.type === 'CallExpression' &&
|
|
||||||
exp.callee.name === 'startProfileAt'
|
|
||||||
)
|
|
||||||
if (startProfileAtCallExp)
|
|
||||||
engineCommandManager.artifactMap[sketchEnginePathId] = {
|
|
||||||
type: 'result',
|
|
||||||
range: [startProfileAtCallExp.start, startProfileAtCallExp.end],
|
|
||||||
commandType: 'start_path',
|
|
||||||
data: null,
|
|
||||||
raw: {} as any,
|
|
||||||
}
|
|
||||||
const lineCallExp = updatedPipeNode.body.find(
|
|
||||||
(exp) =>
|
|
||||||
exp.type === 'CallExpression' && exp.callee.name === 'line'
|
|
||||||
)
|
|
||||||
if (lineCallExp)
|
|
||||||
engineCommandManager.artifactMap[segmentId] = {
|
|
||||||
type: 'result',
|
|
||||||
range: [lineCallExp.start, lineCallExp.end],
|
|
||||||
commandType: 'extend_path',
|
|
||||||
parentId: sketchEnginePathId,
|
|
||||||
data: null,
|
|
||||||
raw: {} as any,
|
|
||||||
}
|
|
||||||
|
|
||||||
kclManager.executeAstMock(astWithUpdatedSource, true)
|
|
||||||
|
|
||||||
return {
|
|
||||||
sketchPathToNode: _pathToNode,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
),
|
|
||||||
'AST add line segment': async (
|
|
||||||
{ sketchPathToNode, sketchEnginePathId },
|
|
||||||
{ data: { coords, segmentId } }
|
|
||||||
) => {
|
|
||||||
if (!sketchPathToNode) return
|
|
||||||
const lastCoord = coords[coords.length - 1]
|
|
||||||
|
|
||||||
const pathInfo = await engineCommandManager.sendSceneCommand({
|
|
||||||
type: 'modeling_cmd_req',
|
|
||||||
cmd_id: uuidv4(),
|
|
||||||
cmd: {
|
|
||||||
type: 'path_get_info',
|
|
||||||
path_id: sketchEnginePathId,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
const firstSegment = pathInfo?.data?.data?.segments.find(
|
|
||||||
(seg: any) => seg.command === 'line_to'
|
|
||||||
)
|
|
||||||
const firstSegCoords = await engineCommandManager.sendSceneCommand({
|
|
||||||
type: 'modeling_cmd_req',
|
|
||||||
cmd_id: uuidv4(),
|
|
||||||
cmd: {
|
|
||||||
type: 'curve_get_control_points',
|
|
||||||
curve_id: firstSegment.command_id,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
const startPathCoord = firstSegCoords?.data?.data?.control_points[0]
|
|
||||||
|
|
||||||
const isClose = compareVec2Epsilon(
|
|
||||||
[startPathCoord.x, startPathCoord.y],
|
|
||||||
[lastCoord.x, lastCoord.y]
|
|
||||||
)
|
|
||||||
|
|
||||||
let _modifiedAst: Program
|
|
||||||
if (!isClose) {
|
|
||||||
const newSketchLn = addNewSketchLn({
|
|
||||||
node: kclManager.ast,
|
|
||||||
programMemory: kclManager.programMemory,
|
|
||||||
to: [lastCoord.x, lastCoord.y],
|
|
||||||
from: [coords[0].x, coords[0].y],
|
|
||||||
fnName: 'line',
|
|
||||||
pathToNode: sketchPathToNode,
|
|
||||||
})
|
|
||||||
const _modifiedAst = newSketchLn.modifiedAst
|
|
||||||
kclManager.executeAstMock(_modifiedAst, true).then(() => {
|
|
||||||
const lineCallExp = getNodeFromPath<CallExpression>(
|
|
||||||
kclManager.ast,
|
|
||||||
newSketchLn.pathToNode
|
|
||||||
).node
|
|
||||||
if (segmentId)
|
|
||||||
engineCommandManager.artifactMap[segmentId] = {
|
|
||||||
type: 'result',
|
|
||||||
range: [lineCallExp.start, lineCallExp.end],
|
|
||||||
commandType: 'extend_path',
|
|
||||||
parentId: sketchEnginePathId,
|
|
||||||
data: null,
|
|
||||||
raw: {} as any,
|
|
||||||
}
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
_modifiedAst = addCloseToPipe({
|
|
||||||
node: kclManager.ast,
|
|
||||||
programMemory: kclManager.programMemory,
|
|
||||||
pathToNode: sketchPathToNode,
|
|
||||||
})
|
|
||||||
engineCommandManager.sendSceneCommand({
|
|
||||||
type: 'modeling_cmd_req',
|
|
||||||
cmd_id: uuidv4(),
|
|
||||||
cmd: { type: 'edit_mode_exit' },
|
|
||||||
})
|
|
||||||
engineCommandManager.sendSceneCommand({
|
|
||||||
type: 'modeling_cmd_req',
|
|
||||||
cmd_id: uuidv4(),
|
|
||||||
cmd: { type: 'default_camera_disable_sketch_mode' },
|
|
||||||
})
|
|
||||||
kclManager.executeAstMock(_modifiedAst, true)
|
|
||||||
// updateAst(_modifiedAst, true)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
'sketch exit execute': () => {
|
'sketch exit execute': () => {
|
||||||
kclManager.executeAst()
|
kclManager.executeAst()
|
||||||
},
|
},
|
||||||
'set tool': () => {}, // TODO
|
|
||||||
'Set selection': assign(({ selectionRanges }, event) => {
|
'Set selection': assign(({ selectionRanges }, event) => {
|
||||||
if (event.type !== 'Set selection') return {} // this was needed for ts after adding 'Set selection' action to on done modal events
|
if (event.type !== 'Set selection') return {} // this was needed for ts after adding 'Set selection' action to on done modal events
|
||||||
const setSelections = event.data
|
const setSelections = event.data
|
||||||
@ -274,36 +92,6 @@ export const ModelingMachineProvider = ({
|
|||||||
if (setSelections.selectionType === 'mirrorCodeMirrorSelections')
|
if (setSelections.selectionType === 'mirrorCodeMirrorSelections')
|
||||||
return { selectionRanges: setSelections.selection }
|
return { selectionRanges: setSelections.selection }
|
||||||
else if (setSelections.selectionType === 'otherSelection') {
|
else if (setSelections.selectionType === 'otherSelection') {
|
||||||
// TODO KittyCAD/engine/issues/1620: send axis highlight when it's working (if that's what we settle on)
|
|
||||||
// const axisAddCmd: EngineCommand = {
|
|
||||||
// type: 'modeling_cmd_req',
|
|
||||||
// cmd: {
|
|
||||||
// type: 'highlight_set_entities',
|
|
||||||
// entities: [
|
|
||||||
// setSelections.selection === 'x-axis'
|
|
||||||
// ? X_AXIS_UUID
|
|
||||||
// : Y_AXIS_UUID,
|
|
||||||
// ],
|
|
||||||
// },
|
|
||||||
// cmd_id: uuidv4(),
|
|
||||||
// }
|
|
||||||
|
|
||||||
// if (!isShiftDown) {
|
|
||||||
// engineCommandManager
|
|
||||||
// .sendSceneCommand({
|
|
||||||
// type: 'modeling_cmd_req',
|
|
||||||
// cmd: {
|
|
||||||
// type: 'select_clear',
|
|
||||||
// },
|
|
||||||
// cmd_id: uuidv4(),
|
|
||||||
// })
|
|
||||||
// .then(() => {
|
|
||||||
// engineCommandManager.sendSceneCommand(axisAddCmd)
|
|
||||||
// })
|
|
||||||
// } else {
|
|
||||||
// engineCommandManager.sendSceneCommand(axisAddCmd)
|
|
||||||
// }
|
|
||||||
|
|
||||||
const {
|
const {
|
||||||
codeMirrorSelection,
|
codeMirrorSelection,
|
||||||
selectionRangeTypeMap,
|
selectionRangeTypeMap,
|
||||||
@ -384,12 +172,6 @@ export const ModelingMachineProvider = ({
|
|||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
guards: {
|
guards: {
|
||||||
'Selection contains axis': () => true,
|
|
||||||
'Selection contains edge': () => true,
|
|
||||||
'Selection contains face': () => true,
|
|
||||||
'Selection contains line': () => true,
|
|
||||||
'Selection contains point': () => true,
|
|
||||||
'Selection is not empty': () => true,
|
|
||||||
'has valid extrude selection': ({ selectionRanges }) => {
|
'has valid extrude selection': ({ selectionRanges }) => {
|
||||||
// A user can begin extruding if they either have 1+ faces selected or nothing selected
|
// 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
|
// TODO: I believe this guard only allows for extruding a single face at a time
|
||||||
@ -401,7 +183,10 @@ export const ModelingMachineProvider = ({
|
|||||||
|
|
||||||
return canExtrudeSelection(selectionRanges)
|
return canExtrudeSelection(selectionRanges)
|
||||||
},
|
},
|
||||||
'Selection is one face': ({ selectionRanges }) => {
|
'Selection is on face': ({ selectionRanges }, { data }) => {
|
||||||
|
if (data?.forceNewSketch) return false
|
||||||
|
if (!isSingleCursorInPipe(selectionRanges, kclManager.ast))
|
||||||
|
return false
|
||||||
return !!isCursorInSketchCommandRange(
|
return !!isCursorInSketchCommandRange(
|
||||||
engineCommandManager.artifactMap,
|
engineCommandManager.artifactMap,
|
||||||
selectionRanges
|
selectionRanges
|
||||||
@ -409,6 +194,41 @@ export const ModelingMachineProvider = ({
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
services: {
|
services: {
|
||||||
|
'AST-undo-startSketchOn': async ({ sketchPathToNode }) => {
|
||||||
|
if (!sketchPathToNode) return
|
||||||
|
const newAst: Program = JSON.parse(JSON.stringify(kclManager.ast))
|
||||||
|
const varDecIndex = sketchPathToNode[1][0]
|
||||||
|
// remove body item at varDecIndex
|
||||||
|
newAst.body = newAst.body.filter((_, i) => i !== varDecIndex)
|
||||||
|
await kclManager.executeAstMock(newAst, { updates: 'code' })
|
||||||
|
sceneInfra.setCallbacks({
|
||||||
|
onClick: () => {},
|
||||||
|
onDrag: () => {},
|
||||||
|
})
|
||||||
|
},
|
||||||
|
'animate-to-face': async (_, { data: { plane, normal } }) => {
|
||||||
|
const { modifiedAst, pathToNode } = startSketchOnDefault(
|
||||||
|
kclManager.ast,
|
||||||
|
plane
|
||||||
|
)
|
||||||
|
await kclManager.updateAst(modifiedAst, false)
|
||||||
|
const quaternion = getSketchQuaternion(pathToNode, normal)
|
||||||
|
await sceneInfra.tweenCameraToQuaternion(quaternion)
|
||||||
|
return {
|
||||||
|
sketchPathToNode: pathToNode,
|
||||||
|
sketchNormalBackUp: normal,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'animate-to-sketch': async ({
|
||||||
|
sketchPathToNode,
|
||||||
|
sketchNormalBackUp,
|
||||||
|
}) => {
|
||||||
|
const quaternion = getSketchQuaternion(
|
||||||
|
sketchPathToNode || [],
|
||||||
|
sketchNormalBackUp
|
||||||
|
)
|
||||||
|
await sceneInfra.tweenCameraToQuaternion(quaternion)
|
||||||
|
},
|
||||||
'Get horizontal info': async ({
|
'Get horizontal info': async ({
|
||||||
selectionRanges,
|
selectionRanges,
|
||||||
}): Promise<SetSelections> => {
|
}): Promise<SetSelections> => {
|
||||||
@ -542,17 +362,6 @@ export const ModelingMachineProvider = ({
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
engineCommandManager.onPlaneSelected((plane_id: string) => {
|
|
||||||
if (modelingState.nextEvents.includes('Select default plane')) {
|
|
||||||
modelingSend({
|
|
||||||
type: 'Select default plane',
|
|
||||||
data: { planeId: plane_id },
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}, [modelingSend, modelingState.nextEvents])
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
kclManager.registerExecuteCallback(() => {
|
kclManager.registerExecuteCallback(() => {
|
||||||
modelingSend({ type: 'Re-execute' })
|
modelingSend({ type: 'Re-execute' })
|
||||||
@ -565,10 +374,7 @@ export const ModelingMachineProvider = ({
|
|||||||
send: modelingSend,
|
send: modelingSend,
|
||||||
actor: modelingActor,
|
actor: modelingActor,
|
||||||
commandBarConfig: modelingMachineConfig,
|
commandBarConfig: modelingMachineConfig,
|
||||||
onCancel: () => {
|
onCancel: () => modelingSend({ type: 'Cancel' }),
|
||||||
console.log('firing onCancel!!')
|
|
||||||
modelingSend({ type: 'Cancel' })
|
|
||||||
},
|
|
||||||
})
|
})
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -3,8 +3,9 @@ import { BrowserRouter } from 'react-router-dom'
|
|||||||
import { GlobalStateProvider } from './GlobalStateProvider'
|
import { GlobalStateProvider } from './GlobalStateProvider'
|
||||||
import CommandBarProvider from './CommandBar/CommandBar'
|
import CommandBarProvider from './CommandBar/CommandBar'
|
||||||
import {
|
import {
|
||||||
NETWORK_CONTENT,
|
NETWORK_HEALTH_TEXT,
|
||||||
NetworkHealthIndicator,
|
NetworkHealthIndicator,
|
||||||
|
NetworkHealthState,
|
||||||
} from './NetworkHealthIndicator'
|
} from './NetworkHealthIndicator'
|
||||||
|
|
||||||
function TestWrap({ children }: { children: React.ReactNode }) {
|
function TestWrap({ children }: { children: React.ReactNode }) {
|
||||||
@ -28,8 +29,8 @@ describe('NetworkHealthIndicator tests', () => {
|
|||||||
|
|
||||||
fireEvent.click(screen.getByTestId('network-toggle'))
|
fireEvent.click(screen.getByTestId('network-toggle'))
|
||||||
|
|
||||||
expect(screen.getByTestId('network-good')).toHaveTextContent(
|
expect(screen.getByTestId('network')).toHaveTextContent(
|
||||||
NETWORK_CONTENT.good
|
NETWORK_HEALTH_TEXT[NetworkHealthState.Ok]
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -43,8 +44,8 @@ describe('NetworkHealthIndicator tests', () => {
|
|||||||
fireEvent.offline(window)
|
fireEvent.offline(window)
|
||||||
fireEvent.click(screen.getByTestId('network-toggle'))
|
fireEvent.click(screen.getByTestId('network-toggle'))
|
||||||
|
|
||||||
expect(screen.getByTestId('network-bad')).toHaveTextContent(
|
expect(screen.getByTestId('network')).toHaveTextContent(
|
||||||
NETWORK_CONTENT.bad
|
NETWORK_HEALTH_TEXT[NetworkHealthState.Disconnected]
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -1,41 +1,186 @@
|
|||||||
import { faExclamation, faWifi } from '@fortawesome/free-solid-svg-icons'
|
|
||||||
import { Popover } from '@headlessui/react'
|
import { Popover } from '@headlessui/react'
|
||||||
import { useEffect, useState } from 'react'
|
import { useEffect, useState } from 'react'
|
||||||
import { ActionIcon } from './ActionIcon'
|
import { ActionIcon, ActionIconProps } from './ActionIcon'
|
||||||
|
import {
|
||||||
|
ConnectingType,
|
||||||
|
ConnectingTypeGroup,
|
||||||
|
DisconnectingType,
|
||||||
|
engineCommandManager,
|
||||||
|
EngineConnectionState,
|
||||||
|
EngineConnectionStateType,
|
||||||
|
ErrorType,
|
||||||
|
initialConnectingTypeGroupState,
|
||||||
|
} from '../lang/std/engineConnection'
|
||||||
|
import Tooltip from './Tooltip'
|
||||||
|
|
||||||
export const NETWORK_CONTENT = {
|
export enum NetworkHealthState {
|
||||||
good: 'Network health is good',
|
Ok,
|
||||||
bad: 'Network issue',
|
Issue,
|
||||||
|
Disconnected,
|
||||||
}
|
}
|
||||||
|
|
||||||
const NETWORK_MESSAGES = {
|
export const NETWORK_HEALTH_TEXT: Record<NetworkHealthState, string> = {
|
||||||
offline: 'You are offline',
|
[NetworkHealthState.Ok]: 'Connected',
|
||||||
|
[NetworkHealthState.Issue]: 'Problem',
|
||||||
|
[NetworkHealthState.Disconnected]: 'Offline',
|
||||||
|
}
|
||||||
|
|
||||||
|
type IconColorConfig = {
|
||||||
|
icon: string
|
||||||
|
bg: string
|
||||||
|
}
|
||||||
|
|
||||||
|
const hasIssueToIcon: Record<
|
||||||
|
string | number | symbol,
|
||||||
|
ActionIconProps['icon']
|
||||||
|
> = {
|
||||||
|
true: 'close',
|
||||||
|
undefined: 'horizontalDash',
|
||||||
|
false: 'checkmark',
|
||||||
|
}
|
||||||
|
|
||||||
|
const hasIssueToIconColors: Record<string | number | symbol, IconColorConfig> =
|
||||||
|
{
|
||||||
|
true: {
|
||||||
|
icon: 'text-destroy-80 dark:text-destroy-10',
|
||||||
|
bg: 'bg-destroy-10 dark:bg-destroy-80',
|
||||||
|
},
|
||||||
|
undefined: {
|
||||||
|
icon: 'text-chalkboard-70 dark:text-chalkboard-30',
|
||||||
|
bg: 'bg-chalkboard-30 dark:bg-chalkboard-70',
|
||||||
|
},
|
||||||
|
false: {
|
||||||
|
icon: 'text-chalkboard-110 dark:!text-chalkboard-10',
|
||||||
|
bg: 'bg-transparent dark:bg-transparent',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
const overallConnectionStateColor: Record<NetworkHealthState, IconColorConfig> =
|
||||||
|
{
|
||||||
|
[NetworkHealthState.Ok]: {
|
||||||
|
icon: 'text-energy-80 dark:text-energy-10',
|
||||||
|
bg: 'bg-energy-10/30 dark:bg-energy-80/50',
|
||||||
|
},
|
||||||
|
[NetworkHealthState.Issue]: {
|
||||||
|
icon: 'text-destroy-80 dark:text-destroy-10',
|
||||||
|
bg: 'bg-destroy-10 dark:bg-destroy-80/80',
|
||||||
|
},
|
||||||
|
[NetworkHealthState.Disconnected]: {
|
||||||
|
icon: 'text-destroy-80 dark:text-destroy-10',
|
||||||
|
bg: 'bg-destroy-10 dark:bg-destroy-80',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
const overallConnectionStateIcon: Record<
|
||||||
|
NetworkHealthState,
|
||||||
|
ActionIconProps['icon']
|
||||||
|
> = {
|
||||||
|
[NetworkHealthState.Ok]: 'network',
|
||||||
|
[NetworkHealthState.Issue]: 'networkCrossedOut',
|
||||||
|
[NetworkHealthState.Disconnected]: 'networkCrossedOut',
|
||||||
}
|
}
|
||||||
|
|
||||||
export const NetworkHealthIndicator = () => {
|
export const NetworkHealthIndicator = () => {
|
||||||
const [networkIssues, setNetworkIssues] = useState<string[]>([])
|
const [steps, setSteps] = useState(initialConnectingTypeGroupState)
|
||||||
const hasIssues = [...networkIssues.values()].length > 0
|
const [internetConnected, setInternetConnected] = useState<boolean>(true)
|
||||||
|
const [overallState, setOverallState] = useState<NetworkHealthState>(
|
||||||
|
NetworkHealthState.Ok
|
||||||
|
)
|
||||||
|
const [hasCopied, setHasCopied] = useState<boolean>(false)
|
||||||
|
|
||||||
|
const [error, setError] = useState<ErrorType | undefined>(undefined)
|
||||||
|
|
||||||
|
const issues: Record<ConnectingTypeGroup, boolean> = {
|
||||||
|
[ConnectingTypeGroup.WebSocket]: steps[ConnectingTypeGroup.WebSocket].some(
|
||||||
|
(a: [ConnectingType, boolean | undefined]) => a[1] === false
|
||||||
|
),
|
||||||
|
[ConnectingTypeGroup.ICE]: steps[ConnectingTypeGroup.ICE].some(
|
||||||
|
(a: [ConnectingType, boolean | undefined]) => a[1] === false
|
||||||
|
),
|
||||||
|
[ConnectingTypeGroup.WebRTC]: steps[ConnectingTypeGroup.WebRTC].some(
|
||||||
|
(a: [ConnectingType, boolean | undefined]) => a[1] === false
|
||||||
|
),
|
||||||
|
}
|
||||||
|
|
||||||
|
const hasIssues: boolean =
|
||||||
|
issues[ConnectingTypeGroup.WebSocket] ||
|
||||||
|
issues[ConnectingTypeGroup.ICE] ||
|
||||||
|
issues[ConnectingTypeGroup.WebRTC]
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const offlineListener = () =>
|
setOverallState(
|
||||||
setNetworkIssues((issues) => {
|
!internetConnected
|
||||||
return [
|
? NetworkHealthState.Disconnected
|
||||||
...issues.filter((issue) => issue !== NETWORK_MESSAGES.offline),
|
: hasIssues
|
||||||
NETWORK_MESSAGES.offline,
|
? NetworkHealthState.Issue
|
||||||
]
|
: NetworkHealthState.Ok
|
||||||
})
|
)
|
||||||
window.addEventListener('offline', offlineListener)
|
}, [hasIssues, internetConnected])
|
||||||
|
|
||||||
const onlineListener = () =>
|
useEffect(() => {
|
||||||
setNetworkIssues((issues) => {
|
const cb1 = () => {
|
||||||
return [...issues.filter((issue) => issue !== NETWORK_MESSAGES.offline)]
|
setSteps(initialConnectingTypeGroupState)
|
||||||
})
|
setInternetConnected(true)
|
||||||
window.addEventListener('online', onlineListener)
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
window.removeEventListener('offline', offlineListener)
|
|
||||||
window.removeEventListener('online', onlineListener)
|
|
||||||
}
|
}
|
||||||
|
const cb2 = () => {
|
||||||
|
setInternetConnected(false)
|
||||||
|
}
|
||||||
|
window.addEventListener('online', cb1)
|
||||||
|
window.addEventListener('offline', cb2)
|
||||||
|
return () => {
|
||||||
|
window.removeEventListener('online', cb1)
|
||||||
|
window.removeEventListener('offline', cb2)
|
||||||
|
}
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
engineCommandManager.onConnectionStateChange(
|
||||||
|
(engineConnectionState: EngineConnectionState) => {
|
||||||
|
let hasSetAStep = false
|
||||||
|
|
||||||
|
if (
|
||||||
|
engineConnectionState.type === EngineConnectionStateType.Connecting
|
||||||
|
) {
|
||||||
|
const groups = Object.values(steps)
|
||||||
|
for (let group of groups) {
|
||||||
|
for (let step of group) {
|
||||||
|
if (step[0] !== engineConnectionState.value.type) continue
|
||||||
|
step[1] = true
|
||||||
|
hasSetAStep = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
engineConnectionState.type === EngineConnectionStateType.Disconnecting
|
||||||
|
) {
|
||||||
|
const groups = Object.values(steps)
|
||||||
|
for (let group of groups) {
|
||||||
|
for (let step of group) {
|
||||||
|
if (
|
||||||
|
engineConnectionState.value.type === DisconnectingType.Error
|
||||||
|
) {
|
||||||
|
if (
|
||||||
|
engineConnectionState.value.value.lastConnectingValue
|
||||||
|
?.type === step[0]
|
||||||
|
) {
|
||||||
|
step[1] = false
|
||||||
|
hasSetAStep = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (engineConnectionState.value.type === DisconnectingType.Error) {
|
||||||
|
setError(engineConnectionState.value.value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasSetAStep) {
|
||||||
|
setSteps(steps)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -45,65 +190,94 @@ export const NetworkHealthIndicator = () => {
|
|||||||
'p-0 border-none bg-transparent dark:bg-transparent relative ' +
|
'p-0 border-none bg-transparent dark:bg-transparent relative ' +
|
||||||
(hasIssues
|
(hasIssues
|
||||||
? 'focus-visible:outline-destroy-80'
|
? 'focus-visible:outline-destroy-80'
|
||||||
: 'focus-visible:outline-succeed-80')
|
: 'focus-visible:outline-energy-80')
|
||||||
}
|
}
|
||||||
data-testid="network-toggle"
|
data-testid="network-toggle"
|
||||||
>
|
>
|
||||||
<span className="sr-only">Network Health</span>
|
<span className="sr-only">Network Health</span>
|
||||||
<ActionIcon
|
<ActionIcon
|
||||||
icon={faWifi}
|
icon={overallConnectionStateIcon[overallState]}
|
||||||
className="p-1"
|
className="p-1"
|
||||||
iconClassName={
|
iconClassName={overallConnectionStateColor[overallState].icon}
|
||||||
hasIssues
|
|
||||||
? 'text-destroy-80 dark:text-destroy-30'
|
|
||||||
: 'text-succeed-80 dark:text-succeed-30'
|
|
||||||
}
|
|
||||||
bgClassName={
|
bgClassName={
|
||||||
'bg-transparent dark:bg-transparent ' +
|
'rounded-sm ' + overallConnectionStateColor[overallState].bg
|
||||||
(hasIssues
|
|
||||||
? 'hover:bg-destroy-10/50 hover:dark:bg-destroy-80/50 rounded'
|
|
||||||
: 'hover:bg-succeed-10/50 hover:dark:bg-succeed-80/50 rounded')
|
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
<Tooltip position="blockEnd" delay={750} className="ui-open:hidden">
|
||||||
|
Network Health ({NETWORK_HEALTH_TEXT[overallState]})
|
||||||
|
</Tooltip>
|
||||||
</Popover.Button>
|
</Popover.Button>
|
||||||
<Popover.Panel className="absolute right-0 left-auto top-full mt-1 w-56 flex flex-col gap-1 divide-y divide-chalkboard-20 dark:divide-chalkboard-70 align-stretch py-2 bg-chalkboard-10 dark:bg-chalkboard-90 rounded shadow-lg border border-solid border-chalkboard-20/50 dark:border-chalkboard-80/50 text-sm">
|
<Popover.Panel className="absolute right-0 left-auto top-full mt-1 w-64 flex flex-col gap-1 align-stretch bg-chalkboard-10 dark:bg-chalkboard-90 rounded shadow-lg border border-solid border-chalkboard-20/50 dark:border-chalkboard-80/50 text-sm">
|
||||||
{!hasIssues ? (
|
<div
|
||||||
<span
|
className={`flex items-center justify-between p-2 rounded-t-sm ${overallConnectionStateColor[overallState].bg} ${overallConnectionStateColor[overallState].icon}`}
|
||||||
className="flex items-center justify-center gap-1 px-4"
|
>
|
||||||
data-testid="network-good"
|
<h2 className="text-sm font-sans font-normal">Network health</h2>
|
||||||
|
<p
|
||||||
|
data-testid="network"
|
||||||
|
className="font-bold text-xs uppercase px-2 py-1 rounded-sm"
|
||||||
>
|
>
|
||||||
<ActionIcon
|
{NETWORK_HEALTH_TEXT[overallState]}
|
||||||
icon="checkmark"
|
</p>
|
||||||
bgClassName={'bg-succeed-10/50 dark:bg-succeed-80/50 rounded-sm'}
|
</div>
|
||||||
iconClassName={'text-succeed-80 dark:text-succeed-30'}
|
<ul className="divide-y divide-chalkboard-20 dark:divide-chalkboard-80">
|
||||||
/>
|
{Object.keys(steps).map((name) => (
|
||||||
{NETWORK_CONTENT.good}
|
<li
|
||||||
</span>
|
key={name}
|
||||||
) : (
|
className={'flex flex-col px-2 py-4 gap-1 last:mb-0 '}
|
||||||
<ul className="divide-y divide-chalkboard-20 dark:divide-chalkboard-80">
|
|
||||||
<span
|
|
||||||
className="font-bold text-xs uppercase text-destroy-60 dark:text-destroy-50 px-4"
|
|
||||||
data-testid="network-bad"
|
|
||||||
>
|
>
|
||||||
{NETWORK_CONTENT.bad}
|
<div className="flex items-center text-left gap-1">
|
||||||
{networkIssues.length > 1 ? 's' : ''}
|
<p className="flex-1">{name}</p>
|
||||||
</span>
|
{internetConnected ? (
|
||||||
{networkIssues.map((issue) => (
|
<ActionIcon
|
||||||
<li
|
size="lg"
|
||||||
key={issue}
|
icon={
|
||||||
className="flex items-center gap-1 py-2 my-2 last:mb-0"
|
hasIssueToIcon[
|
||||||
>
|
issues[name as ConnectingTypeGroup].toString()
|
||||||
<ActionIcon
|
]
|
||||||
icon={faExclamation}
|
}
|
||||||
bgClassName={'bg-destroy-10/50 dark:bg-destroy-80/50 rounded'}
|
iconClassName={
|
||||||
iconClassName={'text-destroy-80 dark:text-destroy-30'}
|
hasIssueToIconColors[
|
||||||
className="ml-4"
|
issues[name as ConnectingTypeGroup].toString()
|
||||||
/>
|
].icon
|
||||||
<p className="flex-1 mr-4">{issue}</p>
|
}
|
||||||
</li>
|
bgClassName={
|
||||||
))}
|
'rounded-sm ' +
|
||||||
</ul>
|
hasIssueToIconColors[
|
||||||
)}
|
issues[name as ConnectingTypeGroup].toString()
|
||||||
|
].bg
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<ActionIcon
|
||||||
|
icon={hasIssueToIcon.true}
|
||||||
|
bgClassName={hasIssueToIconColors.true.bg}
|
||||||
|
iconClassName={hasIssueToIconColors.true.icon}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
{issues[name as ConnectingTypeGroup] && (
|
||||||
|
<button
|
||||||
|
onClick={async () => {
|
||||||
|
await navigator.clipboard.writeText(
|
||||||
|
JSON.stringify(error, null, 2) || ''
|
||||||
|
)
|
||||||
|
setHasCopied(true)
|
||||||
|
setTimeout(() => setHasCopied(false), 5000)
|
||||||
|
}}
|
||||||
|
className="flex w-fit gap-2 items-center bg-transparent text-sm p-1 py-0 my-0 -mx-1 text-destroy-80 dark:text-destroy-10 hover:bg-transparent border-transparent dark:border-transparent hover:border-destroy-80 dark:hover:border-destroy-80 dark:hover:bg-destroy-80"
|
||||||
|
>
|
||||||
|
{hasCopied ? 'Copied' : 'Copy Error'}
|
||||||
|
<ActionIcon
|
||||||
|
size="lg"
|
||||||
|
icon={hasCopied ? 'clipboardCheckmark' : 'clipboardPlus'}
|
||||||
|
iconClassName="text-inherit dark:text-inherit"
|
||||||
|
bgClassName="!bg-transparent"
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
</Popover.Panel>
|
</Popover.Panel>
|
||||||
</Popover>
|
</Popover>
|
||||||
)
|
)
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import { FormEvent, useEffect, useRef, useState } from 'react'
|
import { FormEvent, useEffect, useRef, useState } from 'react'
|
||||||
import { type ProjectWithEntryPointMetadata, paths } from '../Router'
|
import { type ProjectWithEntryPointMetadata } from 'lib/types'
|
||||||
|
import { paths } from 'lib/paths'
|
||||||
import { Link } from 'react-router-dom'
|
import { Link } from 'react-router-dom'
|
||||||
import { ActionButton } from './ActionButton'
|
import { ActionButton } from './ActionButton'
|
||||||
import {
|
import {
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { fireEvent, render, screen } from '@testing-library/react'
|
import { fireEvent, render, screen } from '@testing-library/react'
|
||||||
import { BrowserRouter } from 'react-router-dom'
|
import { BrowserRouter } from 'react-router-dom'
|
||||||
import ProjectSidebarMenu from './ProjectSidebarMenu'
|
import ProjectSidebarMenu from './ProjectSidebarMenu'
|
||||||
import { ProjectWithEntryPointMetadata } from '../Router'
|
import { type ProjectWithEntryPointMetadata } from 'lib/types'
|
||||||
import { GlobalStateProvider } from './GlobalStateProvider'
|
import { GlobalStateProvider } from './GlobalStateProvider'
|
||||||
import CommandBarProvider from './CommandBar/CommandBar'
|
import CommandBarProvider from './CommandBar/CommandBar'
|
||||||
import { APP_NAME } from 'lib/constants'
|
import { APP_NAME } from 'lib/constants'
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
import { Popover, Transition } from '@headlessui/react'
|
import { Popover, Transition } from '@headlessui/react'
|
||||||
import { ActionButton } from './ActionButton'
|
import { ActionButton } from './ActionButton'
|
||||||
import { faHome } from '@fortawesome/free-solid-svg-icons'
|
import { faHome } from '@fortawesome/free-solid-svg-icons'
|
||||||
import { IndexLoaderData, paths } from '../Router'
|
import { type IndexLoaderData } from 'lib/types'
|
||||||
|
import { paths } from 'lib/paths'
|
||||||
import { isTauri } from '../lib/isTauri'
|
import { isTauri } from '../lib/isTauri'
|
||||||
import { Link } from 'react-router-dom'
|
import { Link } from 'react-router-dom'
|
||||||
import { ExportButton } from './ExportButton'
|
import { ExportButton } from './ExportButton'
|
||||||
|
@ -11,16 +11,13 @@ import { getNormalisedCoordinates, throttle } from '../lib/utils'
|
|||||||
import Loading from './Loading'
|
import Loading from './Loading'
|
||||||
import { cameraMouseDragGuards } from 'lib/cameraControls'
|
import { cameraMouseDragGuards } from 'lib/cameraControls'
|
||||||
import { useGlobalStateContext } from 'hooks/useGlobalStateContext'
|
import { useGlobalStateContext } from 'hooks/useGlobalStateContext'
|
||||||
import { CameraDragInteractionType_type } from '@kittycad/lib/dist/types/src/models'
|
|
||||||
import { Models } from '@kittycad/lib'
|
import { Models } from '@kittycad/lib'
|
||||||
import { getNodeFromPath } from 'lang/queryAst'
|
|
||||||
import { VariableDeclarator, recast, CallExpression } from 'lang/wasm'
|
|
||||||
import { engineCommandManager } from '../lang/std/engineConnection'
|
import { engineCommandManager } from '../lang/std/engineConnection'
|
||||||
import { useModelingContext } from 'hooks/useModelingContext'
|
import { useModelingContext } from 'hooks/useModelingContext'
|
||||||
import { kclManager, useKclContext } from 'lang/KclSinglton'
|
import { useKclContext } from 'lang/KclSingleton'
|
||||||
import { changeSketchArguments } from 'lang/std/sketch'
|
import { ClientSideScene } from 'clientSideScene/ClientSideSceneComp'
|
||||||
|
|
||||||
export const Stream = ({ className = '' }) => {
|
export const Stream = ({ className = '' }: { className?: string }) => {
|
||||||
const [isLoading, setIsLoading] = useState(true)
|
const [isLoading, setIsLoading] = useState(true)
|
||||||
const [clickCoords, setClickCoords] = useState<{ x: number; y: number }>()
|
const [clickCoords, setClickCoords] = useState<{ x: number; y: number }>()
|
||||||
const videoRef = useRef<HTMLVideoElement>(null)
|
const videoRef = useRef<HTMLVideoElement>(null)
|
||||||
@ -39,7 +36,7 @@ export const Stream = ({ className = '' }) => {
|
|||||||
}))
|
}))
|
||||||
const { settings } = useGlobalStateContext()
|
const { settings } = useGlobalStateContext()
|
||||||
const cameraControls = settings?.context?.cameraControls
|
const cameraControls = settings?.context?.cameraControls
|
||||||
const { send, state, context } = useModelingContext()
|
const { state } = useModelingContext()
|
||||||
const { isExecuting } = useKclContext()
|
const { isExecuting } = useKclContext()
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -53,8 +50,10 @@ export const Stream = ({ className = '' }) => {
|
|||||||
videoRef.current.srcObject = mediaStream
|
videoRef.current.srcObject = mediaStream
|
||||||
}, [mediaStream])
|
}, [mediaStream])
|
||||||
|
|
||||||
const handleMouseDown: MouseEventHandler<HTMLVideoElement> = (e) => {
|
const handleMouseDown: MouseEventHandler<HTMLDivElement> = (e) => {
|
||||||
if (!videoRef.current) return
|
if (!videoRef.current) return
|
||||||
|
if (state.matches('Sketch')) return
|
||||||
|
if (state.matches('Sketch no face')) return
|
||||||
const { x, y } = getNormalisedCoordinates({
|
const { x, y } = getNormalisedCoordinates({
|
||||||
clientX: e.clientX,
|
clientX: e.clientX,
|
||||||
clientY: e.clientY,
|
clientY: e.clientY,
|
||||||
@ -62,55 +61,6 @@ export const Stream = ({ className = '' }) => {
|
|||||||
...streamDimensions,
|
...streamDimensions,
|
||||||
})
|
})
|
||||||
|
|
||||||
const newId = uuidv4()
|
|
||||||
|
|
||||||
const interactionGuards = cameraMouseDragGuards[cameraControls]
|
|
||||||
let interaction: CameraDragInteractionType_type = 'rotate'
|
|
||||||
|
|
||||||
if (
|
|
||||||
interactionGuards.pan.callback(e) ||
|
|
||||||
interactionGuards.pan.lenientDragStartButton === e.button
|
|
||||||
) {
|
|
||||||
interaction = 'pan'
|
|
||||||
} else if (
|
|
||||||
interactionGuards.rotate.callback(e) ||
|
|
||||||
interactionGuards.rotate.lenientDragStartButton === e.button
|
|
||||||
) {
|
|
||||||
interaction = 'rotate'
|
|
||||||
} else if (
|
|
||||||
interactionGuards.zoom.dragCallback(e) ||
|
|
||||||
interactionGuards.zoom.lenientDragStartButton === e.button
|
|
||||||
) {
|
|
||||||
interaction = 'zoom'
|
|
||||||
}
|
|
||||||
|
|
||||||
if (state.matches('Sketch.Move Tool')) {
|
|
||||||
if (
|
|
||||||
state.matches('Sketch.Move Tool.No move') ||
|
|
||||||
state.matches('Sketch.Move Tool.Move with execute')
|
|
||||||
) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
engineCommandManager.sendSceneCommand({
|
|
||||||
type: 'modeling_cmd_req',
|
|
||||||
cmd: {
|
|
||||||
type: 'handle_mouse_drag_start',
|
|
||||||
window: { x, y },
|
|
||||||
},
|
|
||||||
cmd_id: newId,
|
|
||||||
})
|
|
||||||
} else if (!state.matches('Sketch.Line Tool')) {
|
|
||||||
engineCommandManager.sendSceneCommand({
|
|
||||||
type: 'modeling_cmd_req',
|
|
||||||
cmd: {
|
|
||||||
type: 'camera_drag_start',
|
|
||||||
interaction,
|
|
||||||
window: { x, y },
|
|
||||||
},
|
|
||||||
cmd_id: newId,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
setButtonDownInStream(e.button)
|
setButtonDownInStream(e.button)
|
||||||
setClickCoords({ x, y })
|
setClickCoords({ x, y })
|
||||||
}
|
}
|
||||||
@ -128,13 +78,15 @@ export const Stream = ({ className = '' }) => {
|
|||||||
})
|
})
|
||||||
}, Math.round(1000 / fps))
|
}, Math.round(1000 / fps))
|
||||||
|
|
||||||
const handleMouseUp: MouseEventHandler<HTMLVideoElement> = ({
|
const handleMouseUp: MouseEventHandler<HTMLDivElement> = ({
|
||||||
clientX,
|
clientX,
|
||||||
clientY,
|
clientY,
|
||||||
ctrlKey,
|
ctrlKey,
|
||||||
}) => {
|
}) => {
|
||||||
if (!videoRef.current) return
|
if (!videoRef.current) return
|
||||||
setButtonDownInStream(undefined)
|
setButtonDownInStream(undefined)
|
||||||
|
if (state.matches('Sketch')) return
|
||||||
|
if (state.matches('Sketch no face')) return
|
||||||
const { x, y } = getNormalisedCoordinates({
|
const { x, y } = getNormalisedCoordinates({
|
||||||
clientX,
|
clientX,
|
||||||
clientY,
|
clientY,
|
||||||
@ -155,206 +107,19 @@ export const Stream = ({ className = '' }) => {
|
|||||||
cmd_id: newCmdId,
|
cmd_id: newCmdId,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!didDragInStream && state.matches('Sketch no face')) {
|
if (!didDragInStream) {
|
||||||
command.cmd = {
|
|
||||||
type: 'select_with_point',
|
|
||||||
selection_type: 'add',
|
|
||||||
selected_at_window: { x, y },
|
|
||||||
}
|
|
||||||
engineCommandManager.sendSceneCommand(command)
|
|
||||||
} else if (!didDragInStream && state.matches('Sketch.Line Tool')) {
|
|
||||||
command.cmd = {
|
|
||||||
type: 'mouse_click',
|
|
||||||
window: { x, y },
|
|
||||||
}
|
|
||||||
engineCommandManager.sendSceneCommand(command).then(async (resp) => {
|
|
||||||
const entities_modified = resp?.data?.data?.entities_modified
|
|
||||||
if (!entities_modified) return
|
|
||||||
if (state.matches('Sketch.Line Tool.No Points')) {
|
|
||||||
send('Add point')
|
|
||||||
} else if (state.matches('Sketch.Line Tool.Point Added')) {
|
|
||||||
const curve = await engineCommandManager.sendSceneCommand({
|
|
||||||
type: 'modeling_cmd_req',
|
|
||||||
cmd_id: uuidv4(),
|
|
||||||
cmd: {
|
|
||||||
type: 'curve_get_control_points',
|
|
||||||
curve_id: entities_modified[0],
|
|
||||||
},
|
|
||||||
})
|
|
||||||
const coords: { x: number; y: number }[] =
|
|
||||||
curve.data.data.control_points
|
|
||||||
// We need the normal for the plane we are on.
|
|
||||||
const plane = await engineCommandManager.sendSceneCommand({
|
|
||||||
type: 'modeling_cmd_req',
|
|
||||||
cmd_id: uuidv4(),
|
|
||||||
cmd: {
|
|
||||||
type: 'get_sketch_mode_plane',
|
|
||||||
},
|
|
||||||
})
|
|
||||||
const z_axis = plane.data.data.z_axis
|
|
||||||
|
|
||||||
// Get the current axis.
|
|
||||||
let currentAxis: 'xy' | 'xz' | 'yz' | '-xy' | '-xz' | '-yz' | null =
|
|
||||||
null
|
|
||||||
if (context.sketchPlaneId === kclManager.getPlaneId('xy')) {
|
|
||||||
if (z_axis.z === -1) {
|
|
||||||
currentAxis = '-xy'
|
|
||||||
} else {
|
|
||||||
currentAxis = 'xy'
|
|
||||||
}
|
|
||||||
} else if (context.sketchPlaneId === kclManager.getPlaneId('yz')) {
|
|
||||||
if (z_axis.x === -1) {
|
|
||||||
currentAxis = '-yz'
|
|
||||||
} else {
|
|
||||||
currentAxis = 'yz'
|
|
||||||
}
|
|
||||||
} else if (context.sketchPlaneId === kclManager.getPlaneId('xz')) {
|
|
||||||
if (z_axis.y === -1) {
|
|
||||||
currentAxis = '-xz'
|
|
||||||
} else {
|
|
||||||
currentAxis = 'xz'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
send({
|
|
||||||
type: 'Add point',
|
|
||||||
data: {
|
|
||||||
coords,
|
|
||||||
axis: currentAxis,
|
|
||||||
segmentId: entities_modified[0],
|
|
||||||
},
|
|
||||||
})
|
|
||||||
} else if (state.matches('Sketch.Line Tool.Segment Added')) {
|
|
||||||
const curve = await engineCommandManager.sendSceneCommand({
|
|
||||||
type: 'modeling_cmd_req',
|
|
||||||
cmd_id: uuidv4(),
|
|
||||||
cmd: {
|
|
||||||
type: 'curve_get_control_points',
|
|
||||||
curve_id: entities_modified[0],
|
|
||||||
},
|
|
||||||
})
|
|
||||||
const coords: { x: number; y: number }[] =
|
|
||||||
curve.data.data.control_points
|
|
||||||
send({
|
|
||||||
type: 'Add point',
|
|
||||||
data: { coords, axis: null, segmentId: entities_modified[0] },
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
} else if (
|
|
||||||
!didDragInStream &&
|
|
||||||
(state.matches('Sketch.SketchIdle') || state.matches('idle'))
|
|
||||||
) {
|
|
||||||
command.cmd = {
|
|
||||||
type: 'select_with_point',
|
|
||||||
selected_at_window: { x, y },
|
|
||||||
selection_type: 'add',
|
|
||||||
}
|
|
||||||
|
|
||||||
engineCommandManager.sendSceneCommand(command)
|
|
||||||
} else if (!didDragInStream && state.matches('Sketch.Move Tool')) {
|
|
||||||
command.cmd = {
|
command.cmd = {
|
||||||
type: 'select_with_point',
|
type: 'select_with_point',
|
||||||
selected_at_window: { x, y },
|
selected_at_window: { x, y },
|
||||||
selection_type: 'add',
|
selection_type: 'add',
|
||||||
}
|
}
|
||||||
engineCommandManager.sendSceneCommand(command)
|
engineCommandManager.sendSceneCommand(command)
|
||||||
} else if (didDragInStream && state.matches('Sketch.Move Tool')) {
|
} else if (didDragInStream) {
|
||||||
command.cmd = {
|
command.cmd = {
|
||||||
type: 'handle_mouse_drag_end',
|
type: 'handle_mouse_drag_end',
|
||||||
window: { x, y },
|
window: { x, y },
|
||||||
}
|
}
|
||||||
engineCommandManager.sendSceneCommand(command).then(async () => {
|
void engineCommandManager.sendSceneCommand(command)
|
||||||
if (!context.sketchPathToNode) return
|
|
||||||
getNodeFromPath<VariableDeclarator>(
|
|
||||||
kclManager.ast,
|
|
||||||
context.sketchPathToNode,
|
|
||||||
'VariableDeclarator'
|
|
||||||
)
|
|
||||||
// Get the current plane string for plane we are on.
|
|
||||||
let currentPlaneString = ''
|
|
||||||
if (context.sketchPlaneId === kclManager.getPlaneId('xy')) {
|
|
||||||
currentPlaneString = 'XY'
|
|
||||||
} else if (context.sketchPlaneId === kclManager.getPlaneId('yz')) {
|
|
||||||
currentPlaneString = 'YZ'
|
|
||||||
} else if (context.sketchPlaneId === kclManager.getPlaneId('xz')) {
|
|
||||||
currentPlaneString = 'XZ'
|
|
||||||
}
|
|
||||||
|
|
||||||
// Do not supporting editing/moving lines on a non-default plane.
|
|
||||||
// Eventually we can support this but for now we will just throw an
|
|
||||||
// error.
|
|
||||||
if (currentPlaneString === '') return
|
|
||||||
|
|
||||||
const pathInfo = await engineCommandManager.sendSceneCommand({
|
|
||||||
type: 'modeling_cmd_req',
|
|
||||||
cmd_id: uuidv4(),
|
|
||||||
cmd: {
|
|
||||||
type: 'path_get_info',
|
|
||||||
path_id: context.sketchEnginePathId,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
const segmentsWithMappings = (
|
|
||||||
pathInfo?.data?.data?.segments as { command_id: string }[]
|
|
||||||
)
|
|
||||||
.filter(({ command_id }) => {
|
|
||||||
return command_id && engineCommandManager.artifactMap[command_id]
|
|
||||||
})
|
|
||||||
.map(({ command_id }) => command_id)
|
|
||||||
const segment2dInfo = await Promise.all(
|
|
||||||
segmentsWithMappings.map(async (segmentId) => {
|
|
||||||
const response = await engineCommandManager.sendSceneCommand({
|
|
||||||
type: 'modeling_cmd_req',
|
|
||||||
cmd_id: uuidv4(),
|
|
||||||
cmd: {
|
|
||||||
type: 'curve_get_control_points',
|
|
||||||
curve_id: segmentId,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
const controlPoints: [
|
|
||||||
{ x: number; y: number },
|
|
||||||
{ x: number; y: number }
|
|
||||||
] = response.data.data.control_points
|
|
||||||
return {
|
|
||||||
controlPoints,
|
|
||||||
segmentId,
|
|
||||||
}
|
|
||||||
})
|
|
||||||
)
|
|
||||||
|
|
||||||
let modifiedAst = { ...kclManager.ast }
|
|
||||||
let code = kclManager.code
|
|
||||||
for (const controlPoint of segment2dInfo) {
|
|
||||||
const range =
|
|
||||||
engineCommandManager.artifactMap[controlPoint.segmentId].range
|
|
||||||
if (!range) continue
|
|
||||||
const from = controlPoint.controlPoints[0]
|
|
||||||
const to = controlPoint.controlPoints[1]
|
|
||||||
const modded = changeSketchArguments(
|
|
||||||
modifiedAst,
|
|
||||||
kclManager.programMemory,
|
|
||||||
range,
|
|
||||||
[to.x, to.y],
|
|
||||||
[from.x, from.y]
|
|
||||||
)
|
|
||||||
modifiedAst = modded.modifiedAst
|
|
||||||
|
|
||||||
// update artifact map ranges now that we have updated the ast.
|
|
||||||
code = recast(modded.modifiedAst)
|
|
||||||
const astWithCurrentRanges = kclManager.safeParse(code)
|
|
||||||
if (!astWithCurrentRanges) return
|
|
||||||
const updateNode = getNodeFromPath<CallExpression>(
|
|
||||||
astWithCurrentRanges,
|
|
||||||
modded.pathToNode
|
|
||||||
).node
|
|
||||||
engineCommandManager.artifactMap[controlPoint.segmentId].range = [
|
|
||||||
updateNode.start,
|
|
||||||
updateNode.end,
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
kclManager.executeAstMock(modifiedAst, true)
|
|
||||||
})
|
|
||||||
} else {
|
} else {
|
||||||
engineCommandManager.sendSceneCommand(command)
|
engineCommandManager.sendSceneCommand(command)
|
||||||
}
|
}
|
||||||
@ -364,6 +129,8 @@ export const Stream = ({ className = '' }) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const handleMouseMove: MouseEventHandler<HTMLVideoElement> = (e) => {
|
const handleMouseMove: MouseEventHandler<HTMLVideoElement> = (e) => {
|
||||||
|
if (state.matches('Sketch')) return
|
||||||
|
if (state.matches('Sketch no face')) return
|
||||||
if (!clickCoords) return
|
if (!clickCoords) return
|
||||||
|
|
||||||
const delta =
|
const delta =
|
||||||
@ -376,16 +143,19 @@ export const Stream = ({ className = '' }) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div id="stream" className={className}>
|
<div
|
||||||
|
id="stream"
|
||||||
|
className={className}
|
||||||
|
onMouseUp={handleMouseUp}
|
||||||
|
onMouseDown={handleMouseDown}
|
||||||
|
onContextMenu={(e) => e.preventDefault()}
|
||||||
|
onContextMenuCapture={(e) => e.preventDefault()}
|
||||||
|
>
|
||||||
<video
|
<video
|
||||||
ref={videoRef}
|
ref={videoRef}
|
||||||
muted
|
muted
|
||||||
autoPlay
|
autoPlay
|
||||||
controls={false}
|
controls={false}
|
||||||
onMouseDown={handleMouseDown}
|
|
||||||
onMouseUp={handleMouseUp}
|
|
||||||
onContextMenu={(e) => e.preventDefault()}
|
|
||||||
onContextMenuCapture={(e) => e.preventDefault()}
|
|
||||||
onWheel={handleScroll}
|
onWheel={handleScroll}
|
||||||
onPlay={() => setIsLoading(false)}
|
onPlay={() => setIsLoading(false)}
|
||||||
onMouseMoveCapture={handleMouseMove}
|
onMouseMoveCapture={handleMouseMove}
|
||||||
@ -393,6 +163,7 @@ export const Stream = ({ className = '' }) => {
|
|||||||
disablePictureInPicture
|
disablePictureInPicture
|
||||||
style={{ transitionDuration: '200ms', transitionProperty: 'filter' }}
|
style={{ transitionDuration: '200ms', transitionProperty: 'filter' }}
|
||||||
/>
|
/>
|
||||||
|
<ClientSideScene cameraControls={settings.context.cameraControls} />
|
||||||
{isLoading && (
|
{isLoading && (
|
||||||
<div className="text-center absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2">
|
<div className="text-center absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2">
|
||||||
<Loading>
|
<Loading>
|
||||||
|
@ -3,20 +3,20 @@ import ReactCodeMirror, {
|
|||||||
ViewUpdate,
|
ViewUpdate,
|
||||||
keymap,
|
keymap,
|
||||||
} from '@uiw/react-codemirror'
|
} from '@uiw/react-codemirror'
|
||||||
import { FromServer, IntoServer } from 'editor/lsp/codec'
|
import { FromServer, IntoServer } from 'editor/plugins/lsp/codec'
|
||||||
import Server from '../editor/lsp/server'
|
import Server from '../editor/plugins/lsp/server'
|
||||||
import Client from '../editor/lsp/client'
|
import Client from '../editor/plugins/lsp/client'
|
||||||
import { TEST } from 'env'
|
import { TEST } from 'env'
|
||||||
import { useCommandsContext } from 'hooks/useCommandsContext'
|
import { useCommandsContext } from 'hooks/useCommandsContext'
|
||||||
import { useGlobalStateContext } from 'hooks/useGlobalStateContext'
|
import { useGlobalStateContext } from 'hooks/useGlobalStateContext'
|
||||||
import { useConvertToVariable } from 'hooks/useToolbarGuards'
|
import { useConvertToVariable } from 'hooks/useToolbarGuards'
|
||||||
import { Themes } from 'lib/theme'
|
import { Themes } from 'lib/theme'
|
||||||
import { useMemo } from 'react'
|
import { useMemo, useRef } from 'react'
|
||||||
import { linter, lintGutter } from '@codemirror/lint'
|
import { linter, lintGutter } from '@codemirror/lint'
|
||||||
import { useStore } from 'useStore'
|
import { useStore } from 'useStore'
|
||||||
import { processCodeMirrorRanges } from 'lib/selections'
|
import { processCodeMirrorRanges } from 'lib/selections'
|
||||||
import { LanguageServerClient } from 'editor/lsp'
|
import { LanguageServerClient } from 'editor/plugins/lsp'
|
||||||
import kclLanguage from 'editor/lsp/language'
|
import kclLanguage from 'editor/plugins/lsp/kcl/language'
|
||||||
import { EditorView, lineHighlightField } from 'editor/highlightextension'
|
import { EditorView, lineHighlightField } from 'editor/highlightextension'
|
||||||
import { roundOff } from 'lib/utils'
|
import { roundOff } from 'lib/utils'
|
||||||
import { kclErrToDiagnostic } from 'lang/errors'
|
import { kclErrToDiagnostic } from 'lang/errors'
|
||||||
@ -24,7 +24,12 @@ import { CSSRuleObject } from 'tailwindcss/types/config'
|
|||||||
import { useModelingContext } from 'hooks/useModelingContext'
|
import { useModelingContext } from 'hooks/useModelingContext'
|
||||||
import interact from '@replit/codemirror-interact'
|
import interact from '@replit/codemirror-interact'
|
||||||
import { engineCommandManager } from '../lang/std/engineConnection'
|
import { engineCommandManager } from '../lang/std/engineConnection'
|
||||||
import { kclManager, useKclContext } from 'lang/KclSinglton'
|
import { kclManager, useKclContext } from 'lang/KclSingleton'
|
||||||
|
import { ModelingMachineEvent } from 'machines/modelingMachine'
|
||||||
|
import { sceneInfra } from 'clientSideScene/sceneInfra'
|
||||||
|
import { copilotPlugin } from 'editor/plugins/lsp/copilot'
|
||||||
|
import { isTauri } from 'lib/isTauri'
|
||||||
|
import type * as LSP from 'vscode-languageserver-protocol'
|
||||||
|
|
||||||
export const editorShortcutMeta = {
|
export const editorShortcutMeta = {
|
||||||
formatCode: {
|
formatCode: {
|
||||||
@ -37,6 +42,15 @@ export const editorShortcutMeta = {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getWorkspaceFolders(): LSP.WorkspaceFolder[] {
|
||||||
|
// We only use workspace folders in Tauri since that is where we use more than
|
||||||
|
// one file.
|
||||||
|
if (isTauri()) {
|
||||||
|
return [{ uri: 'file://', name: 'ProjectRoot' }]
|
||||||
|
}
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
|
||||||
export const TextEditor = ({
|
export const TextEditor = ({
|
||||||
theme,
|
theme,
|
||||||
}: {
|
}: {
|
||||||
@ -44,25 +58,31 @@ export const TextEditor = ({
|
|||||||
}) => {
|
}) => {
|
||||||
const {
|
const {
|
||||||
editorView,
|
editorView,
|
||||||
isLSPServerReady,
|
isKclLspServerReady,
|
||||||
|
isCopilotLspServerReady,
|
||||||
setEditorView,
|
setEditorView,
|
||||||
setIsLSPServerReady,
|
setIsKclLspServerReady,
|
||||||
|
setIsCopilotLspServerReady,
|
||||||
isShiftDown,
|
isShiftDown,
|
||||||
} = useStore((s) => ({
|
} = useStore((s) => ({
|
||||||
editorView: s.editorView,
|
editorView: s.editorView,
|
||||||
isLSPServerReady: s.isLSPServerReady,
|
isKclLspServerReady: s.isKclLspServerReady,
|
||||||
|
isCopilotLspServerReady: s.isCopilotLspServerReady,
|
||||||
setEditorView: s.setEditorView,
|
setEditorView: s.setEditorView,
|
||||||
setIsLSPServerReady: s.setIsLSPServerReady,
|
setIsKclLspServerReady: s.setIsKclLspServerReady,
|
||||||
|
setIsCopilotLspServerReady: s.setIsCopilotLspServerReady,
|
||||||
isShiftDown: s.isShiftDown,
|
isShiftDown: s.isShiftDown,
|
||||||
}))
|
}))
|
||||||
const { code, errors } = useKclContext()
|
const { code, errors } = useKclContext()
|
||||||
|
const lastEvent = useRef({ event: '', time: Date.now() })
|
||||||
|
|
||||||
const {
|
const {
|
||||||
context: { selectionRanges, selectionRangeTypeMap },
|
context: { selectionRanges, selectionRangeTypeMap },
|
||||||
send,
|
send,
|
||||||
|
state,
|
||||||
} = useModelingContext()
|
} = useModelingContext()
|
||||||
|
|
||||||
const { settings: { context: { textWrapping } = {} } = {} } =
|
const { settings: { context: { textWrapping } = {} } = {}, auth } =
|
||||||
useGlobalStateContext()
|
useGlobalStateContext()
|
||||||
const { commandBarSend } = useCommandsContext()
|
const { commandBarSend } = useCommandsContext()
|
||||||
const { enable: convertEnabled, handleClick: convertCallback } =
|
const { enable: convertEnabled, handleClick: convertCallback } =
|
||||||
@ -71,20 +91,20 @@ export const TextEditor = ({
|
|||||||
// So this is a bit weird, we need to initialize the lsp server and client.
|
// So this is a bit weird, we need to initialize the lsp server and client.
|
||||||
// But the server happens async so we break this into two parts.
|
// But the server happens async so we break this into two parts.
|
||||||
// Below is the client and server promise.
|
// Below is the client and server promise.
|
||||||
const { lspClient } = useMemo(() => {
|
const { lspClient: kclLspClient } = useMemo(() => {
|
||||||
const intoServer: IntoServer = new IntoServer()
|
const intoServer: IntoServer = new IntoServer()
|
||||||
const fromServer: FromServer = FromServer.create()
|
const fromServer: FromServer = FromServer.create()
|
||||||
const client = new Client(fromServer, intoServer)
|
const client = new Client(fromServer, intoServer)
|
||||||
if (!TEST) {
|
if (!TEST) {
|
||||||
Server.initialize(intoServer, fromServer).then((lspServer) => {
|
Server.initialize(intoServer, fromServer).then((lspServer) => {
|
||||||
lspServer.start()
|
lspServer.start('kcl')
|
||||||
setIsLSPServerReady(true)
|
setIsKclLspServerReady(true)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const lspClient = new LanguageServerClient({ client })
|
const lspClient = new LanguageServerClient({ client, name: 'kcl' })
|
||||||
return { lspClient }
|
return { lspClient }
|
||||||
}, [setIsLSPServerReady])
|
}, [setIsKclLspServerReady])
|
||||||
|
|
||||||
// Here we initialize the plugin which will start the client.
|
// Here we initialize the plugin which will start the client.
|
||||||
// When we have multi-file support the name of the file will be a dep of
|
// When we have multi-file support the name of the file will be a dep of
|
||||||
@ -93,19 +113,57 @@ export const TextEditor = ({
|
|||||||
// We do not want to restart the server, its just wasteful.
|
// We do not want to restart the server, its just wasteful.
|
||||||
const kclLSP = useMemo(() => {
|
const kclLSP = useMemo(() => {
|
||||||
let plugin = null
|
let plugin = null
|
||||||
if (isLSPServerReady && !TEST) {
|
if (isKclLspServerReady && !TEST) {
|
||||||
// Set up the lsp plugin.
|
// Set up the lsp plugin.
|
||||||
const lsp = kclLanguage({
|
const lsp = kclLanguage({
|
||||||
// When we have more than one file, we'll need to change this.
|
// When we have more than one file, we'll need to change this.
|
||||||
documentUri: `file:///we-just-have-one-file-for-now.kcl`,
|
documentUri: `file:///we-just-have-one-file-for-now.kcl`,
|
||||||
workspaceFolders: null,
|
workspaceFolders: getWorkspaceFolders(),
|
||||||
client: lspClient,
|
client: kclLspClient,
|
||||||
})
|
})
|
||||||
|
|
||||||
plugin = lsp
|
plugin = lsp
|
||||||
}
|
}
|
||||||
return plugin
|
return plugin
|
||||||
}, [lspClient, isLSPServerReady])
|
}, [kclLspClient, isKclLspServerReady])
|
||||||
|
|
||||||
|
const { lspClient: copilotLspClient } = useMemo(() => {
|
||||||
|
const intoServer: IntoServer = new IntoServer()
|
||||||
|
const fromServer: FromServer = FromServer.create()
|
||||||
|
const client = new Client(fromServer, intoServer)
|
||||||
|
if (!TEST) {
|
||||||
|
Server.initialize(intoServer, fromServer).then((lspServer) => {
|
||||||
|
const token = auth?.context?.token
|
||||||
|
lspServer.start('copilot', token)
|
||||||
|
setIsCopilotLspServerReady(true)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const lspClient = new LanguageServerClient({ client, name: 'copilot' })
|
||||||
|
return { lspClient }
|
||||||
|
}, [setIsCopilotLspServerReady])
|
||||||
|
|
||||||
|
// Here we initialize the plugin which will start the client.
|
||||||
|
// When we have multi-file support the name of the file will be a dep of
|
||||||
|
// this use memo, as well as the directory structure, which I think is
|
||||||
|
// a good setup because it will restart the client but not the server :)
|
||||||
|
// We do not want to restart the server, its just wasteful.
|
||||||
|
const copilotLSP = useMemo(() => {
|
||||||
|
let plugin = null
|
||||||
|
if (isCopilotLspServerReady && !TEST) {
|
||||||
|
// Set up the lsp plugin.
|
||||||
|
const lsp = copilotPlugin({
|
||||||
|
// When we have more than one file, we'll need to change this.
|
||||||
|
documentUri: `file:///we-just-have-one-file-for-now.kcl`,
|
||||||
|
workspaceFolders: getWorkspaceFolders(),
|
||||||
|
client: copilotLspClient,
|
||||||
|
allowHTMLContent: true,
|
||||||
|
})
|
||||||
|
|
||||||
|
plugin = lsp
|
||||||
|
}
|
||||||
|
return plugin
|
||||||
|
}, [copilotLspClient, isCopilotLspServerReady])
|
||||||
|
|
||||||
// const onChange = React.useCallback((value: string, viewUpdate: ViewUpdate) => {
|
// const onChange = React.useCallback((value: string, viewUpdate: ViewUpdate) => {
|
||||||
const onChange = (newCode: string) => {
|
const onChange = (newCode: string) => {
|
||||||
@ -115,6 +173,12 @@ export const TextEditor = ({
|
|||||||
if (!editorView) {
|
if (!editorView) {
|
||||||
setEditorView(viewUpdate.view)
|
setEditorView(viewUpdate.view)
|
||||||
}
|
}
|
||||||
|
if (sceneInfra.selected) return // mid drag
|
||||||
|
const ignoreEvents: ModelingMachineEvent['type'][] = [
|
||||||
|
'Equip Line tool',
|
||||||
|
'Equip tangential arc to',
|
||||||
|
]
|
||||||
|
if (ignoreEvents.includes(state.event.type)) return
|
||||||
const eventInfo = processCodeMirrorRanges({
|
const eventInfo = processCodeMirrorRanges({
|
||||||
codeMirrorRanges: viewUpdate.state.selection.ranges,
|
codeMirrorRanges: viewUpdate.state.selection.ranges,
|
||||||
selectionRanges,
|
selectionRanges,
|
||||||
@ -122,7 +186,20 @@ export const TextEditor = ({
|
|||||||
isShiftDown,
|
isShiftDown,
|
||||||
})
|
})
|
||||||
if (!eventInfo) return
|
if (!eventInfo) return
|
||||||
|
const deterministicEventInfo = {
|
||||||
|
...eventInfo,
|
||||||
|
engineEvents: eventInfo.engineEvents.map((e) => ({
|
||||||
|
...e,
|
||||||
|
cmd_id: 'static',
|
||||||
|
})),
|
||||||
|
}
|
||||||
|
const stringEvent = JSON.stringify(deterministicEventInfo)
|
||||||
|
if (
|
||||||
|
stringEvent === lastEvent.current.event &&
|
||||||
|
Date.now() - lastEvent.current.time < 500
|
||||||
|
)
|
||||||
|
return // don't repeat events
|
||||||
|
lastEvent.current = { event: stringEvent, time: Date.now() }
|
||||||
send(eventInfo.modelingEvent)
|
send(eventInfo.modelingEvent)
|
||||||
eventInfo.engineEvents.forEach((event) =>
|
eventInfo.engineEvents.forEach((event) =>
|
||||||
engineCommandManager.sendSceneCommand(event)
|
engineCommandManager.sendSceneCommand(event)
|
||||||
@ -161,6 +238,7 @@ export const TextEditor = ({
|
|||||||
] as Extension[]
|
] as Extension[]
|
||||||
|
|
||||||
if (kclLSP) extensions.push(kclLSP)
|
if (kclLSP) extensions.push(kclLSP)
|
||||||
|
if (copilotLSP) extensions.push(copilotLSP)
|
||||||
|
|
||||||
// These extensions have proven to mess with vitest
|
// These extensions have proven to mess with vitest
|
||||||
if (!TEST) {
|
if (!TEST) {
|
||||||
|
@ -11,7 +11,7 @@ import {
|
|||||||
getTransformInfos,
|
getTransformInfos,
|
||||||
PathToNodeMap,
|
PathToNodeMap,
|
||||||
} from '../../lang/std/sketchcombos'
|
} from '../../lang/std/sketchcombos'
|
||||||
import { kclManager } from 'lang/KclSinglton'
|
import { kclManager } from 'lang/KclSingleton'
|
||||||
|
|
||||||
export function equalAngleInfo({
|
export function equalAngleInfo({
|
||||||
selectionRanges,
|
selectionRanges,
|
||||||
|
@ -11,7 +11,7 @@ import {
|
|||||||
getTransformInfos,
|
getTransformInfos,
|
||||||
PathToNodeMap,
|
PathToNodeMap,
|
||||||
} from '../../lang/std/sketchcombos'
|
} from '../../lang/std/sketchcombos'
|
||||||
import { kclManager } from 'lang/KclSinglton'
|
import { kclManager } from 'lang/KclSingleton'
|
||||||
|
|
||||||
export function setEqualLengthInfo({
|
export function setEqualLengthInfo({
|
||||||
selectionRanges,
|
selectionRanges,
|
||||||
|
@ -10,7 +10,7 @@ import {
|
|||||||
getTransformInfos,
|
getTransformInfos,
|
||||||
transformAstSketchLines,
|
transformAstSketchLines,
|
||||||
} from '../../lang/std/sketchcombos'
|
} from '../../lang/std/sketchcombos'
|
||||||
import { kclManager } from 'lang/KclSinglton'
|
import { kclManager } from 'lang/KclSingleton'
|
||||||
|
|
||||||
export function horzVertInfo(
|
export function horzVertInfo(
|
||||||
selectionRanges: Selections,
|
selectionRanges: Selections,
|
||||||
|
@ -15,7 +15,7 @@ import {
|
|||||||
import { GetInfoModal, createInfoModal } from '../SetHorVertDistanceModal'
|
import { GetInfoModal, createInfoModal } from '../SetHorVertDistanceModal'
|
||||||
import { createVariableDeclaration } from '../../lang/modifyAst'
|
import { createVariableDeclaration } from '../../lang/modifyAst'
|
||||||
import { removeDoubleNegatives } from '../AvailableVarsHelpers'
|
import { removeDoubleNegatives } from '../AvailableVarsHelpers'
|
||||||
import { kclManager } from 'lang/KclSinglton'
|
import { kclManager } from 'lang/KclSingleton'
|
||||||
|
|
||||||
const getModalInfo = createInfoModal(GetInfoModal)
|
const getModalInfo = createInfoModal(GetInfoModal)
|
||||||
|
|
||||||
|
@ -10,7 +10,7 @@ import {
|
|||||||
getRemoveConstraintsTransforms,
|
getRemoveConstraintsTransforms,
|
||||||
transformAstSketchLines,
|
transformAstSketchLines,
|
||||||
} from '../../lang/std/sketchcombos'
|
} from '../../lang/std/sketchcombos'
|
||||||
import { kclManager } from 'lang/KclSinglton'
|
import { kclManager } from 'lang/KclSingleton'
|
||||||
|
|
||||||
export function removeConstrainingValuesInfo({
|
export function removeConstrainingValuesInfo({
|
||||||
selectionRanges,
|
selectionRanges,
|
||||||
|
@ -19,7 +19,7 @@ import {
|
|||||||
createVariableDeclaration,
|
createVariableDeclaration,
|
||||||
} from '../../lang/modifyAst'
|
} from '../../lang/modifyAst'
|
||||||
import { removeDoubleNegatives } from '../AvailableVarsHelpers'
|
import { removeDoubleNegatives } from '../AvailableVarsHelpers'
|
||||||
import { kclManager } from 'lang/KclSinglton'
|
import { kclManager } from 'lang/KclSingleton'
|
||||||
|
|
||||||
const getModalInfo = createSetAngleLengthModal(SetAngleLengthModal)
|
const getModalInfo = createSetAngleLengthModal(SetAngleLengthModal)
|
||||||
|
|
||||||
@ -139,7 +139,7 @@ export function applyConstraintAxisAlign({
|
|||||||
constraint,
|
constraint,
|
||||||
}).transforms
|
}).transforms
|
||||||
|
|
||||||
let finalValue = createIdentifier('_0')
|
let finalValue = createIdentifier('ZERO')
|
||||||
|
|
||||||
return transformAstSketchLines({
|
return transformAstSketchLines({
|
||||||
ast: JSON.parse(JSON.stringify(kclManager.ast)),
|
ast: JSON.parse(JSON.stringify(kclManager.ast)),
|
||||||
|
@ -14,7 +14,7 @@ import {
|
|||||||
import { GetInfoModal, createInfoModal } from '../SetHorVertDistanceModal'
|
import { GetInfoModal, createInfoModal } from '../SetHorVertDistanceModal'
|
||||||
import { createVariableDeclaration } from '../../lang/modifyAst'
|
import { createVariableDeclaration } from '../../lang/modifyAst'
|
||||||
import { removeDoubleNegatives } from '../AvailableVarsHelpers'
|
import { removeDoubleNegatives } from '../AvailableVarsHelpers'
|
||||||
import { kclManager } from 'lang/KclSinglton'
|
import { kclManager } from 'lang/KclSingleton'
|
||||||
|
|
||||||
const getModalInfo = createInfoModal(GetInfoModal)
|
const getModalInfo = createInfoModal(GetInfoModal)
|
||||||
|
|
||||||
|
@ -13,7 +13,7 @@ import {
|
|||||||
import { GetInfoModal, createInfoModal } from '../SetHorVertDistanceModal'
|
import { GetInfoModal, createInfoModal } from '../SetHorVertDistanceModal'
|
||||||
import { createLiteral, createVariableDeclaration } from '../../lang/modifyAst'
|
import { createLiteral, createVariableDeclaration } from '../../lang/modifyAst'
|
||||||
import { removeDoubleNegatives } from '../AvailableVarsHelpers'
|
import { removeDoubleNegatives } from '../AvailableVarsHelpers'
|
||||||
import { kclManager } from 'lang/KclSinglton'
|
import { kclManager } from 'lang/KclSingleton'
|
||||||
import { Selections } from 'lib/selections'
|
import { Selections } from 'lib/selections'
|
||||||
|
|
||||||
const getModalInfo = createInfoModal(GetInfoModal)
|
const getModalInfo = createInfoModal(GetInfoModal)
|
||||||
|
@ -21,7 +21,7 @@ import {
|
|||||||
} from '../../lang/modifyAst'
|
} from '../../lang/modifyAst'
|
||||||
import { removeDoubleNegatives } from '../AvailableVarsHelpers'
|
import { removeDoubleNegatives } from '../AvailableVarsHelpers'
|
||||||
import { normaliseAngle } from '../../lib/utils'
|
import { normaliseAngle } from '../../lib/utils'
|
||||||
import { kclManager } from 'lang/KclSinglton'
|
import { kclManager } from 'lang/KclSingleton'
|
||||||
|
|
||||||
const getModalInfo = createSetAngleLengthModal(SetAngleLengthModal)
|
const getModalInfo = createSetAngleLengthModal(SetAngleLengthModal)
|
||||||
|
|
||||||
@ -89,12 +89,16 @@ export async function applyConstraintAngleLength({
|
|||||||
isReferencingXAxis && angleOrLength === 'setAngle'
|
isReferencingXAxis && angleOrLength === 'setAngle'
|
||||||
|
|
||||||
let forceVal = valueUsedInTransform || 0
|
let forceVal = valueUsedInTransform || 0
|
||||||
let calcIdentifier = createIdentifier('_0')
|
let calcIdentifier = createIdentifier('ZERO')
|
||||||
if (isReferencingYAxisAngle) {
|
if (isReferencingYAxisAngle) {
|
||||||
calcIdentifier = createIdentifier(forceVal < 0 ? '_270' : '_90')
|
calcIdentifier = createIdentifier(
|
||||||
|
forceVal < 0 ? 'THREE_QUARTER_TURN' : 'QUARTER_TURN'
|
||||||
|
)
|
||||||
forceVal = normaliseAngle(forceVal + (forceVal < 0 ? 90 : -90))
|
forceVal = normaliseAngle(forceVal + (forceVal < 0 ? 90 : -90))
|
||||||
} else if (isReferencingXAxisAngle) {
|
} else if (isReferencingXAxisAngle) {
|
||||||
calcIdentifier = createIdentifier(Math.abs(forceVal) > 90 ? '_180' : '_0')
|
calcIdentifier = createIdentifier(
|
||||||
|
Math.abs(forceVal) > 90 ? 'HALF_TURN' : 'ZERO'
|
||||||
|
)
|
||||||
forceVal =
|
forceVal =
|
||||||
Math.abs(forceVal) > 90 ? normaliseAngle(forceVal - 180) : forceVal
|
Math.abs(forceVal) > 90 ? normaliseAngle(forceVal - 180) : forceVal
|
||||||
}
|
}
|
||||||
@ -112,7 +116,7 @@ export async function applyConstraintAngleLength({
|
|||||||
)
|
)
|
||||||
if (
|
if (
|
||||||
isReferencingYAxisAngle ||
|
isReferencingYAxisAngle ||
|
||||||
(isReferencingXAxisAngle && calcIdentifier.name !== '_0')
|
(isReferencingXAxisAngle && calcIdentifier.name !== 'ZERO')
|
||||||
) {
|
) {
|
||||||
finalValue = createBinaryExpressionWithUnary([calcIdentifier, finalValue])
|
finalValue = createBinaryExpressionWithUnary([calcIdentifier, finalValue])
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,7 @@ import { faBars, faBug, faSignOutAlt } from '@fortawesome/free-solid-svg-icons'
|
|||||||
import { faGithub } from '@fortawesome/free-brands-svg-icons'
|
import { faGithub } from '@fortawesome/free-brands-svg-icons'
|
||||||
import { useLocation, useNavigate } from 'react-router-dom'
|
import { useLocation, useNavigate } from 'react-router-dom'
|
||||||
import { Fragment, useState } from 'react'
|
import { Fragment, useState } from 'react'
|
||||||
import { paths } from '../Router'
|
import { paths } from 'lib/paths'
|
||||||
import { Models } from '@kittycad/lib'
|
import { Models } from '@kittycad/lib'
|
||||||
import { useGlobalStateContext } from 'hooks/useGlobalStateContext'
|
import { useGlobalStateContext } from 'hooks/useGlobalStateContext'
|
||||||
import { useAbsoluteFilePath } from 'hooks/useAbsoluteFilePath'
|
import { useAbsoluteFilePath } from 'hooks/useAbsoluteFilePath'
|
||||||
@ -117,7 +117,7 @@ const UserSidebarMenu = ({ user }: { user?: User }) => {
|
|||||||
<div className="p-4 flex flex-col gap-2">
|
<div className="p-4 flex flex-col gap-2">
|
||||||
<ActionButton
|
<ActionButton
|
||||||
Element="button"
|
Element="button"
|
||||||
icon={{ icon: 'gear' }}
|
icon={{ icon: 'settings' }}
|
||||||
className="border-transparent dark:border-transparent hover:bg-transparent"
|
className="border-transparent dark:border-transparent hover:bg-transparent"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
// since /settings is a nested route the sidebar doesn't close
|
// since /settings is a nested route the sidebar doesn't close
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { Dialog } from '@headlessui/react'
|
import { Dialog } from '@headlessui/react'
|
||||||
import { useState } from 'react'
|
import { useState } from 'react'
|
||||||
import { ActionButton } from './ActionButton'
|
import { ActionButton } from './ActionButton'
|
||||||
import { useKclContext } from 'lang/KclSinglton'
|
import { useKclContext } from 'lang/KclSingleton'
|
||||||
|
|
||||||
export function WasmErrBanner() {
|
export function WasmErrBanner() {
|
||||||
const [isBannerDismissed, setBannerDismissed] = useState(false)
|
const [isBannerDismissed, setBannerDismissed] = useState(false)
|
||||||
|
@ -65,6 +65,7 @@ export default class Client extends jsrpc.JSONRPCServerAndClient {
|
|||||||
afterInitializedHooks: (() => Promise<void>)[] = []
|
afterInitializedHooks: (() => Promise<void>)[] = []
|
||||||
#fromServer: FromServer
|
#fromServer: FromServer
|
||||||
private serverCapabilities: LSP.ServerCapabilities<any> = {}
|
private serverCapabilities: LSP.ServerCapabilities<any> = {}
|
||||||
|
private notifyFn: ((message: LSP.NotificationMessage) => void) | null = null
|
||||||
|
|
||||||
constructor(fromServer: FromServer, intoServer: IntoServer) {
|
constructor(fromServer: FromServer, intoServer: IntoServer) {
|
||||||
super(
|
super(
|
||||||
@ -167,9 +168,15 @@ export default class Client extends jsrpc.JSONRPCServerAndClient {
|
|||||||
return this.serverCapabilities
|
return this.serverCapabilities
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setNotifyFn(fn: (message: LSP.NotificationMessage) => void): void {
|
||||||
|
this.notifyFn = fn
|
||||||
|
}
|
||||||
|
|
||||||
async processNotifications(): Promise<void> {
|
async processNotifications(): Promise<void> {
|
||||||
for await (const notification of this.#fromServer.notifications) {
|
for await (const notification of this.#fromServer.notifications) {
|
||||||
await this.receiveAndSend(notification)
|
if (this.notifyFn) {
|
||||||
|
this.notifyFn(notification)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|