Compare commits

...

45 Commits

Author SHA1 Message Date
0d7049d90f skip network tests mac 2024-07-08 21:28:07 +10:00
8ebe78c664 Lf94/eco mode save the planet (#2940)
* Trigger shutdown operations after each test

* Idle mode states

* Don't show the reconnect when coming back from tab
2024-07-07 10:10:52 -07:00
a85c1a9375 Scoped tags (#2941)
* start of scoped tags

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* add the tags to the sketch group context

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* scoped tags

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* update docs

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* fix tests

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* updates

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* updates

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* scoped

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* updates

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* fix;

Signed-off-by: Jess Frazelle <github@jessfraz.com>

---------

Signed-off-by: Jess Frazelle <github@jessfraz.com>
2024-07-05 16:53:13 -07:00
5701616f3e Add a current default unit indicator with menu to switch (#2937)
* Add a units indicator with a menu to switch default units

* Add a playwright test, add a SR label

* A snapshot a day keeps the bugs away! 📷🐛 (OS: ubuntu)

* Re-run CI

* A snapshot a day keeps the bugs away! 📷🐛 (OS: ubuntu)

* Re-run CI

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-07-05 18:40:43 -04:00
846acaba2f fix bug with order of operations (#2938)
* fix bug with order of operations

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* fix

Signed-off-by: Jess Frazelle <github@jessfraz.com>

---------

Signed-off-by: Jess Frazelle <github@jessfraz.com>
2024-07-05 15:37:30 -07:00
0a524d42f6 Bump kittycad.rs (#2936)
* update kittycad.rs

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* tauri bump

Signed-off-by: Jess Frazelle <github@jessfraz.com>

---------

Signed-off-by: Jess Frazelle <github@jessfraz.com>
2024-07-05 12:39:58 -07:00
fe28527ef9 update docs (#2933)
Signed-off-by: Jess Frazelle <github@jessfraz.com>
2024-07-05 10:44:29 -07:00
0e8d0083c4 Cut release v0.23.1 (#2916)
* Cut release v0.23.1

* Add if to json download
2024-07-05 05:39:17 -04:00
4f4167b247 Rejig state diagram for equipping tools (#2917)
* switch between line and rectangle tool

* disable line tool if rectangle has started

* make rectangle logic clearer from the diagram
2024-07-05 13:40:16 +10:00
fbc2e9d02c Send cancel event from toolbar 'sketch no face' state to enable ESC (#2592)
* Just cancel out of 'sketch no face' state

* add test

* A snapshot a day keeps the bugs away! 📷🐛 (OS: ubuntu)

* trigger ci

---------

Co-authored-by: Frank Noirot <frank@zoo.dev>
Co-authored-by: Kurt Hutten Irev-Dev <k.hutten@protonmail.ch>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-07-05 08:53:58 +10:00
33b15e818b fix core dump screenshot part 2 (#2913)
* fix core dump screenshot

* make it robust

* test hardening

* A snapshot a day keeps the bugs away! 📷🐛 (OS: ubuntu)

* trigger CI

* harden test

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-07-05 05:42:54 +10:00
6cebb84ae0 Bump all tauri deps except cli (incl. updater fix) (#2914)
* Bump all tauri deps except cli (incl. updater fix)
Fixes #2741

* A snapshot a day keeps the bugs away! 📷🐛 (OS: ubuntu)

* Trigger CI

* Remove promises from getOsInfo for tauri apis

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-07-04 09:39:18 -07:00
85403e47e4 Add message "click plane to sketch on" to toolbar after clicking start sketch (#2591)
* Add message to toolbar

"click plane to sketch on"

* Add margin and make the message text smaller

Plus, wrapped it in a div. The spacing and alignment is slightly nicer with the div compared to adding the classes to the List Item element.

---------

Co-authored-by: Frank Noirot <frank@zoo.dev>
2024-07-04 16:22:41 +10:00
0dfee64e3b fix core dump screenshot (#2911)
* fix core dump screenshot

* make it robust
2024-07-03 22:58:29 -07:00
6370d45f94 Pause stream when exiting sketch or extruding (#2900)
* Pause when exiting sketch or extruding

* tsc
2024-07-03 22:55:06 -07:00
fb3e922180 Hide the view until the scene is initially built (#2894)
* Hide the view until the scene is initially built

* fmt

* Remove log
2024-07-03 22:40:45 -07:00
1257ec0327 Zoom out on extruded object (#2819) 2024-07-03 22:19:24 -07:00
08e9fe2e52 more codemirror enhancements (#2912)
Signed-off-by: Jess Frazelle <github@jessfraz.com>
2024-07-03 22:06:52 -07:00
7cec1d45fe Bump html2canvas-pro from 1.5.1 to 1.5.2 (#2908)
Bumps [html2canvas-pro](https://github.com/yorickshan/html2canvas-pro) from 1.5.1 to 1.5.2.
- [Release notes](https://github.com/yorickshan/html2canvas-pro/releases)
- [Changelog](https://github.com/yorickshan/html2canvas-pro/blob/main/CHANGELOG.md)
- [Commits](https://github.com/yorickshan/html2canvas-pro/compare/v1.5.1...v1.5.2)

---
updated-dependencies:
- dependency-name: html2canvas-pro
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-07-03 22:03:46 -07:00
93710bc8f2 remove react-codemirror and update all the codemirror libs (#2901)
* start of removing react-codemirror

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* updates

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* change theme

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* updates

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* updates

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* updates

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* disable copilot temporarily

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* fixes

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* fixes

Signed-off-by: Jess Frazelle <github@jessfraz.com>

---------

Signed-off-by: Jess Frazelle <github@jessfraz.com>
2024-07-03 21:28:51 -07:00
87e7e9447f cleanup annotations, makes it easier to read (#2905)
ckeanup annotations

Signed-off-by: Jess Frazelle <github@jessfraz.com>
2024-07-03 20:59:54 -07:00
8be113d284 update release docs (#2906) 2024-07-04 12:48:08 +10:00
7cfc927d5c Small codemirror changes (#2898)
* Drop unneeded compute indirection in lspAutocompleteKeymapExt

* Dispatch only a single transaction in requestFormatting

Remove addToHistory.of(true), since that is the default.

* Remove old comment and some useless tests

* Just store the view, not the previous viewUpdate, in CompletionRequester

* small codemirror changes from  marijnh

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* fix some flaky tests

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* fix

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* updates

Signed-off-by: Jess Frazelle <github@jessfraz.com>

---------

Signed-off-by: Jess Frazelle <github@jessfraz.com>
Co-authored-by: Marijn Haverbeke <marijn@haverbeke.berlin>
2024-07-03 19:28:46 -07:00
c0f04d5f86 Cut release v0.23.0 (#2904) 2024-07-04 12:22:06 +10:00
3dbc701f26 remove bad scaling on delete (#2902)
remove weird scaling
2024-07-04 11:05:27 +10:00
16e7ae38e3 fix auto complete for circle (#2903)
crcle

Signed-off-by: Jess Frazelle <github@jessfraz.com>
2024-07-03 18:05:24 -07:00
24c7260327 ctrl-c is copy, we should not bind to copy or paste or any common shit (#2895)
* ctrl-c is copy, we should not bind to copy or paste or any common shit

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* fix tests

Signed-off-by: Jess Frazelle <github@jessfraz.com>

---------

Signed-off-by: Jess Frazelle <github@jessfraz.com>
2024-07-03 15:26:04 -07:00
72cfc4a471 fix version in vercel as main (#2899)
* fix version in vercel as nightly

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* its actually the main branch

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* format

Signed-off-by: Jess Frazelle <github@jessfraz.com>

---------

Signed-off-by: Jess Frazelle <github@jessfraz.com>
2024-07-03 15:24:17 -07:00
2d128ed32e Bump wasm-pack from 0.12.1 to 0.13.0 (#2887)
Bumps [wasm-pack](https://github.com/rustwasm/wasm-pack) from 0.12.1 to 0.13.0.
- [Release notes](https://github.com/rustwasm/wasm-pack/releases)
- [Changelog](https://github.com/rustwasm/wasm-pack/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rustwasm/wasm-pack/compare/v0.12.1...v0.13.0)

---
updated-dependencies:
- dependency-name: wasm-pack
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-07-03 11:34:01 -07:00
cd6749ba02 Bump three from 0.164.1 to 0.166.1 (#2886)
Bumps [three](https://github.com/mrdoob/three.js) from 0.164.1 to 0.166.1.
- [Release notes](https://github.com/mrdoob/three.js/releases)
- [Commits](https://github.com/mrdoob/three.js/commits)

---
updated-dependencies:
- dependency-name: three
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-07-03 11:33:39 -07:00
7243405e1b Bump @playwright/test from 1.44.1 to 1.45.1 (#2885)
Bumps [@playwright/test](https://github.com/microsoft/playwright) from 1.44.1 to 1.45.1.
- [Release notes](https://github.com/microsoft/playwright/releases)
- [Commits](https://github.com/microsoft/playwright/compare/v1.44.1...v1.45.1)

---
updated-dependencies:
- dependency-name: "@playwright/test"
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-07-03 11:33:23 -07:00
c8da057ec2 Bump html2canvas-pro from 1.5.0 to 1.5.1 (#2888)
Bumps [html2canvas-pro](https://github.com/yorickshan/html2canvas-pro) from 1.5.0 to 1.5.1.
- [Release notes](https://github.com/yorickshan/html2canvas-pro/releases)
- [Changelog](https://github.com/yorickshan/html2canvas-pro/blob/main/CHANGELOG.md)
- [Commits](https://github.com/yorickshan/html2canvas-pro/compare/v1.5.0...v1.5.1)

---
updated-dependencies:
- dependency-name: html2canvas-pro
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-07-03 11:14:32 -07:00
220fe5b2b8 bad code on exit-sketch should no delete code (#2890)
* bad code on exitsketch should no delete code

* clean up
2024-07-03 22:03:04 +10:00
4e6429de49 add fixme to failing test (#2891) 2024-07-03 20:54:58 +10:00
5391a65b18 Grouping tests (#2884)
test grouping
2024-07-03 14:34:45 +10:00
592628917a test grouping (#2883) 2024-07-03 12:41:24 +10:00
4c6e8633f7 zustand part 3 (#2878)
* zustand part 3

* clean up

* yarn lock
2024-07-02 16:22:46 -07:00
c5150468a2 more keybindings w copilot tests (#2875)
Signed-off-by: Jess Frazelle <github@jessfraz.com>
2024-07-02 10:08:02 -07:00
39126dbff1 fix copilot regression (#2876)
* fix copilot regression

* fix zoom part of test

* fix enable copilot
2024-07-02 08:50:40 -07:00
f86a69f12a initial migration from zustand (#2852)
* inital migration with a couple lingering concerns

* move is stream ready back

* put htmlRef back in useStore

* final tidy of useStore

* test tweaks

* tweak more

* more test tweaks

* fmt

* test tweaks

* attempts at fixing 'Basic default modeling and sketch hotkeys work'

* more tries

* 😭

* try again

* fmt
2024-07-02 17:16:27 +10:00
de354ee5d3 zustand updated for the lsp provider (#2874)
updates

Signed-off-by: Jess Frazelle <github@jessfraz.com>
2024-07-01 21:43:15 -07:00
dfef7338ee disable copilot in sketch mode (#2865)
* disable copilot in sketch mode

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* fixes

Signed-off-by: Jess Frazelle <github@jessfraz.com>

---------

Signed-off-by: Jess Frazelle <github@jessfraz.com>
2024-07-01 21:05:31 -07:00
ee08948f54 add playwright tests for undo from click and point operations (#2866)
* add playwright tests for undo from click and point operations

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* fix

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* more determinitic

Signed-off-by: Jess Frazelle <github@jessfraz.com>

---------

Signed-off-by: Jess Frazelle <github@jessfraz.com>
2024-07-01 20:55:28 -07:00
832f6b65e2 Bump serde_json from 1.0.119 to 1.0.120 in /src/wasm-lib (#2872)
Bumps [serde_json](https://github.com/serde-rs/json) from 1.0.119 to 1.0.120.
- [Release notes](https://github.com/serde-rs/json/releases)
- [Commits](https://github.com/serde-rs/json/compare/v1.0.119...v1.0.120)

---
updated-dependencies:
- dependency-name: serde_json
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-07-01 20:34:46 -07:00
68efd77c5d fixes perms for releases (#2864)
* updates

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* add to specific job

Signed-off-by: Jess Frazelle <github@jessfraz.com>

---------

Signed-off-by: Jess Frazelle <github@jessfraz.com>
2024-07-01 15:58:08 -07:00
175 changed files with 11253 additions and 4026 deletions

View File

@ -138,6 +138,7 @@ jobs:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- uses: actions/download-artifact@v3 - uses: actions/download-artifact@v3
if: github.event_name == 'schedule'
- name: Copy updated .json files - name: Copy updated .json files
if: github.event_name == 'schedule' if: github.event_name == 'schedule'
@ -377,6 +378,8 @@ jobs:
TS_NODE_COMPILER_OPTIONS: '{"module": "commonjs"}' TS_NODE_COMPILER_OPTIONS: '{"module": "commonjs"}'
publish-apps-release: publish-apps-release:
permissions:
contents: write
runs-on: ubuntu-latest runs-on: ubuntu-latest
if: ${{ github.event_name == 'release' || github.event_name == 'schedule' }} if: ${{ github.event_name == 'release' || github.event_name == 'schedule' }}
needs: [check-format, check-types, check-typos, build-test-web, prepare-json-files, build-test-apps] needs: [check-format, check-types, check-typos, build-test-web, prepare-json-files, build-test-apps]

View File

@ -124,36 +124,20 @@ Before you submit a contribution PR to this repo, please ensure that:
## Release a new version ## Release a new version
1. Bump the versions in the .json files by creating a `Cut release v{x}.{y}.{z}` PR, committing the changes from 1. Bump the versions by running `./make-realease.sh` while on a fresh pull of main
```bash That will create the branch with the updated json files for you.
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` for a patch update
run `./make-release.sh "minor"` for minor run `./make-release.sh "minor"` for minor
run `./make-release.sh "major"` for major 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 paste in the following After it runs you should just need to push the push the branch and open a PR (it will suggest a changelog for you too, delete any that are not user facing)
```typescript The PR may serve as a place to discuss the human-readable changelog and extra QA.
console.log(
'- ' +
Array.from(
document.querySelectorAll('[data-hovercard-type="pull_request"]')
).map((a) => `[${a.innerText}](${a.href})`).join(`
- `)
)
```
grab the md list and delete any that are older than the last bump
2. Merge the PR 2. Merge the PR
3. Create a new release and tag pointing to the bump version commit using semantic versioning `v{x}.{y}.{z}` 3. Profit (A new Action kicks in at https://github.com/KittyCAD/modeling-app/actions if the PR was correctly named)
4. A new Action kicks in at https://github.com/KittyCAD/modeling-app/actions, uploading artifacts to the release
## Fuzzing the parser ## Fuzzing the parser

View File

@ -121,6 +121,9 @@ const extrusion = extrude(5, sketch001)
}, },
// The to point. // The to point.
to: [number, number], to: [number, number],
},
// Tag identifiers that have been declared in this sketch group.
tags: {
}, },
// The paths in the sketch group. // The paths in the sketch group.
value: [{ value: [{
@ -282,6 +285,9 @@ const extrusion = extrude(5, sketch001)
}, },
// The to point. // The to point.
to: [number, number], to: [number, number],
},
// Tag identifiers that have been declared in this sketch group.
tags: {
}, },
// The paths in the sketch group. // The paths in the sketch group.
value: [{ value: [{

View File

@ -125,6 +125,9 @@ const extrusion = extrude(5, sketch001)
}, },
// The to point. // The to point.
to: [number, number], to: [number, number],
},
// Tag identifiers that have been declared in this sketch group.
tags: {
}, },
// The paths in the sketch group. // The paths in the sketch group.
value: [{ value: [{
@ -286,6 +289,9 @@ const extrusion = extrude(5, sketch001)
}, },
// The to point. // The to point.
to: [number, number], to: [number, number],
},
// Tag identifiers that have been declared in this sketch group.
tags: {
}, },
// The paths in the sketch group. // The paths in the sketch group.
value: [{ value: [{

View File

@ -126,6 +126,9 @@ const example = extrude(10, exampleSketch)
}, },
// The to point. // The to point.
to: [number, number], to: [number, number],
},
// Tag identifiers that have been declared in this sketch group.
tags: {
}, },
// The paths in the sketch group. // The paths in the sketch group.
value: [{ value: [{
@ -287,6 +290,9 @@ const example = extrude(10, exampleSketch)
}, },
// The to point. // The to point.
to: [number, number], to: [number, number],
},
// Tag identifiers that have been declared in this sketch group.
tags: {
}, },
// The paths in the sketch group. // The paths in the sketch group.
value: [{ value: [{
@ -476,6 +482,9 @@ const example = extrude(10, exampleSketch)
}, },
// The to point. // The to point.
to: [number, number], to: [number, number],
},
// Tag identifiers that have been declared in this sketch group.
tags: {
}, },
// The paths in the sketch group. // The paths in the sketch group.
value: [{ value: [{
@ -637,6 +646,9 @@ const example = extrude(10, exampleSketch)
}, },
// The to point. // The to point.
to: [number, number], to: [number, number],
},
// Tag identifiers that have been declared in this sketch group.
tags: {
}, },
// The paths in the sketch group. // The paths in the sketch group.
value: [{ value: [{

View File

@ -125,6 +125,9 @@ const extrusion = extrude(10, sketch001)
}, },
// The to point. // The to point.
to: [number, number], to: [number, number],
},
// Tag identifiers that have been declared in this sketch group.
tags: {
}, },
// The paths in the sketch group. // The paths in the sketch group.
value: [{ value: [{
@ -286,6 +289,9 @@ const extrusion = extrude(10, sketch001)
}, },
// The to point. // The to point.
to: [number, number], to: [number, number],
},
// Tag identifiers that have been declared in this sketch group.
tags: {
}, },
// The paths in the sketch group. // The paths in the sketch group.
value: [{ value: [{
@ -475,6 +481,9 @@ const extrusion = extrude(10, sketch001)
}, },
// The to point. // The to point.
to: [number, number], to: [number, number],
},
// Tag identifiers that have been declared in this sketch group.
tags: {
}, },
// The paths in the sketch group. // The paths in the sketch group.
value: [{ value: [{
@ -636,6 +645,9 @@ const extrusion = extrude(10, sketch001)
}, },
// The to point. // The to point.
to: [number, number], to: [number, number],
},
// Tag identifiers that have been declared in this sketch group.
tags: {
}, },
// The paths in the sketch group. // The paths in the sketch group.
value: [{ value: [{

File diff suppressed because one or more lines are too long

View File

@ -133,6 +133,9 @@ const example = extrude(10, exampleSketch)
}, },
// The to point. // The to point.
to: [number, number], to: [number, number],
},
// Tag identifiers that have been declared in this sketch group.
tags: {
}, },
// The paths in the sketch group. // The paths in the sketch group.
value: [{ value: [{
@ -294,6 +297,9 @@ const example = extrude(10, exampleSketch)
}, },
// The to point. // The to point.
to: [number, number], to: [number, number],
},
// Tag identifiers that have been declared in this sketch group.
tags: {
}, },
// The paths in the sketch group. // The paths in the sketch group.
value: [{ value: [{
@ -483,6 +489,9 @@ const example = extrude(10, exampleSketch)
}, },
// The to point. // The to point.
to: [number, number], to: [number, number],
},
// Tag identifiers that have been declared in this sketch group.
tags: {
}, },
// The paths in the sketch group. // The paths in the sketch group.
value: [{ value: [{
@ -644,6 +653,9 @@ const example = extrude(10, exampleSketch)
}, },
// The to point. // The to point.
to: [number, number], to: [number, number],
},
// Tag identifiers that have been declared in this sketch group.
tags: {
}, },
// The paths in the sketch group. // The paths in the sketch group.
value: [{ value: [{

View File

@ -124,6 +124,9 @@ const example = extrude(10, exampleSketch)
}, },
// The to point. // The to point.
to: [number, number], to: [number, number],
},
// Tag identifiers that have been declared in this sketch group.
tags: {
}, },
// The paths in the sketch group. // The paths in the sketch group.
value: [{ value: [{
@ -285,6 +288,9 @@ const example = extrude(10, exampleSketch)
}, },
// The to point. // The to point.
to: [number, number], to: [number, number],
},
// Tag identifiers that have been declared in this sketch group.
tags: {
}, },
// The paths in the sketch group. // The paths in the sketch group.
value: [{ value: [{
@ -474,6 +480,9 @@ const example = extrude(10, exampleSketch)
}, },
// The to point. // The to point.
to: [number, number], to: [number, number],
},
// Tag identifiers that have been declared in this sketch group.
tags: {
}, },
// The paths in the sketch group. // The paths in the sketch group.
value: [{ value: [{
@ -635,6 +644,9 @@ const example = extrude(10, exampleSketch)
}, },
// The to point. // The to point.
to: [number, number], to: [number, number],
},
// Tag identifiers that have been declared in this sketch group.
tags: {
}, },
// The paths in the sketch group. // The paths in the sketch group.
value: [{ value: [{

View File

@ -124,6 +124,9 @@ const example = extrude(10, exampleSketch)
}, },
// The to point. // The to point.
to: [number, number], to: [number, number],
},
// Tag identifiers that have been declared in this sketch group.
tags: {
}, },
// The paths in the sketch group. // The paths in the sketch group.
value: [{ value: [{
@ -285,6 +288,9 @@ const example = extrude(10, exampleSketch)
}, },
// The to point. // The to point.
to: [number, number], to: [number, number],
},
// Tag identifiers that have been declared in this sketch group.
tags: {
}, },
// The paths in the sketch group. // The paths in the sketch group.
value: [{ value: [{
@ -474,6 +480,9 @@ const example = extrude(10, exampleSketch)
}, },
// The to point. // The to point.
to: [number, number], to: [number, number],
},
// Tag identifiers that have been declared in this sketch group.
tags: {
}, },
// The paths in the sketch group. // The paths in the sketch group.
value: [{ value: [{
@ -635,6 +644,9 @@ const example = extrude(10, exampleSketch)
}, },
// The to point. // The to point.
to: [number, number], to: [number, number],
},
// Tag identifiers that have been declared in this sketch group.
tags: {
}, },
// The paths in the sketch group. // The paths in the sketch group.
value: [{ value: [{

View File

@ -135,6 +135,9 @@ const exampleSketch = startSketchOn('XZ')
}, },
// The to point. // The to point.
to: [number, number], to: [number, number],
},
// Tag identifiers that have been declared in this sketch group.
tags: {
}, },
// The paths in the sketch group. // The paths in the sketch group.
value: [{ value: [{
@ -296,6 +299,9 @@ const exampleSketch = startSketchOn('XZ')
}, },
// The to point. // The to point.
to: [number, number], to: [number, number],
},
// Tag identifiers that have been declared in this sketch group.
tags: {
}, },
// The paths in the sketch group. // The paths in the sketch group.
value: [{ value: [{
@ -485,6 +491,9 @@ const exampleSketch = startSketchOn('XZ')
}, },
// The to point. // The to point.
to: [number, number], to: [number, number],
},
// Tag identifiers that have been declared in this sketch group.
tags: {
}, },
// The paths in the sketch group. // The paths in the sketch group.
value: [{ value: [{
@ -646,6 +655,9 @@ const exampleSketch = startSketchOn('XZ')
}, },
// The to point. // The to point.
to: [number, number], to: [number, number],
},
// Tag identifiers that have been declared in this sketch group.
tags: {
}, },
// The paths in the sketch group. // The paths in the sketch group.
value: [{ value: [{

View File

@ -130,6 +130,9 @@ const example = extrude(10, exampleSketch)
}, },
// The to point. // The to point.
to: [number, number], to: [number, number],
},
// Tag identifiers that have been declared in this sketch group.
tags: {
}, },
// The paths in the sketch group. // The paths in the sketch group.
value: [{ value: [{
@ -291,6 +294,9 @@ const example = extrude(10, exampleSketch)
}, },
// The to point. // The to point.
to: [number, number], to: [number, number],
},
// Tag identifiers that have been declared in this sketch group.
tags: {
}, },
// The paths in the sketch group. // The paths in the sketch group.
value: [{ value: [{
@ -480,6 +486,9 @@ const example = extrude(10, exampleSketch)
}, },
// The to point. // The to point.
to: [number, number], to: [number, number],
},
// Tag identifiers that have been declared in this sketch group.
tags: {
}, },
// The paths in the sketch group. // The paths in the sketch group.
value: [{ value: [{
@ -641,6 +650,9 @@ const example = extrude(10, exampleSketch)
}, },
// The to point. // The to point.
to: [number, number], to: [number, number],
},
// Tag identifiers that have been declared in this sketch group.
tags: {
}, },
// The paths in the sketch group. // The paths in the sketch group.
value: [{ value: [{

View File

@ -225,6 +225,9 @@ const mountingPlate = extrude(thickness, mountingPlateSketch)
}, },
// The to point. // The to point.
to: [number, number], to: [number, number],
},
// Tag identifiers that have been declared in this sketch group.
tags: {
}, },
// The paths in the sketch group. // The paths in the sketch group.
value: [{ value: [{
@ -532,6 +535,9 @@ const mountingPlate = extrude(thickness, mountingPlateSketch)
}, },
// The to point. // The to point.
to: [number, number], to: [number, number],
},
// Tag identifiers that have been declared in this sketch group.
tags: {
}, },
// The paths in the sketch group. // The paths in the sketch group.
value: [{ value: [{

View File

@ -123,6 +123,9 @@ const example = extrude(5, exampleSketch)
}, },
// The to point. // The to point.
to: [number, number], to: [number, number],
},
// Tag identifiers that have been declared in this sketch group.
tags: {
}, },
// The paths in the sketch group. // The paths in the sketch group.
value: [{ value: [{
@ -410,6 +413,9 @@ const example = extrude(5, exampleSketch)
}, },
// The to point. // The to point.
to: [number, number], to: [number, number],
},
// Tag identifiers that have been declared in this sketch group.
tags: {
}, },
// The paths in the sketch group. // The paths in the sketch group.
value: [{ value: [{
@ -599,6 +605,9 @@ const example = extrude(5, exampleSketch)
}, },
// The to point. // The to point.
to: [number, number], to: [number, number],
},
// Tag identifiers that have been declared in this sketch group.
tags: {
}, },
// The paths in the sketch group. // The paths in the sketch group.
value: [{ value: [{
@ -760,6 +769,9 @@ const example = extrude(5, exampleSketch)
}, },
// The to point. // The to point.
to: [number, number], to: [number, number],
},
// Tag identifiers that have been declared in this sketch group.
tags: {
}, },
// The paths in the sketch group. // The paths in the sketch group.
value: [{ value: [{

View File

@ -125,6 +125,9 @@ const example = extrude(10, exampleSketch)
}, },
// The to point. // The to point.
to: [number, number], to: [number, number],
},
// Tag identifiers that have been declared in this sketch group.
tags: {
}, },
// The paths in the sketch group. // The paths in the sketch group.
value: [{ value: [{
@ -286,6 +289,9 @@ const example = extrude(10, exampleSketch)
}, },
// The to point. // The to point.
to: [number, number], to: [number, number],
},
// Tag identifiers that have been declared in this sketch group.
tags: {
}, },
// The paths in the sketch group. // The paths in the sketch group.
value: [{ value: [{
@ -475,6 +481,9 @@ const example = extrude(10, exampleSketch)
}, },
// The to point. // The to point.
to: [number, number], to: [number, number],
},
// Tag identifiers that have been declared in this sketch group.
tags: {
}, },
// The paths in the sketch group. // The paths in the sketch group.
value: [{ value: [{
@ -636,6 +645,9 @@ const example = extrude(10, exampleSketch)
}, },
// The to point. // The to point.
to: [number, number], to: [number, number],
},
// Tag identifiers that have been declared in this sketch group.
tags: {
}, },
// The paths in the sketch group. // The paths in the sketch group.
value: [{ value: [{

View File

@ -150,6 +150,9 @@ const example = extrude(10, exampleSketch)
}, },
// The to point. // The to point.
to: [number, number], to: [number, number],
},
// Tag identifiers that have been declared in this sketch group.
tags: {
}, },
// The paths in the sketch group. // The paths in the sketch group.
value: [{ value: [{
@ -311,6 +314,9 @@ const example = extrude(10, exampleSketch)
}, },
// The to point. // The to point.
to: [number, number], to: [number, number],
},
// Tag identifiers that have been declared in this sketch group.
tags: {
}, },
type: "sketchGroup", type: "sketchGroup",
// The paths in the sketch group. // The paths in the sketch group.
@ -580,6 +586,9 @@ const example = extrude(10, exampleSketch)
}, },
// The to point. // The to point.
to: [number, number], to: [number, number],
},
// Tag identifiers that have been declared in this sketch group.
tags: {
}, },
// The paths in the sketch group. // The paths in the sketch group.
value: [{ value: [{

View File

@ -225,6 +225,9 @@ const mountingPlate = extrude(thickness, mountingPlateSketch)
}, },
// The to point. // The to point.
to: [number, number], to: [number, number],
},
// Tag identifiers that have been declared in this sketch group.
tags: {
}, },
// The paths in the sketch group. // The paths in the sketch group.
value: [{ value: [{
@ -524,6 +527,9 @@ const mountingPlate = extrude(thickness, mountingPlateSketch)
}, },
// The to point. // The to point.
to: [number, number], to: [number, number],
},
// Tag identifiers that have been declared in this sketch group.
tags: {
}, },
// The paths in the sketch group. // The paths in the sketch group.
value: [{ value: [{

View File

@ -215,6 +215,9 @@ const revolution = startSketchOn(box, "revolveAxis")
}, },
// The to point. // The to point.
to: [number, number], to: [number, number],
},
// Tag identifiers that have been declared in this sketch group.
tags: {
}, },
// The paths in the sketch group. // The paths in the sketch group.
value: [{ value: [{

View File

@ -211,6 +211,9 @@ const example = extrude(5, exampleSketch)
}, },
// The to point. // The to point.
to: [number, number], to: [number, number],
},
// Tag identifiers that have been declared in this sketch group.
tags: {
}, },
// The paths in the sketch group. // The paths in the sketch group.
value: [{ value: [{

View File

@ -211,6 +211,9 @@ const example = extrude(5, exampleSketch)
}, },
// The to point. // The to point.
to: [number, number], to: [number, number],
},
// Tag identifiers that have been declared in this sketch group.
tags: {
}, },
// The paths in the sketch group. // The paths in the sketch group.
value: [{ value: [{

View File

@ -213,6 +213,9 @@ const example = extrude(5, exampleSketch)
}, },
// The to point. // The to point.
to: [number, number], to: [number, number],
},
// Tag identifiers that have been declared in this sketch group.
tags: {
}, },
// The paths in the sketch group. // The paths in the sketch group.
value: [{ value: [{

View File

@ -213,6 +213,9 @@ const part001 = startSketchOn('XY')
}, },
// The to point. // The to point.
to: [number, number], to: [number, number],
},
// Tag identifiers that have been declared in this sketch group.
tags: {
}, },
// The paths in the sketch group. // The paths in the sketch group.
value: [{ value: [{
@ -512,6 +515,9 @@ const part001 = startSketchOn('XY')
}, },
// The to point. // The to point.
to: [number, number], to: [number, number],
},
// Tag identifiers that have been declared in this sketch group.
tags: {
}, },
// The paths in the sketch group. // The paths in the sketch group.
value: [{ value: [{

View File

@ -136,6 +136,9 @@ const example = extrude(1, exampleSketch)
}, },
// The to point. // The to point.
to: [number, number], to: [number, number],
},
// Tag identifiers that have been declared in this sketch group.
tags: {
}, },
// The paths in the sketch group. // The paths in the sketch group.
value: [{ value: [{
@ -297,6 +300,9 @@ const example = extrude(1, exampleSketch)
}, },
// The to point. // The to point.
to: [number, number], to: [number, number],
},
// Tag identifiers that have been declared in this sketch group.
tags: {
}, },
type: "sketchGroup", type: "sketchGroup",
// The paths in the sketch group. // The paths in the sketch group.
@ -479,6 +485,9 @@ const example = extrude(1, exampleSketch)
}, },
// The to point. // The to point.
to: [number, number], to: [number, number],
},
// Tag identifiers that have been declared in this sketch group.
tags: {
}, },
// The paths in the sketch group. // The paths in the sketch group.
value: [{ value: [{
@ -640,6 +649,9 @@ const example = extrude(1, exampleSketch)
}, },
// The to point. // The to point.
to: [number, number], to: [number, number],
},
// Tag identifiers that have been declared in this sketch group.
tags: {
}, },
// The paths in the sketch group. // The paths in the sketch group.
value: [{ value: [{
@ -821,6 +833,9 @@ const example = extrude(1, exampleSketch)
}, },
// The to point. // The to point.
to: [number, number], to: [number, number],
},
// Tag identifiers that have been declared in this sketch group.
tags: {
}, },
// The paths in the sketch group. // The paths in the sketch group.
value: [{ value: [{
@ -982,6 +997,9 @@ const example = extrude(1, exampleSketch)
}, },
// The to point. // The to point.
to: [number, number], to: [number, number],
},
// Tag identifiers that have been declared in this sketch group.
tags: {
}, },
// The paths in the sketch group. // The paths in the sketch group.
value: [{ value: [{

View File

@ -116,6 +116,9 @@ const example = extrude(5, exampleSketch)
}, },
// The to point. // The to point.
to: [number, number], to: [number, number],
},
// Tag identifiers that have been declared in this sketch group.
tags: {
}, },
// The paths in the sketch group. // The paths in the sketch group.
value: [{ value: [{
@ -277,6 +280,9 @@ const example = extrude(5, exampleSketch)
}, },
// The to point. // The to point.
to: [number, number], to: [number, number],
},
// Tag identifiers that have been declared in this sketch group.
tags: {
}, },
// The paths in the sketch group. // The paths in the sketch group.
value: [{ value: [{

View File

@ -116,6 +116,9 @@ const example = extrude(5, exampleSketch)
}, },
// The to point. // The to point.
to: [number, number], to: [number, number],
},
// Tag identifiers that have been declared in this sketch group.
tags: {
}, },
// The paths in the sketch group. // The paths in the sketch group.
value: [{ value: [{
@ -277,6 +280,9 @@ const example = extrude(5, exampleSketch)
}, },
// The to point. // The to point.
to: [number, number], to: [number, number],
},
// Tag identifiers that have been declared in this sketch group.
tags: {
}, },
// The paths in the sketch group. // The paths in the sketch group.
value: [{ value: [{

View File

@ -129,6 +129,9 @@ const example = extrude(5, exampleSketch)
}, },
// The to point. // The to point.
to: [number, number], to: [number, number],
},
// Tag identifiers that have been declared in this sketch group.
tags: {
}, },
// The paths in the sketch group. // The paths in the sketch group.
value: [{ value: [{
@ -290,6 +293,9 @@ const example = extrude(5, exampleSketch)
}, },
// The to point. // The to point.
to: [number, number], to: [number, number],
},
// Tag identifiers that have been declared in this sketch group.
tags: {
}, },
// The paths in the sketch group. // The paths in the sketch group.
value: [{ value: [{
@ -479,6 +485,9 @@ const example = extrude(5, exampleSketch)
}, },
// The to point. // The to point.
to: [number, number], to: [number, number],
},
// Tag identifiers that have been declared in this sketch group.
tags: {
}, },
// The paths in the sketch group. // The paths in the sketch group.
value: [{ value: [{
@ -640,6 +649,9 @@ const example = extrude(5, exampleSketch)
}, },
// The to point. // The to point.
to: [number, number], to: [number, number],
},
// Tag identifiers that have been declared in this sketch group.
tags: {
}, },
// The paths in the sketch group. // The paths in the sketch group.
value: [{ value: [{

View File

@ -116,6 +116,9 @@ const example = extrude(5, exampleSketch)
}, },
// The to point. // The to point.
to: [number, number], to: [number, number],
},
// Tag identifiers that have been declared in this sketch group.
tags: {
}, },
// The paths in the sketch group. // The paths in the sketch group.
value: [{ value: [{
@ -277,6 +280,9 @@ const example = extrude(5, exampleSketch)
}, },
// The to point. // The to point.
to: [number, number], to: [number, number],
},
// Tag identifiers that have been declared in this sketch group.
tags: {
}, },
// The paths in the sketch group. // The paths in the sketch group.
value: [{ value: [{
@ -466,6 +472,9 @@ const example = extrude(5, exampleSketch)
}, },
// The to point. // The to point.
to: [number, number], to: [number, number],
},
// Tag identifiers that have been declared in this sketch group.
tags: {
}, },
// The paths in the sketch group. // The paths in the sketch group.
value: [{ value: [{
@ -627,6 +636,9 @@ const example = extrude(5, exampleSketch)
}, },
// The to point. // The to point.
to: [number, number], to: [number, number],
},
// Tag identifiers that have been declared in this sketch group.
tags: {
}, },
// The paths in the sketch group. // The paths in the sketch group.
value: [{ value: [{

View File

@ -134,6 +134,9 @@ const example = extrude(1, exampleSketch)
}, },
// The to point. // The to point.
to: [number, number], to: [number, number],
},
// Tag identifiers that have been declared in this sketch group.
tags: {
}, },
// The paths in the sketch group. // The paths in the sketch group.
value: [{ value: [{
@ -295,6 +298,9 @@ const example = extrude(1, exampleSketch)
}, },
// The to point. // The to point.
to: [number, number], to: [number, number],
},
// Tag identifiers that have been declared in this sketch group.
tags: {
}, },
type: "sketchGroup", type: "sketchGroup",
// The paths in the sketch group. // The paths in the sketch group.

View File

@ -217,6 +217,9 @@ const example = extrude(-5, exampleSketch)
}, },
// The to point. // The to point.
to: [number, number], to: [number, number],
},
// Tag identifiers that have been declared in this sketch group.
tags: {
}, },
// The paths in the sketch group. // The paths in the sketch group.
value: [{ value: [{

View File

@ -127,6 +127,9 @@ const example = extrude(1, exampleSketch)
}, },
// The to point. // The to point.
to: [number, number], to: [number, number],
},
// Tag identifiers that have been declared in this sketch group.
tags: {
}, },
// The paths in the sketch group. // The paths in the sketch group.
value: [{ value: [{
@ -288,6 +291,9 @@ const example = extrude(1, exampleSketch)
}, },
// The to point. // The to point.
to: [number, number], to: [number, number],
},
// Tag identifiers that have been declared in this sketch group.
tags: {
}, },
type: "sketchGroup", type: "sketchGroup",
// The paths in the sketch group. // The paths in the sketch group.

View File

@ -215,6 +215,9 @@ const example = extrude(1, exampleSketch)
}, },
// The to point. // The to point.
to: [number, number], to: [number, number],
},
// Tag identifiers that have been declared in this sketch group.
tags: {
}, },
// The paths in the sketch group. // The paths in the sketch group.
value: [{ value: [{

View File

@ -217,6 +217,9 @@ let vase = layer()
}, },
// The to point. // The to point.
to: [number, number], to: [number, number],
},
// Tag identifiers that have been declared in this sketch group.
tags: {
}, },
// The paths in the sketch group. // The paths in the sketch group.
value: [{ value: [{

View File

@ -117,6 +117,9 @@ const sketch001 = startSketchOn('XY')
}, },
// The to point. // The to point.
to: [number, number], to: [number, number],
},
// Tag identifiers that have been declared in this sketch group.
tags: {
}, },
// The paths in the sketch group. // The paths in the sketch group.
value: [{ value: [{
@ -278,6 +281,9 @@ const sketch001 = startSketchOn('XY')
}, },
// The to point. // The to point.
to: [number, number], to: [number, number],
},
// Tag identifiers that have been declared in this sketch group.
tags: {
}, },
// The paths in the sketch group. // The paths in the sketch group.
value: [{ value: [{

View File

@ -112,6 +112,9 @@ const sketch001 = startSketchOn('XY')
}, },
// The to point. // The to point.
to: [number, number], to: [number, number],
},
// Tag identifiers that have been declared in this sketch group.
tags: {
}, },
// The paths in the sketch group. // The paths in the sketch group.
value: [{ value: [{
@ -273,6 +276,9 @@ const sketch001 = startSketchOn('XY')
}, },
// The to point. // The to point.
to: [number, number], to: [number, number],
},
// Tag identifiers that have been declared in this sketch group.
tags: {
}, },
// The paths in the sketch group. // The paths in the sketch group.
value: [{ value: [{

View File

@ -111,6 +111,9 @@ const sketch001 = startSketchOn('XY')
}, },
// The to point. // The to point.
to: [number, number], to: [number, number],
},
// Tag identifiers that have been declared in this sketch group.
tags: {
}, },
// The paths in the sketch group. // The paths in the sketch group.
value: [{ value: [{
@ -272,6 +275,9 @@ const sketch001 = startSketchOn('XY')
}, },
// The to point. // The to point.
to: [number, number], to: [number, number],
},
// Tag identifiers that have been declared in this sketch group.
tags: {
}, },
// The paths in the sketch group. // The paths in the sketch group.
value: [{ value: [{

View File

@ -247,6 +247,9 @@ uuid |
}, },
// The to point. // The to point.
to: [number, number], to: [number, number],
},
// Tag identifiers that have been declared in this sketch group.
tags: {
}, },
// The paths in the sketch group. // The paths in the sketch group.
value: [{ value: [{
@ -408,6 +411,9 @@ uuid |
}, },
// The to point. // The to point.
to: [number, number], to: [number, number],
},
// Tag identifiers that have been declared in this sketch group.
tags: {
}, },
// The paths in the sketch group. // The paths in the sketch group.
value: [{ value: [{
@ -673,6 +679,9 @@ uuid |
}, },
// The to point. // The to point.
to: [number, number], to: [number, number],
},
// Tag identifiers that have been declared in this sketch group.
tags: {
}, },
// The paths in the sketch group. // The paths in the sketch group.
value: [{ value: [{

View File

@ -124,6 +124,9 @@ const example = extrude(4, exampleSketch)
}, },
// The to point. // The to point.
to: [number, number], to: [number, number],
},
// Tag identifiers that have been declared in this sketch group.
tags: {
}, },
// The paths in the sketch group. // The paths in the sketch group.
value: [{ value: [{
@ -285,6 +288,9 @@ const example = extrude(4, exampleSketch)
}, },
// The to point. // The to point.
to: [number, number], to: [number, number],
},
// Tag identifiers that have been declared in this sketch group.
tags: {
}, },
// The paths in the sketch group. // The paths in the sketch group.
value: [{ value: [{

View File

@ -122,6 +122,9 @@ const example = extrude(5, exampleSketch)
}, },
// The to point. // The to point.
to: [number, number], to: [number, number],
},
// Tag identifiers that have been declared in this sketch group.
tags: {
}, },
// The paths in the sketch group. // The paths in the sketch group.
value: [{ value: [{
@ -283,6 +286,9 @@ const example = extrude(5, exampleSketch)
}, },
// The to point. // The to point.
to: [number, number], to: [number, number],
},
// Tag identifiers that have been declared in this sketch group.
tags: {
}, },
// The paths in the sketch group. // The paths in the sketch group.
value: [{ value: [{

View File

@ -123,6 +123,9 @@ const example = extrude(5, exampleSketch)
}, },
// The to point. // The to point.
to: [number, number], to: [number, number],
},
// Tag identifiers that have been declared in this sketch group.
tags: {
}, },
// The paths in the sketch group. // The paths in the sketch group.
value: [{ value: [{
@ -284,6 +287,9 @@ const example = extrude(5, exampleSketch)
}, },
// The to point. // The to point.
to: [number, number], to: [number, number],
},
// Tag identifiers that have been declared in this sketch group.
tags: {
}, },
// The paths in the sketch group. // The paths in the sketch group.
value: [{ value: [{

View File

@ -121,6 +121,9 @@ const example = extrude(5, exampleSketch)
}, },
// The to point. // The to point.
to: [number, number], to: [number, number],
},
// Tag identifiers that have been declared in this sketch group.
tags: {
}, },
// The paths in the sketch group. // The paths in the sketch group.
value: [{ value: [{
@ -282,6 +285,9 @@ const example = extrude(5, exampleSketch)
}, },
// The to point. // The to point.
to: [number, number], to: [number, number],
},
// Tag identifiers that have been declared in this sketch group.
tags: {
}, },
// The paths in the sketch group. // The paths in the sketch group.
value: [{ value: [{

View File

@ -213,6 +213,9 @@ shell({ faces: ['end'], thickness: 0.25 }, firstSketch)
}, },
// The to point. // The to point.
to: [number, number], to: [number, number],
},
// Tag identifiers that have been declared in this sketch group.
tags: {
}, },
// The paths in the sketch group. // The paths in the sketch group.
value: [{ value: [{
@ -512,6 +515,9 @@ shell({ faces: ['end'], thickness: 0.25 }, firstSketch)
}, },
// The to point. // The to point.
to: [number, number], to: [number, number],
},
// Tag identifiers that have been declared in this sketch group.
tags: {
}, },
// The paths in the sketch group. // The paths in the sketch group.
value: [{ value: [{

View File

@ -195,6 +195,9 @@ const example = extrude(5, exampleSketch)
}, },
// The to point. // The to point.
to: [number, number], to: [number, number],
},
// Tag identifiers that have been declared in this sketch group.
tags: {
}, },
// The paths in the sketch group. // The paths in the sketch group.
value: [{ value: [{
@ -442,6 +445,9 @@ const example = extrude(5, exampleSketch)
}, },
// The to point. // The to point.
to: [number, number], to: [number, number],
},
// Tag identifiers that have been declared in this sketch group.
tags: {
}, },
// The paths in the sketch group. // The paths in the sketch group.
value: [{ value: [{
@ -603,6 +609,9 @@ const example = extrude(5, exampleSketch)
}, },
// The to point. // The to point.
to: [number, number], to: [number, number],
},
// Tag identifiers that have been declared in this sketch group.
tags: {
}, },
// The paths in the sketch group. // The paths in the sketch group.
value: [{ value: [{

View File

@ -142,6 +142,9 @@ const example = extrude(5, exampleSketch)
}, },
// The to point. // The to point.
to: [number, number], to: [number, number],
},
// Tag identifiers that have been declared in this sketch group.
tags: {
}, },
// The paths in the sketch group. // The paths in the sketch group.
value: [{ value: [{
@ -303,6 +306,9 @@ const example = extrude(5, exampleSketch)
}, },
// The to point. // The to point.
to: [number, number], to: [number, number],
},
// Tag identifiers that have been declared in this sketch group.
tags: {
}, },
// The paths in the sketch group. // The paths in the sketch group.
value: [{ value: [{

View File

@ -264,6 +264,9 @@ const a1 = startSketchOn({
}, },
// The to point. // The to point.
to: [number, number], to: [number, number],
},
// Tag identifiers that have been declared in this sketch group.
tags: {
}, },
// The paths in the sketch group. // The paths in the sketch group.
value: [{ value: [{
@ -539,6 +542,9 @@ const a1 = startSketchOn({
}, },
// The to point. // The to point.
to: [number, number], to: [number, number],
},
// Tag identifiers that have been declared in this sketch group.
tags: {
}, },
// The paths in the sketch group. // The paths in the sketch group.
value: [{ value: [{

File diff suppressed because it is too large Load Diff

View File

@ -125,6 +125,9 @@ const example = extrude(10, exampleSketch)
}, },
// The to point. // The to point.
to: [number, number], to: [number, number],
},
// Tag identifiers that have been declared in this sketch group.
tags: {
}, },
// The paths in the sketch group. // The paths in the sketch group.
value: [{ value: [{
@ -286,6 +289,9 @@ const example = extrude(10, exampleSketch)
}, },
// The to point. // The to point.
to: [number, number], to: [number, number],
},
// Tag identifiers that have been declared in this sketch group.
tags: {
}, },
// The paths in the sketch group. // The paths in the sketch group.
value: [{ value: [{
@ -475,6 +481,9 @@ const example = extrude(10, exampleSketch)
}, },
// The to point. // The to point.
to: [number, number], to: [number, number],
},
// Tag identifiers that have been declared in this sketch group.
tags: {
}, },
// The paths in the sketch group. // The paths in the sketch group.
value: [{ value: [{
@ -636,6 +645,9 @@ const example = extrude(10, exampleSketch)
}, },
// The to point. // The to point.
to: [number, number], to: [number, number],
},
// Tag identifiers that have been declared in this sketch group.
tags: {
}, },
// The paths in the sketch group. // The paths in the sketch group.
value: [{ value: [{

View File

@ -116,6 +116,9 @@ const example = extrude(10, exampleSketch)
}, },
// The to point. // The to point.
to: [number, number], to: [number, number],
},
// Tag identifiers that have been declared in this sketch group.
tags: {
}, },
// The paths in the sketch group. // The paths in the sketch group.
value: [{ value: [{
@ -277,6 +280,9 @@ const example = extrude(10, exampleSketch)
}, },
// The to point. // The to point.
to: [number, number], to: [number, number],
},
// Tag identifiers that have been declared in this sketch group.
tags: {
}, },
// The paths in the sketch group. // The paths in the sketch group.
value: [{ value: [{
@ -466,6 +472,9 @@ const example = extrude(10, exampleSketch)
}, },
// The to point. // The to point.
to: [number, number], to: [number, number],
},
// Tag identifiers that have been declared in this sketch group.
tags: {
}, },
// The paths in the sketch group. // The paths in the sketch group.
value: [{ value: [{
@ -627,6 +636,9 @@ const example = extrude(10, exampleSketch)
}, },
// The to point. // The to point.
to: [number, number], to: [number, number],
},
// Tag identifiers that have been declared in this sketch group.
tags: {
}, },
// The paths in the sketch group. // The paths in the sketch group.
value: [{ value: [{

View File

@ -74,6 +74,107 @@ You can nest expressions in parenthesis as well:
let myMathExpression = 3 + (1 * 2 / (3 - 7)) let myMathExpression = 3 + (1 * 2 / (3 - 7))
``` ```
Please if you find any issues using any of the above expressions or syntax ## Tags
Tags are used to give a name (tag) to a specific path.
### Tag Declaration
The syntax for declaring a tag is `$myTag` you would use it in the following
way:
```
startSketchOn('XZ')
|> startProfileAt(origin, %)
|> angledLine([0, 191.26], %, $rectangleSegmentA001)
|> angledLine([
segAng(rectangleSegmentA001, %) - 90,
196.99
], %, $rectangleSegmentB001)
|> angledLine([
segAng(rectangleSegmentA001, %),
-segLen(rectangleSegmentA001, %)
], %, $rectangleSegmentC001)
|> lineTo([profileStartX(%), profileStartY(%)], %)
|> close(%)
```
### Tag Identifier
As per the example above you can use the tag identifier to get a reference to the
tagged object. The syntax for this is `myTag`.
In the example above we use the tag identifier to get the angle of the segment
`segAng(rectangleSegmentA001, %)`.
### Tag Scope
Tags are scoped globally if in the root context meaning in this example you can
use the tag `rectangleSegmentA001` in any function or expression in the file.
However if the code was written like this:
```
fn rect = (origin) => {
return startSketchOn('XZ')
|> startProfileAt(origin, %)
|> angledLine([0, 191.26], %, $rectangleSegmentA001)
|> angledLine([
segAng(rectangleSegmentA001, %) - 90,
196.99
], %, $rectangleSegmentB001)
|> angledLine([
segAng(rectangleSegmentA001, %),
-segLen(rectangleSegmentA001, %)
], %, $rectangleSegmentC001)
|> lineTo([profileStartX(%), profileStartY(%)], %)
|> close(%)
}
rect([0, 0])
rect([20, 0])
```
Those tags would only be available in the `rect` function and not globally.
However you likely want to use those tags somewhere outside the `rect` function.
Tags are accessible through the sketch group they are declared in.
For example the following code works.
```
fn rect = (origin) => {
return startSketchOn('XZ')
|> startProfileAt(origin, %)
|> angledLine([0, 191.26], %, $rectangleSegmentA001)
|> angledLine([
segAng(rectangleSegmentA001, %) - 90,
196.99
], %, $rectangleSegmentB001)
|> angledLine([
segAng(rectangleSegmentA001, %),
-segLen(rectangleSegmentA001, %)
], %, $rectangleSegmentC001)
|> lineTo([profileStartX(%), profileStartY(%)], %)
|> close(%)
}
rect([0, 0])
const myRect = rect([20, 0])
myRect
|> extrude(10, %)
|> fillet({radius: 0.5, tags: [myRect.tags.rectangleSegmentA001]}, %)
```
See how we use the tag `rectangleSegmentA001` in the `fillet` function outside
the `rect` function. This is because the `rect` function is returning the
sketch group that contains the tags.
---
If you find any issues using any of the above expressions or syntax,
please file an issue with the `ast` label on the [modeling-app please file an issue with the `ast` label on the [modeling-app
repo](https://github.com/KittyCAD/modeling-app/issues/new). repo](https://github.com/KittyCAD/modeling-app/issues/new).

View File

@ -119,6 +119,9 @@ const example = extrude(10, exampleSketch)
}, },
// The to point. // The to point.
to: [number, number], to: [number, number],
},
// Tag identifiers that have been declared in this sketch group.
tags: {
}, },
// The paths in the sketch group. // The paths in the sketch group.
value: [{ value: [{
@ -280,6 +283,9 @@ const example = extrude(10, exampleSketch)
}, },
// The to point. // The to point.
to: [number, number], to: [number, number],
},
// Tag identifiers that have been declared in this sketch group.
tags: {
}, },
// The paths in the sketch group. // The paths in the sketch group.
value: [{ value: [{
@ -469,6 +475,9 @@ const example = extrude(10, exampleSketch)
}, },
// The to point. // The to point.
to: [number, number], to: [number, number],
},
// Tag identifiers that have been declared in this sketch group.
tags: {
}, },
// The paths in the sketch group. // The paths in the sketch group.
value: [{ value: [{
@ -630,6 +639,9 @@ const example = extrude(10, exampleSketch)
}, },
// The to point. // The to point.
to: [number, number], to: [number, number],
},
// Tag identifiers that have been declared in this sketch group.
tags: {
}, },
// The paths in the sketch group. // The paths in the sketch group.
value: [{ value: [{

View File

@ -119,6 +119,9 @@ const example = extrude(10, exampleSketch)
}, },
// The to point. // The to point.
to: [number, number], to: [number, number],
},
// Tag identifiers that have been declared in this sketch group.
tags: {
}, },
// The paths in the sketch group. // The paths in the sketch group.
value: [{ value: [{
@ -280,6 +283,9 @@ const example = extrude(10, exampleSketch)
}, },
// The to point. // The to point.
to: [number, number], to: [number, number],
},
// Tag identifiers that have been declared in this sketch group.
tags: {
}, },
// The paths in the sketch group. // The paths in the sketch group.
value: [{ value: [{
@ -469,6 +475,9 @@ const example = extrude(10, exampleSketch)
}, },
// The to point. // The to point.
to: [number, number], to: [number, number],
},
// Tag identifiers that have been declared in this sketch group.
tags: {
}, },
// The paths in the sketch group. // The paths in the sketch group.
value: [{ value: [{
@ -630,6 +639,9 @@ const example = extrude(10, exampleSketch)
}, },
// The to point. // The to point.
to: [number, number], to: [number, number],
},
// Tag identifiers that have been declared in this sketch group.
tags: {
}, },
// The paths in the sketch group. // The paths in the sketch group.
value: [{ value: [{

View File

@ -117,6 +117,9 @@ const example = extrude(10, exampleSketch)
}, },
// The to point. // The to point.
to: [number, number], to: [number, number],
},
// Tag identifiers that have been declared in this sketch group.
tags: {
}, },
// The paths in the sketch group. // The paths in the sketch group.
value: [{ value: [{
@ -278,6 +281,9 @@ const example = extrude(10, exampleSketch)
}, },
// The to point. // The to point.
to: [number, number], to: [number, number],
},
// Tag identifiers that have been declared in this sketch group.
tags: {
}, },
// The paths in the sketch group. // The paths in the sketch group.
value: [{ value: [{
@ -467,6 +473,9 @@ const example = extrude(10, exampleSketch)
}, },
// The to point. // The to point.
to: [number, number], to: [number, number],
},
// Tag identifiers that have been declared in this sketch group.
tags: {
}, },
// The paths in the sketch group. // The paths in the sketch group.
value: [{ value: [{
@ -628,6 +637,9 @@ const example = extrude(10, exampleSketch)
}, },
// The to point. // The to point.
to: [number, number], to: [number, number],
},
// Tag identifiers that have been declared in this sketch group.
tags: {
}, },
// The paths in the sketch group. // The paths in the sketch group.
value: [{ value: [{

View File

@ -115,6 +115,9 @@ const example = extrude(5, exampleSketch)
}, },
// The to point. // The to point.
to: [number, number], to: [number, number],
},
// Tag identifiers that have been declared in this sketch group.
tags: {
}, },
// The paths in the sketch group. // The paths in the sketch group.
value: [{ value: [{
@ -276,6 +279,9 @@ const example = extrude(5, exampleSketch)
}, },
// The to point. // The to point.
to: [number, number], to: [number, number],
},
// Tag identifiers that have been declared in this sketch group.
tags: {
}, },
// The paths in the sketch group. // The paths in the sketch group.
value: [{ value: [{
@ -465,6 +471,9 @@ const example = extrude(5, exampleSketch)
}, },
// The to point. // The to point.
to: [number, number], to: [number, number],
},
// Tag identifiers that have been declared in this sketch group.
tags: {
}, },
// The paths in the sketch group. // The paths in the sketch group.
value: [{ value: [{
@ -626,6 +635,9 @@ const example = extrude(5, exampleSketch)
}, },
// The to point. // The to point.
to: [number, number], to: [number, number],
},
// Tag identifiers that have been declared in this sketch group.
tags: {
}, },
// The paths in the sketch group. // The paths in the sketch group.
value: [{ value: [{

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 41 KiB

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 42 KiB

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 48 KiB

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 43 KiB

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 36 KiB

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 69 KiB

After

Width:  |  Height:  |  Size: 71 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 42 KiB

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 60 KiB

After

Width:  |  Height:  |  Size: 61 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

After

Width:  |  Height:  |  Size: 29 KiB

View File

@ -232,6 +232,7 @@ export async function getUtils(page: Page) {
return { return {
waitForAuthSkipAppStart: () => waitForAuthAndLsp(page), waitForAuthSkipAppStart: () => waitForAuthAndLsp(page),
waitForPageLoad: () => 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]) => { updateCamPosition: async (xyz: [number, number, number]) => {
@ -311,9 +312,9 @@ export async function getUtils(page: Page) {
fullPage: true, fullPage: true,
}) })
const screenshot = await PNG.sync.read(buffer) const screenshot = await PNG.sync.read(buffer)
// most likely related to pixel density but the screenshots for webkit are 2x the size const pixMultiplier: number = await page.evaluate(
// there might be a more robust way of doing this. 'window.devicePixelRatio'
const pixMultiplier = browserType === 'webkit' ? 2 : 1 )
const index = const index =
(screenshot.width * coords.y * pixMultiplier + (screenshot.width * coords.y * pixMultiplier +
coords.x * pixMultiplier) * coords.x * pixMultiplier) *
@ -376,11 +377,13 @@ export async function getUtils(page: Page) {
emulateNetworkConditions: async ( emulateNetworkConditions: async (
networkOptions: Protocol.Network.emulateNetworkConditionsParameters networkOptions: Protocol.Network.emulateNetworkConditionsParameters
) => { ) => {
// Skip on non-Chromium browsers, since we need to use the CDP. if (browserType !== 'chromium') {
test.skip( console.warn('emulateNetworkConditions will not work on this browser')
cdpSession === null, }
'Network emulation is only supported in Chromium' if (cdpSession === null) {
) // Use a fail safe if we can't simulate disconnect (on Safari)
return page.evaluate('window.tearDown()')
}
cdpSession?.send('Network.emulateNetworkConditions', networkOptions) cdpSession?.send('Network.emulateNetworkConditions', networkOptions)
}, },

View File

@ -1,14 +1,15 @@
{ {
"name": "untitled-app", "name": "untitled-app",
"version": "0.22.7", "version": "0.23.1",
"private": true, "private": true,
"dependencies": { "dependencies": {
"@codemirror/autocomplete": "^6.16.3", "@codemirror/autocomplete": "^6.17.0",
"@codemirror/commands": "^6.6.0", "@codemirror/commands": "^6.6.0",
"@codemirror/language": "^6.10.2", "@codemirror/language": "^6.10.2",
"@codemirror/lint": "^6.8.1", "@codemirror/lint": "^6.8.1",
"@codemirror/search": "^6.5.6", "@codemirror/search": "^6.5.6",
"@codemirror/state": "^6.4.1", "@codemirror/state": "^6.4.1",
"@codemirror/theme-one-dark": "^6.1.2",
"@csstools/postcss-oklab-function": "^3.0.16", "@csstools/postcss-oklab-function": "^3.0.16",
"@fortawesome/fontawesome-svg-core": "^6.5.2", "@fortawesome/fontawesome-svg-core": "^6.5.2",
"@fortawesome/free-brands-svg-icons": "^6.5.2", "@fortawesome/free-brands-svg-icons": "^6.5.2",
@ -19,23 +20,22 @@
"@kittycad/lib": "^0.0.69", "@kittycad/lib": "^0.0.69",
"@react-hook/resize-observer": "^2.0.1", "@react-hook/resize-observer": "^2.0.1",
"@replit/codemirror-interact": "^6.3.1", "@replit/codemirror-interact": "^6.3.1",
"@tauri-apps/api": "2.0.0-beta.12", "@tauri-apps/api": "^2.0.0-beta.14",
"@tauri-apps/plugin-dialog": "^2.0.0-beta.2", "@tauri-apps/plugin-dialog": "^2.0.0-beta.6",
"@tauri-apps/plugin-fs": "^2.0.0-beta.5", "@tauri-apps/plugin-fs": "^2.0.0-beta.6",
"@tauri-apps/plugin-http": "^2.0.0-beta.2", "@tauri-apps/plugin-http": "^2.0.0-beta.7",
"@tauri-apps/plugin-os": "^2.0.0-beta.3", "@tauri-apps/plugin-os": "^2.0.0-beta.6",
"@tauri-apps/plugin-process": "^2.0.0-beta.2", "@tauri-apps/plugin-process": "^2.0.0-beta.6",
"@tauri-apps/plugin-shell": "^2.0.0-beta.2", "@tauri-apps/plugin-shell": "^2.0.0-beta.7",
"@tauri-apps/plugin-updater": "^2.0.0-beta.3", "@tauri-apps/plugin-updater": "^2.0.0-beta.6",
"@ts-stack/markdown": "^1.5.0", "@ts-stack/markdown": "^1.5.0",
"@tweenjs/tween.js": "^23.1.1", "@tweenjs/tween.js": "^23.1.1",
"@uiw/react-codemirror": "^4.21.25",
"@xstate/inspect": "^0.8.0", "@xstate/inspect": "^0.8.0",
"@xstate/react": "^3.2.2", "@xstate/react": "^3.2.2",
"codemirror": "^6.0.1", "codemirror": "^6.0.1",
"decamelize": "^6.0.0", "decamelize": "^6.0.0",
"fuse.js": "^7.0.0", "fuse.js": "^7.0.0",
"html2canvas-pro": "^1.5.0", "html2canvas-pro": "^1.5.2",
"json-rpc-2.0": "^1.6.0", "json-rpc-2.0": "^1.6.0",
"jszip": "^3.10.1", "jszip": "^3.10.1",
"re-resizable": "^6.9.11", "re-resizable": "^6.9.11",
@ -48,7 +48,7 @@
"react-modal-promise": "^1.0.2", "react-modal-promise": "^1.0.2",
"react-router-dom": "^6.23.1", "react-router-dom": "^6.23.1",
"sketch-helpers": "^0.0.4", "sketch-helpers": "^0.0.4",
"three": "^0.164.1", "three": "^0.166.1",
"typescript": "^5.4.5", "typescript": "^5.4.5",
"ua-parser-js": "^1.0.37", "ua-parser-js": "^1.0.37",
"uuid": "^9.0.1", "uuid": "^9.0.1",
@ -56,8 +56,7 @@
"vscode-languageserver-protocol": "^3.17.5", "vscode-languageserver-protocol": "^3.17.5",
"vscode-uri": "^3.0.8", "vscode-uri": "^3.0.8",
"web-vitals": "^3.5.2", "web-vitals": "^3.5.2",
"xstate": "^4.38.2", "xstate": "^4.38.2"
"zustand": "^4.5.2"
}, },
"scripts": { "scripts": {
"start": "vite", "start": "vite",
@ -110,7 +109,7 @@
"@babel/plugin-proposal-private-property-in-object": "^7.21.11", "@babel/plugin-proposal-private-property-in-object": "^7.21.11",
"@babel/preset-env": "^7.24.3", "@babel/preset-env": "^7.24.3",
"@iarna/toml": "^2.2.5", "@iarna/toml": "^2.2.5",
"@playwright/test": "^1.44.1", "@playwright/test": "^1.45.1",
"@tauri-apps/cli": "==2.0.0-beta.13", "@tauri-apps/cli": "==2.0.0-beta.13",
"@testing-library/jest-dom": "^5.14.1", "@testing-library/jest-dom": "^5.14.1",
"@testing-library/react": "^15.0.2", "@testing-library/react": "^15.0.2",
@ -158,7 +157,7 @@
"vitest": "^1.6.0", "vitest": "^1.6.0",
"vitest-webgl-canvas-mock": "^1.1.0", "vitest-webgl-canvas-mock": "^1.1.0",
"wait-on": "^7.2.0", "wait-on": "^7.2.0",
"wasm-pack": "^0.12.1", "wasm-pack": "^0.13.0",
"ws": "^8.17.0", "ws": "^8.17.0",
"yarn": "^1.22.22" "yarn": "^1.22.22"
} }

View File

@ -1,10 +1,7 @@
import { autocompletion } from '@codemirror/autocomplete' import { foldService } from '@codemirror/language'
import { foldService, syntaxTree } from '@codemirror/language'
import { Extension, EditorState } from '@codemirror/state' import { Extension, EditorState } from '@codemirror/state'
import { ViewPlugin } from '@codemirror/view' import { ViewPlugin } from '@codemirror/view'
import { CompletionTriggerKind } from 'vscode-languageserver-protocol'
import { import {
docPathFacet, docPathFacet,
LanguageServerPlugin, LanguageServerPlugin,
@ -13,7 +10,6 @@ import {
workspaceFolders, workspaceFolders,
LanguageServerOptions, LanguageServerOptions,
} from './plugin/lsp' } from './plugin/lsp'
import { offsetToPos } from './plugin/util'
export type { LanguageServerClientOptions } from './client' export type { LanguageServerClientOptions } from './client'
export { LanguageServerClient } from './client' export { LanguageServerClient } from './client'
@ -24,14 +20,15 @@ export {
LspWorkerEventType, LspWorkerEventType,
} from './client/codec' } from './client/codec'
export type { LanguageServerOptions } from './plugin/lsp' export type { LanguageServerOptions } from './plugin/lsp'
export type { TransactionInfo, RelevantUpdate } from './plugin/annotations'
export { updateInfo, TransactionAnnotation } from './plugin/annotations'
export { export {
LanguageServerPlugin, LanguageServerPlugin,
LanguageServerPluginSpec, LanguageServerPluginSpec,
docPathFacet, docPathFacet,
languageId, languageId,
workspaceFolders, workspaceFolders,
lspSemanticTokensEvent,
lspDiagnosticsEvent,
lspFormatCodeEvent,
} from './plugin/lsp' } from './plugin/lsp'
export { posToOffset, offsetToPos } from './plugin/util' export { posToOffset, offsetToPos } from './plugin/util'
@ -51,63 +48,10 @@ export function lspPlugin(options: LanguageServerOptions): Extension {
if (plugin == null) return null if (plugin == null) return null
// Get the folding ranges from the language server. // Get the folding ranges from the language server.
// Since this is async we directly need to update the folding ranges after. // Since this is async we directly need to update the folding ranges after.
return plugin?.foldingRange(lineStart, lineEnd) const range = plugin?.foldingRange(lineStart, lineEnd)
return range
}), }),
] ]
if (options.client.getServerCapabilities().completionProvider) {
ext.push(
autocompletion({
defaultKeymap: false,
override: [
async (context) => {
if (plugin === null) {
return null
}
const { state, pos, explicit } = context
let nodeBefore = syntaxTree(state).resolveInner(pos, -1)
if (
nodeBefore.name === 'BlockComment' ||
nodeBefore.name === 'LineComment'
)
return null
const line = state.doc.lineAt(pos)
let trigKind: CompletionTriggerKind = CompletionTriggerKind.Invoked
let trigChar: string | undefined
if (
!explicit &&
plugin.client
.getServerCapabilities()
.completionProvider?.triggerCharacters?.includes(
line.text[pos - line.from - 1]
)
) {
trigKind = CompletionTriggerKind.TriggerCharacter
trigChar = line.text[pos - line.from - 1]
}
if (
trigKind === CompletionTriggerKind.Invoked &&
!context.matchBefore(/\w+$/)
) {
return null
}
return await plugin.requestCompletion(
context,
offsetToPos(state.doc, pos),
{
triggerKind: trigKind,
triggerCharacter: trigChar,
}
)
},
],
})
)
}
return ext return ext
} }

View File

@ -1,131 +0,0 @@
import { hasNextSnippetField, pickedCompletion } from '@codemirror/autocomplete'
import { Annotation, Transaction } from '@codemirror/state'
import type { ViewUpdate } from '@codemirror/view'
export enum LspAnnotation {
SemanticTokens = 'semantic-tokens',
FormatCode = 'format-code',
Diagnostics = 'diagnostics',
}
const lspEvent = Annotation.define<LspAnnotation>()
export const lspSemanticTokensEvent = lspEvent.of(LspAnnotation.SemanticTokens)
export const lspFormatCodeEvent = lspEvent.of(LspAnnotation.FormatCode)
export const lspDiagnosticsEvent = lspEvent.of(LspAnnotation.Diagnostics)
export enum TransactionAnnotation {
Remote = 'remote',
UserSelect = 'user.select',
UserInput = 'user.input',
UserMove = 'user.move',
UserDelete = 'user.delete',
UserUndo = 'user.undo',
UserRedo = 'user.redo',
SemanticTokens = 'SemanticTokens',
FormatCode = 'FormatCode',
Diagnostics = 'Diagnostics',
PickedCompletion = 'PickedCompletion',
}
export interface TransactionInfo {
annotations: TransactionAnnotation[]
time: number | null
docChanged: boolean
addToHistory: boolean
inSnippet: boolean
transaction: Transaction
}
export const updateInfo = (update: ViewUpdate): TransactionInfo[] => {
let transactionInfos: TransactionInfo[] = []
for (const tr of update.transactions) {
let annotations: TransactionAnnotation[] = []
if (tr.isUserEvent('select')) {
annotations.push(TransactionAnnotation.UserSelect)
}
if (tr.isUserEvent('input')) {
annotations.push(TransactionAnnotation.UserInput)
}
if (tr.isUserEvent('delete')) {
annotations.push(TransactionAnnotation.UserDelete)
}
if (tr.isUserEvent('undo')) {
annotations.push(TransactionAnnotation.UserUndo)
}
if (tr.isUserEvent('redo')) {
annotations.push(TransactionAnnotation.UserRedo)
}
if (tr.isUserEvent('move')) {
annotations.push(TransactionAnnotation.UserMove)
}
if (tr.annotation(pickedCompletion) !== undefined) {
annotations.push(TransactionAnnotation.PickedCompletion)
}
if (tr.annotation(lspSemanticTokensEvent.type) !== undefined) {
annotations.push(TransactionAnnotation.SemanticTokens)
}
if (tr.annotation(lspFormatCodeEvent.type) !== undefined) {
annotations.push(TransactionAnnotation.FormatCode)
}
if (tr.annotation(lspDiagnosticsEvent.type) !== undefined) {
annotations.push(TransactionAnnotation.Diagnostics)
}
if (tr.annotation(Transaction.remote) !== undefined) {
annotations.push(TransactionAnnotation.Remote)
}
transactionInfos.push({
annotations,
time: tr.annotation(Transaction.time) || null,
docChanged: tr.docChanged,
addToHistory: tr.annotation(Transaction.addToHistory) || false,
inSnippet: hasNextSnippetField(update.state),
transaction: tr,
})
}
return transactionInfos
}
export interface RelevantUpdate {
overall: boolean
userSelect: boolean
time: number | null
}
export const relevantUpdate = (update: ViewUpdate): RelevantUpdate => {
const infos = updateInfo(update)
// Make sure we are not in a snippet
if (infos.some((info) => info.inSnippet)) {
return {
overall: false,
userSelect: false,
time: null,
}
}
return {
overall: infos.some(
(info) =>
info.docChanged ||
info.annotations.includes(TransactionAnnotation.UserInput) ||
info.annotations.includes(TransactionAnnotation.UserDelete) ||
info.annotations.includes(TransactionAnnotation.UserUndo) ||
info.annotations.includes(TransactionAnnotation.UserRedo) ||
info.annotations.includes(TransactionAnnotation.UserMove)
),
userSelect: infos.some((info) =>
info.annotations.includes(TransactionAnnotation.UserSelect)
),
time: infos.length ? infos[0].time : null,
}
}

View File

@ -1,5 +1,6 @@
import { import {
acceptCompletion, acceptCompletion,
autocompletion,
clearSnippet, clearSnippet,
closeCompletion, closeCompletion,
hasNextSnippetField, hasNextSnippetField,
@ -8,10 +9,17 @@ import {
prevSnippetField, prevSnippetField,
startCompletion, startCompletion,
} from '@codemirror/autocomplete' } from '@codemirror/autocomplete'
import { Prec } from '@codemirror/state' import { Prec, Extension } from '@codemirror/state'
import { EditorView, keymap, KeyBinding } from '@codemirror/view' import { EditorView, keymap, KeyBinding, ViewPlugin } from '@codemirror/view'
import { CompletionItemKind } from 'vscode-languageserver-protocol' import {
CompletionItemKind,
CompletionTriggerKind,
} from 'vscode-languageserver-protocol'
import { LanguageServerPlugin } from './lsp'
import { offsetToPos } from './util'
import { syntaxTree } from '@codemirror/language'
export const CompletionItemKindMap = Object.fromEntries( export const CompletionItemKindMap = Object.fromEntries(
Object.entries(CompletionItemKind).map(([key, value]) => [value, key]) Object.entries(CompletionItemKind).map(([key, value]) => [value, key])
@ -46,6 +54,59 @@ const lspAutocompleteKeymap: readonly KeyBinding[] = [
}, },
] ]
export const lspAutocompleteKeymapExt = Prec.highest( const lspAutocompleteKeymapExt = Prec.highest(keymap.of(lspAutocompleteKeymap))
keymap.computeN([], () => [lspAutocompleteKeymap])
) export default function lspAutocompleteExt(
plugin: ViewPlugin<LanguageServerPlugin>
): Extension {
return [
lspAutocompleteKeymapExt,
autocompletion({
defaultKeymap: false,
override: [
async (context) => {
const { state, pos, explicit, view } = context
let value = view?.plugin(plugin)
if (!value) return null
let nodeBefore = syntaxTree(state).resolveInner(pos, -1)
if (
nodeBefore.name === 'BlockComment' ||
nodeBefore.name === 'LineComment'
)
return null
const line = state.doc.lineAt(pos)
let trigKind: CompletionTriggerKind = CompletionTriggerKind.Invoked
let trigChar: string | undefined
if (
!explicit &&
value.client
.getServerCapabilities()
.completionProvider?.triggerCharacters?.includes(
line.text[pos - line.from - 1]
)
) {
trigKind = CompletionTriggerKind.TriggerCharacter
trigChar = line.text[pos - line.from - 1]
}
if (
trigKind === CompletionTriggerKind.Invoked &&
!context.matchBefore(/\w+$/)
) {
return null
}
return await value.requestCompletion(
context,
offsetToPos(state.doc, pos),
{
triggerKind: trigKind,
triggerCharacter: trigChar,
}
)
},
],
}),
]
}

View File

@ -4,7 +4,13 @@ import type {
CompletionResult, CompletionResult,
} from '@codemirror/autocomplete' } from '@codemirror/autocomplete'
import { completeFromList, snippetCompletion } from '@codemirror/autocomplete' import { completeFromList, snippetCompletion } from '@codemirror/autocomplete'
import { Facet, StateEffect, Extension, Transaction } from '@codemirror/state' import {
Facet,
StateEffect,
Extension,
Transaction,
Annotation,
} from '@codemirror/state'
import type { import type {
ViewUpdate, ViewUpdate,
PluginValue, PluginValue,
@ -22,15 +28,10 @@ import {
import { URI } from 'vscode-uri' import { URI } from 'vscode-uri'
import { LanguageServerClient } from '../client' import { LanguageServerClient } from '../client'
import {
lspSemanticTokensEvent,
lspFormatCodeEvent,
relevantUpdate,
} from './annotations'
import { CompletionItemKindMap } from './autocomplete' import { CompletionItemKindMap } from './autocomplete'
import { addToken, SemanticToken } from './semantic-tokens' import { addToken, SemanticToken } from './semantic-tokens'
import { deferExecution, posToOffset, formatMarkdownContents } from './util' import { deferExecution, posToOffset, formatMarkdownContents } from './util'
import { lspAutocompleteKeymapExt } from './autocomplete' import lspAutocompleteExt from './autocomplete'
import lspHoverExt from './hover' import lspHoverExt from './hover'
import lspFormatExt from './format' import lspFormatExt from './format'
import lspIndentExt from './indent' import lspIndentExt from './indent'
@ -47,6 +48,17 @@ export const workspaceFolders = Facet.define<
LSP.WorkspaceFolder[] LSP.WorkspaceFolder[]
>({ combine: useLast }) >({ combine: useLast })
export enum LspAnnotation {
SemanticTokens = 'semantic-tokens',
FormatCode = 'format-code',
Diagnostics = 'diagnostics',
}
const lspEvent = Annotation.define<LspAnnotation>()
export const lspSemanticTokensEvent = lspEvent.of(LspAnnotation.SemanticTokens)
export const lspFormatCodeEvent = lspEvent.of(LspAnnotation.FormatCode)
export const lspDiagnosticsEvent = lspEvent.of(LspAnnotation.Diagnostics)
export interface LanguageServerOptions { export interface LanguageServerOptions {
// We assume this is the main project directory, we are currently working in. // We assume this is the main project directory, we are currently working in.
workspaceFolders: LSP.WorkspaceFolder[] workspaceFolders: LSP.WorkspaceFolder[]
@ -131,11 +143,6 @@ export class LanguageServerPlugin implements PluginValue {
} }
update(viewUpdate: ViewUpdate) { update(viewUpdate: ViewUpdate) {
const isRelevant = relevantUpdate(viewUpdate)
if (!isRelevant.overall) {
return
}
// If the doc didn't change we can return early. // If the doc didn't change we can return early.
if (!viewUpdate.docChanged) { if (!viewUpdate.docChanged) {
return return
@ -284,19 +291,16 @@ export class LanguageServerPlugin implements PluginValue {
}, },
}) })
if (!result) return null if (!result || !result.length) return null
for (let i = 0; i < result.length; i++) { this.view.dispatch({
const { range, newText } = result[i] changes: result.map(({ range, newText }) => ({
this.view.dispatch({ from: posToOffset(this.view.state.doc, range.start)!,
changes: { to: posToOffset(this.view.state.doc, range.end)!,
from: posToOffset(this.view.state.doc, range.start)!, insert: newText,
to: posToOffset(this.view.state.doc, range.end)!, })),
insert: newText, annotations: lspFormatCodeEvent,
}, })
annotations: [lspFormatCodeEvent, Transaction.addToHistory.of(true)],
})
}
} }
async requestCompletion( async requestCompletion(
@ -552,7 +556,7 @@ export class LanguageServerPluginSpec
{ {
provide(plugin: ViewPlugin<LanguageServerPlugin>): Extension { provide(plugin: ViewPlugin<LanguageServerPlugin>): Extension {
return [ return [
lspAutocompleteKeymapExt, lspAutocompleteExt(plugin),
lspFormatExt(plugin), lspFormatExt(plugin),
lspHoverExt(plugin), lspHoverExt(plugin),
lspIndentExt(), lspIndentExt(),

View File

@ -4,7 +4,7 @@ import { EditorView, Decoration, DecorationSet } from '@codemirror/view'
import { Tag, tags } from '@lezer/highlight' import { Tag, tags } from '@lezer/highlight'
import { lspSemanticTokensEvent } from './annotations' import { lspSemanticTokensEvent } from './lsp'
export interface SemanticToken { export interface SemanticToken {
from: number from: number

541
src-tauri/Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -11,27 +11,27 @@ rust-version = "1.70"
# 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 = "2.0.0-beta.13", features = [] } tauri-build = { version = "2.0.0-beta.18", features = [] }
[dependencies] [dependencies]
anyhow = "1" anyhow = "1"
kcl-lib = { version = "0.1.53", path = "../src/wasm-lib/kcl" } kcl-lib = { version = "0.1.53", path = "../src/wasm-lib/kcl" }
kittycad = "0.3.5" kittycad = "0.3.7"
log = "0.4.21" log = "0.4.21"
oauth2 = "4.4.2" oauth2 = "4.4.2"
serde_json = "1.0" serde_json = "1.0"
tauri = { version = "2.0.0-beta.15", features = [ "devtools", "unstable"] } tauri = { version = "2.0.0-beta.23", features = [ "devtools", "unstable"] }
tauri-plugin-cli = { version = "2.0.0-beta.3" } tauri-plugin-cli = { version = "2.0.0-beta.7" }
tauri-plugin-deep-link = { version = "2.0.0-beta.3" } tauri-plugin-deep-link = { version = "2.0.0-beta.8" }
tauri-plugin-dialog = { version = "2.0.0-beta.6" } tauri-plugin-dialog = { version = "2.0.0-beta.6" }
tauri-plugin-fs = { version = "2.0.0-beta.9" } tauri-plugin-fs = { version = "2.0.0-beta.10" }
tauri-plugin-http = { version = "2.0.0-beta.6" } tauri-plugin-http = { version = "2.0.0-beta.11" }
tauri-plugin-log = { version = "2.0.0-beta.4" } tauri-plugin-log = { version = "2.0.0-beta.7" }
tauri-plugin-os = { version = "2.0.0-beta.2" } tauri-plugin-os = { version = "2.0.0-beta.7" }
tauri-plugin-persisted-scope = { version = "2.0.0-beta.7" } tauri-plugin-persisted-scope = { version = "2.0.0-beta.10" }
tauri-plugin-process = { version = "2.0.0-beta.2" } tauri-plugin-process = { version = "2.0.0-beta.7" }
tauri-plugin-shell = { version = "2.0.0-beta.2" } tauri-plugin-shell = { version = "2.0.0-beta.8" }
tauri-plugin-updater = { version = "2.0.0-beta.4" } tauri-plugin-updater = { version = "2.0.0-beta.9" }
tokio = { version = "1.37.0", features = ["time", "fs", "process"] } tokio = { version = "1.37.0", features = ["time", "fs", "process"] }
toml = "0.8.2" toml = "0.8.2"
url = "2.5.0" url = "2.5.0"

View File

@ -63,16 +63,22 @@
"subcommands": {} "subcommands": {}
}, },
"deep-link": { "deep-link": {
"domains": [ "mobile": [
{ {
"host": "app.zoo.dev" "host": "app.zoo.dev"
} }
] ],
"desktop": {
"schemes": [
"zoo",
"zoo-modeling-app"
]
}
}, },
"shell": { "shell": {
"open": true "open": true
} }
}, },
"productName": "Zoo Modeling App", "productName": "Zoo Modeling App",
"version": "0.22.7" "version": "0.23.1"
} }

View File

@ -1,6 +1,5 @@
import { MouseEventHandler, useEffect, useRef } from 'react' import { MouseEventHandler, useEffect, useMemo, useRef } from 'react'
import { uuidv4 } from 'lib/utils' import { uuidv4 } from 'lib/utils'
import { useStore } from './useStore'
import { useHotKeyListener } from './hooks/useHotKeyListener' import { useHotKeyListener } from './hooks/useHotKeyListener'
import { Stream } from './components/Stream' import { Stream } from './components/Stream'
import { EngineCommand } from './lang/std/engineConnection' import { EngineCommand } from './lang/std/engineConnection'
@ -26,6 +25,7 @@ import ModalContainer from 'react-modal-promise'
import useHotkeyWrapper from 'lib/hotkeyWrapper' import useHotkeyWrapper from 'lib/hotkeyWrapper'
import Gizmo from 'components/Gizmo' import Gizmo from 'components/Gizmo'
import { CoreDumpManager } from 'lib/coredump' import { CoreDumpManager } from 'lib/coredump'
import { UnitsMenu } from 'components/UnitsMenu'
export function App() { export function App() {
useRefreshSettings(paths.FILE + 'SETTINGS') useRefreshSettings(paths.FILE + 'SETTINGS')
@ -44,22 +44,15 @@ export function App() {
}, [projectName, projectPath]) }, [projectName, projectPath])
useHotKeyListener() useHotKeyListener()
const { buttonDownInStream, didDragInStream, streamDimensions, setHtmlRef } = const { context } = useModelingContext()
useStore((s) => ({
buttonDownInStream: s.buttonDownInStream,
didDragInStream: s.didDragInStream,
streamDimensions: s.streamDimensions,
setHtmlRef: s.setHtmlRef,
}))
useEffect(() => {
setHtmlRef(ref)
}, [ref])
const { auth, settings } = useSettingsAuthContext() const { auth, settings } = useSettingsAuthContext()
const token = auth?.context?.token const token = auth?.context?.token
const coreDumpManager = new CoreDumpManager(engineCommandManager, ref, token) const coreDumpManager = useMemo(
() => new CoreDumpManager(engineCommandManager, token),
[]
)
const { const {
app: { onboardingStatus }, app: { onboardingStatus },
@ -81,7 +74,7 @@ export function App() {
(p) => p === onboardingStatus.current (p) => p === onboardingStatus.current
) )
? 'opacity-20' ? 'opacity-20'
: didDragInStream : context.store?.didDragInStream
? 'opacity-40' ? 'opacity-40'
: '' : ''
@ -99,11 +92,11 @@ export function App() {
clientX: e.clientX, clientX: e.clientX,
clientY: e.clientY, clientY: e.clientY,
el: e.currentTarget, el: e.currentTarget,
...streamDimensions, ...context.store?.streamDimensions,
}) })
const newCmdId = uuidv4() const newCmdId = uuidv4()
if (buttonDownInStream === undefined) { if (context.store?.buttonDownInStream === undefined) {
debounceSocketSend({ debounceSocketSend({
type: 'modeling_cmd_req', type: 'modeling_cmd_req',
cmd: { cmd: {
@ -125,7 +118,7 @@ export function App() {
className={ className={
'transition-opacity transition-duration-75 ' + 'transition-opacity transition-duration-75 ' +
paneOpacity + paneOpacity +
(buttonDownInStream ? ' pointer-events-none' : '') (context.store?.buttonDownInStream ? ' pointer-events-none' : '')
} }
project={{ project, file }} project={{ project, file }}
enableMenu={true} enableMenu={true}
@ -135,6 +128,7 @@ export function App() {
<Stream /> <Stream />
{/* <CamToggle /> */} {/* <CamToggle /> */}
<LowerRightControls coreDumpManager={coreDumpManager}> <LowerRightControls coreDumpManager={coreDumpManager}>
<UnitsMenu />
<Gizmo /> <Gizmo />
</LowerRightControls> </LowerRightControls>
</div> </div>

41
src/AppState.tsx Normal file
View File

@ -0,0 +1,41 @@
import { createContext, useContext, useState, ReactNode } from 'react'
/*
This is for a very small handful of global state we need that doesn't fit into
any of the Xstate machines.
Please do not fill this up with junk.
*/
interface AppState {
isStreamReady: boolean
setAppState: (newAppState: Partial<AppState>) => void
}
const AppStateContext = createContext<AppState>({
isStreamReady: false,
setAppState: () => {},
})
export const useAppState = () => useContext(AppStateContext)
export const AppStateProvider = ({ children }: { children: ReactNode }) => {
const [appState, _setAppState] = useState<AppState>({
isStreamReady: false,
setAppState: () => {},
})
const setAppState = (newAppState: Partial<AppState>) =>
_setAppState({ ...appState, ...newAppState })
return (
<AppStateContext.Provider
value={{
isStreamReady: appState.isStreamReady,
setAppState,
}}
>
{children}
</AppStateContext.Provider>
)
}

View File

@ -33,6 +33,14 @@ import LspProvider from 'components/LspProvider'
import { KclContextProvider } from 'lang/KclProvider' import { KclContextProvider } from 'lang/KclProvider'
import { BROWSER_PROJECT_NAME } from 'lib/constants' import { BROWSER_PROJECT_NAME } from 'lib/constants'
import { getState, setState } from 'lib/tauri' import { getState, setState } from 'lib/tauri'
import { CoreDumpManager } from 'lib/coredump'
import { engineCommandManager } from 'lib/singletons'
import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext'
import useHotkeyWrapper from 'lib/hotkeyWrapper'
import toast from 'react-hot-toast'
import { coreDump } from 'lang/wasm'
import { useMemo } from 'react'
import { AppStateProvider } from 'AppState'
const router = createBrowserRouter([ const router = createBrowserRouter([
{ {
@ -45,7 +53,9 @@ const router = createBrowserRouter([
<SettingsAuthProvider> <SettingsAuthProvider>
<LspProvider> <LspProvider>
<KclContextProvider> <KclContextProvider>
<Outlet /> <AppStateProvider>
<Outlet />
</AppStateProvider>
</KclContextProvider> </KclContextProvider>
</LspProvider> </LspProvider>
</SettingsAuthProvider> </SettingsAuthProvider>
@ -87,6 +97,7 @@ const router = createBrowserRouter([
<Auth> <Auth>
<FileMachineProvider> <FileMachineProvider>
<ModelingMachineProvider> <ModelingMachineProvider>
<CoreDump />
<Outlet /> <Outlet />
<App /> <App />
<CommandBar /> <CommandBar />
@ -165,3 +176,30 @@ export const Router = () => {
</NetworkContext.Provider> </NetworkContext.Provider>
) )
} }
function CoreDump() {
const { auth } = useSettingsAuthContext()
const token = auth?.context?.token
const coreDumpManager = useMemo(
() => new CoreDumpManager(engineCommandManager, token),
[]
)
useHotkeyWrapper(['meta + shift + .'], () => {
toast.promise(
coreDump(coreDumpManager, true),
{
loading: 'Starting core dump...',
success: 'Core dump completed successfully',
error: 'Error while exporting core dump',
},
{
success: {
// Note: this extended duration is especially important for Playwright e2e testing
// default duration is 2000 - https://react-hot-toast.com/docs/toast#default-durations
duration: 6000,
},
}
)
})
return null
}

View File

@ -8,10 +8,14 @@ import { NetworkHealthState } from 'hooks/useNetworkStatus'
import { ActionButton } from 'components/ActionButton' import { ActionButton } from 'components/ActionButton'
import { isSingleCursorInPipe } from 'lang/queryAst' import { isSingleCursorInPipe } from 'lang/queryAst'
import { useKclContext } from 'lang/KclProvider' import { useKclContext } from 'lang/KclProvider'
import { useStore } from 'useStore'
import { ActionButtonDropdown } from 'components/ActionButtonDropdown' import { ActionButtonDropdown } from 'components/ActionButtonDropdown'
import { useHotkeys } from 'react-hotkeys-hook' import { useHotkeys } from 'react-hotkeys-hook'
import Tooltip from 'components/Tooltip' import Tooltip from 'components/Tooltip'
import { useAppState } from 'AppState'
import {
canRectangleTool,
isEditingExistingSketch,
} from 'machines/modelingMachine'
export function Toolbar({ export function Toolbar({
className = '', className = '',
@ -38,38 +42,56 @@ export function Toolbar({
const toolbarButtonsRef = useRef<HTMLUListElement>(null) const toolbarButtonsRef = useRef<HTMLUListElement>(null)
const { overallState } = useNetworkContext() const { overallState } = useNetworkContext()
const { isExecuting } = useKclContext() const { isExecuting } = useKclContext()
const { isStreamReady } = useStore((s) => ({ const { isStreamReady } = useAppState()
isStreamReady: s.isStreamReady,
}))
const disableAllButtons = const disableAllButtons =
(overallState !== NetworkHealthState.Ok && (overallState !== NetworkHealthState.Ok &&
overallState !== NetworkHealthState.Weak) || overallState !== NetworkHealthState.Weak) ||
isExecuting || isExecuting ||
!isStreamReady !isStreamReady
const disableLineButton =
state.matches('Sketch.Rectangle tool.Awaiting second corner') ||
disableAllButtons
useHotkeys( useHotkeys(
'l', 'l',
() => () =>
state.matches('Sketch.Line tool') state.matches('Sketch.Line tool')
? send('CancelSketch') ? send('CancelSketch')
: send('Equip Line tool'), : send({
{ enabled: !disableAllButtons, scopes: ['sketch'] } type: 'change tool',
data: 'line',
}),
{ enabled: !disableLineButton, scopes: ['sketch'] }
) )
const disableTangentialArc =
(!isEditingExistingSketch(context) &&
!state.matches('Sketch.Tangential arc to')) ||
disableAllButtons
useHotkeys( useHotkeys(
'a', 'a',
() => () =>
state.matches('Sketch.Tangential arc to') state.matches('Sketch.Tangential arc to')
? send('CancelSketch') ? send('CancelSketch')
: send('Equip tangential arc to'), : send({
{ enabled: !disableAllButtons, scopes: ['sketch'] } type: 'change tool',
data: 'tangentialArc',
}),
{ enabled: !disableTangentialArc, scopes: ['sketch'] }
) )
const disableRectangle =
(!canRectangleTool(context) && !state.matches('Sketch.Rectangle tool')) ||
disableAllButtons
useHotkeys( useHotkeys(
'r', 'r',
() => () =>
state.matches('Sketch.Rectangle tool') state.matches('Sketch.Rectangle tool')
? send('CancelSketch') ? send('CancelSketch')
: send('Equip rectangle tool'), : send({
{ enabled: !disableAllButtons, scopes: ['sketch'] } type: 'change tool',
data: 'rectangle',
}),
{ enabled: !disableRectangle, scopes: ['sketch'] }
) )
useHotkeys( useHotkeys(
's', 's',
@ -82,7 +104,7 @@ export function Toolbar({
useHotkeys( useHotkeys(
'esc', 'esc',
() => () =>
state.matches('Sketch.SketchIdle') ['Sketch no face', 'Sketch.SketchIdle'].some(state.matches)
? send('Cancel') ? send('Cancel')
: send('CancelSketch'), : send('CancelSketch'),
{ enabled: !disableAllButtons, scopes: ['sketch'] } { enabled: !disableAllButtons, scopes: ['sketch'] }
@ -225,6 +247,11 @@ export function Toolbar({
</ActionButton> </ActionButton>
</li> </li>
)} )}
{state.matches('Sketch no face') && (
<li className="contents">
<div className="mx-2 text-sm">click plane or face to sketch on</div>
</li>
)}
{state.matches('Sketch') && !state.matches('idle') && ( {state.matches('Sketch') && !state.matches('idle') && (
<> <>
<li className="contents" key="line-button"> <li className="contents" key="line-button">
@ -234,7 +261,10 @@ export function Toolbar({
onClick={() => onClick={() =>
state?.matches('Sketch.Line tool') state?.matches('Sketch.Line tool')
? send('CancelSketch') ? send('CancelSketch')
: send('Equip Line tool') : send({
type: 'change tool',
data: 'line',
})
} }
aria-pressed={state?.matches('Sketch.Line tool')} aria-pressed={state?.matches('Sketch.Line tool')}
iconStart={{ iconStart={{
@ -242,7 +272,7 @@ export function Toolbar({
iconClassName, iconClassName,
bgClassName, bgClassName,
}} }}
disabled={disableAllButtons} disabled={disableLineButton}
> >
Line Line
<Tooltip <Tooltip
@ -261,7 +291,10 @@ export function Toolbar({
onClick={() => onClick={() =>
state.matches('Sketch.Tangential arc to') state.matches('Sketch.Tangential arc to')
? send('CancelSketch') ? send('CancelSketch')
: send('Equip tangential arc to') : send({
type: 'change tool',
data: 'tangentialArc',
})
} }
aria-pressed={state.matches('Sketch.Tangential arc to')} aria-pressed={state.matches('Sketch.Tangential arc to')}
iconStart={{ iconStart={{
@ -269,11 +302,7 @@ export function Toolbar({
iconClassName, iconClassName,
bgClassName, bgClassName,
}} }}
disabled={ disabled={disableTangentialArc}
(!state.can('Equip tangential arc to') &&
!state.matches('Sketch.Tangential arc to')) ||
disableAllButtons
}
> >
Tangential Arc Tangential Arc
<Tooltip <Tooltip
@ -292,7 +321,10 @@ export function Toolbar({
onClick={() => onClick={() =>
state.matches('Sketch.Rectangle tool') state.matches('Sketch.Rectangle tool')
? send('CancelSketch') ? send('CancelSketch')
: send('Equip rectangle tool') : send({
type: 'change tool',
data: 'rectangle',
})
} }
aria-pressed={state.matches('Sketch.Rectangle tool')} aria-pressed={state.matches('Sketch.Rectangle tool')}
iconStart={{ iconStart={{
@ -300,13 +332,9 @@ export function Toolbar({
iconClassName, iconClassName,
bgClassName, bgClassName,
}} }}
disabled={ disabled={disableRectangle}
(!state.can('Equip rectangle tool') &&
!state.matches('Sketch.Rectangle tool')) ||
disableAllButtons
}
title={ title={
state.can('Equip rectangle tool') canRectangleTool(context)
? 'Rectangle' ? 'Rectangle'
: 'Can only be used when a sketch is empty currently' : 'Can only be used when a sketch is empty currently'
} }

View File

@ -37,7 +37,7 @@ import { Dialog, Popover, Transition } from '@headlessui/react'
import { LineInputsType } from 'lang/std/sketchcombos' import { LineInputsType } from 'lang/std/sketchcombos'
import toast from 'react-hot-toast' import toast from 'react-hot-toast'
import { InstanceProps, create } from 'react-modal-promise' import { InstanceProps, create } from 'react-modal-promise'
import { executeAst } from 'useStore' import { executeAst } from 'lang/langHelpers'
import { import {
deleteSegmentFromPipeExpression, deleteSegmentFromPipeExpression,
makeRemoveSingleConstraintInput, makeRemoveSingleConstraintInput,

View File

@ -58,7 +58,7 @@ import {
editorManager, editorManager,
} from 'lib/singletons' } from 'lib/singletons'
import { getNodeFromPath, getNodePathFromSourceRange } from 'lang/queryAst' import { getNodeFromPath, getNodePathFromSourceRange } from 'lang/queryAst'
import { executeAst, useStore } from 'useStore' import { executeAst } from 'lang/langHelpers'
import { import {
createArcGeometry, createArcGeometry,
dashedStraight, dashedStraight,
@ -804,7 +804,7 @@ export class SceneEntities {
// Update the primary AST and unequip the rectangle tool // Update the primary AST and unequip the rectangle tool
await kclManager.executeAstMock(_ast) await kclManager.executeAstMock(_ast)
sceneInfra.modelingSend({ type: 'CancelSketch' }) sceneInfra.modelingSend({ type: 'Finish rectangle' })
const { programMemory } = await executeAst({ const { programMemory } = await executeAst({
ast: _ast, ast: _ast,
@ -1444,11 +1444,10 @@ export class SceneEntities {
selected.material.color = defaultPlaneColor(type) selected.material.color = defaultPlaneColor(type)
}, },
onClick: async (args) => { onClick: async (args) => {
const { streamDimensions } = useStore.getState()
const { entity_id } = await sendSelectEventToEngine( const { entity_id } = await sendSelectEventToEngine(
args?.mouseEvent, args?.mouseEvent,
document.getElementById('video-stream') as HTMLVideoElement, document.getElementById('video-stream') as HTMLVideoElement,
streamDimensions sceneInfra._streamDimensions
) )
let _entity_id = entity_id let _entity_id = entity_id

View File

@ -103,6 +103,10 @@ export class SceneInfra {
_baseUnit: BaseUnit = 'mm' _baseUnit: BaseUnit = 'mm'
_baseUnitMultiplier = 1 _baseUnitMultiplier = 1
_theme: Themes = Themes.System _theme: Themes = Themes.System
_streamDimensions: { streamWidth: number; streamHeight: number } = {
streamWidth: 1280,
streamHeight: 720,
}
extraSegmentTexture: Texture extraSegmentTexture: Texture
lastMouseState: MouseState = { type: 'idle' } lastMouseState: MouseState = { type: 'idle' }
onDragStartCallback: (arg: OnDragCallbackArgs) => void = () => {} onDragStartCallback: (arg: OnDragCallbackArgs) => void = () => {}

View File

@ -10,7 +10,7 @@ import { findAllPreviousVariables, PrevVariable } from '../lang/queryAst'
import { engineCommandManager, kclManager } from 'lib/singletons' import { engineCommandManager, kclManager } from 'lib/singletons'
import { useKclContext } from 'lang/KclProvider' import { useKclContext } from 'lang/KclProvider'
import { useModelingContext } from 'hooks/useModelingContext' import { useModelingContext } from 'hooks/useModelingContext'
import { executeAst } from 'useStore' import { executeAst } from 'lang/langHelpers'
import { trap } from 'lib/trap' import { trap } from 'lib/trap'
export const AvailableVars = ({ export const AvailableVars = ({

View File

@ -24,7 +24,7 @@ export const CommandBar = () => {
}, [pathname]) }, [pathname])
// Hook up keyboard shortcuts // Hook up keyboard shortcuts
useHotkeyWrapper(['mod+k', 'ctrl+c'], () => { useHotkeyWrapper(['mod+k'], () => {
if (commandBarState.context.commands.length === 0) return if (commandBarState.context.commands.length === 0) return
if (commandBarState.matches('Closed')) { if (commandBarState.matches('Closed')) {
commandBarSend({ type: 'Open' }) commandBarSend({ type: 'Open' })

View File

@ -1,5 +1,6 @@
import { Completion } from '@codemirror/autocomplete' import { Completion } from '@codemirror/autocomplete'
import { EditorState, EditorView, useCodeMirror } from '@uiw/react-codemirror' import { EditorView, ViewUpdate } from '@codemirror/view'
import { EditorState } from '@codemirror/state'
import { CustomIcon } from 'components/CustomIcon' import { CustomIcon } from 'components/CustomIcon'
import { useCommandsContext } from 'hooks/useCommandsContext' import { useCommandsContext } from 'hooks/useCommandsContext'
import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext' import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext'
@ -12,6 +13,7 @@ import { useEffect, useRef, useState } from 'react'
import { useHotkeys } from 'react-hotkeys-hook' import { useHotkeys } from 'react-hotkeys-hook'
import styles from './CommandBarKclInput.module.css' import styles from './CommandBarKclInput.module.css'
import { createIdentifier, createVariableDeclaration } from 'lang/modifyAst' import { createIdentifier, createVariableDeclaration } from 'lang/modifyAst'
import { useCodeMirror } from 'components/ModelingSidebar/ModelingPanes/CodeEditor'
function CommandBarKclInput({ function CommandBarKclInput({
arg, arg,
@ -63,9 +65,7 @@ function CommandBarKclInput({
const { setContainer } = useCodeMirror({ const { setContainer } = useCodeMirror({
container: editorRef.current, container: editorRef.current,
value, initialDocValue: value,
indentWithTab: false,
basicSetup: false,
autoFocus: true, autoFocus: true,
selection: { selection: {
anchor: 0, anchor: 0,
@ -74,7 +74,6 @@ function CommandBarKclInput({
? previouslySetValue.valueText.length ? previouslySetValue.valueText.length
: defaultValue.length, : defaultValue.length,
}, },
accessKey: 'command-bar',
theme: theme:
settings.context.app.theme.current === 'system' settings.context.app.theme.current === 'system'
? getSystemTheme() ? getSystemTheme()
@ -96,8 +95,12 @@ function CommandBarKclInput({
} }
return tr return tr
}), }),
EditorView.updateListener.of((vu: ViewUpdate) => {
if (vu.docChanged) {
setValue(vu.state.doc.toString())
}
}),
], ],
onChange: (newValue) => setValue(newValue),
}) })
useEffect(() => { useEffect(() => {

View File

@ -175,7 +175,11 @@ const FileTreeItem = ({
codeManager.code codeManager.code
) )
codeManager.writeToFile() codeManager.writeToFile()
kclManager.executeCode(true, true)
kclManager.isFirstRender = true
kclManager.executeCode(true, true).then(() => {
kclManager.isFirstRender = false
})
} else { } else {
// Let the lsp servers know we closed a file. // Let the lsp servers know we closed a file.
onFileClose(currentFile?.path || null, project?.path || null) onFileClose(currentFile?.path || null, project?.path || null)

View File

@ -119,7 +119,7 @@ export default function Gizmo() {
<div <div
ref={wrapperRef} ref={wrapperRef}
aria-label="View orientation gizmo" aria-label="View orientation gizmo"
className="grid place-content-center rounded-full overflow-hidden border border-solid border-primary/50 pointer-events-auto" className="grid place-content-center rounded-full overflow-hidden border border-solid border-primary/50 pointer-events-auto bg-chalkboard-10/70 dark:bg-chalkboard-100/80 backdrop-blur-sm"
> >
<canvas ref={canvasRef} /> <canvas ref={canvasRef} />
<ContextMenu <ContextMenu

View File

@ -1,11 +1,5 @@
import type * as LSP from 'vscode-languageserver-protocol' import type * as LSP from 'vscode-languageserver-protocol'
import React, { import React, { createContext, useMemo, useContext, useState } from 'react'
createContext,
useMemo,
useEffect,
useContext,
useState,
} from 'react'
import { import {
LanguageServerClient, LanguageServerClient,
FromServer, FromServer,
@ -16,7 +10,6 @@ import {
import { TEST, VITE_KC_API_BASE_URL } from 'env' import { TEST, VITE_KC_API_BASE_URL } from 'env'
import KclLanguageSupport from 'editor/plugins/lsp/kcl/language' import KclLanguageSupport from 'editor/plugins/lsp/kcl/language'
import { copilotPlugin } from 'editor/plugins/lsp/copilot' import { copilotPlugin } from 'editor/plugins/lsp/copilot'
import { useStore } from 'useStore'
import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext' import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext'
import { Extension } from '@codemirror/state' import { Extension } from '@codemirror/state'
import { LanguageSupport } from '@codemirror/language' import { LanguageSupport } from '@codemirror/language'
@ -73,19 +66,8 @@ type LspContext = {
export const LspStateContext = createContext({} as LspContext) export const LspStateContext = createContext({} as LspContext)
export const LspProvider = ({ children }: { children: React.ReactNode }) => { export const LspProvider = ({ children }: { children: React.ReactNode }) => {
const { const [isKclLspReady, setIsKclLspReady] = useState(false)
isKclLspServerReady, const [isCopilotLspReady, setIsCopilotLspReady] = useState(false)
isCopilotLspServerReady,
setIsKclLspServerReady,
setIsCopilotLspServerReady,
} = useStore((s) => ({
isKclLspServerReady: s.isKclLspServerReady,
isCopilotLspServerReady: s.isCopilotLspServerReady,
setIsKclLspServerReady: s.setIsKclLspServerReady,
setIsCopilotLspServerReady: s.setIsCopilotLspServerReady,
}))
const [isLspReady, setIsLspReady] = useState(false)
const [isCopilotReady, setIsCopilotReady] = useState(false)
const { const {
auth, auth,
@ -132,7 +114,7 @@ export const LspProvider = ({ children }: { children: React.ReactNode }) => {
fromServer, fromServer,
intoServer, intoServer,
initializedCallback: () => { initializedCallback: () => {
setIsLspReady(true) setIsKclLspReady(true)
}, },
}) })
@ -143,7 +125,7 @@ export const LspProvider = ({ children }: { children: React.ReactNode }) => {
]) ])
useMemo(() => { useMemo(() => {
if (!isTauri() && isKclLspServerReady && kclLspClient && codeManager.code) { if (!isTauri() && isKclLspReady && kclLspClient && codeManager.code) {
kclLspClient.textDocumentDidOpen({ kclLspClient.textDocumentDidOpen({
textDocument: { textDocument: {
uri: `file:///${PROJECT_ENTRYPOINT}`, uri: `file:///${PROJECT_ENTRYPOINT}`,
@ -153,7 +135,7 @@ export const LspProvider = ({ children }: { children: React.ReactNode }) => {
}, },
}) })
} }
}, [kclLspClient, isKclLspServerReady]) }, [kclLspClient, isKclLspReady])
// Here we initialize the plugin which will start the client. // Here we initialize the plugin which will start the client.
// Now that we have multi-file support the name of the file is a dep of // Now that we have multi-file support the name of the file is a dep of
@ -162,7 +144,7 @@ export const LspProvider = ({ children }: { children: React.ReactNode }) => {
// 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 (isKclLspServerReady && !TEST && kclLspClient) { if (isKclLspReady && !TEST && kclLspClient) {
// Set up the lsp plugin. // Set up the lsp plugin.
const lsp = new KclLanguageSupport({ const lsp = new KclLanguageSupport({
documentUri: `file:///${PROJECT_ENTRYPOINT}`, documentUri: `file:///${PROJECT_ENTRYPOINT}`,
@ -193,7 +175,7 @@ export const LspProvider = ({ children }: { children: React.ReactNode }) => {
plugin = lsp plugin = lsp
} }
return plugin return plugin
}, [kclLspClient, isKclLspServerReady]) }, [kclLspClient, isKclLspReady])
const { lspClient: copilotLspClient } = useMemo(() => { const { lspClient: copilotLspClient } = useMemo(() => {
if (!token || token === '' || TEST) { if (!token || token === '' || TEST) {
@ -225,7 +207,7 @@ export const LspProvider = ({ children }: { children: React.ReactNode }) => {
fromServer, fromServer,
intoServer, intoServer,
initializedCallback: () => { initializedCallback: () => {
setIsCopilotReady(true) setIsCopilotLspReady(true)
}, },
}) })
return { lspClient } return { lspClient }
@ -238,7 +220,7 @@ export const LspProvider = ({ children }: { children: React.ReactNode }) => {
// We do not want to restart the server, its just wasteful. // We do not want to restart the server, its just wasteful.
const copilotLSP = useMemo(() => { const copilotLSP = useMemo(() => {
let plugin = null let plugin = null
if (isCopilotLspServerReady && !TEST && copilotLspClient) { if (isCopilotLspReady && !TEST && copilotLspClient) {
// Set up the lsp plugin. // Set up the lsp plugin.
const lsp = copilotPlugin({ const lsp = copilotPlugin({
documentUri: `file:///${PROJECT_ENTRYPOINT}`, documentUri: `file:///${PROJECT_ENTRYPOINT}`,
@ -250,7 +232,7 @@ export const LspProvider = ({ children }: { children: React.ReactNode }) => {
plugin = lsp plugin = lsp
} }
return plugin return plugin
}, [copilotLspClient, isCopilotLspServerReady]) }, [copilotLspClient, isCopilotLspReady])
let lspClients: LanguageServerClient[] = [] let lspClients: LanguageServerClient[] = []
if (kclLspClient) { if (kclLspClient) {
@ -260,13 +242,6 @@ export const LspProvider = ({ children }: { children: React.ReactNode }) => {
lspClients.push(copilotLspClient) lspClients.push(copilotLspClient)
} }
useEffect(() => {
setIsKclLspServerReady(isLspReady)
}, [isLspReady])
useEffect(() => {
setIsCopilotLspServerReady(isCopilotReady)
}, [isCopilotReady])
const onProjectClose = ( const onProjectClose = (
file: FileEntry | null, file: FileEntry | null,
projectPath: string | null, projectPath: string | null,

View File

@ -30,7 +30,6 @@ import {
applyConstraintAngleBetween, applyConstraintAngleBetween,
} from './Toolbar/SetAngleBetween' } from './Toolbar/SetAngleBetween'
import { applyConstraintAngleLength } from './Toolbar/setAngleLength' import { applyConstraintAngleLength } from './Toolbar/setAngleLength'
import { useStore } from 'useStore'
import { import {
Selections, Selections,
canExtrudeSelection, canExtrudeSelection,
@ -54,15 +53,8 @@ import {
sketchOnExtrudedFace, sketchOnExtrudedFace,
startSketchOnDefault, startSketchOnDefault,
} from 'lang/modifyAst' } from 'lang/modifyAst'
import { Program, parse, recast } from 'lang/wasm'
import { import {
Program,
VariableDeclaration,
coreDump,
parse,
recast,
} from 'lang/wasm'
import {
getNodeFromPath,
getNodePathFromSourceRange, getNodePathFromSourceRange,
hasExtrudableGeometry, hasExtrudableGeometry,
isSingleCursorInPipe, isSingleCursorInPipe,
@ -71,12 +63,10 @@ import { TEST } from 'env'
import { exportFromEngine } from 'lib/exportFromEngine' import { exportFromEngine } from 'lib/exportFromEngine'
import { Models } from '@kittycad/lib/dist/types/src' import { Models } from '@kittycad/lib/dist/types/src'
import toast from 'react-hot-toast' import toast from 'react-hot-toast'
import { EditorSelection, Transaction } from '@uiw/react-codemirror' import { EditorSelection, Transaction } from '@codemirror/state'
import { CoreDumpManager } from 'lib/coredump'
import { useSearchParams } from 'react-router-dom' import { useSearchParams } from 'react-router-dom'
import { letEngineAnimateAndSyncCamAfter } from 'clientSideScene/CameraControls' import { letEngineAnimateAndSyncCamAfter } from 'clientSideScene/CameraControls'
import { getVarNameModal } from 'hooks/useToolbarGuards' import { getVarNameModal } from 'hooks/useToolbarGuards'
import useHotkeyWrapper from 'lib/hotkeyWrapper'
import { uuidv4 } from 'lib/utils' import { uuidv4 } from 'lib/utils'
import { err, trap } from 'lib/trap' import { err, trap } from 'lib/trap'
import { useCommandsContext } from 'hooks/useCommandsContext' import { useCommandsContext } from 'hooks/useCommandsContext'
@ -112,38 +102,6 @@ export const ModelingMachineProvider = ({
let [searchParams] = useSearchParams() let [searchParams] = useSearchParams()
const pool = searchParams.get('pool') const pool = searchParams.get('pool')
useSetupEngineManager(streamRef, token, {
pool: pool,
theme: theme.current,
highlightEdges: highlightEdges.current,
enableSSAO: enableSSAO.current,
showScaleGrid: showScaleGrid.current,
})
const { htmlRef } = useStore((s) => ({
htmlRef: s.htmlRef,
}))
const coreDumpManager = new CoreDumpManager(
engineCommandManager,
htmlRef,
token
)
useHotkeyWrapper(['meta + shift + .'], () => {
toast.promise(
coreDump(coreDumpManager, true),
{
loading: 'Starting core dump...',
success: 'Core dump completed successfully',
error: 'Error while exporting core dump',
},
{
success: {
// Note: this extended duration is especially important for Playwright e2e testing
// default duration is 2000 - https://react-hot-toast.com/docs/toast#default-durations
duration: 6000,
},
}
)
})
const { commandBarState } = useCommandsContext() const { commandBarState } = useCommandsContext()
// Settings machine setup // Settings machine setup
@ -163,7 +121,13 @@ export const ModelingMachineProvider = ({
modelingMachine, modelingMachine,
{ {
actions: { actions: {
'sketch exit execute': () => { 'disable copilot': () => {
editorManager.setCopilotEnabled(false)
},
'enable copilot': () => {
editorManager.setCopilotEnabled(true)
},
'sketch exit execute': ({ store }) => {
;(async () => { ;(async () => {
await sceneInfra.camControls.snapToPerspectiveBeforeHandingBackControlToEngine() await sceneInfra.camControls.snapToPerspectiveBeforeHandingBackControlToEngine()
@ -197,7 +161,12 @@ export const ModelingMachineProvider = ({
}) })
} }
kclManager.executeCode(true) store.videoElement?.pause()
kclManager.executeCode(true).then(() => {
if (engineCommandManager.engineConnection?.freezeFrame) return
store.videoElement?.play()
})
})() })()
}, },
'Set mouse state': assign({ 'Set mouse state': assign({
@ -473,17 +442,6 @@ export const ModelingMachineProvider = ({
if (selectionRanges.codeBasedSelections.length <= 0) return false if (selectionRanges.codeBasedSelections.length <= 0) return false
return true return true
}, },
'Sketch is empty': ({ sketchDetails }) => {
const node = getNodeFromPath<VariableDeclaration>(
kclManager.ast,
sketchDetails?.sketchPathToNode || [],
'VariableDeclaration'
)
// This should not be returning false, and it should be caught
// but we need to simulate old behavior to move on.
if (err(node)) return false
return node.node?.declarations?.[0]?.init.type !== 'PipeExpression'
},
'Selection is on face': ({ selectionRanges }, { data }) => { 'Selection is on face': ({ selectionRanges }, { data }) => {
if (data?.forceNewSketch) return false if (data?.forceNewSketch) return false
if (!isSingleCursorInPipe(selectionRanges, kclManager.ast)) if (!isSingleCursorInPipe(selectionRanges, kclManager.ast))
@ -514,11 +472,15 @@ export const ModelingMachineProvider = ({
services: { services: {
'AST-undo-startSketchOn': async ({ sketchDetails }) => { 'AST-undo-startSketchOn': async ({ sketchDetails }) => {
if (!sketchDetails) return if (!sketchDetails) return
const newAst: Program = JSON.parse(JSON.stringify(kclManager.ast)) if (kclManager.ast.body.length) {
const varDecIndex = sketchDetails.sketchPathToNode[1][0] // this assumes no changes have been made to the sketch besides what we did when entering the sketch
// remove body item at varDecIndex // i.e. doesn't account for user's adding code themselves, maybe we need store a flag userEditedSinceSketchMode?
newAst.body = newAst.body.filter((_, i) => i !== varDecIndex) const newAst: Program = JSON.parse(JSON.stringify(kclManager.ast))
await kclManager.executeAstMock(newAst) const varDecIndex = sketchDetails.sketchPathToNode[1][0]
// remove body item at varDecIndex
newAst.body = newAst.body.filter((_, i) => i !== varDecIndex)
await kclManager.executeAstMock(newAst)
}
sceneInfra.setCallbacks({ sceneInfra.setCallbacks({
onClick: () => {}, onClick: () => {},
onDrag: () => {}, onDrag: () => {},
@ -898,6 +860,16 @@ export const ModelingMachineProvider = ({
} }
) )
useSetupEngineManager(streamRef, token, {
pool: pool,
theme: theme.current,
highlightEdges: highlightEdges.current,
enableSSAO: enableSSAO.current,
modelingSend,
modelingContext: modelingState.context,
showScaleGrid: showScaleGrid.current,
})
useEffect(() => { useEffect(() => {
kclManager.registerExecuteCallback(() => { kclManager.registerExecuteCallback(() => {
modelingSend({ type: 'Re-execute' }) modelingSend({ type: 'Re-execute' })

View File

@ -1,6 +1,6 @@
import { useStore } from 'useStore'
import styles from './ModelingPane.module.css' import styles from './ModelingPane.module.css'
import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext' import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext'
import { useModelingContext } from 'hooks/useModelingContext'
export interface ModelingPaneProps export interface ModelingPaneProps
extends React.PropsWithChildren, extends React.PropsWithChildren,
@ -33,11 +33,9 @@ export const ModelingPane = ({
}: ModelingPaneProps) => { }: ModelingPaneProps) => {
const { settings } = useSettingsAuthContext() const { settings } = useSettingsAuthContext()
const onboardingStatus = settings.context.app.onboardingStatus const onboardingStatus = settings.context.app.onboardingStatus
const { buttonDownInStream } = useStore((s) => ({ const { context } = useModelingContext()
buttonDownInStream: s.buttonDownInStream,
}))
const pointerEventsCssClass = const pointerEventsCssClass =
buttonDownInStream || onboardingStatus.current === 'camera' context.store?.buttonDownInStream || onboardingStatus.current === 'camera'
? 'pointer-events-none ' ? 'pointer-events-none '
: 'pointer-events-auto ' : 'pointer-events-auto '
return ( return (

View File

@ -0,0 +1,171 @@
import React, {
useEffect,
useMemo,
useRef,
useState,
forwardRef,
useImperativeHandle,
} from 'react'
import {
EditorState,
EditorStateConfig,
Extension,
StateEffect,
} from '@codemirror/state'
import { EditorView } from '@codemirror/view'
import { oneDark } from '@codemirror/theme-one-dark'
//reference: https://github.com/sachinraja/rodemirror/blob/main/src/use-first-render.ts
const useFirstRender = () => {
const firstRender = useRef(true)
useEffect(() => {
firstRender.current = false
}, [])
return firstRender.current
}
const defaultLightThemeOption = EditorView.theme(
{
'&': {
backgroundColor: '#fff',
},
},
{
dark: false,
}
)
interface CodeEditorRef {
editor?: HTMLDivElement | null
view?: EditorView
state?: EditorState
}
interface CodeEditorProps {
onCreateEditor?: (view: EditorView | null) => void
initialDocValue?: EditorStateConfig['doc']
extensions?: Extension
theme: 'light' | 'dark'
autoFocus?: boolean
selection?: EditorStateConfig['selection']
}
interface UseCodeMirror extends CodeEditorProps {
container?: HTMLDivElement | null
}
const CodeEditor = forwardRef<CodeEditorRef, CodeEditorProps>((props, ref) => {
const {
onCreateEditor,
extensions = [],
initialDocValue,
theme,
autoFocus = false,
selection,
} = props
const editor = useRef<HTMLDivElement>(null)
const { view, state, container } = useCodeMirror({
container: editor.current,
onCreateEditor,
extensions,
initialDocValue,
theme,
autoFocus,
selection,
})
useImperativeHandle(
ref,
() => ({ editor: editor.current, view: view, state: state }),
[editor, container, view, state]
)
return <div ref={editor}></div>
})
export function useCodeMirror(props: UseCodeMirror) {
const {
onCreateEditor,
extensions = [],
initialDocValue,
theme,
autoFocus = false,
selection,
} = props
const [container, setContainer] = useState<HTMLDivElement | null>()
const [view, setView] = useState<EditorView>()
const [state, setState] = useState<EditorState>()
const isFirstRender = useFirstRender()
const targetExtensions = useMemo(() => {
let exts = Array.isArray(extensions) ? extensions : []
if (theme === 'dark') {
exts = [...exts, oneDark]
} else if (theme === 'light') {
exts = [...exts, defaultLightThemeOption]
}
return exts
}, [extensions, theme])
useEffect(() => {
if (container && !state) {
const config = {
doc: initialDocValue,
selection,
extensions: [...Array.of(extensions)],
}
const stateCurrent = EditorState.create(config)
setState(stateCurrent)
if (!view) {
const viewCurrent = new EditorView({
state: stateCurrent,
parent: container,
})
setView(viewCurrent)
onCreateEditor && onCreateEditor(viewCurrent)
}
}
return () => {
if (view) {
setState(undefined)
setView(undefined)
}
}
}, [container, state])
useEffect(() => setContainer(props.container), [props.container])
useEffect(
() => () => {
if (view) {
view.destroy()
setView(undefined)
}
},
[view]
)
useEffect(() => {
if (autoFocus && view) {
view.focus()
}
}, [autoFocus, view])
useEffect(() => {
if (view && !isFirstRender) {
view.dispatch({
effects: StateEffect.reconfigure.of(targetExtensions),
})
}
}, [targetExtensions])
return { view, setView, container, setContainer, state, setState }
}
export default CodeEditor

View File

@ -1,4 +1,3 @@
import ReactCodeMirror from '@uiw/react-codemirror'
import { TEST } from 'env' import { TEST } from 'env'
import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext' import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext'
import { Themes, getSystemTheme } from 'lib/theme' import { Themes, getSystemTheme } from 'lib/theme'
@ -43,6 +42,7 @@ import {
closeBracketsKeymap, closeBracketsKeymap,
completionKeymap, completionKeymap,
} from '@codemirror/autocomplete' } from '@codemirror/autocomplete'
import CodeEditor from './CodeEditor'
export const editorShortcutMeta = { export const editorShortcutMeta = {
formatCode: { formatCode: {
@ -185,15 +185,15 @@ export const KclEditorPane = () => {
id="code-mirror-override" id="code-mirror-override"
className={'absolute inset-0 ' + (cursorBlinking.current ? 'blink' : '')} className={'absolute inset-0 ' + (cursorBlinking.current ? 'blink' : '')}
> >
<ReactCodeMirror <CodeEditor
value={initialCode.current} initialDocValue={initialCode.current}
extensions={editorExtensions} extensions={editorExtensions}
theme={theme} theme={theme}
onCreateEditor={(_editorView) => { onCreateEditor={(_editorView) => {
if (_editorView === null) return
editorManager.setEditorView(_editorView) editorManager.setEditorView(_editorView)
}} }}
indentWithTab={false}
basicSetup={false}
/> />
</div> </div>
) )

View File

@ -2,7 +2,6 @@ import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext'
import { Resizable } from 're-resizable' import { Resizable } from 're-resizable'
import { HTMLAttributes, useCallback, useEffect, useState } from 'react' import { HTMLAttributes, useCallback, useEffect, useState } from 'react'
import { useHotkeys } from 'react-hotkeys-hook' import { useHotkeys } from 'react-hotkeys-hook'
import { useStore } from 'useStore'
import { Tab } from '@headlessui/react' import { Tab } from '@headlessui/react'
import { import {
SidebarPane, SidebarPane,
@ -15,6 +14,7 @@ import { ActionIcon } from 'components/ActionIcon'
import styles from './ModelingSidebar.module.css' import styles from './ModelingSidebar.module.css'
import { ModelingPane } from './ModelingPane' import { ModelingPane } from './ModelingPane'
import { isTauri } from 'lib/isTauri' import { isTauri } from 'lib/isTauri'
import { useModelingContext } from 'hooks/useModelingContext'
interface ModelingSidebarProps { interface ModelingSidebarProps {
paneOpacity: '' | 'opacity-20' | 'opacity-40' paneOpacity: '' | 'opacity-20' | 'opacity-40'
@ -23,14 +23,11 @@ interface ModelingSidebarProps {
export function ModelingSidebar({ paneOpacity }: ModelingSidebarProps) { export function ModelingSidebar({ paneOpacity }: ModelingSidebarProps) {
const { settings } = useSettingsAuthContext() const { settings } = useSettingsAuthContext()
const onboardingStatus = settings.context.app.onboardingStatus const onboardingStatus = settings.context.app.onboardingStatus
const { openPanes, buttonDownInStream } = useStore((s) => ({ const { context } = useModelingContext()
buttonDownInStream: s.buttonDownInStream,
openPanes: s.openPanes,
}))
const pointerEventsCssClass = const pointerEventsCssClass =
buttonDownInStream || context.store?.buttonDownInStream ||
onboardingStatus.current === 'camera' || onboardingStatus.current === 'camera' ||
openPanes.length === 0 context.store?.openPanes.length === 0
? 'pointer-events-none ' ? 'pointer-events-none '
: 'pointer-events-auto ' : 'pointer-events-auto '
@ -45,7 +42,7 @@ export function ModelingSidebar({ paneOpacity }: ModelingSidebarProps) {
maxWidth={800} maxWidth={800}
handleClasses={{ handleClasses={{
right: right:
(openPanes.length === 0 ? 'hidden ' : 'block ') + (context.store?.openPanes.length === 0 ? 'hidden ' : 'block ') +
'translate-x-1/2 hover:bg-chalkboard-10 hover:dark:bg-chalkboard-110 bg-transparent transition-colors duration-75 transition-ease-out delay-100 ', 'translate-x-1/2 hover:bg-chalkboard-10 hover:dark:bg-chalkboard-110 bg-transparent transition-colors duration-75 transition-ease-out delay-100 ',
left: 'hidden', left: 'hidden',
top: 'hidden', top: 'hidden',
@ -82,11 +79,10 @@ function ModelingSidebarSection({
const { settings } = useSettingsAuthContext() const { settings } = useSettingsAuthContext()
const showDebugPanel = settings.context.modeling.showDebugPanel const showDebugPanel = settings.context.modeling.showDebugPanel
const paneIds = panes.map((pane) => pane.id) const paneIds = panes.map((pane) => pane.id)
const { openPanes, setOpenPanes } = useStore((s) => ({ const { send, context } = useModelingContext()
openPanes: s.openPanes, const foundOpenPane = context.store?.openPanes.find((pane) =>
setOpenPanes: s.setOpenPanes, paneIds.includes(pane)
})) )
const foundOpenPane = openPanes.find((pane) => paneIds.includes(pane))
const [currentPane, setCurrentPane] = useState( const [currentPane, setCurrentPane] = useState(
foundOpenPane || ('none' as SidebarType | 'none') foundOpenPane || ('none' as SidebarType | 'none')
) )
@ -94,17 +90,37 @@ function ModelingSidebarSection({
const togglePane = useCallback( const togglePane = useCallback(
(newPane: SidebarType | 'none') => { (newPane: SidebarType | 'none') => {
if (newPane === 'none') { if (newPane === 'none') {
setOpenPanes(openPanes.filter((p) => p !== currentPane)) send({
type: 'Set context',
data: {
openPanes: context.store?.openPanes.filter(
(p) => p !== currentPane
),
},
})
setCurrentPane('none') setCurrentPane('none')
} else if (newPane === currentPane) { } else if (newPane === currentPane) {
setCurrentPane('none') setCurrentPane('none')
setOpenPanes(openPanes.filter((p) => p !== newPane)) send({
type: 'Set context',
data: {
openPanes: context.store?.openPanes.filter((p) => p !== newPane),
},
})
} else { } else {
setOpenPanes([...openPanes.filter((p) => p !== currentPane), newPane]) send({
type: 'Set context',
data: {
openPanes: [
...context.store?.openPanes.filter((p) => p !== currentPane),
newPane,
],
},
})
setCurrentPane(newPane) setCurrentPane(newPane)
} }
}, },
[openPanes, setOpenPanes, currentPane, setCurrentPane] [context.store?.openPanes, send, currentPane, setCurrentPane]
) )
// Filter out the debug panel if it's not supposed to be shown // Filter out the debug panel if it's not supposed to be shown
@ -122,11 +138,11 @@ function ModelingSidebarSection({
if ( if (
!showDebugPanel.current && !showDebugPanel.current &&
currentPane === 'debug' && currentPane === 'debug' &&
openPanes.includes('debug') context.store?.openPanes.includes('debug')
) { ) {
togglePane('debug') togglePane('debug')
} }
}, [showDebugPanel.current, togglePane, openPanes]) }, [showDebugPanel.current, togglePane, context.store?.openPanes])
return ( return (
<div className={'group contents ' + className} {...props}> <div className={'group contents ' + className} {...props}>
@ -152,7 +168,9 @@ function ModelingSidebarSection({
: ' border-r-0') + : ' border-r-0') +
' p-2 col-start-1 col-span-1 h-fit w-fit flex flex-col items-start gap-2 ' + ' p-2 col-start-1 col-span-1 h-fit w-fit flex flex-col items-start gap-2 ' +
'bg-chalkboard-10 border border-solid border-chalkboard-20 dark:bg-chalkboard-90 dark:border-chalkboard-80 group-focus-within:border-primary dark:group-focus-within:border-chalkboard-50 ' + 'bg-chalkboard-10 border border-solid border-chalkboard-20 dark:bg-chalkboard-90 dark:border-chalkboard-80 group-focus-within:border-primary dark:group-focus-within:border-chalkboard-50 ' +
(openPanes.length === 1 && currentPane === 'none' ? 'pr-0.5' : '') (context.store?.openPanes.length === 1 && currentPane === 'none'
? 'pr-0.5'
: '')
} }
> >
<Tab key="none" className="sr-only"> <Tab key="none" className="sr-only">
@ -172,7 +190,7 @@ function ModelingSidebarSection({
as="article" as="article"
className={ className={
'col-start-2 col-span-1 ' + 'col-start-2 col-span-1 ' +
(openPanes.length === 1 (context.store?.openPanes.length === 1
? currentPane !== 'none' ? currentPane !== 'none'
? `row-start-1 row-end-3` ? `row-start-1 row-end-3`
: `hidden` : `hidden`

View File

@ -2,22 +2,17 @@ import { coreDump } from 'lang/wasm'
import { CoreDumpManager } from 'lib/coredump' import { CoreDumpManager } from 'lib/coredump'
import { CustomIcon } from './CustomIcon' import { CustomIcon } from './CustomIcon'
import { engineCommandManager } from 'lib/singletons' import { engineCommandManager } from 'lib/singletons'
import React from 'react' import React, { useMemo } from 'react'
import toast from 'react-hot-toast' import toast from 'react-hot-toast'
import Tooltip from './Tooltip' import Tooltip from './Tooltip'
import { useStore } from 'useStore'
import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext' import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext'
export const RefreshButton = ({ children }: React.PropsWithChildren) => { export const RefreshButton = ({ children }: React.PropsWithChildren) => {
const { auth } = useSettingsAuthContext() const { auth } = useSettingsAuthContext()
const token = auth?.context?.token const token = auth?.context?.token
const { htmlRef } = useStore((s) => ({ const coreDumpManager = useMemo(
htmlRef: s.htmlRef, () => new CoreDumpManager(engineCommandManager, token),
})) []
const coreDumpManager = new CoreDumpManager(
engineCommandManager,
htmlRef,
token
) )
async function refresh() { async function refresh() {

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