Compare commits

...

148 Commits

Author SHA1 Message Date
c9e27ffb95 WIP 2023-12-04 12:16:50 -06:00
6a9a0a8bd7 Rename mod in_memory to composite (#1169) 2023-12-04 12:16:08 -06:00
90e432b10e Read/write composite values to KCEP memory (#1164)
KCEP's memory model stores 'values', which can be either numbers or strings. But we'll need to support storing complex objects like points, lines, sketchgroups, etc in memory too.

This PR adds a trait for KCEP composite types, which are laid out in KCEP memory as a consecutive series of values, one for each field.

Part of https://github.com/KittyCAD/modeling-app/issues/993
2023-12-04 11:20:42 -06:00
90499e086f Bump @adobe/css-tools from 4.3.1 to 4.3.2 (#1158)
Bumps [@adobe/css-tools](https://github.com/adobe/css-tools) from 4.3.1 to 4.3.2.
- [Changelog](https://github.com/adobe/css-tools/blob/main/History.md)
- [Commits](https://github.com/adobe/css-tools/commits)

---
updated-dependencies:
- dependency-name: "@adobe/css-tools"
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-12-04 07:16:51 -05:00
8b398a8dd5 Bump google-github-actions/auth from 1.2.0 to 2.0.0 (#1147)
Bumps [google-github-actions/auth](https://github.com/google-github-actions/auth) from 1.2.0 to 2.0.0.
- [Release notes](https://github.com/google-github-actions/auth/releases)
- [Changelog](https://github.com/google-github-actions/auth/blob/main/CHANGELOG.md)
- [Commits](https://github.com/google-github-actions/auth/compare/v1.2.0...v2.0.0)

---
updated-dependencies:
- dependency-name: google-github-actions/auth
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-12-04 07:16:28 -05:00
23d2dc8dc8 Bump @types/react-modal from 3.16.0 to 3.16.3 (#1144)
Bumps [@types/react-modal](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/react-modal) from 3.16.0 to 3.16.3.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/react-modal)

---
updated-dependencies:
- dependency-name: "@types/react-modal"
  dependency-type: direct:development
  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>
Co-authored-by: Pierre Jacquier <pierrejacquier39@gmail.com>
2023-12-04 07:15:52 -05:00
764a73ec8b Bump @types/react from 18.2.18 to 18.2.41 (#1166)
Bumps [@types/react](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/react) from 18.2.18 to 18.2.41.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/react)

---
updated-dependencies:
- dependency-name: "@types/react"
  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>
2023-12-04 06:30:21 -05:00
b69451d2fe Bump @lezer/javascript from 1.4.7 to 1.4.9 (#1060)
Bumps [@lezer/javascript](https://github.com/lezer-parser/javascript) from 1.4.7 to 1.4.9.
- [Changelog](https://github.com/lezer-parser/javascript/blob/main/CHANGELOG.md)
- [Commits](https://github.com/lezer-parser/javascript/compare/1.4.7...1.4.9)

---
updated-dependencies:
- dependency-name: "@lezer/javascript"
  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>
Co-authored-by: Pierre Jacquier <pierrejacquier39@gmail.com>
2023-12-04 05:05:12 -05:00
173d50517c EP instructions must be serializable (#1163)
We want to be able to extract the execution plan (EP) from a KCL program, copy it, and paste it into an engine unit test. Therefore they must be de/serializable.
2023-12-01 19:36:39 -06:00
3b63632005 Start execution plans (#1155) 2023-12-01 16:19:54 -06:00
2bd3b06178 Sort constraint buttons (#1161)
add sorting to constraintns
2023-12-01 20:49:26 +11:00
9c58cde35f side quest for screenshot diffs (#1160)
side just for screenshot diffs
2023-12-01 20:49:12 +11:00
3eb92bb0c4 Select axis and relevant constraints (#1154)
* update select logic for axis

* add abs Y and X constraints

* make selection tests much more thorough including axis selections

* fmt

* tweak

* tweak

* add snap to XY constraints

* side just for screenshot diffs

* update angle constraint to allow axis seleciton

* fix bux in absY constraint

* add sorting to constraintns

* add issue to TODO

* Revert "side just for screenshot diffs"

This reverts commit aae7874859.

* fix number because something must have updated in the engine

* typo

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

* Revert "add sorting to constraintns"

This reverts commit 36054a4069.

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

* triggre CI

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2023-12-01 20:18:51 +11:00
f3083eb59d more e2e export fixes (#1150)
* more e2e export fixes

* fixes

* fmt

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

* trigger CI

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2023-11-30 17:20:52 +11:00
cef29013b8 Add tauri e2e test for auth on Linux (#1040)
* WIP: Add tauri e2e test for auth on Linux
Fixes #968

* WIP

* WIP

* Working of through /tmp file for user code sharing

* rust int

* User code only in /tmp, fixes

* Longer timeout for github actions

* Remove timeout

* Fmt

* Fmt, 30sec timeout

* Test BUILD_RELEASE true

* Revert "Test BUILD_RELEASE true"

This reverts commit d3b59d4a6c.

* Disable concurrency limit for faster iterations on this PR

* Add logs for responses

* Test manual tauri build before e2e

* WIP

* Catch error on tauri::api:🐚:open

* Clean up

* Clean up

* timeout

* Force BUILD_RELEASE: true

* Back to debug, longer timeout

* Print if url opens ok too

* Check default browser in actions

* Remote shell call on linux (aka e2e for now)

* Fix fmt

* Move to data-testid, clean up

* Add log out section

* Clean up

* Fix typo

* Fix text detection

* Test AppImage

* Revert "Test AppImage"

This reverts commit cf126b1aa6.

* Add comments

* Change to env var

* Clean up

* Fmt fix

* Better package json name

* Add import @wdio/globals

* Back to require

* Update wdio, fix globals

* Move to typescript

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

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2023-11-29 05:15:04 -05:00
58d1303468 fix ply and stl exports (#1141)
* fix ply and stl exports

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

* trigger ci

* explanation comment

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2023-11-29 13:24:09 +11:00
7c11b7b739 test exports (#1139)
* test exports

* fmt

* tidy
2023-11-29 11:20:23 +11:00
9f27f3c1ce ensure import files always sent as bson (#1138)
* ensure import files always sent as bson

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

* bump version

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

---------

Signed-off-by: Jess Frazelle <github@jessfraz.com>
2023-11-28 15:20:59 -08:00
f6cbc752d7 selections e2e test (#1136)
* selections e2e test

* .only

* adde test

* make invalid kcl test pass on macos runner

* further attempt to solve invalid code test on macos

* add comment
2023-11-28 10:23:20 +00:00
6df1ae7161 Add Playwright tests for onboarding (#1125)
Add tests for onboarding load and code changes
2023-11-27 19:46:15 -05:00
159ec08211 Bump uuid from 1.6.0 to 1.6.1 in /src/wasm-lib (#1098)
Bumps [uuid](https://github.com/uuid-rs/uuid) from 1.6.0 to 1.6.1.
- [Release notes](https://github.com/uuid-rs/uuid/releases)
- [Commits](https://github.com/uuid-rs/uuid/compare/1.6.0...1.6.1)

---
updated-dependencies:
- dependency-name: uuid
  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>
2023-11-27 15:58:44 -08:00
6aab9c6e23 Bump openapitor from 9caff1f to 920ba7c in /src/wasm-lib (#1099)
Bumps [openapitor](https://github.com/KittyCAD/kittycad.rs) from `9caff1f` to `920ba7c`.
- [Release notes](https://github.com/KittyCAD/kittycad.rs/releases)
- [Commits](9caff1f4f0...920ba7c69f)

---
updated-dependencies:
- dependency-name: openapitor
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-11-27 15:58:34 -08:00
afd8daae15 Bump tauri-plugin-fs-extra from 20ef22f to 537053d in /src-tauri (#1124)
Bumps [tauri-plugin-fs-extra](https://github.com/tauri-apps/plugins-workspace) from `20ef22f` to `537053d`.
- [Release notes](https://github.com/tauri-apps/plugins-workspace/releases)
- [Commits](20ef22fc3a...537053d317)

---
updated-dependencies:
- dependency-name: tauri-plugin-fs-extra
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-11-27 15:58:09 -08:00
1132779b4b Bump proc-macro2 from 1.0.69 to 1.0.70 in /src/wasm-lib (#1123)
Bumps [proc-macro2](https://github.com/dtolnay/proc-macro2) from 1.0.69 to 1.0.70.
- [Release notes](https://github.com/dtolnay/proc-macro2/releases)
- [Commits](https://github.com/dtolnay/proc-macro2/compare/1.0.69...1.0.70)

---
updated-dependencies:
- dependency-name: proc-macro2
  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>
2023-11-27 15:57:59 -08:00
e3a65f5b3f Bump actions/checkout from 3 to 4 (#1120)
Bumps [actions/checkout](https://github.com/actions/checkout) from 3 to 4.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v3...v4)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-11-27 15:46:57 -08:00
d53665a12a Bump google-github-actions/auth from 1.1.1 to 1.2.0 (#1101)
Bumps [google-github-actions/auth](https://github.com/google-github-actions/auth) from 1.1.1 to 1.2.0.
- [Release notes](https://github.com/google-github-actions/auth/releases)
- [Changelog](https://github.com/google-github-actions/auth/blob/main/CHANGELOG.md)
- [Commits](https://github.com/google-github-actions/auth/compare/v1.1.1...v1.2.0)

---
updated-dependencies:
- dependency-name: google-github-actions/auth
  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>
2023-11-27 15:46:16 -08:00
447f4f9f8f make error not so long (#1129)
* make error not so long

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

* somehow the versions were off by one

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>
2023-11-27 15:43:26 -08:00
859927c06d readme (#1119) 2023-11-24 09:06:13 +11:00
b88425efc8 initial playwright setup and test (#1039)
* initial playwright setup and test

* try verbose logging

* double check resolution

* double check token

* remove logs

* try running on ubuntu and macos

* move e2e tests

* vitest ignores playwright tests

* add a series of tests

* tweak yarn setup

* typo

* update fmt

* action typo

* rust toolchain?

* remove .only from playwright test

* A snapshot a day keeps the bugs away! 📷🐛

* A snapshot a day keeps the bugs away! 📷🐛

* add pan and zoom back

* try puting os in commit message

* A snapshot a day keeps the bugs away! 📷🐛 .os

* A snapshot a day keeps the bugs away! 📷🐛 .os

* fix commit message

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

* add command logs to UI for axis tests

* typo

* fmt

* remove .only

* add auto complete test

* remove .only

* update queries to be more playwright recommended

* move test utils

* remove waits from first test

* remove old snapshots

* re-arrange files and tests

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

* trigger CI

* refactor plane test

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

* remove .only

* try longer wait

* try snap shoting

* fmt

* better CI names

* fix

* fix linux

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

* try make executes on load a bit more robust

* fix

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

* stabilise snapshots

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

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

* make tests run linearly

* update naming

* tidy

* .only

* update readme

* trigger CI

* add set tool

* util clean up

* update readme

* readme tweaks

* self-review clean up

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

* tweak

* update readme

* Ensure Vite preview server is running before running any Playwright tests (#1114)

* Ensure that Vite is serving before tests run

* Don't break secrets if developer has extra blank line in env file

* @mxschmitt's suggestions

* fix

* add wait-on types

* tsconfig fix

* update readme

* code template for sktech on each plane test

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Frank Noirot <frank@kittycad.io>
2023-11-24 08:59:24 +11:00
c37dfc61ef Bump serde from 1.0.192 to 1.0.193 in /src-tauri (#1104)
Bumps [serde](https://github.com/serde-rs/serde) from 1.0.192 to 1.0.193.
- [Release notes](https://github.com/serde-rs/serde/releases)
- [Commits](https://github.com/serde-rs/serde/compare/v1.0.192...v1.0.193)

---
updated-dependencies:
- dependency-name: serde
  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>
2023-11-22 16:33:20 +00:00
b170ac739f Bump serde from 1.0.192 to 1.0.193 in /src/wasm-lib (#1103)
Bumps [serde](https://github.com/serde-rs/serde) from 1.0.192 to 1.0.193.
- [Release notes](https://github.com/serde-rs/serde/releases)
- [Commits](https://github.com/serde-rs/serde/compare/v1.0.192...v1.0.193)

---
updated-dependencies:
- dependency-name: serde
  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>
2023-11-22 16:33:03 +00:00
d712add4da Cargo.lock updates (#1108)
Cargo.lock updates, fix tests
2023-11-22 10:24:10 -06:00
d8aad4bd4f remove the buffer we were using for debugging, its too small for import (#1100)
Signed-off-by: Jess Frazelle <github@jessfraz.com>
2023-11-21 09:23:48 -08:00
1f1c44e598 Bump kittycad from 0.2.42 to 0.2.43 in /src/wasm-lib (#1097)
Bumps [kittycad](https://github.com/KittyCAD/kittycad.rs) from 0.2.42 to 0.2.43.
- [Release notes](https://github.com/KittyCAD/kittycad.rs/releases)
- [Commits](https://github.com/KittyCAD/kittycad.rs/compare/v0.2.42...v0.2.43)

---
updated-dependencies:
- dependency-name: kittycad
  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>
2023-11-20 14:47:50 -08:00
b20e685eea Bump tauri-plugin-fs-extra from 642a195 to 20ef22f in /src-tauri (#1090)
Bumps [tauri-plugin-fs-extra](https://github.com/tauri-apps/plugins-workspace) from `642a195` to `20ef22f`.
- [Release notes](https://github.com/tauri-apps/plugins-workspace/releases)
- [Commits](642a195d34...20ef22fc3a)

---
updated-dependencies:
- dependency-name: tauri-plugin-fs-extra
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-11-20 13:55:59 -08:00
3690d986c1 Bump kittycad from 0.2.41 to 0.2.42 in /src-tauri (#1091)
Bumps [kittycad](https://github.com/KittyCAD/kittycad.rs) from 0.2.41 to 0.2.42.
- [Release notes](https://github.com/KittyCAD/kittycad.rs/releases)
- [Commits](https://github.com/KittyCAD/kittycad.rs/compare/v0.2.41...v0.2.42)

---
updated-dependencies:
- dependency-name: kittycad
  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>
2023-11-20 13:55:43 -08:00
9a7f434ede Derive Databake now that it has HashMap support (#1095) 2023-11-20 12:24:37 -06:00
6afacd7427 KCL optional parameters (#1087)
Part of https://github.com/KittyCAD/modeling-app/issues/1006#issuecomment-1816978586

This adds support for optional parameters to the AST. They are declared with a ? suffix, e.g. `(x, tag?) => {...}`.

This PR does not actually _use_ these optional parameters anywhere. In particular, it does not change the KCL stdlib or any existing function definitions. That will happen in a follow-up PR.
2023-11-20 11:19:08 -06:00
957001ee88 Bump uuid from 1.5.0 to 1.6.0 in /src/wasm-lib (#1089)
Bumps [uuid](https://github.com/uuid-rs/uuid) from 1.5.0 to 1.6.0.
- [Release notes](https://github.com/uuid-rs/uuid/releases)
- [Commits](https://github.com/uuid-rs/uuid/compare/1.5.0...1.6.0)

---
updated-dependencies:
- dependency-name: uuid
  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>
2023-11-20 11:17:04 -06:00
8b4cc306af Bump openapitor from 5ed477b to 9caff1f in /src/wasm-lib (#1085)
Bumps [openapitor](https://github.com/KittyCAD/kittycad.rs) from `5ed477b` to `9caff1f`.
- [Release notes](https://github.com/KittyCAD/kittycad.rs/releases)
- [Commits](5ed477b5c9...9caff1f4f0)

---
updated-dependencies:
- dependency-name: openapitor
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-11-20 11:16:33 -06:00
52d88171ca Bump kittycad from 0.2.41 to 0.2.42 in /src/wasm-lib (#1086)
Bumps [kittycad](https://github.com/KittyCAD/kittycad.rs) from 0.2.41 to 0.2.42.
- [Release notes](https://github.com/KittyCAD/kittycad.rs/releases)
- [Commits](https://github.com/KittyCAD/kittycad.rs/compare/v0.2.41...v0.2.42)

---
updated-dependencies:
- dependency-name: kittycad
  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>
2023-11-20 11:16:18 -06:00
9142cf3af7 Bump databake from 0.1.6 to 0.1.7 in /src/wasm-lib (#1082)
Bumps [databake](https://github.com/unicode-org/icu4x) from 0.1.6 to 0.1.7.
- [Release notes](https://github.com/unicode-org/icu4x/releases)
- [Changelog](https://github.com/unicode-org/icu4x/blob/main/CHANGELOG.md)
- [Commits](https://github.com/unicode-org/icu4x/commits)

---
updated-dependencies:
- dependency-name: databake
  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>
2023-11-20 11:16:01 -06:00
361500058c Bump tauri-plugin-fs-extra from b13d97c to 642a195 in /src-tauri (#1084)
Bumps [tauri-plugin-fs-extra](https://github.com/tauri-apps/plugins-workspace) from `b13d97c` to `642a195`.
- [Release notes](https://github.com/tauri-apps/plugins-workspace/releases)
- [Commits](b13d97c7c7...642a195d34)

---
updated-dependencies:
- dependency-name: tauri-plugin-fs-extra
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-11-17 11:00:43 -08:00
198479a71a Bump openapitor from 3afe554 to 5ed477b in /src/wasm-lib (#1080)
Bumps [openapitor](https://github.com/KittyCAD/kittycad.rs) from `3afe554` to `5ed477b`.
- [Release notes](https://github.com/KittyCAD/kittycad.rs/releases)
- [Commits](3afe554cf9...5ed477b5c9)

---
updated-dependencies:
- dependency-name: openapitor
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-11-17 11:00:04 -08:00
905784c1e5 Bump @vitest/coverage-istanbul from 0.34.1 to 0.34.6 (#1071)
Bumps [@vitest/coverage-istanbul](https://github.com/vitest-dev/vitest/tree/HEAD/packages/coverage-istanbul) from 0.34.1 to 0.34.6.
- [Release notes](https://github.com/vitest-dev/vitest/releases)
- [Commits](https://github.com/vitest-dev/vitest/commits/v0.34.6/packages/coverage-istanbul)

---
updated-dependencies:
- dependency-name: "@vitest/coverage-istanbul"
  dependency-type: direct:development
  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>
2023-11-16 05:48:35 -05:00
c33aaad800 Release KCL 0.1.36 (#1078) 2023-11-15 17:50:08 +00:00
d175c75780 Bump schemars, itertools, add comments to crates (#1077)
Bump itertools, schemars, add comments to crates
2023-11-15 11:41:12 -06:00
ba348d1222 Bump openapitor from 1b2562f to 3afe554 in /src/wasm-lib (#1073)
Bumps [openapitor](https://github.com/KittyCAD/kittycad.rs) from `1b2562f` to `3afe554`.
- [Release notes](https://github.com/KittyCAD/kittycad.rs/releases)
- [Commits](1b2562f4b3...3afe554cf9)

---
updated-dependencies:
- dependency-name: openapitor
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-11-14 13:31:34 -08:00
1f49ddfc29 Bump tauri-plugin-fs-extra from 61e8625 to b13d97c in /src-tauri (#1072)
Bumps [tauri-plugin-fs-extra](https://github.com/tauri-apps/plugins-workspace) from `61e8625` to `b13d97c`.
- [Release notes](https://github.com/tauri-apps/plugins-workspace/releases)
- [Commits](61e862597e...b13d97c7c7)

---
updated-dependencies:
- dependency-name: tauri-plugin-fs-extra
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-11-14 13:31:25 -08:00
58659652c1 Cut release v0.12.0 (#1069) 2023-11-13 21:11:38 -05:00
251971238d Fit resolutions to less than 2k x 2k (#1065)
* Fit resolutions to less than 2k x 2k

* Integer resolutions

* Scale mouse clicks

* Move stream maxResolution logic to getDimensions
2023-11-13 19:32:07 -05:00
381d0b3bc8 Add a console.error when ICE fails (#1067)
This is purely cosmetic right now, but I want to try and make it easier
to tell when the browser is unable to communicate with the ICE server
by writing it to the console.

I want to add some Sentry logging in the future, but for now, when we
see a stream failing to start, we can at least see what I suspect is the
most common trigger in the console.

Signed-off-by: Paul R. Tagliamonte <paul@kittycad.io>
2023-11-14 06:13:15 +11:00
fa7943d06a Bump clap from 4.4.7 to 4.4.8 in /src/wasm-lib (#1062)
Bumps [clap](https://github.com/clap-rs/clap) from 4.4.7 to 4.4.8.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/v4.4.7...v4.4.8)

---
updated-dependencies:
- dependency-name: clap
  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>
2023-11-13 13:08:16 -06:00
7a384251d4 Bump @babel/preset-env from 7.22.9 to 7.23.3 (#1058)
Bumps [@babel/preset-env](https://github.com/babel/babel/tree/HEAD/packages/babel-preset-env) from 7.22.9 to 7.23.3.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.23.3/packages/babel-preset-env)

---
updated-dependencies:
- dependency-name: "@babel/preset-env"
  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>
2023-11-13 05:13:28 -05:00
8e07ea32a6 Bump tailwindcss from 3.3.3 to 3.3.5 (#1059)
Bumps [tailwindcss](https://github.com/tailwindlabs/tailwindcss) from 3.3.3 to 3.3.5.
- [Release notes](https://github.com/tailwindlabs/tailwindcss/releases)
- [Changelog](https://github.com/tailwindlabs/tailwindcss/blob/master/CHANGELOG.md)
- [Commits](https://github.com/tailwindlabs/tailwindcss/compare/v3.3.3...v3.3.5)

---
updated-dependencies:
- dependency-name: tailwindcss
  dependency-type: direct:development
  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>
2023-11-13 05:13:16 -05:00
23adf9d905 Disable eslint rule react-hooks/exhaustive-deps (#1055)
Disable react-hooks/exhaustive-deps
Workaround for #1014
2023-11-11 16:27:12 -05:00
9f0ac5f6fd Macro for parsing KCL at Rust compile-time (#1049)
Macro for parsing KCL at Rust compile-time

This adds a new `parse_kcl!` macro which takes a string as input. It parses the string into KCL source code _at compile-time_. Invalid KCL will make your Rust project fail to compile.

Fixes https://github.com/KittyCAD/modeling-app/issues/1018
2023-11-10 10:36:21 -06:00
08dbd2e9c3 Bump web-sys from 0.3.64 to 0.3.65 in /src/wasm-lib (#1052)
Bumps [web-sys](https://github.com/rustwasm/wasm-bindgen) from 0.3.64 to 0.3.65.
- [Release notes](https://github.com/rustwasm/wasm-bindgen/releases)
- [Changelog](https://github.com/rustwasm/wasm-bindgen/blob/main/CHANGELOG.md)
- [Commits](https://github.com/rustwasm/wasm-bindgen/commits)

---
updated-dependencies:
- dependency-name: web-sys
  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>
2023-11-10 06:49:33 -05:00
2e2ba5adbd Bump tauri-plugin-fs-extra from 6865299 to 61e8625 in /src-tauri (#1054)
Bumps [tauri-plugin-fs-extra](https://github.com/tauri-apps/plugins-workspace) from `6865299` to `61e8625`.
- [Release notes](https://github.com/tauri-apps/plugins-workspace/releases)
- [Commits](6865299149...61e862597e)

---
updated-dependencies:
- dependency-name: tauri-plugin-fs-extra
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-11-10 06:49:12 -05:00
a21dbf1055 Bump tokio from 1.33.0 to 1.34.0 in /src-tauri (#1053)
Bumps [tokio](https://github.com/tokio-rs/tokio) from 1.33.0 to 1.34.0.
- [Release notes](https://github.com/tokio-rs/tokio/releases)
- [Commits](https://github.com/tokio-rs/tokio/compare/tokio-1.33.0...tokio-1.34.0)

---
updated-dependencies:
- dependency-name: tokio
  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>
2023-11-10 05:24:59 -05:00
5ecb176467 Bump tokio from 1.33.0 to 1.34.0 in /src/wasm-lib (#1051)
Bumps [tokio](https://github.com/tokio-rs/tokio) from 1.33.0 to 1.34.0.
- [Release notes](https://github.com/tokio-rs/tokio/releases)
- [Commits](https://github.com/tokio-rs/tokio/compare/tokio-1.33.0...tokio-1.34.0)

---
updated-dependencies:
- dependency-name: tokio
  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>
2023-11-10 05:24:40 -05:00
66135636ec Bump wasm-bindgen-futures from 0.4.37 to 0.4.38 in /src/wasm-lib (#1050)
Bumps [wasm-bindgen-futures](https://github.com/rustwasm/wasm-bindgen) from 0.4.37 to 0.4.38.
- [Release notes](https://github.com/rustwasm/wasm-bindgen/releases)
- [Changelog](https://github.com/rustwasm/wasm-bindgen/blob/main/CHANGELOG.md)
- [Commits](https://github.com/rustwasm/wasm-bindgen/commits)

---
updated-dependencies:
- dependency-name: wasm-bindgen-futures
  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>
2023-11-10 05:24:10 -05:00
685a16545c Bump @vitejs/plugin-react from 4.0.4 to 4.1.1 (#1035)
Bumps [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/tree/HEAD/packages/plugin-react) from 4.0.4 to 4.1.1.
- [Release notes](https://github.com/vitejs/vite-plugin-react/releases)
- [Changelog](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/CHANGELOG.md)
- [Commits](https://github.com/vitejs/vite-plugin-react/commits/v4.1.1/packages/plugin-react)

---
updated-dependencies:
- dependency-name: "@vitejs/plugin-react"
  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>
2023-11-10 05:20:45 -05:00
9adb15ee93 Bump @headlessui/react from 1.7.16 to 1.7.17 (#1037)
Bumps [@headlessui/react](https://github.com/tailwindlabs/headlessui/tree/HEAD/packages/@headlessui-react) from 1.7.16 to 1.7.17.
- [Release notes](https://github.com/tailwindlabs/headlessui/releases)
- [Changelog](https://github.com/tailwindlabs/headlessui/blob/main/packages/@headlessui-react/CHANGELOG.md)
- [Commits](https://github.com/tailwindlabs/headlessui/commits/@headlessui/react@v1.7.17/packages/@headlessui-react)

---
updated-dependencies:
- dependency-name: "@headlessui/react"
  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>
2023-11-10 05:20:02 -05:00
a8c4c97d79 Bump fuse.js from 6.6.2 to 7.0.0 (#1036)
Bumps [fuse.js](https://github.com/krisk/Fuse) from 6.6.2 to 7.0.0.
- [Release notes](https://github.com/krisk/Fuse/releases)
- [Changelog](https://github.com/krisk/Fuse/blob/main/CHANGELOG.md)
- [Commits](https://github.com/krisk/Fuse/compare/v6.6.2...v7.0.0)

---
updated-dependencies:
- dependency-name: fuse.js
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-11-10 05:19:46 -05:00
39e8e1f259 Bump eslint-plugin-css-modules from 2.11.0 to 2.12.0 (#1034)
Bumps [eslint-plugin-css-modules](https://github.com/atfzl/eslint-plugin-css-modules) from 2.11.0 to 2.12.0.
- [Release notes](https://github.com/atfzl/eslint-plugin-css-modules/releases)
- [Commits](https://github.com/atfzl/eslint-plugin-css-modules/compare/v2.11.0...v2.12.0)

---
updated-dependencies:
- dependency-name: eslint-plugin-css-modules
  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>
2023-11-10 05:18:09 -05:00
1672c1fd1f Impl databake for all AST node types (#1047)
Databake doesn't have any derive for HashMap, so for NonCodeMeta I decided to skip serializing the non_code_nodes. This should be OK for now.

See <https://github.com/unicode-org/icu4x/issues/4266> for the Databake hashmap issue.
2023-11-09 16:39:12 -06:00
6ec5881985 top level expressions fix (#1046)
#1033 top level expressions fix

Signed-off-by: Jess Frazelle <github@jessfraz.com>
2023-11-09 13:08:11 -08:00
7272cc9fbd Describe Rust version for devs (#1044)
Fixes #1042
2023-11-09 17:01:52 +00:00
b925ed9b65 KCL stdlib and circle function (#1029)
Allows stdlib functions to be written as KCL, not as Rust. 

Rust stdlib functions will hereafter be referred to as "core" not "std".

Right now the only stdlib function I implemented is a circle function (it's a wrapper around the core arc function which sets the arc's start/end to 0 and 360 respectively). I know I want to change this function as soon as KCL has enums, which is my next task. So, I don't want users to start using this right away. To that end, I've named this function "unstable_stdlib_circle" not "circle". Once the function is ready to be stabilized, I can rename it to just "circle".

Note that this PR modifies the existing "sketch and extrude a cylinder" KCL test so that instead of using a user-defined circle function, it uses the unstable_stdlib_circle function now. And the twenty-twenty tests pass, so we know my stdlib is working.

https://github.com/KittyCAD/modeling-app/issues/922
2023-11-09 09:58:20 -06:00
0db5db2181 Bump js-sys from 0.3.64 to 0.3.65 in /src/wasm-lib (#980)
Bumps [js-sys](https://github.com/rustwasm/wasm-bindgen) from 0.3.64 to 0.3.65.
- [Release notes](https://github.com/rustwasm/wasm-bindgen/releases)
- [Changelog](https://github.com/rustwasm/wasm-bindgen/blob/main/CHANGELOG.md)
- [Commits](https://github.com/rustwasm/wasm-bindgen/commits)

---
updated-dependencies:
- dependency-name: js-sys
  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>
2023-11-08 23:41:53 -06:00
898e3db9d1 AST function nodes no longer have stdlib function members (#1031)
* AST function nodes no longer have stdlib function members

IMO the AST should not need an actual pointer to a stdlib function -- that is a completely separate concern from the AST.

Instead, the AST nodes can just store function names, and the executor will have a stdlib which it can look up those names in.

* Fix tests

* Update snapshot tests
2023-11-08 20:23:59 -06:00
d337ac2546 Test with a circle function (#1030) 2023-11-08 15:06:41 -06:00
371d8e08f7 Refactor the call_fn fn to be more readable (#1028) 2023-11-08 13:44:31 -06:00
338c43a29d Fix auto-version in nightly builds (#1026)
* WIP Fix auto-version in nightly builds
Fixes #1015

* WIP

* Revert "WIP"

This reverts commit 7838bf1298.

* Need to update src-tauri/tauri.release.conf.json after CI changes

* Fixes just to test

* Clean up after tests
2023-11-08 13:58:37 +00:00
52bb5a2657 Bump @sentry/react from 7.65.0 to 7.77.0 (#977)
Bumps [@sentry/react](https://github.com/getsentry/sentry-javascript) from 7.65.0 to 7.77.0.
- [Release notes](https://github.com/getsentry/sentry-javascript/releases)
- [Changelog](https://github.com/getsentry/sentry-javascript/blob/develop/CHANGELOG.md)
- [Commits](https://github.com/getsentry/sentry-javascript/compare/7.65.0...7.77.0)

---
updated-dependencies:
- dependency-name: "@sentry/react"
  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>
2023-11-08 10:39:40 +00:00
1b6a06d266 Bump vscode-languageserver-protocol from 3.17.3 to 3.17.5 (#978)
Bumps [vscode-languageserver-protocol](https://github.com/Microsoft/vscode-languageserver-node/tree/HEAD/protocol) from 3.17.3 to 3.17.5.
- [Commits](https://github.com/Microsoft/vscode-languageserver-node/commits/release/types/3.17.5/protocol)

---
updated-dependencies:
- dependency-name: vscode-languageserver-protocol
  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>
2023-11-08 05:28:24 -05:00
c68d4778a5 Bump @tauri-apps/api from 1.5.0 to 1.5.1 (#979)
Bumps [@tauri-apps/api](https://github.com/tauri-apps/tauri) from 1.5.0 to 1.5.1.
- [Release notes](https://github.com/tauri-apps/tauri/releases)
- [Commits](https://github.com/tauri-apps/tauri/compare/@tauri-apps/api-v1.5...@tauri-apps/api-v1.5.1)

---
updated-dependencies:
- dependency-name: "@tauri-apps/api"
  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>
2023-11-08 05:28:01 -05:00
a8abea4fb5 Bump eslint from 8.46.0 to 8.53.0 (#1001)
Bumps [eslint](https://github.com/eslint/eslint) from 8.46.0 to 8.53.0.
- [Release notes](https://github.com/eslint/eslint/releases)
- [Changelog](https://github.com/eslint/eslint/blob/main/CHANGELOG.md)
- [Commits](https://github.com/eslint/eslint/compare/v8.46.0...v8.53.0)

---
updated-dependencies:
- dependency-name: eslint
  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>
2023-11-08 05:27:13 -05:00
a0678d22a8 fix epsilon bug (#1025) 2023-11-08 09:27:53 +00:00
acbfae2e65 Bump wasm-bindgen from 0.2.87 to 0.2.88 in /src/wasm-lib (#981)
Bumps [wasm-bindgen](https://github.com/rustwasm/wasm-bindgen) from 0.2.87 to 0.2.88.
- [Release notes](https://github.com/rustwasm/wasm-bindgen/releases)
- [Changelog](https://github.com/rustwasm/wasm-bindgen/blob/main/CHANGELOG.md)
- [Commits](https://github.com/rustwasm/wasm-bindgen/compare/0.2.87...0.2.88)

---
updated-dependencies:
- dependency-name: wasm-bindgen
  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>
2023-11-07 23:56:23 -06:00
1e1bec6a8a Bump winnow (#1024) 2023-11-07 23:55:45 -06:00
06462b5a65 Bump syn from 2.0.38 to 2.0.39 in /src/wasm-lib (#1000)
Bumps [syn](https://github.com/dtolnay/syn) from 2.0.38 to 2.0.39.
- [Release notes](https://github.com/dtolnay/syn/releases)
- [Commits](https://github.com/dtolnay/syn/compare/2.0.38...2.0.39)

---
updated-dependencies:
- dependency-name: syn
  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>
2023-11-07 22:45:58 -06:00
2f292fb1be Bump tauri-plugin-fs-extra from 11048fd to 6865299 in /src-tauri (#1023)
Bumps [tauri-plugin-fs-extra](https://github.com/tauri-apps/plugins-workspace) from `11048fd` to `6865299`.
- [Release notes](https://github.com/tauri-apps/plugins-workspace/releases)
- [Commits](11048fd997...6865299149)

---
updated-dependencies:
- dependency-name: tauri-plugin-fs-extra
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-11-07 22:45:20 -06:00
8184e7b376 Bump serde from 1.0.190 to 1.0.192 in /src-tauri (#1009)
Bumps [serde](https://github.com/serde-rs/serde) from 1.0.190 to 1.0.192.
- [Release notes](https://github.com/serde-rs/serde/releases)
- [Commits](https://github.com/serde-rs/serde/compare/v1.0.190...v1.0.192)

---
updated-dependencies:
- dependency-name: serde
  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>
2023-11-07 22:45:03 -06:00
b1084cbf80 Bump serde from 1.0.190 to 1.0.192 in /src/wasm-lib (#1012)
Bumps [serde](https://github.com/serde-rs/serde) from 1.0.190 to 1.0.192.
- [Release notes](https://github.com/serde-rs/serde/releases)
- [Commits](https://github.com/serde-rs/serde/compare/v1.0.190...v1.0.192)

---
updated-dependencies:
- dependency-name: serde
  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>
2023-11-07 22:44:55 -06:00
548b45905e Cut release v0.11.3 (#1020)
cut release v0.11.3
2023-11-08 12:57:00 +11:00
141fd2f3f1 fix variables panel and others (#1021) 2023-11-08 01:27:43 +00:00
604d931962 selections fix follow up (#1019) 2023-11-07 23:10:30 +00:00
b1668410f8 Neaten up stdlib code (#1017) 2023-11-07 12:12:18 -06:00
13176cec38 Cut release v0.11.2 (#1005)
* Cut release v0.11.2

* Replace 'release' with '*' for artifact upload

* New logic to prevent top-level artifact.zip changes

* Fix upload

---------

Co-authored-by: Kurt Hutten <k.hutten@protonmail.ch>
2023-11-07 05:20:26 -05:00
3a59ae13b6 fix selections (#1013) 2023-11-07 07:21:32 +00:00
57c2481943 unused imports (#1011) 2023-11-07 03:29:50 +00:00
a1c555c51e clear errors on good parse (#1008) 2023-11-07 02:02:44 +00:00
4d520541be Build release & sign only on merge / release / release PR (#991)
* Add env variable for release || schedule || Cut out v PR

* Skip Windows, Apple, and Tauri Updater signing

* Add tauri args to bypass updater on debug

* Trying to address includeRelease and includeDebug issues

* WIP

* Clean up, fix bool eval

* -c to --config

* Remove src-tauri

* inline config

* Cleanup

* Remove concurrency block

* Test release

* Escape backslash

* Clean up

* Add back concurrency and BUILD_RELEASE eval

* Back to build:wasm (no speed impr noticed)

* Adam's suggestions

Co-authored-by: Adam Chalmers <adam.chalmers@kittycad.io>

* New logic to prevent top-level artifact.zip changes

---------

Co-authored-by: Adam Chalmers <adam.chalmers@kittycad.io>
2023-11-06 19:51:32 -05:00
82586f002b Test Jess's KCL error (#1004) 2023-11-06 14:01:51 -06:00
4bd08f7444 safe parse (#996)
* safe parse

* use safe parse on mouse up, stream
2023-11-06 06:47:45 +00:00
6b2603b1c4 don't overwrite current file on onboarding-replay (#1003)
don't over write file on onboarding
2023-11-06 04:03:21 +00:00
af49bebde3 compare formated asts before execute (#1002) 2023-11-06 03:43:01 +00:00
ca056996fd stop double execute on project open (#997) 2023-11-06 14:33:19 +11:00
34163da361 start of fixing changing files and cleaning up after execute (#897)
* start of fixing changing files and cleaning up after execute

* stop constraints from leaving artifacts

* don't write file on initial load
2023-11-06 11:49:13 +11:00
7c22bac638 Remove unwraps from binary expression algorithm (#992) 2023-11-03 13:30:19 -05:00
37a65b166b Fix silly mistakes in previous CI.yml PRs (#988)
* Fix silly mistakes in previous CI.yml PRs

This is what happens when I code before my morning matcha

Part of #987
2023-11-02 12:34:38 -05:00
1189f272ba Use debug builds for wasm-pack on PRs (#986) 2023-11-02 09:55:58 -05:00
ca5bc880dc Build tauri app in debug mode on PRs, release mode on releases (#985)
Build tauri app in debug mode on PRs, release mode on merges.

Release builds are very slow and should only be used for actual releases. Fast debug builds should be used for CI on PRs.
2023-11-02 09:55:36 -05:00
828daba304 Tweak Codespell CI (#975)
Ensure codespell CI job actually catches typos
2023-11-01 23:35:41 +00:00
0b9ba55bb4 Nicer error messages for unknown tokens (#974) 2023-11-01 23:08:22 +00:00
2d2a85ae7d remove view change from debug panel (#866)
remove view change from debug
2023-11-01 23:03:58 +00:00
cc57a302cc Update twenty-twenty (#973)
Due to engine PR #1566
2023-11-01 22:35:17 +00:00
fdbfd0c4b6 Fix typos (#972)
* Update twenty-twenty

Due to engine PR #1566

* Fix typos
2023-11-01 22:34:54 +00:00
2e419907e6 Tokenizing fallibility (#883)
Tokenization tracks invalid tokens and produces a nice error about them

---------

Co-authored-by: Adam Chalmers <adam.chalmers@kittycad.io>
2023-11-01 17:20:49 -05:00
3d0c5c10b0 KCL literals are typed, not JSON values (#971)
We now control the KCL type system instead of reusing JSON's type system.
2023-11-01 13:52:50 -05:00
4d47c067b7 Snapshot testing for parser (#969)
See https://docs.rs/insta for more.
2023-11-01 12:40:40 -05:00
3b3b5371eb Bump wasm-streams from 0.3.0 to 0.4.0 in /src/wasm-lib (#961)
Bumps [wasm-streams](https://github.com/MattiasBuelens/wasm-streams) from 0.3.0 to 0.4.0.
- [Release notes](https://github.com/MattiasBuelens/wasm-streams/releases)
- [Changelog](https://github.com/MattiasBuelens/wasm-streams/blob/main/CHANGELOG.md)
- [Commits](https://github.com/MattiasBuelens/wasm-streams/compare/v0.3.0...v0.4.0)

---
updated-dependencies:
- dependency-name: wasm-streams
  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>
2023-11-01 08:43:18 -05:00
3ea77f8e1e Bump zustand from 4.4.0 to 4.4.5 (#963)
Bumps [zustand](https://github.com/pmndrs/zustand) from 4.4.0 to 4.4.5.
- [Release notes](https://github.com/pmndrs/zustand/releases)
- [Commits](https://github.com/pmndrs/zustand/compare/v4.4.0...v4.4.5)

---
updated-dependencies:
- dependency-name: zustand
  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>
Co-authored-by: Pierre Jacquier <pierrejacquier39@gmail.com>
2023-11-01 09:14:57 -04:00
4fa7c07e54 Bump @uiw/react-codemirror from 4.21.13 to 4.21.20 (#967)
Bumps [@uiw/react-codemirror](https://github.com/uiwjs/react-codemirror) from 4.21.13 to 4.21.20.
- [Release notes](https://github.com/uiwjs/react-codemirror/releases)
- [Commits](https://github.com/uiwjs/react-codemirror/compare/v4.21.13...v4.21.20)

---
updated-dependencies:
- dependency-name: "@uiw/react-codemirror"
  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>
Co-authored-by: Pierre Jacquier <pierrejacquier39@gmail.com>
2023-11-01 09:14:04 -04:00
c66a96a333 Bump @testing-library/react from 13.4.0 to 14.0.0 (#965)
Bumps [@testing-library/react](https://github.com/testing-library/react-testing-library) from 13.4.0 to 14.0.0.
- [Release notes](https://github.com/testing-library/react-testing-library/releases)
- [Changelog](https://github.com/testing-library/react-testing-library/blob/main/CHANGELOG.md)
- [Commits](https://github.com/testing-library/react-testing-library/compare/v13.4.0...v14.0.0)

---
updated-dependencies:
- dependency-name: "@testing-library/react"
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-11-01 07:42:24 -04:00
4196ff91ac Bump vite from 4.4.9 to 4.5.0 (#966)
Bumps [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) from 4.4.9 to 4.5.0.
- [Release notes](https://github.com/vitejs/vite/releases)
- [Changelog](https://github.com/vitejs/vite/blob/main/packages/vite/CHANGELOG.md)
- [Commits](https://github.com/vitejs/vite/commits/v4.5.0/packages/vite)

---
updated-dependencies:
- dependency-name: vite
  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>
2023-11-01 07:41:58 -04:00
cf66b93963 Fix most Github Actions 'annotations' in PRs (#417)
* Fix Github Actions 'annotations' in PRs
Fixes #383

* Fix the React Hooks complains, and _app for rust

* Revert "Fix the React Hooks complains, and _app for rust"

This reverts commit 4a2ff925e5.

* Add back prettier fix and rust _app

* Fmt

* More annotation fixes

* More non-hooks fixes

* Rollback with eslint rule

* Add APPLE_TEAM_ID secret

* Revert "Add APPLE_TEAM_ID secret"

This reverts commit 346aaff5f4.

* More fixes
2023-11-01 07:39:31 -04:00
0b0219b810 Add repository field to Cargo.toml (#960) 2023-10-31 18:24:19 -05:00
36c7fcf6d7 Add a basic tauri e2e test on Linux (#923)
* WIP e2e test

* Working test on Linux

* Clean up

* Add to CI

* Fix

* Install tauri-driver

* Migrate to ubuntu-latest

* Add button check and click

* Update name

* Separate job for e2e test

* Fix path

* Fix perms

* Fix perms

* Single build-test-apps job

* Lint wdio file
2023-10-31 18:30:24 -04:00
023c3cbb90 New math parser (#956)
* New math parser

* Remove old parser

* Comments

* Move tests into parser_impl, remove dead code

* Backport some math tests
2023-10-31 14:16:18 -05:00
387f7e0912 New benchmark for parsing binary expressions (#957) 2023-10-31 18:04:24 +00:00
9b55b1fd12 Bump serde_json from 1.0.107 to 1.0.108 in /src-tauri (#954)
Bumps [serde_json](https://github.com/serde-rs/json) from 1.0.107 to 1.0.108.
- [Release notes](https://github.com/serde-rs/json/releases)
- [Commits](https://github.com/serde-rs/json/compare/v1.0.107...v1.0.108)

---
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>
Co-authored-by: Pierre Jacquier <pierrejacquier39@gmail.com>
2023-10-31 09:58:27 -05:00
4b6662169c Bump tauri-plugin-fs-extra from 68d77f9 to 11048fd in /src-tauri (#955)
Bumps [tauri-plugin-fs-extra](https://github.com/tauri-apps/plugins-workspace) from `68d77f9` to `11048fd`.
- [Release notes](https://github.com/tauri-apps/plugins-workspace/releases)
- [Commits](68d77f999c...11048fd997)

---
updated-dependencies:
- dependency-name: tauri-plugin-fs-extra
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-10-31 06:50:50 -04:00
d36abfcb3d Bump serde_json from 1.0.107 to 1.0.108 in /src/wasm-lib (#953)
Bumps [serde_json](https://github.com/serde-rs/json) from 1.0.107 to 1.0.108.
- [Release notes](https://github.com/serde-rs/json/releases)
- [Commits](https://github.com/serde-rs/json/compare/v1.0.107...v1.0.108)

---
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>
2023-10-31 06:50:07 -04:00
9002ae9efb Bump winnow from 0.5.17 to 0.5.18 in /src/wasm-lib (#952)
Bumps [winnow](https://github.com/winnow-rs/winnow) from 0.5.17 to 0.5.18.
- [Changelog](https://github.com/winnow-rs/winnow/blob/main/CHANGELOG.md)
- [Commits](https://github.com/winnow-rs/winnow/compare/v0.5.17...v0.5.18)

---
updated-dependencies:
- dependency-name: winnow
  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>
2023-10-31 06:49:51 -04:00
4deea25394 Bump @codemirror/autocomplete from 6.9.0 to 6.10.2 (#951)
Bumps [@codemirror/autocomplete](https://github.com/codemirror/autocomplete) from 6.9.0 to 6.10.2.
- [Changelog](https://github.com/codemirror/autocomplete/blob/main/CHANGELOG.md)
- [Commits](https://github.com/codemirror/autocomplete/compare/6.9.0...6.10.2)

---
updated-dependencies:
- dependency-name: "@codemirror/autocomplete"
  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>
2023-10-31 06:49:15 -04:00
b5940d2cb7 Bump @tauri-apps/cli from 1.5.2 to 1.5.6 (#950)
Bumps [@tauri-apps/cli](https://github.com/tauri-apps/tauri) from 1.5.2 to 1.5.6.
- [Release notes](https://github.com/tauri-apps/tauri/releases)
- [Commits](https://github.com/tauri-apps/tauri/compare/@tauri-apps/cli-v1.5.2...@tauri-apps/cli-v1.5.6)

---
updated-dependencies:
- dependency-name: "@tauri-apps/cli"
  dependency-type: direct:development
  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>
2023-10-31 06:48:51 -04:00
932b467c1e Bump @testing-library/user-event from 13.5.0 to 14.5.1 (#949)
Bumps [@testing-library/user-event](https://github.com/testing-library/user-event) from 13.5.0 to 14.5.1.
- [Release notes](https://github.com/testing-library/user-event/releases)
- [Changelog](https://github.com/testing-library/user-event/blob/main/CHANGELOG.md)
- [Commits](https://github.com/testing-library/user-event/compare/v13.5.0...v14.5.1)

---
updated-dependencies:
- dependency-name: "@testing-library/user-event"
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-10-31 06:48:34 -04:00
7c7f5c81c4 Bump web-vitals from 2.1.4 to 3.5.0 (#948)
Bumps [web-vitals](https://github.com/GoogleChrome/web-vitals) from 2.1.4 to 3.5.0.
- [Changelog](https://github.com/GoogleChrome/web-vitals/blob/main/CHANGELOG.md)
- [Commits](https://github.com/GoogleChrome/web-vitals/compare/v2.1.4...v3.5.0)

---
updated-dependencies:
- dependency-name: web-vitals
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-10-31 06:48:05 -04:00
066b4f3e06 Bump typescript from 4.9.5 to 5.2.2 (#778)
Bumps [typescript](https://github.com/Microsoft/TypeScript) from 4.9.5 to 5.2.2.
- [Release notes](https://github.com/Microsoft/TypeScript/releases)
- [Commits](https://github.com/Microsoft/TypeScript/compare/v4.9.5...v5.2.2)

---
updated-dependencies:
- dependency-name: typescript
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-10-31 06:47:34 -04:00
c6067bfc7a Bump kittycad from 0.2.38 to 0.2.41 in /src-tauri (#935)
Bumps [kittycad](https://github.com/KittyCAD/kittycad.rs) from 0.2.38 to 0.2.41.
- [Release notes](https://github.com/KittyCAD/kittycad.rs/releases)
- [Commits](https://github.com/KittyCAD/kittycad.rs/compare/v0.2.38...v0.2.41)

---
updated-dependencies:
- dependency-name: kittycad
  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>
2023-10-30 22:22:05 -05:00
2018f0d517 Bump actions/setup-node from 3 to 4 (#924)
Bumps [actions/setup-node](https://github.com/actions/setup-node) from 3 to 4.
- [Release notes](https://github.com/actions/setup-node/releases)
- [Commits](https://github.com/actions/setup-node/compare/v3...v4)

---
updated-dependencies:
- dependency-name: actions/setup-node
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-10-30 22:21:57 -05:00
74aae3d15f Bump openapitor from 57b4d8b to 1b2562f in /src/wasm-lib (#938)
Bumps [openapitor](https://github.com/KittyCAD/kittycad.rs) from `57b4d8b` to `1b2562f`.
- [Release notes](https://github.com/KittyCAD/kittycad.rs/releases)
- [Commits](57b4d8b168...1b2562f4b3)

---
updated-dependencies:
- dependency-name: openapitor
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-10-30 22:21:35 -05:00
812f419e75 Bump serde from 1.0.189 to 1.0.190 in /src/wasm-lib (#941)
Bumps [serde](https://github.com/serde-rs/serde) from 1.0.189 to 1.0.190.
- [Release notes](https://github.com/serde-rs/serde/releases)
- [Commits](https://github.com/serde-rs/serde/compare/v1.0.189...v1.0.190)

---
updated-dependencies:
- dependency-name: serde
  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>
2023-10-30 22:21:23 -05:00
5ec8cc69db Bump tauri-plugin-fs-extra from 9b20f28 to 68d77f9 in /src-tauri (#942)
Bumps [tauri-plugin-fs-extra](https://github.com/tauri-apps/plugins-workspace) from `9b20f28` to `68d77f9`.
- [Release notes](https://github.com/tauri-apps/plugins-workspace/releases)
- [Commits](9b20f28d74...68d77f999c)

---
updated-dependencies:
- dependency-name: tauri-plugin-fs-extra
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-10-30 22:21:14 -05:00
a5302b6e0e Nitpick my Winnow code (#946) 2023-10-30 22:20:37 -05:00
2114cc0d94 Bump @babel/traverse from 7.22.8 to 7.23.2 (#879)
Bumps [@babel/traverse](https://github.com/babel/babel/tree/HEAD/packages/babel-traverse) from 7.22.8 to 7.23.2.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.23.2/packages/babel-traverse)

---
updated-dependencies:
- dependency-name: "@babel/traverse"
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-10-30 20:45:36 -04:00
2471ce1aba Bump swr from 2.2.0 to 2.2.2 (#332)
Bumps [swr](https://github.com/vercel/swr) from 2.2.0 to 2.2.2.
- [Release notes](https://github.com/vercel/swr/releases)
- [Commits](https://github.com/vercel/swr/compare/v2.2.0...v2.2.2)

---
updated-dependencies:
- dependency-name: swr
  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>
Co-authored-by: Pierre Jacquier <pierrejacquier39@gmail.com>
2023-10-30 20:45:18 -04:00
35772475b9 Bump uuid and @types/uuid (#617)
Bumps [uuid](https://github.com/uuidjs/uuid) and [@types/uuid](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/uuid). These dependencies needed to be updated together.

Updates `uuid` from 9.0.0 to 9.0.1
- [Changelog](https://github.com/uuidjs/uuid/blob/main/CHANGELOG.md)
- [Commits](https://github.com/uuidjs/uuid/compare/v9.0.0...v9.0.1)

Updates `@types/uuid` from 9.0.2 to 9.0.4
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/uuid)

---
updated-dependencies:
- dependency-name: uuid
  dependency-type: direct:production
  update-type: version-update:semver-patch
- dependency-name: "@types/uuid"
  dependency-type: direct:development
  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>
2023-10-30 20:44:51 -04:00
86c592c0f6 Bump vite-tsconfig-paths from 4.2.0 to 4.2.1 (#750)
Bumps [vite-tsconfig-paths](https://github.com/aleclarson/vite-tsconfig-paths) from 4.2.0 to 4.2.1.
- [Release notes](https://github.com/aleclarson/vite-tsconfig-paths/releases)
- [Commits](https://github.com/aleclarson/vite-tsconfig-paths/compare/v4.2.0...v4.2.1)

---
updated-dependencies:
- dependency-name: vite-tsconfig-paths
  dependency-type: direct:development
  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>
2023-10-30 20:44:33 -04:00
0e98973cfa Bump @types/debounce-promise from 3.1.6 to 3.1.8 (#899)
Bumps [@types/debounce-promise](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/debounce-promise) from 3.1.6 to 3.1.8.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/debounce-promise)

---
updated-dependencies:
- dependency-name: "@types/debounce-promise"
  dependency-type: direct:development
  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>
2023-10-30 20:43:46 -04:00
7dd16fe6de Bump clap from 4.4.6 to 4.4.7 in /src/wasm-lib (#939)
Bumps [clap](https://github.com/clap-rs/clap) from 4.4.6 to 4.4.7.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/v4.4.6...v4.4.7)

---
updated-dependencies:
- dependency-name: clap
  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>
2023-10-30 20:42:58 -04:00
478b636049 Bump crypto-js from 4.1.1 to 4.2.0 (#940)
Bumps [crypto-js](https://github.com/brix/crypto-js) from 4.1.1 to 4.2.0.
- [Commits](https://github.com/brix/crypto-js/compare/4.1.1...4.2.0)

---
updated-dependencies:
- dependency-name: crypto-js
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-10-30 20:42:43 -04:00
c779311a56 Bump serde from 1.0.189 to 1.0.190 in /src-tauri (#943)
Bumps [serde](https://github.com/serde-rs/serde) from 1.0.189 to 1.0.190.
- [Release notes](https://github.com/serde-rs/serde/releases)
- [Commits](https://github.com/serde-rs/serde/compare/v1.0.189...v1.0.190)

---
updated-dependencies:
- dependency-name: serde
  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>
2023-10-30 20:42:01 -04:00
ca02ec1151 Bump futures from 0.3.28 to 0.3.29 in /src/wasm-lib (#944)
Bumps [futures](https://github.com/rust-lang/futures-rs) from 0.3.28 to 0.3.29.
- [Release notes](https://github.com/rust-lang/futures-rs/releases)
- [Changelog](https://github.com/rust-lang/futures-rs/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rust-lang/futures-rs/compare/0.3.28...0.3.29)

---
updated-dependencies:
- dependency-name: futures
  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>
2023-10-30 20:41:37 -04:00
b271d5060e Fix typos and unnecessary import paths (#945) 2023-10-27 18:42:35 +00:00
208 changed files with 20588 additions and 6469 deletions

3
.codespellrc Normal file
View File

@ -0,0 +1,3 @@
[codespell]
ignore-words-list: crate,everytime
skip: **/target,node_modules,build

View File

@ -1,4 +1,8 @@
{
"parser": "@typescript-eslint/parser",
"parserOptions": {
"project": "./tsconfig.json"
},
"plugins": [
"css-modules"
],
@ -11,6 +15,16 @@
"semi": [
"error",
"never"
]
}
],
"react-hooks/exhaustive-deps": "off",
"@typescript-eslint/no-floating-promises": "warn"
},
"overrides": [
{
"files": ["e2e/**/*.ts"], // Update the pattern based on your file structure
"rules": {
"testing-library/prefer-screen-queries": "off"
}
}
]
}

View File

@ -12,15 +12,19 @@ on:
# Daily at 04:00 AM UTC
# Will checkout the last commit from the default branch (main as of 2023-10-04)
env:
BUILD_RELEASE: ${{ github.event_name == 'release' || github.event_name == 'schedule' || github.event_name == 'pull_request' && contains(github.event.pull_request.title, 'Cut release v') }}
concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
cancel-in-progress: true
jobs:
check-format:
runs-on: 'ubuntu-20.04'
runs-on: 'ubuntu-latest'
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v3
- uses: actions/setup-node@v4
with:
node-version-file: '.nvmrc'
cache: 'yarn'
@ -28,11 +32,11 @@ jobs:
- run: yarn fmt-check
check-types:
runs-on: ubuntu-20.04
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v3
- uses: actions/setup-node@v4
with:
node-version-file: '.nvmrc'
cache: 'yarn'
@ -44,12 +48,27 @@ jobs:
- run: yarn build:wasm
- run: yarn tsc
check-typos:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v4
- name: Install codespell
run: |
python -m pip install codespell
- name: Run codespell
run: codespell --config .codespellrc # Edit this file to tweak the typo list and other configuration.
build-test-web:
runs-on: ubuntu-20.04
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v3
- uses: actions/setup-node@v4
with:
node-version-file: '.nvmrc'
cache: 'yarn'
@ -68,14 +87,15 @@ jobs:
- run: yarn test:cov
prepare-json-files:
runs-on: ubuntu-20.04 # seperate job on Ubuntu for easy string manipulations (compared to Windows)
runs-on: ubuntu-latest # seperate job on Ubuntu for easy string manipulations (compared to Windows)
outputs:
version: ${{ steps.export_version.outputs.version }}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v3
- uses: actions/setup-node@v4
with:
node-version-file: '.nvmrc'
cache: 'yarn'
@ -85,7 +105,7 @@ jobs:
run: |
VERSION=$(date +'%-y.%-m.%-d') yarn bump-jsons
echo "$(jq --arg url 'https://dl.kittycad.io/releases/modeling-app/nightly/last_update.json' \
'.tauri.updater.endpoints[]=$url' src-tauri/tauri.conf.json --indent 2)" > src-tauri/tauri.conf.json
'.tauri.updater.endpoints[]=$url' src-tauri/tauri.release.conf.json --indent 2)" > src-tauri/tauri.release.conf.json
- uses: actions/upload-artifact@v3
if: github.event_name == 'schedule'
@ -93,16 +113,18 @@ jobs:
path: |
package.json
src-tauri/tauri.conf.json
src-tauri/tauri.release.conf.json
- id: export_version
run: echo "version=`cat package.json | jq -r '.version'`" >> "$GITHUB_OUTPUT"
build-apps:
needs: [check-format, build-test-web, prepare-json-files, check-types]
build-test-apps:
needs: [prepare-json-files]
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [macos-latest, ubuntu-20.04, windows-latest]
os: [macos-latest, ubuntu-latest, windows-latest]
steps:
- uses: actions/checkout@v4
@ -114,25 +136,32 @@ jobs:
ls -l artifact
cp artifact/package.json package.json
cp artifact/src-tauri/tauri.conf.json src-tauri/tauri.conf.json
cp artifact/src-tauri/tauri.release.conf.json src-tauri/tauri.release.conf.json
- name: install ubuntu system dependencies
if: matrix.os == 'ubuntu-20.04'
run: |
sudo apt-get update
sudo apt-get install -y libgtk-3-dev libwebkit2gtk-4.0-dev libayatana-appindicator3-dev librsvg2-dev
- name: Install ubuntu system dependencies
if: matrix.os == 'ubuntu-latest'
run: >
sudo apt-get update &&
sudo apt-get install -y
libgtk-3-dev
libgtksourceview-3.0-dev
webkit2gtk-4.0
libappindicator3-dev
webkit2gtk-driver
xvfb
- name: Sync node version and setup cache
uses: actions/setup-node@v3
uses: actions/setup-node@v4
with:
node-version-file: '.nvmrc'
cache: 'yarn' # Set this to npm, yarn or pnpm.
- run: yarn install
- name: Rust setup
- name: Setup Rust
uses: dtolnay/rust-toolchain@stable
- name: Rust cache
- name: Setup Rust cache
uses: swatinem/rust-cache@v2
with:
workspaces: './src-tauri -> target'
@ -141,24 +170,27 @@ jobs:
with:
workspaces: './src/wasm-lib'
- name: wasm prep
- name: Run build:wasm manually
shell: bash
env:
MODE: ${{ env.BUILD_RELEASE == 'true' && '--release' || '--debug' }}
run: |
mkdir src/wasm-lib/pkg; cd src/wasm-lib
npx wasm-pack build --target web --out-dir pkg
echo "building with ${{ env.MODE }}"
npx wasm-pack build --target web --out-dir pkg ${{ env.MODE }}
cd ../../
cp src/wasm-lib/pkg/wasm_lib_bg.wasm public
- name: Fix format
run: yarn fmt
- name: install apple silicon target mac
- name: Install Universal target (MacOS only)
if: matrix.os == 'macos-latest'
run: |
rustup target add aarch64-apple-darwin
- name: Prepare Windows certificate and variables
if: matrix.os == 'windows-latest'
- name: Prepare certificate and variables (Windows only)
if: ${{ matrix.os == 'windows-latest' && env.BUILD_RELEASE == 'true' }}
run: |
echo "${{secrets.SM_CLIENT_CERT_FILE_B64 }}" | base64 --decode > /d/Certificate_pkcs12.p12
cat /d/Certificate_pkcs12.p12
@ -172,8 +204,8 @@ jobs:
echo "C:\Program Files\DigiCert\DigiCert One Signing Manager Tools" >> $GITHUB_PATH
shell: bash
- name: Setup Windows certicate with SSM KSP
if: matrix.os == 'windows-latest'
- name: Setup certicate with SSM KSP (Windows only)
if: ${{ matrix.os == 'windows-latest' && env.BUILD_RELEASE == 'true' }}
run: |
curl -X GET https://one.digicert.com/signingmanager/api-ui/v1/releases/smtools-windows-x64.msi/download -H "x-api-key:%SM_API_KEY%" -o smtools-windows-x64.msi
msiexec /i smtools-windows-x64.msi /quiet /qn
@ -183,8 +215,17 @@ jobs:
smksp_cert_sync.exe
shell: cmd
- name: Build and sign the app for the current platform
- name: Build the app (debug)
uses: tauri-apps/tauri-action@v0
if: ${{ env.BUILD_RELEASE == 'false' }}
with:
includeRelease: false
includeDebug: true
args: ${{ matrix.os == 'macos-latest' && '--target universal-apple-darwin' || '' }}
- name: Build the app (release) and sign
uses: tauri-apps/tauri-action@v0
if: ${{ env.BUILD_RELEASE == 'true' }}
env:
TAURI_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }}
TAURI_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }}
@ -194,17 +235,31 @@ jobs:
APPLE_ID: ${{ secrets.APPLE_ID }}
APPLE_PASSWORD: ${{ secrets.APPLE_PASSWORD }}
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
TAURI_CONF_ARGS: "--config ${{ matrix.os == 'windows-latest' && 'src-tauri\\tauri.release.conf.json' || 'src-tauri/tauri.release.conf.json' }}"
with:
args: ${{ matrix.os == 'macos-latest' && '--target universal-apple-darwin' || '' }}
args: "${{ matrix.os == 'macos-latest' && '--target universal-apple-darwin' || '' }} ${{ env.TAURI_CONF_ARGS }}"
- uses: actions/upload-artifact@v3
env:
PREFIX: ${{ matrix.os == 'macos-latest' && 'src-tauri/target/universal-apple-darwin' || 'src-tauri/target' }}
MODE: ${{ env.BUILD_RELEASE == 'true' && 'release' || 'debug' }}
with:
path: ${{ matrix.os == 'macos-latest' && 'src-tauri/target/universal-apple-darwin/release/bundle/*/*' || 'src-tauri/target/release/bundle/*/*' }}
path: "${{ env.PREFIX }}/${{ env.MODE }}/bundle/*/*"
- name: Run e2e tests (linux only)
if: matrix.os == 'ubuntu-latest'
run: |
cargo install tauri-driver
xvfb-run yarn test:e2e:tauri
env:
E2E_APPLICATION: "./src-tauri/target/${{ env.BUILD_RELEASE == 'true' && 'release' || 'debug' }}/kittycad-modeling"
KITTYCAD_API_TOKEN: ${{ secrets.KITTYCAD_API_TOKEN }}
publish-apps-release:
runs-on: ubuntu-20.04
runs-on: ubuntu-latest
if: ${{ github.event_name == 'release' || github.event_name == 'schedule' }}
needs: [build-test-web, prepare-json-files, build-apps]
needs: [check-format, check-types, check-typos, build-test-web, prepare-json-files, build-test-apps]
env:
VERSION_NO_V: ${{ needs.prepare-json-files.outputs.version }}
VERSION: ${{ github.event_name == 'release' && format('v{0}', needs.prepare-json-files.outputs.version) || needs.prepare-json-files.outputs.version }}
@ -285,7 +340,7 @@ jobs:
cat last_download.json
- name: Authenticate to Google Cloud
uses: 'google-github-actions/auth@v1.1.1'
uses: 'google-github-actions/auth@v2.0.0'
with:
credentials_json: '${{ secrets.GOOGLE_CLOUD_DL_SA }}'

114
.github/workflows/playwright.yml vendored Normal file
View File

@ -0,0 +1,114 @@
name: Playwright Tests
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
jobs:
playwright-ubuntu:
timeout-minutes: 60
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version-file: '.nvmrc'
cache: 'yarn'
- name: Install dependencies
run: yarn
- name: Install Playwright Browsers
run: yarn playwright install --with-deps
- name: Setup Rust
uses: dtolnay/rust-toolchain@stable
- name: Cache wasm
uses: Swatinem/rust-cache@v2
with:
workspaces: './src/wasm-lib'
- name: build wasm
run: yarn build:wasm
- name: build web
run: yarn build:local
- name: Run ubuntu/chrome snapshots
run: yarn playwright test --project="Google Chrome" --update-snapshots e2e/playwright/snapshot-tests.spec.ts
env:
CI: true
token: ${{ secrets.KITTYCAD_API_TOKEN_DEV }}
- uses: actions/upload-artifact@v3
if: always()
with:
name: playwright-report
path: playwright-report/
retention-days: 30
- name: check for changes
id: git-check
run: |
git add .
if git status | grep -q "Changes to be committed"
then
echo "::set-output name=modified::true"
else
echo "::set-output name=modified::false"
fi
- name: Commit changes, if any
if: steps.git-check.outputs.modified == 'true'
run: |
git add .
git config --local user.email "github-actions[bot]@users.noreply.github.com"
git config --local user.name "github-actions[bot]"
git remote set-url origin https://${{ github.actor }}:${{ secrets.GITHUB_TOKEN }}@github.com/${{ github.repository }}.git
git fetch origin
echo ${{ github.head_ref }}
git checkout ${{ github.head_ref }}
# TODO when safari works on ubuntu remove the os part of the commit message
git commit -am "A snapshot a day keeps the bugs away! 📷🐛 (OS: ubuntu)" || true
git push
git push origin ${{ github.head_ref }}
- name: Run ubuntu/chrome flow
run: yarn playwright test --project="Google Chrome" e2e/playwright/flow-tests.spec.ts
env:
CI: true
token: ${{ secrets.KITTYCAD_API_TOKEN_DEV }}
- uses: actions/upload-artifact@v3
if: always()
with:
name: playwright-report
path: playwright-report/
retention-days: 30
playwright-macos:
timeout-minutes: 60
runs-on: macos-latest
needs: playwright-ubuntu
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version-file: '.nvmrc'
cache: 'yarn'
- name: Install dependencies
run: yarn
- name: Install Playwright Browsers
run: yarn playwright install --with-deps
- name: Setup Rust
uses: dtolnay/rust-toolchain@stable
- name: Cache wasm
uses: Swatinem/rust-cache@v2
with:
workspaces: './src/wasm-lib'
- name: build wasm
run: yarn build:wasm
- name: build web
run: yarn build:local
- name: Run macos/safari flow
# safari doesn't work on Ubuntu because of the same reason tauri doesn't (webRTC issues)
# TODO remove this and the matrix and run all tests on ubuntu when this is fixed
run: yarn playwright test --project="webkit" e2e/playwright/flow-tests.spec.ts
env:
CI: true
token: ${{ secrets.KITTYCAD_API_TOKEN_DEV }}
- uses: actions/upload-artifact@v3
if: always()
with:
name: playwright-report
path: playwright-report/
retention-days: 30

8
.gitignore vendored
View File

@ -33,3 +33,11 @@ src/wasm-lib/bindings
src/wasm-lib/kcl/bindings
public/wasm_lib_bg.wasm
src/wasm-lib/lcov.info
e2e/playwright/playwright-secrets.env
e2e/playwright/temp1.png
e2e/playwright/temp2.png
/test-results/
/playwright-report/
/blob-report/
/playwright/.cache/

View File

@ -7,6 +7,7 @@ coverage
target
src/wasm-lib/pkg
src/wasm-lib/kcl/bindings
e2e/playwright/export-snapshots
# XState generated files
src/machines/modelingMachine.typegen.ts

113
README.md
View File

@ -48,7 +48,7 @@ We recommend downloading the latest application binary from [our Releases page](
## Running a development build
First, [install Rust via `rustup`](https://www.rust-lang.org/tools/install). This project uses a lot of Rust compiled to [WASM](https://webassembly.org/) within it. Then, run:
First, [install Rust via `rustup`](https://www.rust-lang.org/tools/install). This project uses a lot of Rust compiled to [WASM](https://webassembly.org/) within it. We always use the latest stable version of Rust, so you may need to run `rustup update stable`. Then, run:
```
yarn install
@ -104,7 +104,7 @@ To spin up up tauri dev, `yarn install` and `yarn build:wasm-dev` need to have b
yarn tauri dev
```
Will spin up the web app before opening up the tauri dev desktop app. Note that it's probably a good idea to close the browser tab that gets opened since at the time of writting they can conflict.
Will spin up the web app before opening up the tauri dev desktop app. Note that it's probably a good idea to close the browser tab that gets opened since at the time of writing they can conflict.
The dev instance automatically opens up the browser devtools which can be disabled by [commenting it out](https://github.com/KittyCAD/modeling-app/blob/main/src-tauri/src/main.rs#L92.)
@ -176,3 +176,112 @@ $ cargo +nightly fuzz run parser
For more information on fuzzing you can check out
[this guide](https://rust-fuzz.github.io/book/cargo-fuzz.html).
### Playwright
First time running plawright locally, you'll need to add the secrets file
```bash
touch ./e2e/playwright/playwright-secrets.env
echo 'token="your-token"' > ./e2e/playwright/playwright-secrets.env
```
then replace "your-token" with a dev token from dev.kittycad.io/account/api-tokens
then:
run playwright
```
yarn playwright test
```
run a specific test suite
```
yarn playwright test src/e2e-tests/example.spec.ts
```
run a specific test change the test from `test('...` to `test.only('...`
(note if you commit this, the tests will instantly fail without running any of the tests)
run headed
```
yarn playwright test --headed
```
run with step through debugger
```
PWDEBUG=1 yarn playwright test
```
However, if you want a debugger I recommend using VSCode and the `playwright` extension, as the above command is a cruder debugger that steps into every function call which is annoying.
With the extension you can set a breakpoint after `waitForDefaultPlanesVisibilityChange` in order to skip app loading, then the vscode debugger's "step over" is much better for being able to stay at the right level of abstraction as you debug the code.
If you want to limit to a single browser use `--project="webkit"` or `firefox`, `Google Chrome`
Or comment out browsers in `playwright.config.ts`.
note chromium has encoder compat issues which is why were testing against the branded 'Google Chrome'
You may consider using the VSCode extension, it's useful for running individual threads, but some some reason the "record a test" is locked to chromium with we can't use. A work around is to us the CI `yarn playwright codegen -b wk --load-storage ./store localhost:3000`
<details>
<summary>
Where `./store` should look like this
</summary>
```JSON
{
"cookies": [],
"origins": [
{
"origin": "http://localhost:3000",
"localStorage": [
{
"name": "store",
"value": "{\"state\":{\"openPanes\":[\"code\"]},\"version\":0}"
},
{
"name": "persistCode",
"value": ""
},
{
"name": "TOKEN_PERSIST_KEY",
"value": "your-token"
}
]
}
]
}
```
</details>
However because much of our tests involve clicking in the stream at specific locations, it's code-gen looks `await page.locator('video').click();` when really we need to use a pixel coord, so I think it's of limited use.
#### Some notes on CI
The tests are broken into snapshot tests and non-snapshot tests, and they run in that order, they automatically commit new snap shots, so if you see an image commit check it was an intended change. If we have non-determinism in the snapshots such that they are always committing new images, hopefully this annoyance makes us fix them asap, if you notice this happening let Kurt know. But for the odd occasion `git reset --hard HEAD~ && git push -f` is your friend.
How to interpret failing playwright tests?
If your tests fail, click through to the action and see that the tests failed on a line that includes `await page.getByTestId('loading').waitFor({ state: 'detached' })`, this means the test fail because the stream never started. It's you choice if you want to re-run the test, or ignore the failure.
We run on ubuntu and macos, because safari doesn't work on linux because of the dreaded "no RTCPeerConnection variable" error. But linux runs first and then macos for the same reason that we limit the number of parallel tests to 1 because we limit stream connections per user, so tests would start failing we if let them run together.
If something fails on CI you can download the artifact, unzip it and then open `playwright-report/data/<UUID>.zip` with https://trace.playwright.dev/ to see what happened.
#### Getting started writing a playwright test in our app
Besides following the instructions above and using the playwright docs, our app is weird because of the whole stream thing, which means our testing is weird. Because we've just figured out this stuff and therefore docs might go stale quick here's a 15min vid/tutorial
https://github.com/KittyCAD/modeling-app/assets/29681384/6f5e8e85-1003-4fd9-be7f-f36ce833942d
<details>
<summary>
Ps for the debug panel, the following JSON is useful for snapping the camera
</summary>
```JSON
{"type":"modeling_cmd_req","cmd_id":"054e5472-e5e9-4071-92d7-1ce3bac61956","cmd":{"type":"default_camera_look_at","center":{"x":15,"y":0,"z":0},"up":{"x":0,"y":0,"z":1},"vantage":{"x":30,"y":30,"z":30}}}
```
</details>

Binary file not shown.

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

Binary file not shown.

View File

@ -0,0 +1,189 @@
v 0 -4 0
v 0 0 0
v 0 -4 -1
v 0 0 -1
v 3.0950184 -4 -1
v 3.0950184 0 -1
v 5.9513144 -4 -3
v 5.9513144 0 -3
v 9.5 -4 -3
v 9.5 0 -3
v 9.5 -4 -2.5
v 9.5 0 -2.5
v 6.108964 -4 -2.5
v 6.108964 0 -2.5
v 3.4311862 -4 -0.625
v 4.323779 -4 -1.25
v 4.323779 -0 -1.25
v 3.4311862 -0 -0.625
v 2.5385938 0 0
v 2.5385938 -4 0
v 3.342784 -4 0.375
v 4.146974 -4 0.75
v 3.342784 -0 0.375
v 4.146974 -0 0.75
v 5.755354 0 1.5
v 5.755354 -4 1.5
v 9.5 -4 1.5
v 9.5 0 1.5
v 9.5 -4 2
v 9.5 0 2
v 5.644507 -4 2
v 5.644507 0 2
v 3.5 -4 1
v 3.5 0 1
v 0 -4 1
v 0 0 1
vt 0.0127 -0.0508
vt 0.0127 0.0508
vt -0.0127 -0.0508
vt -0.0127 0.0508
vt -0.039306734 0.0508
vt -0.039306734 -0.0508
vt 0.039306734 0.0508
vt 0.039306734 -0.0508
vt -0.04428355 0.0508
vt -0.04428355 -0.0508
vt 0.04428355 0.0508
vt 0.04428355 -0.0508
vt -0.045068305 0.0508
vt -0.045068305 -0.0508
vt 0.045068305 0.0508
vt 0.045068305 -0.0508
vt -0.00635 0.0508
vt -0.00635 -0.0508
vt 0.00635 0.0508
vt 0.00635 -0.0508
vt 0.04306616 -0.0508
vt 0.04306616 0.0508
vt -0.04306616 -0.0508
vt -0.04306616 0.0508
vt -0.027677217 -0.0508
vt 0.000000000000000048572257 -0.0508
vt 0.000000000000000048572257 0.0508
vt 0.055354435 -0.0508
vt 0.055354435 0.0508
vt -0.027677217 0.0508
vt -0.055354435 0.0508
vt -0.055354435 -0.0508
vt -0.02253807 0.0508
vt -0.04507614 0.0508
vt -0.04507614 -0.0508
vt 0.00000000000000005551115 0.0508
vt -0.02253807 -0.0508
vt 0.00000000000000005551115 -0.0508
vt 0.04507614 -0.0508
vt 0.04507614 0.0508
vt -0.047557 0.0508
vt -0.047557 -0.0508
vt 0.047557 0.0508
vt 0.047557 -0.0508
vt 0.04896476 -0.0508
vt 0.04896476 0.0508
vt -0.04896476 -0.0508
vt -0.04896476 0.0508
vt 0.03005076 -0.0508
vt 0.03005076 0.0508
vt -0.03005076 -0.0508
vt -0.03005076 0.0508
vt 0.04445 -0.0508
vt 0.04445 0.0508
vt -0.04445 -0.0508
vt -0.04445 0.0508
vt 0.08490671 0.009525
vt 0.06448028 0
vt 0.0889 0.0254
vt 0.08715213 -0.015875
vt 0.10982399 -0.03175
vt 0.07861347 -0.0254
vt 0.10533314 0.01905
vt 0.15116338 -0.0762
vt 0 -0.0254
vt 0 0
vt 0.2413 -0.0762
vt 0.15516768 -0.0635
vt 0.2413 -0.0635
vt 0.14337048 0.0508
vt 0.146186 0.0381
vt 0.2413 0.0381
vt 0.2413 0.0508
vt 0 0.0254
vn -1 -0 0
vn 0 -0 -1
vn -0.57357645 -0 -0.81915206
vn 1 -0 0
vn 0 -0 1
vn 0.57357645 -0 0.81915206
vn 0.42261827 -0 -0.9063078
vn -0.42261827 -0 0.9063078
vn -0 1 -0
vn 0 -1 0
o Unnamed-0
f 1/1/1 2/2/1 3/3/1
f 3/3/1 2/2/1 4/4/1
f 3/5/2 4/6/2 5/7/2
f 5/7/2 4/6/2 6/8/2
f 5/9/3 6/10/3 7/11/3
f 7/11/3 6/10/3 8/12/3
f 7/13/2 8/14/2 9/15/2
f 9/15/2 8/14/2 10/16/2
f 9/17/4 10/18/4 11/19/4
f 11/19/4 10/18/4 12/20/4
f 11/21/5 12/22/5 13/23/5
f 13/23/5 12/22/5 14/24/5
f 15/25/6 16/26/6 17/27/6
f 16/26/6 13/28/6 14/29/6
f 18/30/6 19/31/6 20/32/6
f 15/25/6 18/30/6 20/32/6
f 16/26/6 14/29/6 17/27/6
f 18/30/6 15/25/6 17/27/6
f 21/33/7 20/34/7 19/35/7
f 22/36/7 21/33/7 23/37/7
f 23/37/7 24/38/7 22/36/7
f 24/38/7 25/39/7 26/40/7
f 21/33/7 19/35/7 23/37/7
f 26/40/7 22/36/7 24/38/7
f 26/41/2 25/42/2 27/43/2
f 27/43/2 25/42/2 28/44/2
f 27/17/4 28/18/4 29/19/4
f 29/19/4 28/18/4 30/20/4
f 29/45/5 30/46/5 31/47/5
f 31/47/5 30/46/5 32/48/5
f 31/49/8 32/50/8 33/51/8
f 33/51/8 32/50/8 34/52/8
f 33/53/5 34/54/5 35/55/5
f 35/55/5 34/54/5 36/56/5
f 35/1/1 36/2/1 1/3/1
f 1/3/1 36/2/1 2/4/1
f 23/57/9 19/58/9 34/59/9
f 18/60/9 17/61/9 6/62/9
f 23/57/9 34/59/9 24/63/9
f 17/61/9 8/64/9 6/62/9
f 4/65/9 19/58/9 6/62/9
f 4/65/9 2/66/9 19/58/9
f 10/67/9 14/68/9 12/69/9
f 10/67/9 8/64/9 14/68/9
f 8/64/9 17/61/9 14/68/9
f 32/70/9 25/71/9 24/63/9
f 6/62/9 19/58/9 18/60/9
f 24/63/9 34/59/9 32/70/9
f 28/72/9 25/71/9 30/73/9
f 25/71/9 32/70/9 30/73/9
f 19/58/9 2/66/9 36/74/9
f 34/59/9 19/58/9 36/74/9
f 21/57/10 33/59/10 20/58/10
f 22/63/10 33/59/10 21/57/10
f 15/60/10 5/62/10 16/61/10
f 22/63/10 26/71/10 31/70/10
f 35/74/10 20/58/10 33/59/10
f 35/74/10 1/66/10 20/58/10
f 31/70/10 26/71/10 29/73/10
f 29/73/10 26/71/10 27/72/10
f 22/63/10 31/70/10 33/59/10
f 20/58/10 5/62/10 15/60/10
f 16/61/10 5/62/10 7/64/10
f 13/68/10 16/61/10 7/64/10
f 11/69/10 13/68/10 9/67/10
f 13/68/10 7/64/10 9/67/10
f 20/58/10 3/65/10 5/62/10
f 3/65/10 20/58/10 1/66/10

View File

@ -0,0 +1,282 @@
ply
format ascii 1.0
comment Generated by kittycad.io
element vertex 204
property float x
property float y
property float z
element face 68
property list uchar uint vertex_indices
end_header
0 0 4
0 0 0
0 -1 4
0 -1 4
0 0 0
0 -1 0
0 -1 4
0 -1 0
3.0950184 -1 4
3.0950184 -1 4
0 -1 0
3.0950184 -1 0
3.0950184 -1 4
3.0950184 -1 0
5.9513144 -3 4
5.9513144 -3 4
3.0950184 -1 0
5.9513144 -3 0
5.9513144 -3 4
5.9513144 -3 0
9.5 -3 4
9.5 -3 4
5.9513144 -3 0
9.5 -3 0
9.5 -3 4
9.5 -3 0
9.5 -2.5 4
9.5 -2.5 4
9.5 -3 0
9.5 -2.5 0
9.5 -2.5 4
9.5 -2.5 0
6.108964 -2.5 4
6.108964 -2.5 4
9.5 -2.5 0
6.108964 -2.5 0
3.4311862 -0.625 4
4.323779 -1.25 4
4.323779 -1.25 0
4.323779 -1.25 4
6.108964 -2.5 4
6.108964 -2.5 0
3.4311862 -0.625 0
2.5385938 0 0
2.5385938 0 4
3.4311862 -0.625 4
3.4311862 -0.625 0
2.5385938 0 4
4.323779 -1.25 4
6.108964 -2.5 0
4.323779 -1.25 0
3.4311862 -0.625 0
3.4311862 -0.625 4
4.323779 -1.25 0
3.342784 0.375 4
2.5385938 0 4
2.5385938 0 0
4.146974 0.75 4
3.342784 0.375 4
3.342784 0.375 0
3.342784 0.375 0
4.146974 0.75 0
4.146974 0.75 4
4.146974 0.75 0
5.755354 1.5 0
5.755354 1.5 4
3.342784 0.375 4
2.5385938 0 0
3.342784 0.375 0
5.755354 1.5 4
4.146974 0.75 4
4.146974 0.75 0
5.755354 1.5 4
5.755354 1.5 0
9.5 1.5 4
9.5 1.5 4
5.755354 1.5 0
9.5 1.5 0
9.5 1.5 4
9.5 1.5 0
9.5 2 4
9.5 2 4
9.5 1.5 0
9.5 2 0
9.5 2 4
9.5 2 0
5.644507 2 4
5.644507 2 4
9.5 2 0
5.644507 2 0
5.644507 2 4
5.644507 2 0
3.5 1 4
3.5 1 4
5.644507 2 0
3.5 1 0
3.5 1 4
3.5 1 0
0 1 4
0 1 4
3.5 1 0
0 1 0
0 1 4
0 1 0
0 0 4
0 0 4
0 1 0
0 0 0
3.342784 0.375 0
2.5385938 0 0
3.5 1 0
3.4311862 -0.625 0
4.323779 -1.25 0
3.0950184 -1 0
3.342784 0.375 0
3.5 1 0
4.146974 0.75 0
4.323779 -1.25 0
5.9513144 -3 0
3.0950184 -1 0
0 -1 0
2.5385938 0 0
3.0950184 -1 0
0 -1 0
0 0 0
2.5385938 0 0
9.5 -3 0
6.108964 -2.5 0
9.5 -2.5 0
9.5 -3 0
5.9513144 -3 0
6.108964 -2.5 0
5.9513144 -3 0
4.323779 -1.25 0
6.108964 -2.5 0
5.644507 2 0
5.755354 1.5 0
4.146974 0.75 0
3.0950184 -1 0
2.5385938 0 0
3.4311862 -0.625 0
4.146974 0.75 0
3.5 1 0
5.644507 2 0
9.5 1.5 0
5.755354 1.5 0
9.5 2 0
5.755354 1.5 0
5.644507 2 0
9.5 2 0
2.5385938 0 0
0 0 0
0 1 0
3.5 1 0
2.5385938 0 0
0 1 0
3.342784 0.375 4
3.5 1 4
2.5385938 0 4
4.146974 0.75 4
3.5 1 4
3.342784 0.375 4
3.4311862 -0.625 4
3.0950184 -1 4
4.323779 -1.25 4
4.146974 0.75 4
5.755354 1.5 4
5.644507 2 4
0 1 4
2.5385938 0 4
3.5 1 4
0 1 4
0 0 4
2.5385938 0 4
5.644507 2 4
5.755354 1.5 4
9.5 2 4
9.5 2 4
5.755354 1.5 4
9.5 1.5 4
4.146974 0.75 4
5.644507 2 4
3.5 1 4
2.5385938 0 4
3.0950184 -1 4
3.4311862 -0.625 4
4.323779 -1.25 4
3.0950184 -1 4
5.9513144 -3 4
6.108964 -2.5 4
4.323779 -1.25 4
5.9513144 -3 4
9.5 -2.5 4
6.108964 -2.5 4
9.5 -3 4
6.108964 -2.5 4
5.9513144 -3 4
9.5 -3 4
2.5385938 0 4
0 -1 4
3.0950184 -1 4
0 -1 4
2.5385938 0 4
0 0 4
3 0 1 2
3 3 4 5
3 6 7 8
3 9 10 11
3 12 13 14
3 15 16 17
3 18 19 20
3 21 22 23
3 24 25 26
3 27 28 29
3 30 31 32
3 33 34 35
3 36 37 38
3 39 40 41
3 42 43 44
3 45 46 47
3 48 49 50
3 51 52 53
3 54 55 56
3 57 58 59
3 60 61 62
3 63 64 65
3 66 67 68
3 69 70 71
3 72 73 74
3 75 76 77
3 78 79 80
3 81 82 83
3 84 85 86
3 87 88 89
3 90 91 92
3 93 94 95
3 96 97 98
3 99 100 101
3 102 103 104
3 105 106 107
3 108 109 110
3 111 112 113
3 114 115 116
3 117 118 119
3 120 121 122
3 123 124 125
3 126 127 128
3 129 130 131
3 132 133 134
3 135 136 137
3 138 139 140
3 141 142 143
3 144 145 146
3 147 148 149
3 150 151 152
3 153 154 155
3 156 157 158
3 159 160 161
3 162 163 164
3 165 166 167
3 168 169 170
3 171 172 173
3 174 175 176
3 177 178 179
3 180 181 182
3 183 184 185
3 186 187 188
3 189 190 191
3 192 193 194
3 195 196 197
3 198 199 200
3 201 202 203

View File

@ -0,0 +1,494 @@
ISO-10303-21;
HEADER;
FILE_DESCRIPTION((('kittycad.io export')), '2;1');
FILE_NAME('dump.step', '1970-01-01T00:00:00.0+00:00', ('Author unknown'), ('Organization unknown'), 'kittycad.io beta', 'kittycad.io', 'Authorization unknown');
FILE_SCHEMA(('AP203_CONFIGURATION_CONTROLLED_3D_DESIGN_OF_MECHANICAL_PARTS_AND_ASSEMBLIES_MIM_LF'));
ENDSEC;
DATA;
#1 = (
LENGTH_UNIT()
NAMED_UNIT(*)
SI_UNIT($, .METRE.)
);
#2 = UNCERTAINTY_MEASURE_WITH_UNIT(0.00001, #1, 'DISTANCE_ACCURACY_VALUE', $);
#3 = (
GEOMETRIC_REPRESENTATION_CONTEXT(3)
GLOBAL_UNCERTAINTY_ASSIGNED_CONTEXT((#2))
GLOBAL_UNIT_ASSIGNED_CONTEXT((#1))
REPRESENTATION_CONTEXT('', '3D')
);
#4 = CARTESIAN_POINT('NONE', (0, 0, -0));
#5 = VERTEX_POINT('NONE', #4);
#6 = CARTESIAN_POINT('NONE', (0, -0.0254, -0));
#7 = VERTEX_POINT('NONE', #6);
#8 = CARTESIAN_POINT('NONE', (0, -0.0254, 0.1016));
#9 = VERTEX_POINT('NONE', #8);
#10 = CARTESIAN_POINT('NONE', (0, 0, 0.1016));
#11 = VERTEX_POINT('NONE', #10);
#12 = CARTESIAN_POINT('NONE', (0.07861346939195568, -0.0254, -0));
#13 = VERTEX_POINT('NONE', #12);
#14 = CARTESIAN_POINT('NONE', (0.07861346939195568, -0.0254, 0.1016));
#15 = VERTEX_POINT('NONE', #14);
#16 = CARTESIAN_POINT('NONE', (0.1511633881344551, -0.07619999999999998, -0));
#17 = VERTEX_POINT('NONE', #16);
#18 = CARTESIAN_POINT('NONE', (0.1511633881344551, -0.07619999999999998, 0.1016));
#19 = VERTEX_POINT('NONE', #18);
#20 = CARTESIAN_POINT('NONE', (0.2413, -0.0762, -0));
#21 = VERTEX_POINT('NONE', #20);
#22 = CARTESIAN_POINT('NONE', (0.2413, -0.0762, 0.1016));
#23 = VERTEX_POINT('NONE', #22);
#24 = CARTESIAN_POINT('NONE', (0.2413, -0.0635, -0));
#25 = VERTEX_POINT('NONE', #24);
#26 = CARTESIAN_POINT('NONE', (0.2413, -0.0635, 0.1016));
#27 = VERTEX_POINT('NONE', #26);
#28 = CARTESIAN_POINT('NONE', (0.1551676827532182, -0.0635, -0));
#29 = VERTEX_POINT('NONE', #28);
#30 = CARTESIAN_POINT('NONE', (0.1551676827532182, -0.0635, 0.1016));
#31 = VERTEX_POINT('NONE', #30);
#32 = CARTESIAN_POINT('NONE', (0.06448028432509392, 0, -0));
#33 = VERTEX_POINT('NONE', #32);
#34 = CARTESIAN_POINT('NONE', (0.06448028432509392, 0, 0.1016));
#35 = VERTEX_POINT('NONE', #34);
#36 = CARTESIAN_POINT('NONE', (0.14618599799650817, 0.03810000000000001, -0));
#37 = VERTEX_POINT('NONE', #36);
#38 = CARTESIAN_POINT('NONE', (0.14618599799650817, 0.03810000000000001, 0.1016));
#39 = VERTEX_POINT('NONE', #38);
#40 = CARTESIAN_POINT('NONE', (0.2413, 0.0381, -0));
#41 = VERTEX_POINT('NONE', #40);
#42 = CARTESIAN_POINT('NONE', (0.2413, 0.0381, 0.1016));
#43 = VERTEX_POINT('NONE', #42);
#44 = CARTESIAN_POINT('NONE', (0.2413, 0.0508, -0));
#45 = VERTEX_POINT('NONE', #44);
#46 = CARTESIAN_POINT('NONE', (0.2413, 0.0508, 0.1016));
#47 = VERTEX_POINT('NONE', #46);
#48 = CARTESIAN_POINT('NONE', (0.14337047578094278, 0.0508, -0));
#49 = VERTEX_POINT('NONE', #48);
#50 = CARTESIAN_POINT('NONE', (0.14337047578094278, 0.0508, 0.1016));
#51 = VERTEX_POINT('NONE', #50);
#52 = CARTESIAN_POINT('NONE', (0.08889999999999999, 0.0254, -0));
#53 = VERTEX_POINT('NONE', #52);
#54 = CARTESIAN_POINT('NONE', (0.08889999999999999, 0.0254, 0.1016));
#55 = VERTEX_POINT('NONE', #54);
#56 = CARTESIAN_POINT('NONE', (0, 0.0254, -0));
#57 = VERTEX_POINT('NONE', #56);
#58 = CARTESIAN_POINT('NONE', (0, 0.0254, 0.1016));
#59 = VERTEX_POINT('NONE', #58);
#60 = DIRECTION('NONE', (0, -1, 0));
#61 = VECTOR('NONE', #60, 0.0254);
#62 = CARTESIAN_POINT('NONE', (0, 0, -0));
#63 = LINE('NONE', #62, #61);
#64 = DIRECTION('NONE', (0, 0, 1));
#65 = VECTOR('NONE', #64, 0.1016);
#66 = CARTESIAN_POINT('NONE', (0, -0.0254, -0));
#67 = LINE('NONE', #66, #65);
#68 = DIRECTION('NONE', (0, -1, 0));
#69 = VECTOR('NONE', #68, 0.0254);
#70 = CARTESIAN_POINT('NONE', (0, 0, 0.1016));
#71 = LINE('NONE', #70, #69);
#72 = DIRECTION('NONE', (0, 0, 1));
#73 = VECTOR('NONE', #72, 0.1016);
#74 = CARTESIAN_POINT('NONE', (0, 0, -0));
#75 = LINE('NONE', #74, #73);
#76 = DIRECTION('NONE', (1, 0, 0));
#77 = VECTOR('NONE', #76, 0.07861346939195568);
#78 = CARTESIAN_POINT('NONE', (0, -0.0254, -0));
#79 = LINE('NONE', #78, #77);
#80 = DIRECTION('NONE', (0, 0, 1));
#81 = VECTOR('NONE', #80, 0.1016);
#82 = CARTESIAN_POINT('NONE', (0.07861346939195568, -0.0254, -0));
#83 = LINE('NONE', #82, #81);
#84 = DIRECTION('NONE', (1, 0, 0));
#85 = VECTOR('NONE', #84, 0.07861346939195568);
#86 = CARTESIAN_POINT('NONE', (0, -0.0254, 0.1016));
#87 = LINE('NONE', #86, #85);
#88 = DIRECTION('NONE', (0.8191520442889919, -0.5735764363510459, 0));
#89 = VECTOR('NONE', #88, 0.08856709721755177);
#90 = CARTESIAN_POINT('NONE', (0.07861346939195568, -0.0254, -0));
#91 = LINE('NONE', #90, #89);
#92 = DIRECTION('NONE', (0, 0, 1));
#93 = VECTOR('NONE', #92, 0.1016);
#94 = CARTESIAN_POINT('NONE', (0.1511633881344551, -0.07619999999999998, -0));
#95 = LINE('NONE', #94, #93);
#96 = DIRECTION('NONE', (0.8191520442889919, -0.5735764363510459, 0));
#97 = VECTOR('NONE', #96, 0.08856709721755177);
#98 = CARTESIAN_POINT('NONE', (0.07861346939195568, -0.0254, 0.1016));
#99 = LINE('NONE', #98, #97);
#100 = DIRECTION('NONE', (1, -0.0000000000000003079278779307945, 0));
#101 = VECTOR('NONE', #100, 0.09013661186554489);
#102 = CARTESIAN_POINT('NONE', (0.1511633881344551, -0.07619999999999998, -0));
#103 = LINE('NONE', #102, #101);
#104 = DIRECTION('NONE', (0, 0, 1));
#105 = VECTOR('NONE', #104, 0.1016);
#106 = CARTESIAN_POINT('NONE', (0.2413, -0.0762, -0));
#107 = LINE('NONE', #106, #105);
#108 = DIRECTION('NONE', (1, -0.0000000000000003079278779307945, 0));
#109 = VECTOR('NONE', #108, 0.09013661186554489);
#110 = CARTESIAN_POINT('NONE', (0.1511633881344551, -0.07619999999999998, 0.1016));
#111 = LINE('NONE', #110, #109);
#112 = DIRECTION('NONE', (0, 1, 0));
#113 = VECTOR('NONE', #112, 0.012700000000000003);
#114 = CARTESIAN_POINT('NONE', (0.2413, -0.0762, -0));
#115 = LINE('NONE', #114, #113);
#116 = DIRECTION('NONE', (0, 0, 1));
#117 = VECTOR('NONE', #116, 0.1016);
#118 = CARTESIAN_POINT('NONE', (0.2413, -0.0635, -0));
#119 = LINE('NONE', #118, #117);
#120 = DIRECTION('NONE', (0, 1, 0));
#121 = VECTOR('NONE', #120, 0.012700000000000003);
#122 = CARTESIAN_POINT('NONE', (0.2413, -0.0762, 0.1016));
#123 = LINE('NONE', #122, #121);
#124 = DIRECTION('NONE', (-1, 0, 0));
#125 = VECTOR('NONE', #124, 0.08613231724678178);
#126 = CARTESIAN_POINT('NONE', (0.2413, -0.0635, -0));
#127 = LINE('NONE', #126, #125);
#128 = DIRECTION('NONE', (0, 0, 1));
#129 = VECTOR('NONE', #128, 0.1016);
#130 = CARTESIAN_POINT('NONE', (0.1551676827532182, -0.0635, -0));
#131 = LINE('NONE', #130, #129);
#132 = DIRECTION('NONE', (-1, 0, 0));
#133 = VECTOR('NONE', #132, 0.08613231724678178);
#134 = CARTESIAN_POINT('NONE', (0.2413, -0.0635, 0.1016));
#135 = LINE('NONE', #134, #133);
#136 = DIRECTION('NONE', (-0.8191520442889918, 0.573576436351046, 0));
#137 = VECTOR('NONE', #136, 0.11070887152193974);
#138 = CARTESIAN_POINT('NONE', (0.1551676827532182, -0.0635, -0));
#139 = LINE('NONE', #138, #137);
#140 = DIRECTION('NONE', (0, 0, 1));
#141 = VECTOR('NONE', #140, 0.1016);
#142 = CARTESIAN_POINT('NONE', (0.06448028432509392, 0, -0));
#143 = LINE('NONE', #142, #141);
#144 = DIRECTION('NONE', (-0.8191520442889918, 0.573576436351046, 0));
#145 = VECTOR('NONE', #144, 0.11070887152193974);
#146 = CARTESIAN_POINT('NONE', (0.1551676827532182, -0.0635, 0.1016));
#147 = LINE('NONE', #146, #145);
#148 = DIRECTION('NONE', (0.90630778703665, 0.4226182617406993, 0));
#149 = VECTOR('NONE', #148, 0.09015228031811025);
#150 = CARTESIAN_POINT('NONE', (0.06448028432509392, 0, -0));
#151 = LINE('NONE', #150, #149);
#152 = DIRECTION('NONE', (0, 0, 1));
#153 = VECTOR('NONE', #152, 0.1016);
#154 = CARTESIAN_POINT('NONE', (0.14618599799650817, 0.03810000000000001, -0));
#155 = LINE('NONE', #154, #153);
#156 = DIRECTION('NONE', (0.90630778703665, 0.4226182617406993, 0));
#157 = VECTOR('NONE', #156, 0.09015228031811025);
#158 = CARTESIAN_POINT('NONE', (0.06448028432509392, 0, 0.1016));
#159 = LINE('NONE', #158, #157);
#160 = DIRECTION('NONE', (1, -0.00000000000000007295344279228718, 0));
#161 = VECTOR('NONE', #160, 0.09511400200349182);
#162 = CARTESIAN_POINT('NONE', (0.14618599799650817, 0.03810000000000001, -0));
#163 = LINE('NONE', #162, #161);
#164 = DIRECTION('NONE', (0, 0, 1));
#165 = VECTOR('NONE', #164, 0.1016);
#166 = CARTESIAN_POINT('NONE', (0.2413, 0.0381, -0));
#167 = LINE('NONE', #166, #165);
#168 = DIRECTION('NONE', (1, -0.00000000000000007295344279228718, 0));
#169 = VECTOR('NONE', #168, 0.09511400200349182);
#170 = CARTESIAN_POINT('NONE', (0.14618599799650817, 0.03810000000000001, 0.1016));
#171 = LINE('NONE', #170, #169);
#172 = DIRECTION('NONE', (0, 1, 0));
#173 = VECTOR('NONE', #172, 0.012699999999999996);
#174 = CARTESIAN_POINT('NONE', (0.2413, 0.0381, -0));
#175 = LINE('NONE', #174, #173);
#176 = DIRECTION('NONE', (0, 0, 1));
#177 = VECTOR('NONE', #176, 0.1016);
#178 = CARTESIAN_POINT('NONE', (0.2413, 0.0508, -0));
#179 = LINE('NONE', #178, #177);
#180 = DIRECTION('NONE', (0, 1, 0));
#181 = VECTOR('NONE', #180, 0.012699999999999996);
#182 = CARTESIAN_POINT('NONE', (0.2413, 0.0381, 0.1016));
#183 = LINE('NONE', #182, #181);
#184 = DIRECTION('NONE', (-1, 0, 0));
#185 = VECTOR('NONE', #184, 0.0979295242190572);
#186 = CARTESIAN_POINT('NONE', (0.2413, 0.0508, -0));
#187 = LINE('NONE', #186, #185);
#188 = DIRECTION('NONE', (0, 0, 1));
#189 = VECTOR('NONE', #188, 0.1016);
#190 = CARTESIAN_POINT('NONE', (0.14337047578094278, 0.0508, -0));
#191 = LINE('NONE', #190, #189);
#192 = DIRECTION('NONE', (-1, 0, 0));
#193 = VECTOR('NONE', #192, 0.0979295242190572);
#194 = CARTESIAN_POINT('NONE', (0.2413, 0.0508, 0.1016));
#195 = LINE('NONE', #194, #193);
#196 = DIRECTION('NONE', (-0.9063077870366499, -0.42261826174069944, 0));
#197 = VECTOR('NONE', #196, 0.06010152021207346);
#198 = CARTESIAN_POINT('NONE', (0.14337047578094278, 0.0508, -0));
#199 = LINE('NONE', #198, #197);
#200 = DIRECTION('NONE', (0, 0, 1));
#201 = VECTOR('NONE', #200, 0.1016);
#202 = CARTESIAN_POINT('NONE', (0.08889999999999999, 0.0254, -0));
#203 = LINE('NONE', #202, #201);
#204 = DIRECTION('NONE', (-0.9063077870366499, -0.42261826174069944, 0));
#205 = VECTOR('NONE', #204, 0.06010152021207346);
#206 = CARTESIAN_POINT('NONE', (0.14337047578094278, 0.0508, 0.1016));
#207 = LINE('NONE', #206, #205);
#208 = DIRECTION('NONE', (-1, 0, 0));
#209 = VECTOR('NONE', #208, 0.08889999999999999);
#210 = CARTESIAN_POINT('NONE', (0.08889999999999999, 0.0254, -0));
#211 = LINE('NONE', #210, #209);
#212 = DIRECTION('NONE', (0, 0, 1));
#213 = VECTOR('NONE', #212, 0.1016);
#214 = CARTESIAN_POINT('NONE', (0, 0.0254, -0));
#215 = LINE('NONE', #214, #213);
#216 = DIRECTION('NONE', (-1, 0, 0));
#217 = VECTOR('NONE', #216, 0.08889999999999999);
#218 = CARTESIAN_POINT('NONE', (0.08889999999999999, 0.0254, 0.1016));
#219 = LINE('NONE', #218, #217);
#220 = DIRECTION('NONE', (0, -1, 0));
#221 = VECTOR('NONE', #220, 0.0254);
#222 = CARTESIAN_POINT('NONE', (0, 0.0254, -0));
#223 = LINE('NONE', #222, #221);
#224 = DIRECTION('NONE', (0, -1, 0));
#225 = VECTOR('NONE', #224, 0.0254);
#226 = CARTESIAN_POINT('NONE', (0, 0.0254, 0.1016));
#227 = LINE('NONE', #226, #225);
#228 = EDGE_CURVE('NONE', #5, #7, #63, .T.);
#229 = EDGE_CURVE('NONE', #7, #9, #67, .T.);
#230 = EDGE_CURVE('NONE', #11, #9, #71, .T.);
#231 = EDGE_CURVE('NONE', #5, #11, #75, .T.);
#232 = EDGE_CURVE('NONE', #7, #13, #79, .T.);
#233 = EDGE_CURVE('NONE', #13, #15, #83, .T.);
#234 = EDGE_CURVE('NONE', #9, #15, #87, .T.);
#235 = EDGE_CURVE('NONE', #13, #17, #91, .T.);
#236 = EDGE_CURVE('NONE', #17, #19, #95, .T.);
#237 = EDGE_CURVE('NONE', #15, #19, #99, .T.);
#238 = EDGE_CURVE('NONE', #17, #21, #103, .T.);
#239 = EDGE_CURVE('NONE', #21, #23, #107, .T.);
#240 = EDGE_CURVE('NONE', #19, #23, #111, .T.);
#241 = EDGE_CURVE('NONE', #21, #25, #115, .T.);
#242 = EDGE_CURVE('NONE', #25, #27, #119, .T.);
#243 = EDGE_CURVE('NONE', #23, #27, #123, .T.);
#244 = EDGE_CURVE('NONE', #25, #29, #127, .T.);
#245 = EDGE_CURVE('NONE', #29, #31, #131, .T.);
#246 = EDGE_CURVE('NONE', #27, #31, #135, .T.);
#247 = EDGE_CURVE('NONE', #29, #33, #139, .T.);
#248 = EDGE_CURVE('NONE', #33, #35, #143, .T.);
#249 = EDGE_CURVE('NONE', #31, #35, #147, .T.);
#250 = EDGE_CURVE('NONE', #33, #37, #151, .T.);
#251 = EDGE_CURVE('NONE', #37, #39, #155, .T.);
#252 = EDGE_CURVE('NONE', #35, #39, #159, .T.);
#253 = EDGE_CURVE('NONE', #37, #41, #163, .T.);
#254 = EDGE_CURVE('NONE', #41, #43, #167, .T.);
#255 = EDGE_CURVE('NONE', #39, #43, #171, .T.);
#256 = EDGE_CURVE('NONE', #41, #45, #175, .T.);
#257 = EDGE_CURVE('NONE', #45, #47, #179, .T.);
#258 = EDGE_CURVE('NONE', #43, #47, #183, .T.);
#259 = EDGE_CURVE('NONE', #45, #49, #187, .T.);
#260 = EDGE_CURVE('NONE', #49, #51, #191, .T.);
#261 = EDGE_CURVE('NONE', #47, #51, #195, .T.);
#262 = EDGE_CURVE('NONE', #49, #53, #199, .T.);
#263 = EDGE_CURVE('NONE', #53, #55, #203, .T.);
#264 = EDGE_CURVE('NONE', #51, #55, #207, .T.);
#265 = EDGE_CURVE('NONE', #53, #57, #211, .T.);
#266 = EDGE_CURVE('NONE', #57, #59, #215, .T.);
#267 = EDGE_CURVE('NONE', #55, #59, #219, .T.);
#268 = EDGE_CURVE('NONE', #57, #5, #223, .T.);
#269 = EDGE_CURVE('NONE', #59, #11, #227, .T.);
#270 = ORIENTED_EDGE('NONE', *, *, #228, .T.);
#271 = ORIENTED_EDGE('NONE', *, *, #229, .T.);
#272 = ORIENTED_EDGE('NONE', *, *, #230, .F.);
#273 = ORIENTED_EDGE('NONE', *, *, #231, .F.);
#274 = EDGE_LOOP('NONE', (#270, #271, #272, #273));
#275 = ORIENTED_EDGE('NONE', *, *, #232, .T.);
#276 = ORIENTED_EDGE('NONE', *, *, #233, .T.);
#277 = ORIENTED_EDGE('NONE', *, *, #234, .F.);
#278 = ORIENTED_EDGE('NONE', *, *, #229, .F.);
#279 = EDGE_LOOP('NONE', (#275, #276, #277, #278));
#280 = ORIENTED_EDGE('NONE', *, *, #235, .T.);
#281 = ORIENTED_EDGE('NONE', *, *, #236, .T.);
#282 = ORIENTED_EDGE('NONE', *, *, #237, .F.);
#283 = ORIENTED_EDGE('NONE', *, *, #233, .F.);
#284 = EDGE_LOOP('NONE', (#280, #281, #282, #283));
#285 = ORIENTED_EDGE('NONE', *, *, #238, .T.);
#286 = ORIENTED_EDGE('NONE', *, *, #239, .T.);
#287 = ORIENTED_EDGE('NONE', *, *, #240, .F.);
#288 = ORIENTED_EDGE('NONE', *, *, #236, .F.);
#289 = EDGE_LOOP('NONE', (#285, #286, #287, #288));
#290 = ORIENTED_EDGE('NONE', *, *, #241, .T.);
#291 = ORIENTED_EDGE('NONE', *, *, #242, .T.);
#292 = ORIENTED_EDGE('NONE', *, *, #243, .F.);
#293 = ORIENTED_EDGE('NONE', *, *, #239, .F.);
#294 = EDGE_LOOP('NONE', (#290, #291, #292, #293));
#295 = ORIENTED_EDGE('NONE', *, *, #244, .T.);
#296 = ORIENTED_EDGE('NONE', *, *, #245, .T.);
#297 = ORIENTED_EDGE('NONE', *, *, #246, .F.);
#298 = ORIENTED_EDGE('NONE', *, *, #242, .F.);
#299 = EDGE_LOOP('NONE', (#295, #296, #297, #298));
#300 = ORIENTED_EDGE('NONE', *, *, #247, .T.);
#301 = ORIENTED_EDGE('NONE', *, *, #248, .T.);
#302 = ORIENTED_EDGE('NONE', *, *, #249, .F.);
#303 = ORIENTED_EDGE('NONE', *, *, #245, .F.);
#304 = EDGE_LOOP('NONE', (#300, #301, #302, #303));
#305 = ORIENTED_EDGE('NONE', *, *, #250, .T.);
#306 = ORIENTED_EDGE('NONE', *, *, #251, .T.);
#307 = ORIENTED_EDGE('NONE', *, *, #252, .F.);
#308 = ORIENTED_EDGE('NONE', *, *, #248, .F.);
#309 = EDGE_LOOP('NONE', (#305, #306, #307, #308));
#310 = ORIENTED_EDGE('NONE', *, *, #253, .T.);
#311 = ORIENTED_EDGE('NONE', *, *, #254, .T.);
#312 = ORIENTED_EDGE('NONE', *, *, #255, .F.);
#313 = ORIENTED_EDGE('NONE', *, *, #251, .F.);
#314 = EDGE_LOOP('NONE', (#310, #311, #312, #313));
#315 = ORIENTED_EDGE('NONE', *, *, #256, .T.);
#316 = ORIENTED_EDGE('NONE', *, *, #257, .T.);
#317 = ORIENTED_EDGE('NONE', *, *, #258, .F.);
#318 = ORIENTED_EDGE('NONE', *, *, #254, .F.);
#319 = EDGE_LOOP('NONE', (#315, #316, #317, #318));
#320 = ORIENTED_EDGE('NONE', *, *, #259, .T.);
#321 = ORIENTED_EDGE('NONE', *, *, #260, .T.);
#322 = ORIENTED_EDGE('NONE', *, *, #261, .F.);
#323 = ORIENTED_EDGE('NONE', *, *, #257, .F.);
#324 = EDGE_LOOP('NONE', (#320, #321, #322, #323));
#325 = ORIENTED_EDGE('NONE', *, *, #262, .T.);
#326 = ORIENTED_EDGE('NONE', *, *, #263, .T.);
#327 = ORIENTED_EDGE('NONE', *, *, #264, .F.);
#328 = ORIENTED_EDGE('NONE', *, *, #260, .F.);
#329 = EDGE_LOOP('NONE', (#325, #326, #327, #328));
#330 = ORIENTED_EDGE('NONE', *, *, #265, .T.);
#331 = ORIENTED_EDGE('NONE', *, *, #266, .T.);
#332 = ORIENTED_EDGE('NONE', *, *, #267, .F.);
#333 = ORIENTED_EDGE('NONE', *, *, #263, .F.);
#334 = EDGE_LOOP('NONE', (#330, #331, #332, #333));
#335 = ORIENTED_EDGE('NONE', *, *, #268, .T.);
#336 = ORIENTED_EDGE('NONE', *, *, #231, .T.);
#337 = ORIENTED_EDGE('NONE', *, *, #269, .F.);
#338 = ORIENTED_EDGE('NONE', *, *, #266, .F.);
#339 = EDGE_LOOP('NONE', (#335, #336, #337, #338));
#340 = ORIENTED_EDGE('NONE', *, *, #228, .T.);
#341 = ORIENTED_EDGE('NONE', *, *, #232, .T.);
#342 = ORIENTED_EDGE('NONE', *, *, #235, .T.);
#343 = ORIENTED_EDGE('NONE', *, *, #238, .T.);
#344 = ORIENTED_EDGE('NONE', *, *, #241, .T.);
#345 = ORIENTED_EDGE('NONE', *, *, #244, .T.);
#346 = ORIENTED_EDGE('NONE', *, *, #247, .T.);
#347 = ORIENTED_EDGE('NONE', *, *, #250, .T.);
#348 = ORIENTED_EDGE('NONE', *, *, #253, .T.);
#349 = ORIENTED_EDGE('NONE', *, *, #256, .T.);
#350 = ORIENTED_EDGE('NONE', *, *, #259, .T.);
#351 = ORIENTED_EDGE('NONE', *, *, #262, .T.);
#352 = ORIENTED_EDGE('NONE', *, *, #265, .T.);
#353 = ORIENTED_EDGE('NONE', *, *, #268, .T.);
#354 = EDGE_LOOP('NONE', (#340, #341, #342, #343, #344, #345, #346, #347, #348, #349, #350, #351, #352, #353));
#355 = ORIENTED_EDGE('NONE', *, *, #230, .T.);
#356 = ORIENTED_EDGE('NONE', *, *, #234, .T.);
#357 = ORIENTED_EDGE('NONE', *, *, #237, .T.);
#358 = ORIENTED_EDGE('NONE', *, *, #240, .T.);
#359 = ORIENTED_EDGE('NONE', *, *, #243, .T.);
#360 = ORIENTED_EDGE('NONE', *, *, #246, .T.);
#361 = ORIENTED_EDGE('NONE', *, *, #249, .T.);
#362 = ORIENTED_EDGE('NONE', *, *, #252, .T.);
#363 = ORIENTED_EDGE('NONE', *, *, #255, .T.);
#364 = ORIENTED_EDGE('NONE', *, *, #258, .T.);
#365 = ORIENTED_EDGE('NONE', *, *, #261, .T.);
#366 = ORIENTED_EDGE('NONE', *, *, #264, .T.);
#367 = ORIENTED_EDGE('NONE', *, *, #267, .T.);
#368 = ORIENTED_EDGE('NONE', *, *, #269, .T.);
#369 = EDGE_LOOP('NONE', (#355, #356, #357, #358, #359, #360, #361, #362, #363, #364, #365, #366, #367, #368));
#370 = CARTESIAN_POINT('NONE', (0, -0.0127, 0.0508));
#371 = DIRECTION('NONE', (-1, 0, -0));
#372 = AXIS2_PLACEMENT_3D('NONE', #370, #371, $);
#373 = PLANE('NONE', #372);
#374 = CARTESIAN_POINT('NONE', (0.039306734695977924, -0.025399999999999995, 0.0508));
#375 = DIRECTION('NONE', (0, -1, -0));
#376 = AXIS2_PLACEMENT_3D('NONE', #374, #375, $);
#377 = PLANE('NONE', #376);
#378 = CARTESIAN_POINT('NONE', (0.11488842876320533, -0.05079999999999996, 0.05079999999999999));
#379 = DIRECTION('NONE', (-0.5735764363510459, -0.819152044288992, 0));
#380 = AXIS2_PLACEMENT_3D('NONE', #378, #379, $);
#381 = PLANE('NONE', #380);
#382 = CARTESIAN_POINT('NONE', (0.19623169406722757, -0.07619999999999999, 0.0508));
#383 = DIRECTION('NONE', (0, -1, -0));
#384 = AXIS2_PLACEMENT_3D('NONE', #382, #383, $);
#385 = PLANE('NONE', #384);
#386 = CARTESIAN_POINT('NONE', (0.2413, -0.06985, 0.0508));
#387 = DIRECTION('NONE', (1, 0, -0));
#388 = AXIS2_PLACEMENT_3D('NONE', #386, #387, $);
#389 = PLANE('NONE', #388);
#390 = CARTESIAN_POINT('NONE', (0.19823384137660915, -0.0635, 0.0508));
#391 = DIRECTION('NONE', (0, 1, -0));
#392 = AXIS2_PLACEMENT_3D('NONE', #390, #391, $);
#393 = PLANE('NONE', #392);
#394 = CARTESIAN_POINT('NONE', (0.10982398353915601, -0.03174999999999997, 0.0508));
#395 = DIRECTION('NONE', (0.573576436351046, 0.8191520442889918, -0));
#396 = AXIS2_PLACEMENT_3D('NONE', #394, #395, $);
#397 = PLANE('NONE', #396);
#398 = CARTESIAN_POINT('NONE', (0.105333141160801, 0.019049999999999987, 0.0508));
#399 = DIRECTION('NONE', (0.4226182617406993, -0.90630778703665, -0));
#400 = AXIS2_PLACEMENT_3D('NONE', #398, #399, $);
#401 = PLANE('NONE', #400);
#402 = CARTESIAN_POINT('NONE', (0.19374299899825406, 0.0381, 0.0508));
#403 = DIRECTION('NONE', (0, -1, -0));
#404 = AXIS2_PLACEMENT_3D('NONE', #402, #403, $);
#405 = PLANE('NONE', #404);
#406 = CARTESIAN_POINT('NONE', (0.2413, 0.044449999999999996, 0.0508));
#407 = DIRECTION('NONE', (1, 0, -0));
#408 = AXIS2_PLACEMENT_3D('NONE', #406, #407, $);
#409 = PLANE('NONE', #408);
#410 = CARTESIAN_POINT('NONE', (0.19233523789047138, 0.0508, 0.0508));
#411 = DIRECTION('NONE', (0, 1, -0));
#412 = AXIS2_PLACEMENT_3D('NONE', #410, #411, $);
#413 = PLANE('NONE', #412);
#414 = CARTESIAN_POINT('NONE', (0.11613523789047137, 0.0381, 0.05079999999999999));
#415 = DIRECTION('NONE', (-0.42261826174069966, 0.90630778703665, -0));
#416 = AXIS2_PLACEMENT_3D('NONE', #414, #415, $);
#417 = PLANE('NONE', #416);
#418 = CARTESIAN_POINT('NONE', (0.044449999999999996, 0.0254, 0.0508));
#419 = DIRECTION('NONE', (0, 1, -0));
#420 = AXIS2_PLACEMENT_3D('NONE', #418, #419, $);
#421 = PLANE('NONE', #420);
#422 = CARTESIAN_POINT('NONE', (0, 0.0127, 0.0508));
#423 = DIRECTION('NONE', (-1, 0, -0));
#424 = AXIS2_PLACEMENT_3D('NONE', #422, #423, $);
#425 = PLANE('NONE', #424);
#426 = CARTESIAN_POINT('NONE', (0, 0, -0));
#427 = DIRECTION('NONE', (0, 0, 1));
#428 = AXIS2_PLACEMENT_3D('NONE', #426, #427, $);
#429 = PLANE('NONE', #428);
#430 = CARTESIAN_POINT('NONE', (0, 0, 0.1016));
#431 = DIRECTION('NONE', (0, 0, 1));
#432 = AXIS2_PLACEMENT_3D('NONE', #430, #431, $);
#433 = PLANE('NONE', #432);
#434 = FACE_OUTER_BOUND('NONE', #274, .T.);
#435 = ADVANCED_FACE('NONE', (#434), #373, .T.);
#436 = FACE_OUTER_BOUND('NONE', #279, .T.);
#437 = ADVANCED_FACE('NONE', (#436), #377, .T.);
#438 = FACE_OUTER_BOUND('NONE', #284, .T.);
#439 = ADVANCED_FACE('NONE', (#438), #381, .T.);
#440 = FACE_OUTER_BOUND('NONE', #289, .T.);
#441 = ADVANCED_FACE('NONE', (#440), #385, .T.);
#442 = FACE_OUTER_BOUND('NONE', #294, .T.);
#443 = ADVANCED_FACE('NONE', (#442), #389, .T.);
#444 = FACE_OUTER_BOUND('NONE', #299, .T.);
#445 = ADVANCED_FACE('NONE', (#444), #393, .T.);
#446 = FACE_OUTER_BOUND('NONE', #304, .T.);
#447 = ADVANCED_FACE('NONE', (#446), #397, .T.);
#448 = FACE_OUTER_BOUND('NONE', #309, .T.);
#449 = ADVANCED_FACE('NONE', (#448), #401, .T.);
#450 = FACE_OUTER_BOUND('NONE', #314, .T.);
#451 = ADVANCED_FACE('NONE', (#450), #405, .T.);
#452 = FACE_OUTER_BOUND('NONE', #319, .T.);
#453 = ADVANCED_FACE('NONE', (#452), #409, .T.);
#454 = FACE_OUTER_BOUND('NONE', #324, .T.);
#455 = ADVANCED_FACE('NONE', (#454), #413, .T.);
#456 = FACE_OUTER_BOUND('NONE', #329, .T.);
#457 = ADVANCED_FACE('NONE', (#456), #417, .T.);
#458 = FACE_OUTER_BOUND('NONE', #334, .T.);
#459 = ADVANCED_FACE('NONE', (#458), #421, .T.);
#460 = FACE_OUTER_BOUND('NONE', #339, .T.);
#461 = ADVANCED_FACE('NONE', (#460), #425, .T.);
#462 = FACE_OUTER_BOUND('NONE', #354, .T.);
#463 = ADVANCED_FACE('NONE', (#462), #429, .F.);
#464 = FACE_OUTER_BOUND('NONE', #369, .T.);
#465 = ADVANCED_FACE('NONE', (#464), #433, .T.);
#466 = CLOSED_SHELL('NONE', (#435, #437, #439, #441, #443, #445, #447, #449, #451, #453, #455, #457, #459, #461, #463, #465));
#467 = ORIENTED_CLOSED_SHELL('NONE', *, #466, .T.);
#468 = MANIFOLD_SOLID_BREP('NONE', #467);
#469 = APPLICATION_CONTEXT('configuration controlled 3D design of mechanical parts and assemblies');
#470 = PRODUCT_DEFINITION_CONTEXT('part definition', #469, 'design');
#471 = PRODUCT('UNIDENTIFIED_PRODUCT', 'NONE', $, ());
#472 = PRODUCT_DEFINITION_FORMATION('', $, #471);
#473 = PRODUCT_DEFINITION('design', $, #472, #470);
#474 = PRODUCT_DEFINITION_SHAPE('NONE', $, #473);
#475 = ADVANCED_BREP_SHAPE_REPRESENTATION('NONE', (#468), #3);
#476 = SHAPE_DEFINITION_REPRESENTATION(#474, #475);
ENDSEC;
END-ISO-10303-21;

View File

@ -0,0 +1,478 @@
solid unnamed
facet normal -1 0 0
outer loop
vertex 0 -4 0
vertex 0 -0 0
vertex 0 -4 -1
endloop
endfacet
facet normal -1 0 0
outer loop
vertex 0 -4 -1
vertex 0 -0 0
vertex 0 -0 -1
endloop
endfacet
facet normal 0 0 -1
outer loop
vertex 0 -4 -1
vertex 0 -0 -1
vertex 3.0950184 -4 -1
endloop
endfacet
facet normal 0 0 -1
outer loop
vertex 3.0950184 -4 -1
vertex 0 -0 -1
vertex 3.0950184 -0 -1
endloop
endfacet
facet normal -0.57357645 0 -0.81915206
outer loop
vertex 3.0950184 -4 -1
vertex 3.0950184 -0 -1
vertex 5.9513144 -4 -3
endloop
endfacet
facet normal -0.57357645 0 -0.81915206
outer loop
vertex 5.9513144 -4 -3
vertex 3.0950184 -0 -1
vertex 5.9513144 -0 -3
endloop
endfacet
facet normal 0 0 -1
outer loop
vertex 5.9513144 -4 -3
vertex 5.9513144 -0 -3
vertex 9.5 -4 -3
endloop
endfacet
facet normal 0 0 -1
outer loop
vertex 9.5 -4 -3
vertex 5.9513144 -0 -3
vertex 9.5 -0 -3
endloop
endfacet
facet normal 1 0 0
outer loop
vertex 9.5 -4 -3
vertex 9.5 -0 -3
vertex 9.5 -4 -2.5
endloop
endfacet
facet normal 1 -0 0
outer loop
vertex 9.5 -4 -2.5
vertex 9.5 -0 -3
vertex 9.5 -0 -2.5
endloop
endfacet
facet normal 0 -0 0.99999994
outer loop
vertex 9.5 -4 -2.5
vertex 9.5 -0 -2.5
vertex 6.108964 -4 -2.5
endloop
endfacet
facet normal 0 0 0.99999994
outer loop
vertex 6.108964 -4 -2.5
vertex 9.5 -0 -2.5
vertex 6.108964 -0 -2.5
endloop
endfacet
facet normal 0.5735763 0 0.8191522
outer loop
vertex 3.4311862 -4 -0.625
vertex 4.323779 -4 -1.25
vertex 4.323779 -0 -1.25
endloop
endfacet
facet normal 0.57357645 0 0.819152
outer loop
vertex 4.323779 -4 -1.25
vertex 6.108964 -4 -2.5
vertex 6.108964 -0 -2.5
endloop
endfacet
facet normal 0.57357645 0 0.819152
outer loop
vertex 3.4311862 -0 -0.625
vertex 2.5385938 -0 0
vertex 2.5385938 -4 0
endloop
endfacet
facet normal 0.57357645 -0 0.819152
outer loop
vertex 3.4311862 -4 -0.625
vertex 3.4311862 -0 -0.625
vertex 2.5385938 -4 0
endloop
endfacet
facet normal 0.57357645 -0 0.819152
outer loop
vertex 4.323779 -4 -1.25
vertex 6.108964 -0 -2.5
vertex 4.323779 -0 -1.25
endloop
endfacet
facet normal 0.5735763 0 0.8191522
outer loop
vertex 3.4311862 -0 -0.625
vertex 3.4311862 -4 -0.625
vertex 4.323779 -0 -1.25
endloop
endfacet
facet normal 0.42261824 0 -0.9063078
outer loop
vertex 3.342784 -4 0.375
vertex 2.5385938 -4 0
vertex 2.5385938 -0 0
endloop
endfacet
facet normal 0.42261824 0 -0.9063078
outer loop
vertex 4.146974 -4 0.75
vertex 3.342784 -4 0.375
vertex 3.342784 -0 0.375
endloop
endfacet
facet normal 0.42261824 0 -0.9063078
outer loop
vertex 3.342784 -0 0.375
vertex 4.146974 -0 0.75
vertex 4.146974 -4 0.75
endloop
endfacet
facet normal 0.42261833 0 -0.90630776
outer loop
vertex 4.146974 -0 0.75
vertex 5.755354 -0 1.5
vertex 5.755354 -4 1.5
endloop
endfacet
facet normal 0.42261824 0 -0.9063078
outer loop
vertex 3.342784 -4 0.375
vertex 2.5385938 -0 0
vertex 3.342784 -0 0.375
endloop
endfacet
facet normal 0.42261833 0 -0.90630776
outer loop
vertex 5.755354 -4 1.5
vertex 4.146974 -4 0.75
vertex 4.146974 -0 0.75
endloop
endfacet
facet normal 0 0 -1
outer loop
vertex 5.755354 -4 1.5
vertex 5.755354 -0 1.5
vertex 9.5 -4 1.5
endloop
endfacet
facet normal 0 0 -1
outer loop
vertex 9.5 -4 1.5
vertex 5.755354 -0 1.5
vertex 9.5 -0 1.5
endloop
endfacet
facet normal 1 0 0
outer loop
vertex 9.5 -4 1.5
vertex 9.5 -0 1.5
vertex 9.5 -4 2
endloop
endfacet
facet normal 1 -0 0
outer loop
vertex 9.5 -4 2
vertex 9.5 -0 1.5
vertex 9.5 -0 2
endloop
endfacet
facet normal 0 -0 1
outer loop
vertex 9.5 -4 2
vertex 9.5 -0 2
vertex 5.644507 -4 2
endloop
endfacet
facet normal 0 0 1
outer loop
vertex 5.644507 -4 2
vertex 9.5 -0 2
vertex 5.644507 -0 2
endloop
endfacet
facet normal -0.42261824 0 0.90630776
outer loop
vertex 5.644507 -4 2
vertex 5.644507 -0 2
vertex 3.5 -4 1
endloop
endfacet
facet normal -0.42261824 0 0.90630776
outer loop
vertex 3.5 -4 1
vertex 5.644507 -0 2
vertex 3.5 -0 1
endloop
endfacet
facet normal 0 -0 1
outer loop
vertex 3.5 -4 1
vertex 3.5 -0 1
vertex 0 -4 1
endloop
endfacet
facet normal 0 0 1
outer loop
vertex 0 -4 1
vertex 3.5 -0 1
vertex 0 -0 1
endloop
endfacet
facet normal -1 0 0
outer loop
vertex 0 -4 1
vertex 0 -0 1
vertex 0 -4 0
endloop
endfacet
facet normal -1 0 0
outer loop
vertex 0 -4 0
vertex 0 -0 1
vertex 0 -0 0
endloop
endfacet
facet normal 0 1 -0
outer loop
vertex 3.342784 -0 0.375
vertex 2.5385938 -0 0
vertex 3.5 -0 1
endloop
endfacet
facet normal 0 1 0
outer loop
vertex 3.4311862 -0 -0.625
vertex 4.323779 -0 -1.25
vertex 3.0950184 -0 -1
endloop
endfacet
facet normal 0 1 0
outer loop
vertex 3.342784 -0 0.375
vertex 3.5 -0 1
vertex 4.146974 -0 0.75
endloop
endfacet
facet normal 0 0.99999994 0
outer loop
vertex 4.323779 -0 -1.25
vertex 5.9513144 -0 -3
vertex 3.0950184 -0 -1
endloop
endfacet
facet normal 0 1 0
outer loop
vertex 0 -0 -1
vertex 2.5385938 -0 0
vertex 3.0950184 -0 -1
endloop
endfacet
facet normal 0 1 0
outer loop
vertex 0 -0 -1
vertex 0 -0 0
vertex 2.5385938 -0 0
endloop
endfacet
facet normal 0 0.99999994 -0
outer loop
vertex 9.5 -0 -3
vertex 6.108964 -0 -2.5
vertex 9.5 -0 -2.5
endloop
endfacet
facet normal 0 1 0
outer loop
vertex 9.5 -0 -3
vertex 5.9513144 -0 -3
vertex 6.108964 -0 -2.5
endloop
endfacet
facet normal 0 1 -0
outer loop
vertex 5.9513144 -0 -3
vertex 4.323779 -0 -1.25
vertex 6.108964 -0 -2.5
endloop
endfacet
facet normal 0 1 0
outer loop
vertex 5.644507 -0 2
vertex 5.755354 -0 1.5
vertex 4.146974 -0 0.75
endloop
endfacet
facet normal 0 0.99999994 -0
outer loop
vertex 3.0950184 -0 -1
vertex 2.5385938 -0 0
vertex 3.4311862 -0 -0.625
endloop
endfacet
facet normal 0 1 -0
outer loop
vertex 4.146974 -0 0.75
vertex 3.5 -0 1
vertex 5.644507 -0 2
endloop
endfacet
facet normal 0 1 -0
outer loop
vertex 9.5 -0 1.5
vertex 5.755354 -0 1.5
vertex 9.5 -0 2
endloop
endfacet
facet normal 0 1 -0
outer loop
vertex 5.755354 -0 1.5
vertex 5.644507 -0 2
vertex 9.5 -0 2
endloop
endfacet
facet normal 0 1 0
outer loop
vertex 2.5385938 -0 0
vertex 0 -0 0
vertex 0 -0 1
endloop
endfacet
facet normal 0 1 0
outer loop
vertex 3.5 -0 1
vertex 2.5385938 -0 0
vertex 0 -0 1
endloop
endfacet
facet normal -0 -1 0
outer loop
vertex 3.342784 -4 0.375
vertex 3.5 -4 1
vertex 2.5385938 -4 0
endloop
endfacet
facet normal -0 -1 0
outer loop
vertex 4.146974 -4 0.75
vertex 3.5 -4 1
vertex 3.342784 -4 0.375
endloop
endfacet
facet normal 0 -1 -0
outer loop
vertex 3.4311862 -4 -0.625
vertex 3.0950184 -4 -1
vertex 4.323779 -4 -1.25
endloop
endfacet
facet normal 0 -0.99999994 0
outer loop
vertex 4.146974 -4 0.75
vertex 5.755354 -4 1.5
vertex 5.644507 -4 2
endloop
endfacet
facet normal 0 -1 0
outer loop
vertex 0 -4 1
vertex 2.5385938 -4 0
vertex 3.5 -4 1
endloop
endfacet
facet normal 0 -1 0
outer loop
vertex 0 -4 1
vertex 0 -4 0
vertex 2.5385938 -4 0
endloop
endfacet
facet normal 0 -1 0
outer loop
vertex 5.644507 -4 2
vertex 5.755354 -4 1.5
vertex 9.5 -4 2
endloop
endfacet
facet normal 0 -1 -0
outer loop
vertex 9.5 -4 2
vertex 5.755354 -4 1.5
vertex 9.5 -4 1.5
endloop
endfacet
facet normal 0 -1 0
outer loop
vertex 4.146974 -4 0.75
vertex 5.644507 -4 2
vertex 3.5 -4 1
endloop
endfacet
facet normal 0 -0.99999994 0
outer loop
vertex 2.5385938 -4 0
vertex 3.0950184 -4 -1
vertex 3.4311862 -4 -0.625
endloop
endfacet
facet normal -0 -0.99999994 -0
outer loop
vertex 4.323779 -4 -1.25
vertex 3.0950184 -4 -1
vertex 5.9513144 -4 -3
endloop
endfacet
facet normal -0 -1 0
outer loop
vertex 6.108964 -4 -2.5
vertex 4.323779 -4 -1.25
vertex 5.9513144 -4 -3
endloop
endfacet
facet normal -0 -0.99999994 -0
outer loop
vertex 9.5 -4 -2.5
vertex 6.108964 -4 -2.5
vertex 9.5 -4 -3
endloop
endfacet
facet normal 0 -1 -0
outer loop
vertex 6.108964 -4 -2.5
vertex 5.9513144 -4 -3
vertex 9.5 -4 -3
endloop
endfacet
facet normal 0 -1 -0
outer loop
vertex 2.5385938 -4 0
vertex 0 -4 -1
vertex 3.0950184 -4 -1
endloop
endfacet
facet normal 0 -1 0
outer loop
vertex 0 -4 -1
vertex 2.5385938 -4 0
vertex 0 -4 0
endloop
endfacet
endsolid unnamed

Binary file not shown.

View File

@ -0,0 +1,615 @@
import { test, expect } from '@playwright/test'
import { secrets } from './secrets'
import { EngineCommand } from '../../src/lang/std/engineConnection'
import { v4 as uuidv4 } from 'uuid'
import { getUtils } from './test-utils'
import waitOn from 'wait-on'
import { Models } from '@kittycad/lib'
import fsp from 'fs/promises'
/*
debug helper: unfortunately we do rely on exact coord mouse clicks in a few places
just from the nature of the stream, running the test with debugger and pasting the below
into the console can be useful to get coords
document.addEventListener('mousemove', (e) =>
console.log(`await page.mouse.click(${e.clientX}, ${e.clientY})`)
)
*/
test.beforeEach(async ({ context, page }) => {
// wait for Vite preview server to be up
await waitOn({
resources: ['tcp:3000'],
timeout: 5000,
})
await context.addInitScript(async (token) => {
localStorage.setItem('TOKEN_PERSIST_KEY', token)
localStorage.setItem('persistCode', ``)
localStorage.setItem(
'SETTINGS_PERSIST_KEY',
JSON.stringify({
baseUnit: 'in',
cameraControls: 'KittyCAD',
defaultDirectory: '',
defaultProjectName: 'project-$nnn',
onboardingStatus: 'dismissed',
showDebugPanel: true,
textWrapping: 'On',
theme: 'system',
unitSystem: 'imperial',
})
)
}, secrets.token)
// kill animations, speeds up tests and reduced flakiness
await page.emulateMedia({ reducedMotion: 'reduce' })
})
test.setTimeout(60000)
test('Basic sketch', async ({ page }) => {
const u = getUtils(page)
await page.setViewportSize({ width: 1200, height: 500 })
const PUR = 400 / 37.5 //pixeltoUnitRatio
await page.goto('/')
await u.waitForAuthSkipAppStart()
await u.openDebugPanel()
await u.waitForDefaultPlanesVisibilityChange()
await expect(page.getByRole('button', { name: 'Start Sketch' })).toBeVisible()
// click on "Start Sketch" button
await u.clearCommandLogs()
await Promise.all([
u.doAndWaitForImageDiff(
() => page.getByRole('button', { name: 'Start Sketch' }).click(),
200
),
u.waitForDefaultPlanesVisibilityChange(),
])
// select a plane
await u.doAndWaitForCmd(() => page.mouse.click(700, 200), 'edit_mode_enter')
await u.waitForCmdReceive('set_tool')
await u.doAndWaitForCmd(
() => page.getByRole('button', { name: 'Line' }).click(),
'set_tool'
)
const startXPx = 600
await u.doAndWaitForCmd(
() => page.mouse.click(startXPx + PUR * 10, 500 - PUR * 10),
'mouse_click',
false
)
await page.mouse.click(startXPx + PUR * 20, 500 - PUR * 10)
const startAt = '[10.97, -14.79]'
const tenish = '11.07'
await expect(page.locator('.cm-content'))
.toHaveText(`const part001 = startSketchOn('-XZ')
|> startProfileAt(${startAt}, %)
|> line([${tenish}, 0], %)`)
await page.mouse.click(startXPx + PUR * 20, 500 - PUR * 20)
await expect(page.locator('.cm-content'))
.toHaveText(`const part001 = startSketchOn('-XZ')
|> startProfileAt(${startAt}, %)
|> line([${tenish}, 0], %)
|> line([0, ${tenish}], %)`)
await page.mouse.click(startXPx, 500 - PUR * 20)
await expect(page.locator('.cm-content'))
.toHaveText(`const part001 = startSketchOn('-XZ')
|> startProfileAt(${startAt}, %)
|> line([${tenish}, 0], %)
|> line([0, ${tenish}], %)
|> line([-22.04, 0], %)`)
// deselect line tool
await u.doAndWaitForCmd(
() => page.getByRole('button', { name: 'Line' }).click(),
'set_tool'
)
// click between first two clicks to get center of the line
await u.doAndWaitForCmd(
() => page.mouse.click(startXPx + PUR * 15, 500 - PUR * 10),
'select_with_point'
)
await u.closeDebugPanel()
// hold down shift
await page.keyboard.down('Shift')
// click between the latest two clicks to get center of the line
await page.mouse.click(startXPx + PUR * 10, 500 - PUR * 20)
// selected two lines therefore there should be two cursors
await expect(page.locator('.cm-cursor')).toHaveCount(2)
await page.getByRole('button', { name: 'Equal Length' }).click()
await expect(page.locator('.cm-content'))
.toHaveText(`const part001 = startSketchOn('-XZ')
|> startProfileAt(${startAt}, %)
|> line({ to: [${tenish}, 0], tag: 'seg01' }, %)
|> line([0, ${tenish}], %)
|> angledLine([180, segLen('seg01', %)], %)`)
})
test('if you write invalid kcl you get inlined errors', async ({ page }) => {
const u = getUtils(page)
await page.setViewportSize({ width: 1000, height: 500 })
await page.goto('/')
await u.waitForAuthSkipAppStart()
// check no error to begin with
await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible()
/* add the following code to the editor (# error is not a valid line)
# error
const topAng = 30
const bottomAng = 25
*/
await page.click('.cm-content')
await page.keyboard.type('# error')
// press arrows to clear autocomplete
await page.keyboard.press('ArrowLeft')
await page.keyboard.press('ArrowRight')
await page.keyboard.press('Enter')
await page.keyboard.type('const topAng = 30')
await page.keyboard.press('Enter')
await page.keyboard.type('const bottomAng = 25')
await page.keyboard.press('Enter')
// error in guter
await expect(page.locator('.cm-lint-marker-error')).toBeVisible()
// error text on hover
await page.hover('.cm-lint-marker-error')
await expect(page.getByText("found unknown token '#'")).toBeVisible()
// select the line that's causing the error and delete it
await page.getByText('# error').click()
await page.keyboard.press('End')
await page.keyboard.down('Shift')
await page.keyboard.press('Home')
await page.keyboard.up('Shift')
await page.keyboard.press('Backspace')
// wait for .cm-lint-marker-error not to be visible
await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible()
})
test('executes on load', async ({ page, context }) => {
const u = getUtils(page)
await context.addInitScript(async (token) => {
localStorage.setItem(
'persistCode',
`const part001 = startSketchOn('-XZ')
|> startProfileAt([-6.95, 4.98], %)
|> line([25.1, 0.41], %)
|> line([0.73, -14.93], %)
|> line([-23.44, 0.52], %)`
)
})
await page.setViewportSize({ width: 1000, height: 500 })
await page.goto('/')
await u.waitForAuthSkipAppStart()
// expand variables section
await page.getByText('Variables').click()
// can find part001 in the variables summary (pretty-json-container, makes sure we're not looking in the code editor)
// part001 only shows up in the variables summary if it's been executed
await page.waitForFunction(() => {
const variablesElement = document.querySelector(
'.pretty-json-container'
) as HTMLDivElement
return variablesElement.innerHTML.includes('part001')
})
await expect(
page.locator('.pretty-json-container >> text=part001')
).toBeVisible()
})
test('re-executes', async ({ page, context }) => {
const u = getUtils(page)
await context.addInitScript(async (token) => {
localStorage.setItem('persistCode', `const myVar = 5`)
})
await page.setViewportSize({ width: 1000, height: 500 })
await page.goto('/')
await u.waitForAuthSkipAppStart()
await page.getByText('Variables').click()
// expect to see "myVar:5"
await expect(
page.locator('.pretty-json-container >> text=myVar:5')
).toBeVisible()
// change 5 to 67
await page.getByText('const myVar').click()
await page.keyboard.press('End')
await page.keyboard.press('Backspace')
await page.keyboard.type('67')
await expect(
page.locator('.pretty-json-container >> text=myVar:67')
).toBeVisible()
})
test('Can create sketches on all planes and their back sides', async ({
page,
}) => {
const u = getUtils(page)
const PUR = 400 / 37.5 //pixeltoUnitRatio
await page.setViewportSize({ width: 1200, height: 500 })
await page.goto('/')
await u.waitForAuthSkipAppStart()
await u.openDebugPanel()
await u.waitForDefaultPlanesVisibilityChange()
const camCmd: EngineCommand = {
type: 'modeling_cmd_req',
cmd_id: uuidv4(),
cmd: {
type: 'default_camera_look_at',
center: { x: 15, y: 0, z: 0 },
up: { x: 0, y: 0, z: 1 },
vantage: { x: 30, y: 30, z: 30 },
},
}
const TestSinglePlane = async ({
viewCmd,
expectedCode,
clickCoords,
}: {
viewCmd: EngineCommand
expectedCode: string
clickCoords: { x: number; y: number }
}) => {
await u.openDebugPanel()
await u.sendCustomCmd(viewCmd)
await u.clearCommandLogs()
// await page.waitForTimeout(200)
await page.getByRole('button', { name: 'Start Sketch' }).click()
await u.waitForDefaultPlanesVisibilityChange()
await u.closeDebugPanel()
await page.mouse.click(clickCoords.x, clickCoords.y)
await u.openDebugPanel()
await expect(page.getByRole('button', { name: 'Line' })).toBeVisible()
// draw a line
const startXPx = 600
await u.clearCommandLogs()
await page.getByRole('button', { name: 'Line' }).click()
await u.waitForCmdReceive('set_tool')
await u.clearCommandLogs()
await u.closeDebugPanel()
await page.mouse.click(startXPx + PUR * 10, 500 - PUR * 10)
await u.openDebugPanel()
await u.waitForCmdReceive('mouse_click')
await u.closeDebugPanel()
await page.mouse.click(startXPx + PUR * 20, 500 - PUR * 10)
await u.openDebugPanel()
await expect(page.locator('.cm-content')).toHaveText(expectedCode)
await page.getByRole('button', { name: 'Line' }).click()
await u.clearCommandLogs()
await page.getByRole('button', { name: 'Exit Sketch' }).click()
await u.expectCmdLog('[data-message-type="execution-done"]')
await u.clearCommandLogs()
await u.removeCurrentCode()
}
const codeTemplate = (
plane = 'XY',
sign = ''
) => `const part001 = startSketchOn('${plane}')
|> startProfileAt([${sign}6.88, -9.29], %)
|> line([${sign}6.95, 0], %)`
await TestSinglePlane({
viewCmd: camCmd,
expectedCode: codeTemplate('XY'),
clickCoords: { x: 700, y: 350 }, // red plane
})
await TestSinglePlane({
viewCmd: camCmd,
expectedCode: codeTemplate('YZ'),
clickCoords: { x: 1000, y: 200 }, // green plane
})
await TestSinglePlane({
viewCmd: camCmd,
expectedCode: codeTemplate('XZ', '-'),
clickCoords: { x: 630, y: 130 }, // blue plane
})
// new camera angle to click the back side of all three planes
const camCmdBackSide: EngineCommand = {
type: 'modeling_cmd_req',
cmd_id: uuidv4(),
cmd: {
type: 'default_camera_look_at',
center: { x: -15, y: 0, z: 0 },
up: { x: 0, y: 0, z: 1 },
vantage: { x: -30, y: -30, z: -30 },
},
}
await TestSinglePlane({
viewCmd: camCmdBackSide,
expectedCode: codeTemplate('-XY', '-'),
clickCoords: { x: 705, y: 136 }, // back of red plane
})
await TestSinglePlane({
viewCmd: camCmdBackSide,
expectedCode: codeTemplate('-YZ', '-'),
clickCoords: { x: 1000, y: 350 }, // back of green plane
})
await TestSinglePlane({
viewCmd: camCmdBackSide,
expectedCode: codeTemplate('-XZ'),
clickCoords: { x: 600, y: 400 }, // back of blue plane
})
})
test('Auto complete works', async ({ page }) => {
const u = getUtils(page)
// const PUR = 400 / 37.5 //pixeltoUnitRatio
await page.setViewportSize({ width: 1200, height: 500 })
await page.goto('/')
await u.waitForAuthSkipAppStart()
await u.waitForDefaultPlanesVisibilityChange()
// this test might be brittle as we add and remove functions
// but should also be easy to update.
// tests clicking on an option, selection the first option
// and arrowing down to an option
await page.click('.cm-content')
await page.keyboard.type('const part001 = start')
// expect there to be three auto complete options
await expect(page.locator('.cm-completionLabel')).toHaveCount(3)
await page.getByText('startSketchOn').click()
await page.keyboard.type("('XY')")
await page.keyboard.press('Enter')
await page.keyboard.type(' |> startProfi')
// expect there be a single auto complete option that we can just hit enter on
await expect(page.locator('.cm-completionLabel')).toBeVisible()
await page.keyboard.press('Enter') // accepting the auto complete, not a new line
await page.keyboard.type('([0,0], %)')
await page.keyboard.press('Enter')
await page.keyboard.type(' |> lin')
await expect(page.locator('.cm-tooltip-autocomplete')).toBeVisible()
// press arrow down twice then enter to accept xLine
await page.keyboard.press('ArrowDown')
await page.keyboard.press('ArrowDown')
await page.keyboard.press('Enter')
await page.keyboard.type('(5, %)')
await expect(page.locator('.cm-content'))
.toHaveText(`const part001 = startSketchOn('XY')
|> startProfileAt([0,0], %)
|> xLine(5, %)`)
})
// Onboarding tests
test('Onboarding redirects and code updating', async ({ page, context }) => {
const u = getUtils(page)
// Override beforeEach test setup
await context.addInitScript(async () => {
// Give some initial code, so we can test that it's cleared
localStorage.setItem('persistCode', 'const sigmaAllow = 15000')
const storedSettings = JSON.parse(
localStorage.getItem('SETTINGS_PERSIST_KEY') || '{}'
)
storedSettings.onboardingStatus = '/export'
localStorage.setItem('SETTINGS_PERSIST_KEY', JSON.stringify(storedSettings))
})
await page.setViewportSize({ width: 1200, height: 500 })
await page.goto('/')
await u.waitForAuthSkipAppStart()
// Test that the redirect happened
await expect(page.url().split(':3000').slice(-1)[0]).toBe(
`/file/new/onboarding/export`
)
// Test that you come back to this page when you refresh
await page.reload()
await expect(page.url().split(':3000').slice(-1)[0]).toBe(
`/file/new/onboarding/export`
)
// Test that the onboarding pane loaded
const title = page.locator('[data-testid="onboarding-content"]')
await expect(title).toBeAttached()
// Test that the code changes when you advance to the next step
await page.locator('[data-testid="onboarding-next"]').click()
await expect(page.locator('.cm-content')).toHaveText('')
// Test that the code is not empty when you click on the next step
await page.locator('[data-testid="onboarding-next"]').click()
await expect(page.locator('.cm-content')).toHaveText(/.+/)
})
test('Selections work on fresh and edited sketch', async ({ page }) => {
// tests mapping works on fresh sketch and edited sketch
// tests using hovers which is the same as selections, because if
// source ranges are wrong, hovers won't work
const u = getUtils(page)
const PUR = 400 / 37.5 //pixeltoUnitRatio
await page.setViewportSize({ width: 1200, height: 500 })
await page.goto('/')
await u.waitForAuthSkipAppStart()
await u.openDebugPanel()
await u.waitForDefaultPlanesVisibilityChange()
const xAxisClick = () => page.mouse.click(700, 250)
const emptySpaceClick = () => page.mouse.click(700, 300)
const topHorzSegmentClick = () => page.mouse.click(700, 285)
const bottomHorzSegmentClick = () => page.mouse.click(750, 393)
await u.clearCommandLogs()
await page.getByRole('button', { name: 'Start Sketch' }).click()
await u.waitForDefaultPlanesVisibilityChange()
// select a plane
await u.doAndWaitForCmd(() => page.mouse.click(700, 200), 'edit_mode_enter')
await u.waitForCmdReceive('set_tool')
await u.doAndWaitForCmd(
() => page.getByRole('button', { name: 'Line' }).click(),
'set_tool'
)
const startXPx = 600
await u.doAndWaitForCmd(
() => page.mouse.click(startXPx + PUR * 10, 500 - PUR * 10),
'mouse_click',
false
)
await page.mouse.click(startXPx + PUR * 20, 500 - PUR * 10)
const startAt = '[10.97, -14.79]'
const tenish = '11.07'
const twentyish = '22.04'
await expect(page.locator('.cm-content'))
.toHaveText(`const part001 = startSketchOn('-XZ')
|> startProfileAt(${startAt}, %)
|> line([${tenish}, 0], %)`)
await page.mouse.click(startXPx + PUR * 20, 500 - PUR * 20)
await expect(page.locator('.cm-content'))
.toHaveText(`const part001 = startSketchOn('-XZ')
|> startProfileAt(${startAt}, %)
|> line([${tenish}, 0], %)
|> line([0, ${tenish}], %)`)
await page.mouse.click(startXPx, 500 - PUR * 20)
await expect(page.locator('.cm-content'))
.toHaveText(`const part001 = startSketchOn('-XZ')
|> startProfileAt(${startAt}, %)
|> line([${tenish}, 0], %)
|> line([0, ${tenish}], %)
|> line([-${twentyish}, 0], %)`)
// deselect line tool
await u.doAndWaitForCmd(
() => page.getByRole('button', { name: 'Line' }).click(),
'set_tool'
)
await u.closeDebugPanel()
const selectionSequence = async () => {
await expect(page.getByTestId('hover-highlight')).not.toBeVisible()
await page.mouse.move(startXPx + PUR * 15, 500 - PUR * 10)
await expect(page.getByTestId('hover-highlight')).toBeVisible()
// bg-yellow-200 is more brittle than hover-highlight, but is closer to the user experience
// and will be an easy fix if it breaks because we change the colour
await expect(page.locator('.bg-yellow-200')).toBeVisible()
// check mousing off, than mousing onto another line
await page.mouse.move(startXPx + PUR * 10, 500 - PUR * 15) // mouse off
await expect(page.getByTestId('hover-highlight')).not.toBeVisible()
await page.mouse.move(startXPx + PUR * 10, 500 - PUR * 20) // mouse onto another line
await expect(page.getByTestId('hover-highlight')).toBeVisible()
// now check clicking works including axis
// click a segment hold shift and click an axis, see that a relevant constraint is enabled
await u.doAndWaitForCmd(topHorzSegmentClick, 'select_with_point', false)
await page.keyboard.down('Shift')
const absYButton = page.getByRole('button', { name: 'ABS Y' })
await expect(absYButton).toBeDisabled()
await u.doAndWaitForCmd(xAxisClick, 'select_with_point', false)
await page.keyboard.up('Shift')
await absYButton.and(page.locator(':not([disabled])')).waitFor()
await expect(absYButton).not.toBeDisabled()
// clear selection by clicking on nothing
await u.doAndWaitForCmd(emptySpaceClick, 'select_clear', false)
// same selection but click the axis first
await u.doAndWaitForCmd(xAxisClick, 'select_with_point', false)
await expect(absYButton).toBeDisabled()
await page.keyboard.down('Shift')
await u.doAndWaitForCmd(topHorzSegmentClick, 'select_with_point', false)
await page.keyboard.up('Shift')
await expect(absYButton).not.toBeDisabled()
// clear selection by clicking on nothing
await u.doAndWaitForCmd(emptySpaceClick, 'select_clear', false)
// check the same selection again by putting cursor in code first then selecting axis
await u.doAndWaitForCmd(
() => page.getByText(` |> line([-${twentyish}, 0], %)`).click(),
'select_clear',
false
)
await page.keyboard.down('Shift')
await expect(absYButton).toBeDisabled()
await u.doAndWaitForCmd(xAxisClick, 'select_with_point', false)
await page.keyboard.up('Shift')
await expect(absYButton).not.toBeDisabled()
// clear selection by clicking on nothing
await u.doAndWaitForCmd(emptySpaceClick, 'select_clear', false)
// select segment in editor than another segment in scene and check there are two cursors
await u.doAndWaitForCmd(
() => page.getByText(` |> line([-${twentyish}, 0], %)`).click(),
'select_clear',
false
)
await page.keyboard.down('Shift')
await expect(page.locator('.cm-cursor')).toHaveCount(1)
await u.doAndWaitForCmd(bottomHorzSegmentClick, 'select_with_point', false) // another segment, bottom one
await page.keyboard.up('Shift')
await expect(page.locator('.cm-cursor')).toHaveCount(2)
// clear selection by clicking on nothing
await u.doAndWaitForCmd(emptySpaceClick, 'select_clear', false)
}
await selectionSequence()
// hovering in fresh sketch worked, lets try exiting and re-entering
await u.doAndWaitForCmd(
() => page.getByRole('button', { name: 'Exit Sketch' }).click(),
'edit_mode_exit'
)
// wait for execution done
await u.expectCmdLog('[data-message-type="execution-done"]')
// select a line
await u.doAndWaitForCmd(topHorzSegmentClick, 'select_clear', false)
// enter sketch again
await u.doAndWaitForCmd(
() => page.getByRole('button', { name: 'Start Sketch' }).click(),
'edit_mode_enter',
false
)
// hover again and check it works
await selectionSequence()
})

20
e2e/playwright/secrets.ts Normal file
View File

@ -0,0 +1,20 @@
import { readFileSync } from 'fs'
const secrets: Record<string, string> = {}
try {
const file = readFileSync('./e2e/playwright/playwright-secrets.env', 'utf8')
file
.split('\n')
.filter((line) => line && line.length > 1)
.forEach((line) => {
const [key, value] = line.split('=')
// prefer env vars over secrets file
secrets[key] = process.env[key] || (value as any).replaceAll('"', '')
})
} catch (err) {
// probably running in CI
secrets.token = process.env.token || ''
// add more env vars here to make them available in CI
}
export { secrets }

View File

@ -0,0 +1,324 @@
import { test, expect } from '@playwright/test'
import { secrets } from './secrets'
import { EngineCommand } from '../../src/lang/std/engineConnection'
import { v4 as uuidv4 } from 'uuid'
import { getUtils } from './test-utils'
import { Models } from '@kittycad/lib'
import fsp from 'fs/promises'
test.beforeEach(async ({ context, page }) => {
await context.addInitScript(async (token) => {
localStorage.setItem('TOKEN_PERSIST_KEY', token)
localStorage.setItem('persistCode', ``)
localStorage.setItem(
'SETTINGS_PERSIST_KEY',
JSON.stringify({
baseUnit: 'in',
cameraControls: 'KittyCAD',
defaultDirectory: '',
defaultProjectName: 'project-$nnn',
onboardingStatus: 'dismissed',
showDebugPanel: true,
textWrapping: 'On',
theme: 'system',
unitSystem: 'imperial',
})
)
}, secrets.token)
// reducedMotion kills animations, which speeds up tests and reduces flakiness
await page.emulateMedia({ reducedMotion: 'reduce' })
})
test.setTimeout(60000)
test('change camera, show planes', async ({ page, context }) => {
const u = getUtils(page)
await page.setViewportSize({ width: 1200, height: 500 })
await page.goto('/')
await u.waitForAuthSkipAppStart()
await u.openAndClearDebugPanel()
const camCmd: EngineCommand = {
type: 'modeling_cmd_req',
cmd_id: uuidv4(),
cmd: {
type: 'default_camera_look_at',
center: { x: 0, y: 0, z: 0 },
up: { x: 0, y: 0, z: 1 },
vantage: { x: 0, y: 50, z: 50 },
},
}
await u.sendCustomCmd(camCmd)
await u.waitForCmdReceive('default_camera_look_at')
// rotate
await u.closeDebugPanel()
await page.mouse.move(700, 200)
await page.mouse.down({ button: 'right' })
await page.mouse.move(600, 300)
await page.mouse.up({ button: 'right' })
await u.openDebugPanel()
await u.waitForCmdReceive('camera_drag_end')
await page.waitForTimeout(500)
await u.clearCommandLogs()
await page.getByRole('button', { name: 'Start Sketch' }).click()
await u.waitForDefaultPlanesVisibilityChange()
await u.closeDebugPanel()
await expect(page).toHaveScreenshot({
maxDiffPixels: 100,
})
await u.openAndClearDebugPanel()
await page.getByRole('button', { name: 'Exit Sketch' }).click()
await u.waitForDefaultPlanesVisibilityChange()
await u.sendCustomCmd(camCmd)
await u.waitForCmdReceive('default_camera_look_at')
await u.clearCommandLogs()
await u.closeDebugPanel()
// pan
await page.keyboard.down('Shift')
await page.mouse.move(600, 200)
await page.mouse.down({ button: 'right' })
await page.mouse.move(700, 200)
await page.mouse.up({ button: 'right' })
await page.keyboard.up('Shift')
await u.openDebugPanel()
await u.waitForCmdReceive('camera_drag_end')
await page.waitForTimeout(300)
await u.clearCommandLogs()
await page.getByRole('button', { name: 'Start Sketch' }).click()
await u.waitForDefaultPlanesVisibilityChange()
await u.closeDebugPanel()
await expect(page).toHaveScreenshot({
maxDiffPixels: 100,
})
await u.openAndClearDebugPanel()
await page.getByRole('button', { name: 'Exit Sketch' }).click()
await u.waitForDefaultPlanesVisibilityChange()
await u.sendCustomCmd(camCmd)
await u.waitForCmdReceive('default_camera_look_at')
await u.clearCommandLogs()
await u.closeDebugPanel()
// zoom
await page.keyboard.down('Control')
await page.mouse.move(700, 400)
await page.mouse.down({ button: 'right' })
await page.mouse.move(700, 350)
await page.mouse.up({ button: 'right' })
await page.keyboard.up('Control')
await u.openDebugPanel()
await u.waitForCmdReceive('camera_drag_end')
await page.waitForTimeout(300)
await u.clearCommandLogs()
await page.getByRole('button', { name: 'Start Sketch' }).click()
await u.waitForDefaultPlanesVisibilityChange()
await u.closeDebugPanel()
await expect(page).toHaveScreenshot({
maxDiffPixels: 100,
})
})
test('exports of each format should work', async ({ page, context }) => {
// FYI this test doesn't work with only engine running locally
const u = getUtils(page)
await context.addInitScript(async () => {
;(window as any).playwrightSkipFilePicker = true
localStorage.setItem(
'persistCode',
`const topAng = 25
const bottomAng = 35
const baseLen = 3.5
const baseHeight = 1
const totalHeightHalf = 2
const armThick = 0.5
const totalLen = 9.5
const part001 = startSketchOn('-XZ')
|> startProfileAt([0, 0], %)
|> yLine(baseHeight, %)
|> xLine(baseLen, %)
|> angledLineToY({
angle: topAng,
to: totalHeightHalf,
tag: 'seg04'
}, %)
|> xLineTo({ to: totalLen, tag: 'seg03' }, %)
|> yLine({ length: -armThick, tag: 'seg01' }, %)
|> angledLineThatIntersects({
angle: _180,
offset: -armThick,
intersectTag: 'seg04'
}, %)
|> angledLineToY([segAng('seg04', %) + 180, _0], %)
|> angledLineToY({
angle: -bottomAng,
to: -totalHeightHalf - armThick,
tag: 'seg02'
}, %)
|> xLineTo(segEndX('seg03', %) + 0, %)
|> yLine(-segLen('seg01', %), %)
|> angledLineThatIntersects({
angle: _180,
offset: -armThick,
intersectTag: 'seg02'
}, %)
|> angledLineToY([segAng('seg02', %) + 180, -baseHeight], %)
|> xLineTo(_0, %)
|> close(%)
|> extrude(4, %)`
)
})
await page.setViewportSize({ width: 1200, height: 500 })
await page.goto('/')
await u.waitForAuthSkipAppStart()
await u.openDebugPanel()
await u.waitForDefaultPlanesVisibilityChange()
await u.expectCmdLog('[data-message-type="execution-done"]')
await u.waitForCmdReceive('extrude')
await page.waitForTimeout(1000)
await u.clearAndCloseDebugPanel()
await page.getByRole('button', { name: 'KittyCAD Modeling App' }).click()
const doExport = async (output: Models['OutputFormat_type']) => {
await page.getByRole('button', { name: 'Export Model' }).click()
const exportSelect = page.getByTestId('export-type')
await exportSelect.selectOption({ label: output.type })
if ('storage' in output) {
const storageSelect = page.getByTestId('export-storage')
await storageSelect.selectOption({ label: output.storage })
}
const downloadPromise = page.waitForEvent('download')
await page.getByRole('button', { name: 'Export', exact: true }).click()
const download = await downloadPromise
const downloadLocationer = (extra = '') =>
`./e2e/playwright/export-snapshots/${output.type}-${
'storage' in output ? output.storage : ''
}${extra}.${output.type}`
const downloadLocation = downloadLocationer()
const downloadLocation2 = downloadLocationer('-2')
if (output.type === 'gltf' && output.storage === 'standard') {
// wait for second download
const download2 = await page.waitForEvent('download')
await download.saveAs(downloadLocation)
await download2.saveAs(downloadLocation2)
// rewrite uri to reference our file name
const fileContents = await fsp.readFile(downloadLocation, 'utf-8')
const isJson = fileContents.includes('buffers')
let contents = fileContents
let reWriteLocation = downloadLocation
let uri = downloadLocation2.split('/').pop()
if (!isJson) {
contents = await fsp.readFile(downloadLocation2, 'utf-8')
reWriteLocation = downloadLocation2
uri = downloadLocation.split('/').pop()
}
contents = contents.replace(/"uri": ".*"/g, `"uri": "${uri}"`)
await fsp.writeFile(reWriteLocation, contents)
} else {
await download.saveAs(downloadLocation)
}
if (output.type === 'step') {
// stable timestamps for step files
const fileContents = await fsp.readFile(downloadLocation, 'utf-8')
const newFileContents = fileContents.replace(
/[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}\.[0-9]+[0-9]+[0-9]\+[0-9]{2}:[0-9]{2}/g,
'1970-01-01T00:00:00.0+00:00'
)
await fsp.writeFile(downloadLocation, newFileContents)
}
}
const axisDirectionPair: Models['AxisDirectionPair_type'] = {
axis: 'z',
direction: 'positive',
}
const sysType: Models['System_type'] = {
forward: axisDirectionPair,
up: axisDirectionPair,
}
// NOTE it was easiest to leverage existing types and have doExport take Models['OutputFormat_type'] as in input
// just note that only `type` and `storage` are used for selecting the drop downs is the app
// the rest are only there to make typescript happy
await doExport({
type: 'step',
coords: sysType,
})
await doExport({
type: 'gltf',
storage: 'embedded',
presentation: 'pretty',
})
await doExport({
type: 'gltf',
storage: 'binary',
presentation: 'pretty',
})
await doExport({
type: 'gltf',
storage: 'standard',
presentation: 'pretty',
})
await doExport({
type: 'ply',
coords: sysType,
selection: { type: 'default_scene' },
storage: 'ascii',
units: 'in',
})
await doExport({
type: 'ply',
storage: 'binary_little_endian',
coords: sysType,
selection: { type: 'default_scene' },
units: 'in',
})
await doExport({
type: 'ply',
storage: 'binary_big_endian',
coords: sysType,
selection: { type: 'default_scene' },
units: 'in',
})
await doExport({
type: 'stl',
storage: 'ascii',
coords: sysType,
units: 'in',
selection: { type: 'default_scene' },
})
await doExport({
type: 'stl',
storage: 'binary',
coords: sysType,
units: 'in',
selection: { type: 'default_scene' },
})
await doExport({
// obj seems to be a little flaky, times out tests sometimes
type: 'obj',
coords: sysType,
units: 'in',
})
})

Binary file not shown.

After

Width:  |  Height:  |  Size: 118 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 80 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

View File

@ -0,0 +1,156 @@
import { expect, Page } from '@playwright/test'
import { EngineCommand } from '../../src/lang/std/engineConnection'
import fsp from 'fs/promises'
import pixelMatch from 'pixelmatch'
import { PNG } from 'pngjs'
async function waitForPageLoad(page: Page) {
// wait for 'Loading stream...' spinner
await page.getByTestId('loading-stream').waitFor()
// wait for all spinners to be gone
await page.getByTestId('loading').waitFor({ state: 'detached' })
await page.getByTestId('start-sketch').waitFor()
}
async function removeCurrentCode(page: Page) {
const hotkey = process.platform === 'darwin' ? 'Meta' : 'Control'
await page.click('.cm-content')
await page.keyboard.down(hotkey)
await page.keyboard.press('a')
await page.keyboard.up(hotkey)
await page.keyboard.press('Backspace')
await expect(page.locator('.cm-content')).toHaveText('')
}
async function sendCustomCmd(page: Page, cmd: EngineCommand) {
await page.fill('[data-testid="custom-cmd-input"]', JSON.stringify(cmd))
await page.click('[data-testid="custom-cmd-send-button"]')
}
async function clearCommandLogs(page: Page) {
await page.click('[data-testid="clear-commands"]')
}
async function expectCmdLog(page: Page, locatorStr: string) {
await expect(page.locator(locatorStr)).toBeVisible()
}
async function waitForDefaultPlanesToBeVisible(page: Page) {
await page.waitForFunction(
() =>
document.querySelectorAll('[data-receive-command-type="object_visible"]')
.length >= 3
)
}
async function openDebugPanel(page: Page) {
const isOpen =
(await page
.locator('[data-testid="debug-panel"]')
?.getAttribute('open')) === ''
if (!isOpen) {
await page.getByText('Debug').click()
await page.getByTestId('debug-panel').and(page.locator('[open]')).waitFor()
}
}
async function closeDebugPanel(page: Page) {
const isOpen =
(await page.getByTestId('debug-panel')?.getAttribute('open')) === ''
if (isOpen) {
await page.getByText('Debug').click()
await page
.getByTestId('debug-panel')
.and(page.locator(':not([open])'))
.waitFor()
}
}
async function waitForCmdReceive(page: Page, commandType: string) {
return page
.locator(`[data-receive-command-type="${commandType}"]`)
.first()
.waitFor()
}
export function getUtils(page: Page) {
return {
waitForAuthSkipAppStart: () => waitForPageLoad(page),
removeCurrentCode: () => removeCurrentCode(page),
sendCustomCmd: (cmd: EngineCommand) => sendCustomCmd(page, cmd),
clearCommandLogs: () => clearCommandLogs(page),
expectCmdLog: (locatorStr: string) => expectCmdLog(page, locatorStr),
waitForDefaultPlanesVisibilityChange: () =>
waitForDefaultPlanesToBeVisible(page),
openDebugPanel: () => openDebugPanel(page),
closeDebugPanel: () => closeDebugPanel(page),
openAndClearDebugPanel: async () => {
await openDebugPanel(page)
return clearCommandLogs(page)
},
clearAndCloseDebugPanel: async () => {
await clearCommandLogs(page)
return closeDebugPanel(page)
},
waitForCmdReceive: (commandType: string) =>
waitForCmdReceive(page, commandType),
doAndWaitForCmd: async (
fn: () => Promise<void>,
commandType: string,
endWithDebugPanelOpen = true
) => {
await openDebugPanel(page)
await clearCommandLogs(page)
await closeDebugPanel(page)
await fn()
await openDebugPanel(page)
await waitForCmdReceive(page, commandType)
if (!endWithDebugPanelOpen) {
await closeDebugPanel(page)
}
},
doAndWaitForImageDiff: (fn: () => Promise<any>, diffCount = 200) =>
new Promise(async (resolve) => {
await page.screenshot({
path: './e2e/playwright/temp1.png',
fullPage: true,
})
await fn()
const isImageDiff = async () => {
await page.screenshot({
path: './e2e/playwright/temp2.png',
fullPage: true,
})
const screenshot1 = PNG.sync.read(
await fsp.readFile('./e2e/playwright/temp1.png')
)
const screenshot2 = PNG.sync.read(
await fsp.readFile('./e2e/playwright/temp2.png')
)
const actualDiffCount = pixelMatch(
screenshot1.data,
screenshot2.data,
null,
screenshot1.width,
screenshot2.height
)
return actualDiffCount > diffCount
}
// run isImageDiff every 50ms until it returns true or 5 seconds have passed (100 times)
let count = 0
const interval = setInterval(async () => {
count++
if (await isImageDiff()) {
clearInterval(interval)
resolve(true)
} else if (count > 100) {
clearInterval(interval)
resolve(false)
}
}, 50)
}),
}
}

View File

@ -0,0 +1,62 @@
import { browser, $, expect } from '@wdio/globals'
import fs from 'fs/promises'
describe('KCMA (Tauri, Linux)', () => {
it('opens the auth page, signs in, and signs out', async () => {
// Clean up previous tests
await new Promise((resolve) => setTimeout(resolve, 100))
await fs.rm('/tmp/kittycad_user_code', { force: true })
await browser.execute('window.localStorage.clear()')
const signInButton = await $('[data-testid="sign-in-button"]')
expect(await signInButton.getText()).toEqual('Sign in')
// Workaround for .click(), see https://github.com/tauri-apps/tauri/issues/6541
await signInButton.waitForClickable()
await browser.execute('arguments[0].click();', signInButton)
await new Promise((resolve) => setTimeout(resolve, 2000))
// Get from main.rs
const userCode = await (
await fs.readFile('/tmp/kittycad_user_code')
).toString()
console.log(`Found user code ${userCode}`)
// Device flow: verify
const token = process.env.KITTYCAD_API_TOKEN
const headers = {
Authorization: `Bearer ${token}`,
Accept: 'application/json',
'Content-Type': 'application/json',
}
const verifyUrl = `https://api.kittycad.io/oauth2/device/verify?user_code=${userCode}`
console.log(`GET ${verifyUrl}`)
const vr = await fetch(verifyUrl, { headers })
console.log(vr.status)
// Device flow: confirm
const confirmUrl = 'https://api.kittycad.io/oauth2/device/confirm'
const data = JSON.stringify({ user_code: userCode })
console.log(`POST ${confirmUrl} ${data}`)
const cr = await fetch(confirmUrl, {
headers,
method: 'POST',
body: data,
})
console.log(cr.status)
// Now should be signed in
const newFileButton = await $('[data-testid="home-new-file"]')
expect(await newFileButton.getText()).toEqual('New file')
// So let's sign out!
const menuButton = await $('[data-testid="user-sidebar-toggle"]')
await menuButton.waitForClickable()
await browser.execute('arguments[0].click();', menuButton)
const signoutButton = await $('[data-testid="user-sidebar-sign-out"]')
await signoutButton.waitForClickable()
await browser.execute('arguments[0].click();', signoutButton)
const newSignInButton = await $('[data-testid="sign-in-button"]')
expect(await newSignInButton.getText()).toEqual('Sign in')
})
})

View File

@ -1,36 +1,36 @@
{
"name": "untitled-app",
"version": "0.11.1",
"version": "0.12.0",
"private": true,
"dependencies": {
"@codemirror/autocomplete": "^6.9.0",
"@codemirror/autocomplete": "^6.10.2",
"@fortawesome/fontawesome-svg-core": "^6.4.2",
"@fortawesome/free-brands-svg-icons": "^6.4.2",
"@fortawesome/free-solid-svg-icons": "^6.4.2",
"@fortawesome/react-fontawesome": "^0.2.0",
"@headlessui/react": "^1.7.13",
"@headlessui/react": "^1.7.17",
"@headlessui/tailwindcss": "^0.2.0",
"@kittycad/lib": "^0.0.45",
"@lezer/javascript": "^1.4.7",
"@kittycad/lib": "^0.0.46",
"@lezer/javascript": "^1.4.9",
"@open-rpc/client-js": "^1.8.1",
"@react-hook/resize-observer": "^1.2.6",
"@replit/codemirror-interact": "^6.3.0",
"@sentry/react": "^7.65.0",
"@tauri-apps/api": "^1.5.0",
"@sentry/react": "^7.77.0",
"@tauri-apps/api": "^1.5.1",
"@testing-library/jest-dom": "^5.14.1",
"@testing-library/react": "^13.0.0",
"@testing-library/user-event": "^13.2.1",
"@testing-library/react": "^14.0.0",
"@testing-library/user-event": "^14.5.1",
"@ts-stack/markdown": "^1.5.0",
"@types/node": "^16.7.13",
"@types/react": "^18.0.0",
"@types/react": "^18.2.41",
"@types/react-dom": "^18.0.0",
"@uiw/react-codemirror": "^4.21.13",
"@uiw/react-codemirror": "^4.21.20",
"@xstate/inspect": "^0.8.0",
"@xstate/react": "^3.2.2",
"crypto-js": "^4.1.1",
"crypto-js": "^4.2.0",
"debounce-promise": "^3.1.2",
"formik": "^2.4.3",
"fuse.js": "^6.6.2",
"fuse.js": "^7.0.0",
"http-server": "^14.1.1",
"json-rpc-2.0": "^1.6.0",
"re-resizable": "^6.9.11",
@ -43,23 +43,24 @@
"react-modal-promise": "^1.0.2",
"react-router-dom": "^6.14.2",
"sketch-helpers": "^0.0.4",
"swr": "^2.0.4",
"swr": "^2.2.2",
"tauri-plugin-fs-extra-api": "https://github.com/tauri-apps/tauri-plugin-fs-extra#v1",
"toml": "^3.0.0",
"ts-node": "^10.9.1",
"typescript": "^4.4.2",
"uuid": "^9.0.0",
"typescript": "^5.2.2",
"uuid": "^9.0.1",
"vitest": "^0.34.6",
"vscode-jsonrpc": "^8.1.0",
"vscode-languageserver-protocol": "^3.17.3",
"vscode-languageserver-protocol": "^3.17.5",
"wasm-pack": "^0.12.1",
"web-vitals": "^2.1.0",
"web-vitals": "^3.5.0",
"ws": "^8.13.0",
"xstate": "^4.38.2",
"zustand": "^4.1.4"
"zustand": "^4.4.5"
},
"scripts": {
"start": "vite",
"serve": "vite serve --port=3000",
"build": "curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y && source \"$HOME/.cargo/env\" && curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh -s -- -y && yarn build:wasm && vite build",
"build:local": "vite build",
"build:both": "vite build",
@ -69,10 +70,11 @@
"test:nowatch": "vitest run --mode development",
"test:rust": "(cd src/wasm-lib && cargo test --all && cargo clippy --all --tests --benches)",
"test:cov": "vitest run --coverage --mode development",
"test:e2e:tauri": "E2E_TAURI_ENABLED=true TS_NODE_COMPILER_OPTIONS='{\"module\": \"commonjs\"}' wdio run wdio.conf.ts",
"simpleserver:ci": "yarn pretest && http-server ./public --cors -p 3000 &",
"simpleserver": "yarn pretest && http-server ./public --cors -p 3000",
"fmt": "prettier --write ./src",
"fmt-check": "prettier --check ./src",
"fmt": "prettier --write ./src && prettier --write ./e2e",
"fmt-check": "prettier --check ./src && prettier --check ./e2e",
"build:wasm-dev": "(cd src/wasm-lib && wasm-pack build --dev --target web --out-dir pkg && cargo test -p kcl-lib export_bindings) && cp src/wasm-lib/pkg/wasm_lib_bg.wasm public && yarn fmt",
"build:wasm": "(cd src/wasm-lib && wasm-pack build --target web --out-dir pkg && cargo test -p kcl-lib export_bindings) && cp src/wasm-lib/pkg/wasm_lib_bg.wasm public && yarn fmt",
"build:wasm-clean": "yarn wasm-prep && yarn build:wasm",
@ -101,30 +103,42 @@
},
"devDependencies": {
"@babel/plugin-proposal-private-property-in-object": "^7.21.11",
"@babel/preset-env": "^7.22.9",
"@tauri-apps/cli": "^1.5.0",
"@babel/preset-env": "^7.23.3",
"@playwright/test": "^1.39.0",
"@tauri-apps/cli": "^1.5.6",
"@types/crypto-js": "^4.1.1",
"@types/debounce-promise": "^3.1.6",
"@types/debounce-promise": "^3.1.8",
"@types/isomorphic-fetch": "^0.0.36",
"@types/react-modal": "^3.16.0",
"@types/uuid": "^9.0.1",
"@types/pixelmatch": "^5.2.6",
"@types/pngjs": "^6.0.4",
"@types/react-modal": "^3.16.3",
"@types/uuid": "^9.0.4",
"@types/wait-on": "^5.3.4",
"@types/wicg-file-system-access": "^2020.9.6",
"@types/ws": "^8.5.5",
"@vitejs/plugin-react": "^4.0.3",
"@vitest/coverage-istanbul": "^0.34.1",
"@vitejs/plugin-react": "^4.1.1",
"@vitest/coverage-istanbul": "^0.34.6",
"@wdio/cli": "^8.24.3",
"@wdio/globals": "^8.24.3",
"@wdio/local-runner": "^8.24.3",
"@wdio/mocha-framework": "^8.24.3",
"@wdio/spec-reporter": "^8.24.2",
"autoprefixer": "^10.4.13",
"eslint": "^8.44.0",
"eslint": "^8.53.0",
"eslint-config-react-app": "^7.0.1",
"eslint-plugin-css-modules": "^2.11.0",
"eslint-plugin-css-modules": "^2.12.0",
"happy-dom": "^10.8.0",
"husky": "^8.0.3",
"pixelmatch": "^5.3.0",
"pngjs": "^7.0.0",
"postcss": "^8.4.31",
"prettier": "^2.8.0",
"setimmediate": "^1.0.5",
"tailwindcss": "^3.2.4",
"vite": "^4.4.3",
"tailwindcss": "^3.3.5",
"vite": "^4.5.0",
"vite-plugin-eslint": "^1.8.1",
"vite-tsconfig-paths": "^4.2.0",
"vite-tsconfig-paths": "^4.2.1",
"wait-on": "^7.2.0",
"yarn": "^1.22.19"
}
}

82
playwright.config.ts Normal file
View File

@ -0,0 +1,82 @@
import { defineConfig, devices } from '@playwright/test';
/**
* Read environment variables from file.
* https://github.com/motdotla/dotenv
*/
// require('dotenv').config();
/**
* See https://playwright.dev/docs/test-configuration.
*/
export default defineConfig({
testDir: './e2e/playwright',
/* Run tests in files in parallel */
fullyParallel: true,
/* Fail the build on CI if you accidentally left test.only in the source code. */
forbidOnly: !!process.env.CI,
/* Retry on CI only */
retries: process.env.CI ? 3 : 0,
/* Opt out of parallel tests on CI. */
workers: process.env.CI ? 1 : 1,
/* Reporter to use. See https://playwright.dev/docs/test-reporters */
reporter: 'html',
/* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
use: {
/* Base URL to use in actions like `await page.goto('/')`. */
baseURL: 'http://localhost:3000',
/* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
trace: 'on-first-retry',
},
/* Configure projects for major browsers */
projects: [
{
name: 'Google Chrome',
use: { ...devices['Desktop Chrome'], channel: 'chrome' }, // or 'chrome-beta'
},
{
name: 'webkit',
use: { ...devices['Desktop Safari'] },
},
// {
// name: 'firefox',
// use: { ...devices['Desktop Firefox'] },
// },
// {
// name: 'chromium', // compat issue with encoding atm, so we're using the branded 'Google Chrome' instead
// use: { ...devices['Desktop Chrome'] },
// },
/* Test against mobile viewports. */
// {
// name: 'Mobile Chrome',
// use: { ...devices['Pixel 5'] },
// },
// {
// name: 'Mobile Safari',
// use: { ...devices['iPhone 12'] },
// },
/* Test against branded browsers. */
// {
// name: 'Microsoft Edge',
// use: { ...devices['Desktop Edge'], channel: 'msedge' },
// },
// {
// name: 'Google Chrome',
// use: { ...devices['Desktop Chrome'], channel: 'chrome' },
// },
],
/* Run your local dev server before starting the tests */
webServer: {
command: 'yarn serve',
// url: 'http://127.0.0.1:3000',
reuseExistingServer: !process.env.CI,
},
});

83
src-tauri/Cargo.lock generated
View File

@ -1573,7 +1573,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b"
dependencies = [
"hermit-abi 0.3.1",
"rustix 0.38.13",
"rustix 0.38.21",
"windows-sys 0.48.0",
]
@ -1664,9 +1664,9 @@ dependencies = [
[[package]]
name = "kittycad"
version = "0.2.38"
version = "0.2.42"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "633a728fb7209b398b7fa5b67460cb7f3cdb268c6b2a9e81967dda464cfbb5c1"
checksum = "6aa554d86b6dbbd976a659c912ae25ce817b4378eb12a5684907e263410f0a7b"
dependencies = [
"anyhow",
"async-trait",
@ -1732,9 +1732,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "libc"
version = "0.2.148"
version = "0.2.150"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9cdc71e17332e86d2e1d38c1f99edcb6288ee11b815fb1a4b049eaa2114d369b"
checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c"
[[package]]
name = "libm"
@ -1765,9 +1765,9 @@ checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519"
[[package]]
name = "linux-raw-sys"
version = "0.4.7"
version = "0.4.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a9bad9f94746442c783ca431b22403b519cd7fbeed0533fdd6328b2f2212128"
checksum = "da2479e8c062e40bf0066ffa0bc823de0a9368974af99c9f6df941d2c231e03f"
[[package]]
name = "lock_api"
@ -1913,12 +1913,6 @@ version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
[[package]]
name = "minisign-verify"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "933dca44d65cdd53b355d0b73d380a2ff5da71f87f036053188bf1eab6a19881"
[[package]]
name = "miniz_oxide"
version = "0.6.2"
@ -1940,9 +1934,9 @@ dependencies = [
[[package]]
name = "mio"
version = "0.8.8"
version = "0.8.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "927a765cd3fc26206e66b296465fa9d3e5ab003e651c1b3c060e7956d96b19d2"
checksum = "3dce281c5e46beae905d4de1870d8b1509a9142b62eedf18b443b011ca8343d0"
dependencies = [
"libc",
"wasi 0.11.0+wasi-snapshot-preview1",
@ -3018,9 +3012,9 @@ dependencies = [
[[package]]
name = "rustix"
version = "0.37.26"
version = "0.37.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "84f3f8f960ed3b5a59055428714943298bf3fa2d4a1d53135084e0544829d995"
checksum = "fea8ca367a3a01fe35e6943c400addf443c0f57670e6ec51196f71a4b8762dd2"
dependencies = [
"bitflags 1.3.2",
"errno",
@ -3032,14 +3026,14 @@ dependencies = [
[[package]]
name = "rustix"
version = "0.38.13"
version = "0.38.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d7db8590df6dfcd144d22afd1b83b36c21a18d7cbc1dc4bb5295a8712e9eb662"
checksum = "2b426b0506e5d50a7d8dafcf2e81471400deb602392c7dd110815afb4eaf02a3"
dependencies = [
"bitflags 2.4.0",
"errno",
"libc",
"linux-raw-sys 0.4.7",
"linux-raw-sys 0.4.10",
"windows-sys 0.48.0",
]
@ -3112,9 +3106,9 @@ dependencies = [
[[package]]
name = "schemars"
version = "0.8.15"
version = "0.8.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1f7b0ce13155372a76ee2e1c5ffba1fe61ede73fbea5630d61eee6fac4929c0c"
checksum = "45a28f4c49489add4ce10783f7911893516f15afe45d015608d41faca6bc4d29"
dependencies = [
"bigdecimal",
"bytes",
@ -3129,9 +3123,9 @@ dependencies = [
[[package]]
name = "schemars_derive"
version = "0.8.15"
version = "0.8.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e85e2a16b12bdb763244c69ab79363d71db2b4b918a2def53f80b02e0574b13c"
checksum = "c767fd6fa65d9ccf9cf026122c1b555f2ef9a4f0cea69da4d7dbc3e258d30967"
dependencies = [
"proc-macro2",
"quote",
@ -3215,9 +3209,9 @@ dependencies = [
[[package]]
name = "serde"
version = "1.0.189"
version = "1.0.193"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e422a44e74ad4001bdc8eede9a4570ab52f71190e9c076d14369f38b9200537"
checksum = "25dd9975e68d0cb5aa1120c288333fc98731bd1dd12f561e468ea4728c042b89"
dependencies = [
"serde_derive",
]
@ -3233,9 +3227,9 @@ dependencies = [
[[package]]
name = "serde_derive"
version = "1.0.189"
version = "1.0.193"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e48d1f918009ce3145511378cf68d613e3b3d9137d67272562080d68a2b32d5"
checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3"
dependencies = [
"proc-macro2",
"quote",
@ -3255,9 +3249,9 @@ dependencies = [
[[package]]
name = "serde_json"
version = "1.0.107"
version = "1.0.108"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6b420ce6e3d8bd882e9b243c6eed35dbc9a6110c9769e74b584e0d68d1f20c65"
checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b"
dependencies = [
"itoa 1.0.6",
"ryu",
@ -3438,9 +3432,9 @@ dependencies = [
[[package]]
name = "socket2"
version = "0.5.4"
version = "0.5.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4031e820eb552adee9295814c0ced9e5cf38ddf1e8b7d566d6de8e2538ea989e"
checksum = "7b5fac59a5cb5dd637972e5fca70daf0523c9067fcdc4842f053dae04a18f8e9"
dependencies = [
"libc",
"windows-sys 0.48.0",
@ -3745,7 +3739,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9bfe673cf125ef364d6f56b15e8ce7537d9ca7e4dae1cf6fbbdeed2e024db3d9"
dependencies = [
"anyhow",
"base64 0.21.2",
"bytes",
"cocoa",
"dirs-next",
@ -3759,7 +3752,6 @@ dependencies = [
"heck 0.4.1",
"http",
"ignore",
"minisign-verify",
"objc",
"once_cell",
"open",
@ -3784,14 +3776,12 @@ dependencies = [
"tauri-utils",
"tempfile",
"thiserror",
"time",
"tokio",
"url",
"uuid",
"webkit2gtk",
"webview2-com",
"windows 0.39.0",
"zip",
]
[[package]]
@ -3856,7 +3846,7 @@ dependencies = [
[[package]]
name = "tauri-plugin-fs-extra"
version = "0.0.0"
source = "git+https://github.com/tauri-apps/plugins-workspace?branch=v1#9b20f28d747f6ec3ba5a80bfcd5edc1d573b4c90"
source = "git+https://github.com/tauri-apps/plugins-workspace?branch=v1#537053d3171a7374a1a86fed422523e7b45a4fb8"
dependencies = [
"log",
"serde",
@ -3955,7 +3945,7 @@ dependencies = [
"cfg-if",
"fastrand",
"redox_syscall 0.3.5",
"rustix 0.37.26",
"rustix 0.37.27",
"windows-sys 0.45.0",
]
@ -4035,9 +4025,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
[[package]]
name = "tokio"
version = "1.33.0"
version = "1.34.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4f38200e3ef7995e5ef13baec2f432a6da0aa9ac495b2c0e8f3b7eec2c92d653"
checksum = "d0c014766411e834f7af5b8f4cf46257aab4036ca95e9d2c144a10f59ad6f5b9"
dependencies = [
"backtrace",
"bytes",
@ -4045,7 +4035,7 @@ dependencies = [
"mio",
"num_cpus",
"pin-project-lite",
"socket2 0.5.4",
"socket2 0.5.5",
"windows-sys 0.48.0",
]
@ -5012,14 +5002,3 @@ checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85"
dependencies = [
"linked-hash-map",
]
[[package]]
name = "zip"
version = "0.6.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "760394e246e4c28189f19d488c058bf16f564016aefac5d32bb1f3b51d5e9261"
dependencies = [
"byteorder",
"crc32fast",
"crossbeam-utils",
]

View File

@ -4,7 +4,7 @@ version = "0.1.0"
description = "A Tauri App"
authors = ["you"]
license = ""
repository = ""
repository = "https://github.com/KittyCAD/modeling-app"
default-run = "app"
edition = "2021"
rust-version = "1.60"
@ -16,13 +16,13 @@ tauri-build = { version = "1.5.0", features = [] }
[dependencies]
anyhow = "1"
kittycad = "0.2.33"
kittycad = "0.2.42"
oauth2 = "4.4.2"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
tauri = { version = "1.5.2", features = [ "os-all", "dialog-all", "fs-all", "http-request", "path-all", "shell-open", "shell-open-api", "updater", "devtools"] }
tauri = { version = "1.5.2", features = [ "os-all", "dialog-all", "fs-all", "http-request", "path-all", "shell-open", "shell-open-api", "devtools"] }
tauri-plugin-fs-extra = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "v1" }
tokio = { version = "1.33.0", features = ["time"] }
tokio = { version = "1.34.0", features = ["time"] }
toml = "0.8.2"
[features]

View File

@ -1,6 +1,8 @@
// Prevents additional console window on Windows in release, DO NOT REMOVE!!
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
use std::env;
use std::fs;
use std::io::Read;
use anyhow::Result;
@ -68,10 +70,26 @@ async fn login(app: tauri::AppHandle, host: &str) -> Result<String, InvokeError>
};
// Open the system browser with the auth_uri.
// We do this in the browser and not a seperate window because we want 1password and
// We do this in the browser and not a separate window because we want 1password and
// other crap to work well.
tauri::api::shell::open(&app.shell_scope(), auth_uri.secret(), None)
.map_err(|e| InvokeError::from_anyhow(e.into()))?;
// TODO: find a better way to share this value with tauri e2e tests
// Here we're using an env var to enable the /tmp file (windows not supported for now)
// and bypass the shell::open call as it fails on GitHub Actions.
let e2e_tauri_enabled = env::var("E2E_TAURI_ENABLED").is_ok();
if (e2e_tauri_enabled) {
println!(
"E2E_TAURI_ENABLED is set, won't open {} externally",
auth_uri.secret()
);
fs::write(
"/tmp/kittycad_user_code",
details.user_code().secret().to_string(),
)
.expect("Unable to write /tmp/kittycad_user_code file");
} else {
tauri::api::shell::open(&app.shell_scope(), auth_uri.secret(), None)
.map_err(|e| InvokeError::from_anyhow(e.into()))?;
}
// Wait for the user to login.
let token = auth_client
@ -129,10 +147,10 @@ async fn get_user(
fn main() {
tauri::Builder::default()
.setup(|app| {
.setup(|_app| {
#[cfg(debug_assertions)] // only include this code on debug builds
{
let window = app.get_window("main").unwrap();
let window = _app.get_window("main").unwrap();
// comment out the below if you don't devtools to open everytime.
// it's useful because otherwise devtools shuts everytime rust code changes.
window.open_devtools();

View File

@ -8,7 +8,7 @@
},
"package": {
"productName": "kittycad-modeling",
"version": "0.11.1"
"version": "0.12.0"
},
"tauri": {
"allowlist": {
@ -72,23 +72,13 @@
},
"resources": [],
"shortDescription": "",
"targets": "all",
"windows": {
"certificateThumbprint": "F4C9A52FF7BC26EE5E054946F6B11DEEA94C748D",
"digestAlgorithm": "sha256",
"timestampUrl": "http://timestamp.digicert.com"
}
"targets": "all"
},
"security": {
"csp": null
},
"updater": {
"active": true,
"endpoints": [
"https://dl.kittycad.io/releases/modeling-app/last_update.json"
],
"dialog": true,
"pubkey": "dW50cnVzdGVkIGNvbW1lbnQ6IG1pbmlzaWduIHB1YmxpYyBrZXk6IEUzNzA4MjBEQjFBRTY4NzYKUldSMmFLNnhEWUp3NCtsT21Jd05wQktOaGVkOVp6MUFma0hNTDRDSnI2RkJJTEZOWG1ncFhqcU8K"
"active": false
},
"windows": [
{

View File

@ -1,4 +1,3 @@
{
"$schema": "../node_modules/@tauri-apps/cli/schema.json",
"package": {

View File

@ -0,0 +1,21 @@
{
"$schema": "../node_modules/@tauri-apps/cli/schema.json",
"tauri": {
"updater": {
"active": true,
"endpoints": [
"https://dl.kittycad.io/releases/modeling-app/last_update.json"
],
"dialog": true,
"pubkey": "dW50cnVzdGVkIGNvbW1lbnQ6IG1pbmlzaWduIHB1YmxpYyBrZXk6IEUzNzA4MjBEQjFBRTY4NzYKUldSMmFLNnhEWUp3NCtsT21Jd05wQktOaGVkOVp6MUFma0hNTDRDSnI2RkJJTEZOWG1ncFhqcU8K"
},
"bundle": {
"identifier": "io.kittycad.modeling-app",
"windows": {
"certificateThumbprint": "F4C9A52FF7BC26EE5E054946F6B11DEEA94C748D",
"digestAlgorithm": "sha256",
"timestampUrl": "http://timestamp.digicert.com"
}
}
}
}

View File

@ -1,4 +1,3 @@
{
"$schema": "../node_modules/@tauri-apps/cli/schema.json",
"package": {

View File

@ -1,4 +1,4 @@
import { useEffect, useCallback, MouseEventHandler } from 'react'
import { useCallback, MouseEventHandler } from 'react'
import { DebugPanel } from './components/DebugPanel'
import { v4 as uuidv4 } from 'uuid'
import { PaneType, useStore } from './useStore'
@ -19,7 +19,6 @@ import {
} from '@fortawesome/free-solid-svg-icons'
import { useHotkeys } from 'react-hotkeys-hook'
import { getNormalisedCoordinates } from './lib/utils'
import { isTauri } from './lib/isTauri'
import { useLoaderData } from 'react-router-dom'
import { IndexLoaderData } from './Router'
import { useGlobalStateContext } from 'hooks/useGlobalStateContext'
@ -31,11 +30,10 @@ import { TextEditor } from 'components/TextEditor'
import { Themes, getSystemTheme } from 'lib/theme'
import { useEngineConnectionSubscriptions } from 'hooks/useEngineConnectionSubscriptions'
import { engineCommandManager } from './lang/std/engineConnection'
import { kclManager } from 'lang/KclSinglton'
import { useModelingContext } from 'hooks/useModelingContext'
export function App() {
const { code: loadedCode, project, file } = useLoaderData() as IndexLoaderData
const { project, file } = useLoaderData() as IndexLoaderData
useHotKeyListener()
const {
@ -82,26 +80,6 @@ export function App() {
? 'opacity-40'
: ''
// Use file code loaded from disk
// on mount, and overwrite any locally-stored code
useEffect(() => {
if (isTauri() && loadedCode !== null) {
if (kclManager.engineCommandManager.engineConnection?.isReady()) {
// If the engine is ready, promptly execute the loaded code
kclManager.setCodeAndExecute(loadedCode)
} else {
// Otherwise, just set the code and wait for the connection to complete
kclManager.setCode(loadedCode)
}
}
return () => {
// Clear code on unmount if in desktop app
if (isTauri()) {
kclManager.setCode('')
}
}
}, [loadedCode])
useEngineConnectionSubscriptions()
const debounceSocketSend = throttle<EngineCommand>((message) => {

View File

@ -7,7 +7,9 @@ export const Auth = ({ children }: React.PropsWithChildren) => {
const isLoggingIn = auth?.state.matches('checkIfLoggedIn')
return isLoggingIn ? (
<Loading>Loading KittyCAD Modeling App...</Loading>
<Loading>
<span data-testid="initial-load">Loading KittyCAD Modeling App...</span>
</Loading>
) : (
<>{children}</>
)

View File

@ -42,7 +42,7 @@ import CommandBarProvider from 'components/CommandBar'
import { TEST, VITE_KC_SENTRY_DSN } from './env'
import * as Sentry from '@sentry/react'
import ModelingMachineProvider from 'components/ModelingMachineProvider'
import { KclContextProvider } from 'lang/KclSinglton'
import { KclContextProvider, kclManager } from 'lang/KclSinglton'
import FileMachineProvider from 'components/FileMachineProvider'
import { sep } from '@tauri-apps/api/path'
@ -207,6 +207,7 @@ const router = createBrowserRouter(
projectPath + sep + PROJECT_ENTRYPOINT
)
const children = await readDir(projectPath, { recursive: true })
kclManager.setCodeAndExecute(code, false)
return {
code,

View File

@ -1,4 +1,3 @@
import { ToolTip } from './useStore'
import { Fragment, WheelEvent, useRef, useMemo } from 'react'
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { faSearch, faX } from '@fortawesome/free-solid-svg-icons'
@ -15,23 +14,6 @@ export const sketchButtonClassnames = {
icon: 'text-fern-20 h-auto group-hover:text-fern-10 hover:text-fern-10 dark:text-chalkboard-100 dark:group-hover:text-chalkboard-100 dark:hover:text-chalkboard-100 group-disabled:bg-chalkboard-60 hover:group-disabled:text-inherit',
}
const sketchFnLabels: Record<ToolTip | 'sketch_line' | 'move', string> = {
sketch_line: 'Line',
line: 'Line',
move: 'Move',
angledLine: 'Angled Line',
angledLineThatIntersects: 'Angled Line That Intersects',
angledLineOfXLength: 'Angled Line Of X Length',
angledLineOfYLength: 'Angled Line Of Y Length',
angledLineToX: 'Angled Line To X',
angledLineToY: 'Angled Line To Y',
lineTo: 'Line to Point',
xLine: 'Horizontal Line',
yLine: 'Vertical Line',
xLineTo: 'Horizontal Line to Point',
yLineTo: 'Vertical Line to Point',
}
export const Toolbar = () => {
const { state, send, context } = useModelingContext()
const toolbarButtonsRef = useRef<HTMLSpanElement>(null)
@ -66,7 +48,7 @@ export const Toolbar = () => {
className="group"
>
<ActionIcon icon="sketch" className="!p-0.5" size="md" />
Start Sketch
<span data-testid="start-sketch">Start Sketch</span>
</button>
)}
{state.nextEvents.includes('Enter sketch') && pathId && (
@ -127,6 +109,21 @@ export const Toolbar = () => {
eventName.includes('Make segment') ||
eventName.includes('Constrain')
)
.sort((a, b) => {
const aisEnabled = state.nextEvents
.filter((event) => state.can(event as any))
.includes(a)
const bIsEnabled = state.nextEvents
.filter((event) => state.can(event as any))
.includes(b)
if (aisEnabled && !bIsEnabled) {
return -1
}
if (!aisEnabled && bIsEnabled) {
return 1
}
return 0
})
.map((eventName) => (
<button
key={eventName}

View File

@ -184,6 +184,7 @@ function DisplayObj({
</li>
)
}
return null
})}
</ul>
</span>

View File

@ -1,5 +1,5 @@
import { useEffect, useState, useRef } from 'react'
import { parse, BinaryPart, Value, executor } from '../lang/wasm'
import { parse, BinaryPart, Value } from '../lang/wasm'
import {
createIdentifier,
createLiteral,
@ -10,6 +10,7 @@ import { findAllPreviousVariables, PrevVariable } from '../lang/queryAst'
import { engineCommandManager } from '../lang/std/engineConnection'
import { kclManager, useKclContext } from 'lang/KclSinglton'
import { useModelingContext } from 'hooks/useModelingContext'
import { executeAst } from 'useStore'
export const AvailableVars = ({
onVarClick,
@ -130,27 +131,29 @@ export function useCalc({
if (!programMemory || !selectionRange) return
const varInfo = findAllPreviousVariables(
kclManager.ast,
programMemory,
kclManager.programMemory,
selectionRange
)
setAvailableVarInfo(varInfo)
}, [kclManager.ast, programMemory, selectionRange])
}, [kclManager.ast, kclManager.programMemory, selectionRange])
useEffect(() => {
try {
const code = `const __result__ = ${value}\nshow(__result__)`
const code = `const __result__ = ${value}`
const ast = parse(code)
const _programMem: any = { root: {}, return: null }
availableVarInfo.variables.forEach(({ key, value }) => {
_programMem.root[key] = { type: 'userVal', value, __meta: [] }
})
executor(
executeAst({
ast,
_programMem,
engineCommandManager,
kclManager.defaultPlanes
).then((programMemory) => {
defaultPlanes: kclManager.defaultPlanes,
useFakeExecutor: true,
programMemoryOverride: JSON.parse(
JSON.stringify(kclManager.programMemory)
),
}).then(({ programMemory }) => {
const resultDeclaration = ast.body.find(
(a) =>
a.type === 'VariableDeclaration' &&
@ -167,7 +170,7 @@ export function useCalc({
setCalcResult('NAN')
setValueNode(null)
}
}, [value])
}, [value, availableVarInfo])
return {
valueNode,
@ -212,7 +215,10 @@ export const CreateNewVariable = ({
}) => {
return (
<>
<label htmlFor="create-new-variable" className="block mt-3 font-mono">
<label
htmlFor="create-new-variable"
className="block mt-3 font-mono text-gray-900"
>
Create new variable
</label>
<div className="mt-1 flex gap-2 items-center">
@ -223,6 +229,7 @@ export const CreateNewVariable = ({
onChange={(e) => {
setShouldCreateVariable(e.target.checked)
}}
className="bg-white text-gray-900"
/>
)}
<input

View File

@ -9,6 +9,7 @@ export interface CollapsiblePanelProps
icon?: IconDefinition
open?: boolean
menu?: React.ReactNode
detailsTestId?: string
iconClassNames?: {
bg?: string
icon?: string
@ -51,11 +52,13 @@ export const CollapsiblePanel = ({
className,
iconClassNames,
menu,
detailsTestId,
...props
}: CollapsiblePanelProps) => {
return (
<details
{...props}
data-testid={detailsTestId}
className={styles.panel + ' group ' + (className || '')}
>
<PanelHeader

View File

@ -1,100 +1,20 @@
import { CollapsiblePanel, CollapsiblePanelProps } from './CollapsiblePanel'
import { v4 as uuidv4 } from 'uuid'
import { EngineCommand } from '../lang/std/engineConnection'
import { useState } from 'react'
import { ActionButton } from '../components/ActionButton'
import { faCheck } from '@fortawesome/free-solid-svg-icons'
import { isReducedMotion } from 'lang/util'
import { AstExplorer } from './AstExplorer'
import { engineCommandManager } from '../lang/std/engineConnection'
type SketchModeCmd = Extract<
Extract<EngineCommand, { type: 'modeling_cmd_req' }>['cmd'],
{ type: 'default_camera_enable_sketch_mode' }
>
import { EngineCommands } from './EngineCommands'
export const DebugPanel = ({ className, ...props }: CollapsiblePanelProps) => {
const [sketchModeCmd, setSketchModeCmd] = useState<SketchModeCmd>({
type: 'default_camera_enable_sketch_mode',
origin: { x: 0, y: 0, z: 0 },
x_axis: { x: 1, y: 0, z: 0 },
y_axis: { x: 0, y: 1, z: 0 },
distance_to_plane: 100,
ortho: true,
animated: !isReducedMotion(),
})
if (!sketchModeCmd) return null
return (
<CollapsiblePanel
{...props}
className={
'!absolute overflow-hidden !h-auto bottom-5 right-5 ' + className
'!absolute overflow-auto !h-auto bottom-5 right-5 ' + className
}
// header height, top-5, and bottom-5
style={{ maxHeight: 'calc(100% - 3rem - 1.25rem - 1.25rem)' }}
detailsTestId="debug-panel"
>
<section className="p-4 flex flex-col gap-4">
<Xyz
onChange={setSketchModeCmd}
pointKey="origin"
data={sketchModeCmd}
/>
<Xyz
onChange={setSketchModeCmd}
pointKey="x_axis"
data={sketchModeCmd}
/>
<Xyz
onChange={setSketchModeCmd}
pointKey="y_axis"
data={sketchModeCmd}
/>
<div className="flex">
<div className="pr-4">distance_to_plane</div>
<input
className="w-16 dark:bg-chalkboard-90"
type="number"
value={sketchModeCmd.distance_to_plane}
onChange={({ target }) => {
setSketchModeCmd({
...sketchModeCmd,
distance_to_plane: Number(target.value),
})
}}
/>
<div className="pr-4">ortho</div>
<input
className="w-16"
type="checkbox"
checked={sketchModeCmd.ortho}
onChange={(a) =>
setSketchModeCmd({
...sketchModeCmd,
ortho: a.target.checked,
})
}
/>
</div>
<ActionButton
Element="button"
onClick={() => {
engineCommandManager.sendSceneCommand({
type: 'modeling_cmd_req',
cmd: sketchModeCmd,
cmd_id: uuidv4(),
})
}}
className="hover:border-succeed-50"
icon={{
icon: faCheck,
bgClassName:
'bg-succeed-80 group-hover:bg-succeed-70 hover:bg-succeed-70',
iconClassName:
'text-succeed-20 group-hover:text-succeed-10 hover:text-succeed-10',
}}
>
Send sketch mode command
</ActionButton>
<EngineCommands />
<div style={{ height: '400px' }} className="overflow-y-auto">
<AstExplorer />
</div>
@ -102,41 +22,3 @@ export const DebugPanel = ({ className, ...props }: CollapsiblePanelProps) => {
</CollapsiblePanel>
)
}
const Xyz = ({
pointKey,
data,
onChange,
}: {
pointKey: 'origin' | 'y_axis' | 'x_axis'
data: SketchModeCmd
onChange: (a: SketchModeCmd) => void
}) => {
if (!data) return null
return (
<div className="flex">
<div className="pr-4">{pointKey}</div>
{Object.entries(data[pointKey]).map(([axis, val]) => {
return (
<div key={axis} className="flex">
<div className="w-4">{axis}</div>
<input
className="w-16 dark:bg-chalkboard-90"
type="number"
value={val}
onChange={({ target }) => {
onChange({
...data,
[pointKey]: {
...data[pointKey],
[axis]: Number(target.value),
},
})
}}
/>
</div>
)
})}
</div>
)
}

View File

@ -0,0 +1,85 @@
import { CommandLog, engineCommandManager } from 'lang/std/engineConnection'
import { useState, useEffect } from 'react'
function useEngineCommands(): [CommandLog[], () => void] {
const [engineCommands, setEngineCommands] = useState<CommandLog[]>([])
useEffect(() => {
engineCommandManager.registerCommandLogCallback((commands) =>
setEngineCommands(commands)
)
}, [])
return [engineCommands, () => engineCommandManager.clearCommandLogs()]
}
export const EngineCommands = () => {
const [engineCommands, clearEngineCommands] = useEngineCommands()
const [containsFilter, setContainsFilter] = useState('')
const [customCmd, setCustomCmd] = useState('')
return (
<div>
<input
className="text-gray-800 bg-slate-300 px-2"
data-testid="filter-input"
type="text"
value={containsFilter}
onChange={(e) => setContainsFilter(e.target.value)}
placeholder="Filter"
/>
<div className="max-w-xl max-h-36 overflow-auto">
{engineCommands.map((command, index) => {
const stringer = JSON.stringify(command)
if (containsFilter && !stringer.includes(containsFilter)) return null
return (
<pre className="text-xs" key={index}>
<code
key={index}
data-message-type={command.type}
data-command-type={
(command.type === 'send-modeling' ||
command.type === 'send-scene') &&
command.data.type === 'modeling_cmd_req'
? command?.data?.cmd?.type
: ''
}
data-command-id={
(command.type === 'send-modeling' ||
command.type === 'send-scene') &&
command.data.type === 'modeling_cmd_req'
? command.data.cmd_id
: ''
}
data-receive-command-type={
command.type === 'receive-reliable' ? command.cmd_type : ''
}
>
{JSON.stringify(command, null, 2)}
</code>
</pre>
)
})}
</div>
<button data-testid="clear-commands" onClick={clearEngineCommands}>
Clear
</button>
<br />
<input
className="text-gray-800 bg-slate-300 px-2"
type="text"
value={customCmd}
onChange={(e) => setCustomCmd(e.target.value)}
placeholder="JSON command"
data-testid="custom-cmd-input"
/>
<button
data-testid="custom-cmd-send-button"
onClick={() =>
engineCommandManager.sendSceneCommand(JSON.parse(customCmd))
}
>
Send custom command
</button>
</div>
)
}

View File

@ -75,7 +75,11 @@ export const ExportButton = ({ children, className }: ExportButtonProps) => {
},
}
}
if (values.type === 'obj' || values.type === 'stl') {
if (
values.type === 'obj' ||
values.type === 'stl' ||
values.type === 'ply'
) {
values.units = baseUnit
}
if (
@ -86,6 +90,9 @@ export const ExportButton = ({ children, className }: ExportButtonProps) => {
// Set the storage type.
values.storage = storage
}
if (values.type === 'ply' || values.type === 'stl') {
values.selection = { type: 'default_scene' }
}
engineCommandManager.sendSceneCommand({
type: 'modeling_cmd_req',
cmd: {
@ -133,6 +140,7 @@ export const ExportButton = ({ children, className }: ExportButtonProps) => {
<select
id="type"
name="type"
data-testid="export-type"
onChange={(e) => {
setType(e.target.value as OutputTypeKey)
if (e.target.value === 'gltf') {
@ -162,6 +170,7 @@ export const ExportButton = ({ children, className }: ExportButtonProps) => {
<select
id="storage"
name="storage"
data-testid="export-storage"
onChange={(e) => {
setStorage(e.target.value as StorageUnion)
formik.handleChange(e)
@ -175,13 +184,13 @@ export const ExportButton = ({ children, className }: ExportButtonProps) => {
<option value="standard">standard</option>
</>
)}
{type === 'ply' && (
{type === 'stl' && (
<>
<option value="ascii">ascii</option>
<option value="binary">binary</option>
</>
)}
{type === 'stl' && (
{type === 'ply' && (
<>
<option value="ascii">ascii</option>
<option value="binary_little_endian">

View File

@ -2,14 +2,13 @@ import { IndexLoaderData, paths } from 'Router'
import { ActionButton } from './ActionButton'
import Tooltip from './Tooltip'
import { FileEntry } from '@tauri-apps/api/fs'
import { Dispatch, useEffect, useRef, useState } from 'react'
import { Dispatch, useRef, useState } from 'react'
import { useNavigate } from 'react-router-dom'
import { Dialog, Disclosure } from '@headlessui/react'
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { faChevronRight, faTrashAlt } from '@fortawesome/free-solid-svg-icons'
import { useFileContext } from 'hooks/useFileContext'
import { useHotkeys } from 'react-hotkeys-hook'
import { kclManager } from 'lang/KclSinglton'
import styles from './FileTree.module.css'
import { sortProject } from 'lib/tauriFS'
@ -163,7 +162,6 @@ const FileTreeItem = ({
function openFile() {
if (fileOrDir.children !== undefined) return // Don't open directories
kclManager.setCode('')
navigate(`${paths.FILE}/${encodeURIComponent(fileOrDir.path)}`)
closePanel()
}

View File

@ -10,7 +10,10 @@ const Loading = ({ children }: React.PropsWithChildren) => {
return () => clearTimeout(timer)
}, [setHasLongLoadTime])
return (
<div className="body-bg flex flex-col items-center justify-center h-screen">
<div
className="body-bg flex flex-col items-center justify-center h-screen"
data-testid="loading"
>
<svg viewBox="0 0 10 10" className="w-8 h-8">
<circle cx="5" cy="5" r="4" stroke="var(--liquid-20)" fill="none" />
<circle

View File

@ -20,7 +20,6 @@ import {
recast,
parse,
Program,
VariableDeclarator,
PipeExpression,
CallExpression,
} from 'lang/wasm'
@ -32,13 +31,17 @@ import {
} from 'lang/std/sketch'
import { kclManager } from 'lang/KclSinglton'
import { applyConstraintHorzVertDistance } from './Toolbar/SetHorzVertDistance'
import { applyConstraintAngleBetween } from './Toolbar/SetAngleBetween'
import {
angleBetweenInfo,
applyConstraintAngleBetween,
} from './Toolbar/SetAngleBetween'
import { applyConstraintAngleLength } from './Toolbar/setAngleLength'
import { toast } from 'react-hot-toast'
import { pathMapToSelections } from 'lang/util'
import { useStore } from 'useStore'
import { handleSelectionBatch, handleSelectionWithShift } from 'lib/selections'
import { applyConstraintIntersect } from './Toolbar/Intersect'
import { applyConstraintAbsDistance } from './Toolbar/SetAbsDistance'
type MachineContext<T extends AnyStateMachine> = {
state: StateFrom<T>
@ -148,7 +151,7 @@ export const ModelingMachineProvider = ({
engineCommandManager.artifactMap[sketchEnginePathId] = {
type: 'result',
range: [startProfileAtCallExp.start, startProfileAtCallExp.end],
commandType: 'extend_path',
commandType: 'start_path',
data: null,
raw: {} as any,
}
@ -263,17 +266,62 @@ export const ModelingMachineProvider = ({
'Set selection': assign(({ selectionRanges }, event) => {
if (event.type !== 'Set selection') return {} // this was needed for ts after adding 'Set selection' action to on done modal events
const setSelections = event.data
if (!editorView) return {}
if (setSelections.selectionType === 'mirrorCodeMirrorSelections')
return { selectionRanges: setSelections.selection }
else if (setSelections.selectionType === 'otherSelection')
else if (setSelections.selectionType === 'otherSelection') {
// TODO KittyCAD/engine/issues/1620: send axis highlight when it's working (if that's what we settle on)
// const axisAddCmd: EngineCommand = {
// type: 'modeling_cmd_req',
// cmd: {
// type: 'highlight_set_entities',
// entities: [
// setSelections.selection === 'x-axis'
// ? X_AXIS_UUID
// : Y_AXIS_UUID,
// ],
// },
// cmd_id: uuidv4(),
// }
// if (!isShiftDown) {
// engineCommandManager
// .sendSceneCommand({
// type: 'modeling_cmd_req',
// cmd: {
// type: 'select_clear',
// },
// cmd_id: uuidv4(),
// })
// .then(() => {
// engineCommandManager.sendSceneCommand(axisAddCmd)
// })
// } else {
// engineCommandManager.sendSceneCommand(axisAddCmd)
// }
const {
codeMirrorSelection,
selectionRangeTypeMap,
otherSelections,
} = handleSelectionWithShift({
otherSelection: setSelections.selection,
currentSelections: selectionRanges,
isShiftDown,
})
setTimeout(() => {
editorView.dispatch({
selection: codeMirrorSelection,
})
})
return {
selectionRangeTypeMap,
selectionRanges: {
...selectionRanges,
otherSelections: [setSelections.selection],
codeBasedSelections: selectionRanges.codeBasedSelections,
otherSelections,
},
}
else if (!editorView) return {}
else if (setSelections.selectionType === 'singleCodeCursor') {
} else if (setSelections.selectionType === 'singleCodeCursor') {
// This DOES NOT set the `selectionRanges` in xstate context
// instead it updates/dispatches to the editor, which in turn updates the xstate context
// I've found this the best way to deal with the editor without causing an infinite loop
@ -281,12 +329,16 @@ export const ModelingMachineProvider = ({
// because we want to respect the user manually placing the cursor too.
// for more details on how selections see `src/lib/selections.ts`.
const { codeMirrorSelection, selectionRangeTypeMap } =
handleSelectionWithShift({
codeSelection: setSelections.selection,
currestSelections: selectionRanges,
isShiftDown,
})
const {
codeMirrorSelection,
selectionRangeTypeMap,
otherSelections,
} = handleSelectionWithShift({
codeSelection: setSelections.selection,
currentSelections: selectionRanges,
isShiftDown,
})
if (codeMirrorSelection) {
setTimeout(() => {
editorView.dispatch({
@ -294,7 +346,22 @@ export const ModelingMachineProvider = ({
})
})
}
return { selectionRangeTypeMap }
if (!setSelections.selection) {
return {
selectionRangeTypeMap,
selectionRanges: {
codeBasedSelections: selectionRanges.codeBasedSelections,
otherSelections,
},
}
}
return {
selectionRangeTypeMap,
selectionRanges: {
codeBasedSelections: selectionRanges.codeBasedSelections,
otherSelections,
},
}
}
// This DOES NOT set the `selectionRanges` in xstate context
// same as comment above
@ -364,10 +431,16 @@ export const ModelingMachineProvider = ({
}
},
'Get angle info': async ({ selectionRanges }): Promise<SetSelections> => {
const { modifiedAst, pathToNodeMap } =
await applyConstraintAngleBetween({
selectionRanges,
})
const { modifiedAst, pathToNodeMap } = await (angleBetweenInfo({
selectionRanges,
}).enabled
? applyConstraintAngleBetween({
selectionRanges,
})
: applyConstraintAngleLength({
selectionRanges,
angleOrLength: 'setAngle',
}))
await kclManager.updateAst(modifiedAst, true)
return {
selectionType: 'completeSelection',
@ -410,6 +483,40 @@ export const ModelingMachineProvider = ({
),
}
},
'Get ABS X info': async ({ selectionRanges }): Promise<SetSelections> => {
const { modifiedAst, pathToNodeMap } = await applyConstraintAbsDistance(
{
constraint: 'xAbs',
selectionRanges,
}
)
await kclManager.updateAst(modifiedAst, true)
return {
selectionType: 'completeSelection',
selection: pathMapToSelections(
kclManager.ast,
selectionRanges,
pathToNodeMap
),
}
},
'Get ABS Y info': async ({ selectionRanges }): Promise<SetSelections> => {
const { modifiedAst, pathToNodeMap } = await applyConstraintAbsDistance(
{
constraint: 'yAbs',
selectionRanges,
}
)
await kclManager.updateAst(modifiedAst, true)
return {
selectionType: 'completeSelection',
selection: pathMapToSelections(
kclManager.ast,
selectionRanges,
pathToNodeMap
),
}
},
},
devTools: true,
})

View File

@ -108,7 +108,7 @@ export const SetAngleLengthModal = ({
</label>
<div className="mt-1 flex">
<button
className="border border-gray-300 px-2"
className="border border-gray-300 px-2 text-gray-900"
onClick={() => setSign(-sign)}
>
{sign > 0 ? '+' : '-'}
@ -118,7 +118,7 @@ export const SetAngleLengthModal = ({
type="text"
name="val"
id="val"
className="shadow-sm focus:ring-blue-500 focus:border-blue-500 block w-full sm:text-sm border-gray-300 rounded-md font-mono pl-1"
className="shadow-sm focus:ring-blue-500 focus:border-blue-500 block w-full sm:text-sm border-gray-300 rounded-md font-mono pl-1 text-gray-900"
value={value}
onChange={(e) => {
setValue(e.target.value)

View File

@ -87,7 +87,7 @@ export const GetInfoModal = ({
leaveFrom="opacity-100 scale-100"
leaveTo="opacity-0 scale-95"
>
<Dialog.Panel className="w-full max-w-md transform overflow-hidden rounded-2xl bg-white p-6 text-left align-middle shadow-xl transition-all">
<Dialog.Panel className="w-full max-w-md transform overflow-hidden rounded-2xl bg-white/90 p-6 text-left align-middle shadow-xl transition-all">
<Dialog.Title
as="h3"
className="text-lg font-medium leading-6 text-gray-900"
@ -109,7 +109,7 @@ export const GetInfoModal = ({
</label>
<div className="mt-1 flex">
<button
className="border border-gray-300 px-2 mr-1"
className="border border-gray-400 px-2 mr-1 text-gray-900"
onClick={() => setSign(-sign)}
>
{sign > 0 ? '+' : '-'}
@ -119,7 +119,7 @@ export const GetInfoModal = ({
name="val"
id="val"
ref={inputRef}
className="shadow-sm focus:ring-blue-500 focus:border-blue-500 block w-full sm:text-sm border-gray-300 rounded-md font-mono"
className="shadow-sm focus:ring-blue-500 focus:border-blue-500 block w-full sm:text-sm text-gray-900 border-gray-300 rounded-md font-mono"
value={value}
onChange={(e) => {
setValue(e.target.value)
@ -139,7 +139,7 @@ export const GetInfoModal = ({
name="segName"
id="segName"
disabled={!isSegNameEditable}
className="shadow-sm focus:ring-blue-500 focus:border-blue-500 block w-full sm:text-sm border-gray-300 rounded-md font-mono"
className="shadow-sm focus:ring-blue-500 focus:border-blue-500 block w-full sm:text-sm text-gray-900 border-gray-300 rounded-md font-mono"
value={segName}
onChange={(e) => {
setSegName(e.target.value)

View File

@ -14,7 +14,7 @@ import { useGlobalStateContext } from 'hooks/useGlobalStateContext'
import { CameraDragInteractionType_type } from '@kittycad/lib/dist/types/src/models'
import { Models } from '@kittycad/lib'
import { getNodeFromPath } from 'lang/queryAst'
import { VariableDeclarator, recast, parse, CallExpression } from 'lang/wasm'
import { VariableDeclarator, recast, CallExpression } from 'lang/wasm'
import { engineCommandManager } from '../lang/std/engineConnection'
import { useModelingContext } from 'hooks/useModelingContext'
import { kclManager, useKclContext } from 'lang/KclSinglton'
@ -267,11 +267,11 @@ export const Stream = ({ className = '' }) => {
}
engineCommandManager.sendSceneCommand(command).then(async () => {
if (!context.sketchPathToNode) return
const varDec = getNodeFromPath<VariableDeclarator>(
getNodeFromPath<VariableDeclarator>(
kclManager.ast,
context.sketchPathToNode,
'VariableDeclarator'
).node
)
// Get the current plane string for plane we are on.
let currentPlaneString = ''
if (context.sketchPlaneId === kclManager.getPlaneId('xy')) {
@ -342,7 +342,8 @@ export const Stream = ({ className = '' }) => {
// update artifact map ranges now that we have updated the ast.
code = recast(modded.modifiedAst)
const astWithCurrentRanges = parse(code)
const astWithCurrentRanges = kclManager.safeParse(code)
if (!astWithCurrentRanges) return
const updateNode = getNodeFromPath<CallExpression>(
astWithCurrentRanges,
modded.pathToNode
@ -355,6 +356,8 @@ export const Stream = ({ className = '' }) => {
kclManager.executeAstMock(modifiedAst, true)
})
} else {
engineCommandManager.sendSceneCommand(command)
}
setDidDragInStream(false)
@ -393,7 +396,9 @@ export const Stream = ({ className = '' }) => {
/>
{isLoading && (
<div className="text-center absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2">
<Loading>Loading stream...</Loading>
<Loading>
<span data-testid="loading-stream">Loading stream...</span>
</Loading>
</div>
)}
</div>

View File

@ -17,15 +17,7 @@ import { useStore } from 'useStore'
import { processCodeMirrorRanges } from 'lib/selections'
import { LanguageServerClient } from 'editor/lsp'
import kclLanguage from 'editor/lsp/language'
import { isTauri } from 'lib/isTauri'
import { useParams } from 'react-router-dom'
import { writeTextFile } from '@tauri-apps/api/fs'
import { toast } from 'react-hot-toast'
import {
EditorView,
addLineHighlight,
lineHighlightField,
} from 'editor/highlightextension'
import { EditorView, lineHighlightField } from 'editor/highlightextension'
import { roundOff } from 'lib/utils'
import { kclErrToDiagnostic } from 'lang/errors'
import { CSSRuleObject } from 'tailwindcss/types/config'
@ -50,14 +42,19 @@ export const TextEditor = ({
}: {
theme: Themes.Light | Themes.Dark
}) => {
const pathParams = useParams()
const { editorView, isLSPServerReady, setEditorView, setIsLSPServerReady } =
useStore((s) => ({
editorView: s.editorView,
isLSPServerReady: s.isLSPServerReady,
setEditorView: s.setEditorView,
setIsLSPServerReady: s.setIsLSPServerReady,
}))
const {
editorView,
isLSPServerReady,
setEditorView,
setIsLSPServerReady,
isShiftDown,
} = useStore((s) => ({
editorView: s.editorView,
isLSPServerReady: s.isLSPServerReady,
setEditorView: s.setEditorView,
setIsLSPServerReady: s.setIsLSPServerReady,
isShiftDown: s.isShiftDown,
}))
const { code, errors } = useKclContext()
const {
@ -92,7 +89,7 @@ export const TextEditor = ({
// Here we initialize the plugin which will start the client.
// When we have multi-file support the name of the file will be a dep of
// this use memo, as well as the directory structure, which I think is
// a good setup becuase it will restart the client but not the server :)
// a good setup because it will restart the client but not the server :)
// We do not want to restart the server, its just wasteful.
const kclLSP = useMemo(() => {
let plugin = null
@ -113,18 +110,6 @@ export const TextEditor = ({
// const onChange = React.useCallback((value: string, viewUpdate: ViewUpdate) => {
const onChange = (newCode: string) => {
kclManager.setCodeAndExecute(newCode)
if (isTauri() && pathParams.id) {
// Save the file to disk
// Note that PROJECT_ENTRYPOINT is hardcoded until we support multiple files
writeTextFile(pathParams.id, newCode).catch((err) => {
// TODO: add Sentry per GH issue #254 (https://github.com/KittyCAD/modeling-app/issues/254)
console.error('error saving file', err)
toast.error('Error saving file, please check file permissions')
})
}
if (editorView) {
editorView?.dispatch({ effects: addLineHighlight.of([0, 0]) })
}
} //, []);
const onUpdate = (viewUpdate: ViewUpdate) => {
if (!editorView) {
@ -134,6 +119,7 @@ export const TextEditor = ({
codeMirrorRanges: viewUpdate.state.selection.ranges,
selectionRanges,
selectionRangeTypeMap,
isShiftDown,
})
if (!eventInfo) return

View File

@ -69,7 +69,7 @@ export function applyConstraintEqualLength({
modifiedAst: Program
pathToNodeMap: PathToNodeMap
} {
const { enabled, transforms } = setEqualLengthInfo({ selectionRanges })
const { transforms } = setEqualLengthInfo({ selectionRanges })
const { modifiedAst, pathToNodeMap } = transformSecondarySketchLinesTagFirst({
ast: kclManager.ast,
selectionRanges,

View File

@ -59,6 +59,7 @@ export function angleBetweenInfo({
)
const _enableEqual =
selectionRanges.otherSelections.length === 0 &&
secondaryVarDecs.length === 1 &&
isAllTooltips &&
isOthersLinkedToPrimary &&

View File

@ -25,7 +25,7 @@ import { kclManager } from 'lang/KclSinglton'
const getModalInfo = createSetAngleLengthModal(SetAngleLengthModal)
export function setAngleLengthInfo({
export function angleLengthInfo({
selectionRanges,
angleOrLength = 'setLength',
}: {
@ -50,7 +50,10 @@ export function setAngleLengthInfo({
kclManager.ast,
angleOrLength
)
const enabled = isAllTooltips && transforms.every(Boolean)
const enabled =
selectionRanges.codeBasedSelections.length <= 1 &&
isAllTooltips &&
transforms.every(Boolean)
return { enabled, transforms }
}
@ -64,7 +67,7 @@ export async function applyConstraintAngleLength({
modifiedAst: Program
pathToNodeMap: PathToNodeMap
}> {
const { transforms } = setAngleLengthInfo({ selectionRanges, angleOrLength })
const { transforms } = angleLengthInfo({ selectionRanges, angleOrLength })
const { valueUsedInTransform } = transformAstSketchLines({
ast: JSON.parse(JSON.stringify(kclManager.ast)),
selectionRanges,

View File

@ -165,6 +165,7 @@ const UserSidebarMenu = ({ user }: { user?: User }) => {
'text-destroy-20 group-hover:text-destroy-10 hover:text-destroy-10',
}}
className="border-transparent dark:border-transparent hover:border-destroy-40 dark:hover:border-destroy-60"
data-testid="user-sidebar-sign-out"
>
Sign out
</ActionButton>

View File

@ -27,4 +27,7 @@ export const lineHighlightField = StateField.define({
provide: (f) => EditorView.decorations.from(f),
})
const matchDeco = Decoration.mark({ class: 'bg-yellow-200' })
const matchDeco = Decoration.mark({
class: 'bg-yellow-200',
attributes: { 'data-testid': 'hover-highlight' },
})

View File

@ -108,6 +108,7 @@ export default class Client extends jsrpc.JSONRPCServerAndClient {
break
}
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
messageString += message
return
})

View File

@ -26,7 +26,7 @@ export class Codec {
}
}
// FIXME: tracing effiency
// FIXME: tracing efficiency
export class IntoServer
extends Queue<Uint8Array>
implements AsyncGenerator<Uint8Array, never, void>

View File

@ -2,8 +2,6 @@ import { useEffect } from 'react'
import { useStore } from 'useStore'
import { engineCommandManager } from '../lang/std/engineConnection'
import { useModelingContext } from './useModelingContext'
import { v4 as uuidv4 } from 'uuid'
import { SourceRange } from 'lang/wasm'
import { getEventForSelectWithPoint } from 'lib/selections'
export function useEngineConnectionSubscriptions() {
@ -13,11 +11,6 @@ export function useEngineConnectionSubscriptions() {
}))
const { send, context } = useModelingContext()
interface RangeAndId {
id: string
range: SourceRange
}
useEffect(() => {
if (!engineCommandManager) return
@ -42,7 +35,7 @@ export function useEngineConnectionSubscriptions() {
const event = await getEventForSelectWithPoint(engineEvent, {
sketchEnginePathId: context.sketchEnginePathId,
})
send(event)
event && send(event)
},
})
return () => {

View File

@ -1,5 +1,5 @@
import { useLayoutEffect, useEffect, useRef } from 'react'
import { _executor, parse } from '../lang/wasm'
import { parse } from '../lang/wasm'
import { useStore } from '../useStore'
import { engineCommandManager } from '../lang/std/engineConnection'
import { deferExecution } from 'lib/utils'
@ -26,10 +26,6 @@ export function useSetupEngineManager(
const hasSetNonZeroDimensions = useRef<boolean>(false)
useEffect(() => {
kclManager.executeCode()
}, [])
useLayoutEffect(() => {
// Load the engine command manager once with the initial width and height,
// then we do not want to reload it.
@ -86,9 +82,14 @@ export function useSetupEngineManager(
}
function getDimensions(streamWidth?: number, streamHeight?: number) {
const maxResolution = 2000
const width = streamWidth ? streamWidth : 0
const quadWidth = Math.round(width / 4) * 4
const height = streamHeight ? streamHeight : 0
const quadHeight = Math.round(height / 4) * 4
const ratio = Math.min(
Math.min(maxResolution / width, maxResolution / height),
1.0
)
const quadWidth = Math.round((width * ratio) / 4) * 4
const quadHeight = Math.round((height * ratio) / 4) * 4
return { width: quadWidth, height: quadHeight }
}

View File

@ -7,10 +7,11 @@ import { HotkeysProvider } from 'react-hotkeys-hook'
import { inspect } from '@xstate/inspect'
import { DEV } from 'env'
if (DEV)
inspect({
iframe: false,
})
// uncomment for xstate inspector
// if (DEV)
// inspect({
// iframe: false,
// })
const root = ReactDOM.createRoot(document.getElementById('root') as HTMLElement)

View File

@ -18,7 +18,11 @@ import { bracket } from 'lib/exampleKcl'
import { createContext, useContext, useEffect, useState } from 'react'
import { getNodeFromPath } from './queryAst'
import { IndexLoaderData } from 'Router'
import { useLoaderData } from 'react-router-dom'
import { Params, useLoaderData } from 'react-router-dom'
import { isTauri } from 'lib/isTauri'
import { writeTextFile } from '@tauri-apps/api/fs'
import { toast } from 'react-hot-toast'
import { useParams } from 'react-router-dom'
const PERSIST_CODE_TOKEN = 'persistCode'
@ -41,10 +45,20 @@ class KclManager {
private _kclErrors: KCLError[] = []
private _isExecuting = false
private _wasmInitFailed = true
private _params: Params<string> = {}
engineCommandManager: EngineCommandManager
private _defferer = deferExecution((code: string) => {
const ast = parse(code)
const ast = this.safeParse(code)
if (!ast) return
try {
const fmtAndStringify = (ast: Program) =>
JSON.stringify(parse(recast(ast)))
const isAstTheSame = fmtAndStringify(ast) === fmtAndStringify(this._ast)
if (isAstTheSame) return
} catch (e) {
console.error(e)
}
this.executeAst(ast)
}, 600)
@ -71,6 +85,21 @@ class KclManager {
set code(code) {
this._code = code
this._codeCallBack(code)
if (isTauri()) {
setTimeout(() => {
// Wait one event loop to give a chance for params to be set
// Save the file to disk
// Note that PROJECT_ENTRYPOINT is hardcoded until we support multiple files
this._params.id &&
writeTextFile(this._params.id, code).catch((err) => {
// TODO: add Sentry per GH issue #254 (https://github.com/KittyCAD/modeling-app/issues/254)
console.error('error saving file', err)
toast.error('Error saving file, please check file permissions')
})
})
} else {
localStorage.setItem(PERSIST_CODE_TOKEN, code)
}
}
get programMemory() {
@ -117,10 +146,19 @@ class KclManager {
this._wasmInitFailedCallback(wasmInitFailed)
}
setParams(params: Params<string>) {
this._params = params
}
constructor(engineCommandManager: EngineCommandManager) {
this.engineCommandManager = engineCommandManager
if (isTauri()) {
this.code = ''
return
}
const storedCode = localStorage.getItem(PERSIST_CODE_TOKEN)
// TODO #819 remove zustand persistance logic in a few months
// TODO #819 remove zustand persistence logic in a few months
// short term migration, shouldn't make a difference for tauri app users
// anyway since that's filesystem based.
const zustandStore = JSON.parse(localStorage.getItem('store') || '{}')
@ -130,6 +168,7 @@ class KclManager {
zustandStore.state.code = ''
localStorage.setItem('store', JSON.stringify(zustandStore))
} else if (storedCode === null) {
console.log('stored brack thing')
this.code = bracket
} else {
this.code = storedCode
@ -164,6 +203,21 @@ class KclManager {
this._executeCallback = callback
}
safeParse(code: string): Program | null {
try {
const ast = parse(code)
this.kclErrors = []
return ast
} catch (e) {
console.error('error parsing code', e)
if (e instanceof KCLError) {
this.kclErrors = [e]
if (e.msg === 'file is empty') engineCommandManager.endSession()
}
return null
}
}
async ensureWasmInit() {
try {
await initPromise
@ -184,20 +238,24 @@ class KclManager {
defaultPlanes: this.defaultPlanes,
})
this.isExecuting = false
this._logs = logs
this._kclErrors = errors
this._programMemory = programMemory
this._ast = { ...ast }
this.logs = logs
this.kclErrors = errors
this.programMemory = programMemory
this.ast = { ...ast }
if (updateCode) {
this._code = recast(ast)
this._codeCallBack(this._code)
this.code = recast(ast)
}
this._executeCallback()
engineCommandManager.addCommandLog({
type: 'execution-done',
data: null,
})
}
async executeAstMock(ast: Program = this._ast, updateCode = false) {
await this.ensureWasmInit()
const newCode = recast(ast)
const newAst = parse(newCode)
const newAst = this.safeParse(newCode)
if (!newAst) return
await this?.engineCommandManager?.waitForReady
if (updateCode) {
this.setCode(recast(ast))
@ -233,13 +291,17 @@ class KclManager {
this.ast = ast
if (code) this.code = code
}
setCode(code: string) {
setCode(code: string, shouldWriteFile = true) {
if (shouldWriteFile) {
// use the normal code setter
this.code = code
return
}
this._code = code
this._codeCallBack(code)
localStorage.setItem(PERSIST_CODE_TOKEN, code)
}
setCodeAndExecute(code: string) {
this.setCode(code)
setCodeAndExecute(code: string, shouldWriteFile = true) {
this.setCode(code, shouldWriteFile)
if (code.trim()) {
this._defferer(code)
return
@ -260,9 +322,11 @@ class KclManager {
this.engineCommandManager.endSession()
}
format() {
this.code = recast(parse(kclManager.code))
const ast = this.safeParse(this.code)
if (!ast) return
this.code = recast(ast)
}
// There's overlapping resposibility between updateAst and executeAst.
// There's overlapping responsibility between updateAst and executeAst.
// updateAst was added as it was used a lot before xState migration so makes the port easier.
// but should probably have think about which of the function to keep
async updateAst(
@ -270,15 +334,13 @@ class KclManager {
execute: boolean,
optionalParams?: {
focusPath?: PathToNode
callBack?: (ast: Program) => void
}
): Promise<Selections | null> {
const newCode = recast(ast)
const astWithUpdatedSource = parse(newCode)
optionalParams?.callBack?.(astWithUpdatedSource)
const astWithUpdatedSource = this.safeParse(newCode)
if (!astWithUpdatedSource) return null
let returnVal: Selections | null = null
this.code = newCode
if (optionalParams?.focusPath) {
const { node } = getNodeFromPath<any>(
astWithUpdatedSource,
@ -299,12 +361,12 @@ class KclManager {
if (execute) {
// Call execute on the set ast.
await this.executeAst(astWithUpdatedSource)
await this.executeAst(astWithUpdatedSource, true)
} else {
// When we don't re-execute, we still want to update the program
// memory with the new ast. So we will hit the mock executor
// instead.
await this.executeAstMock(astWithUpdatedSource)
await this.executeAstMock(astWithUpdatedSource, true)
}
return returnVal
}
@ -369,6 +431,11 @@ export function KclContextProvider({
setWasmInitFailed,
})
}, [])
const params = useParams()
useEffect(() => {
kclManager.setParams(params)
}, [params])
return (
<KclContext.Provider
value={{

View File

@ -169,16 +169,24 @@ describe('testing function declaration', () => {
end: 39,
params: [
{
type: 'Identifier',
start: 12,
end: 13,
name: 'a',
type: 'Parameter',
identifier: {
type: 'Identifier',
start: 12,
end: 13,
name: 'a',
},
optional: false,
},
{
type: 'Identifier',
start: 15,
end: 16,
name: 'b',
type: 'Parameter',
identifier: {
type: 'Identifier',
start: 15,
end: 16,
name: 'b',
},
optional: false,
},
],
body: {
@ -244,16 +252,24 @@ const myVar = funcN(1, 2)`
end: 37,
params: [
{
type: 'Identifier',
start: 12,
end: 13,
name: 'a',
type: 'Parameter',
identifier: {
type: 'Identifier',
start: 12,
end: 13,
name: 'a',
},
optional: false,
},
{
type: 'Identifier',
start: 15,
end: 16,
name: 'b',
type: 'Parameter',
identifier: {
type: 'Identifier',
start: 15,
end: 16,
name: 'b',
},
optional: false,
},
],
body: {
@ -331,9 +347,6 @@ const myVar = funcN(1, 2)`
raw: '2',
},
],
function: {
type: 'InMemory',
},
optional: false,
},
},
@ -403,7 +416,6 @@ describe('testing pipe operator special', () => {
],
},
],
function: expect.any(Object),
optional: false,
},
{
@ -440,7 +452,6 @@ describe('testing pipe operator special', () => {
},
{ type: 'PipeSubstitution', start: 59, end: 60 },
],
function: expect.any(Object),
optional: false,
},
{
@ -513,7 +524,6 @@ describe('testing pipe operator special', () => {
},
{ type: 'PipeSubstitution', start: 105, end: 106 },
],
function: expect.any(Object),
optional: false,
},
{
@ -550,7 +560,6 @@ describe('testing pipe operator special', () => {
},
{ type: 'PipeSubstitution', start: 128, end: 129 },
],
function: expect.any(Object),
optional: false,
},
{
@ -573,9 +582,6 @@ describe('testing pipe operator special', () => {
},
{ type: 'PipeSubstitution', start: 143, end: 144 },
],
function: {
type: 'InMemory',
},
optional: false,
},
],
@ -655,9 +661,6 @@ describe('testing pipe operator special', () => {
end: 35,
},
],
function: {
type: 'InMemory',
},
optional: false,
},
],
@ -1345,7 +1348,7 @@ describe('nests binary expressions correctly', () => {
],
})
})
it('should nest properly with two opperators of equal precedence', () => {
it('should nest properly with two operators of equal precedence', () => {
const code = `const yo = 1 + 2 - 3`
const { body } = parse(code)
expect((body[0] as any).declarations[0].init).toEqual({
@ -1382,7 +1385,7 @@ describe('nests binary expressions correctly', () => {
},
})
})
it('should nest properly with two opperators of equal (but higher) precedence', () => {
it('should nest properly with two operators of equal (but higher) precedence', () => {
const code = `const yo = 1 * 2 / 3`
const { body } = parse(code)
expect((body[0] as any).declarations[0].init).toEqual({
@ -1443,7 +1446,7 @@ describe('nests binary expressions correctly', () => {
type: 'BinaryExpression',
operator: '*',
start: 15,
end: 26,
end: 25,
left: { type: 'Literal', value: 2, raw: '2', start: 15, end: 16 },
right: {
type: 'BinaryExpression',
@ -1567,7 +1570,6 @@ describe('test UnaryExpression', () => {
{ type: 'Literal', start: 19, end: 20, value: 4, raw: '4' },
{ type: 'Literal', start: 22, end: 25, value: 100, raw: '100' },
],
function: expect.any(Object),
optional: false,
},
})
@ -1601,12 +1603,10 @@ describe('testing nested call expressions', () => {
{ type: 'Literal', start: 34, end: 35, value: 5, raw: '5' },
{ type: 'Literal', start: 37, end: 38, value: 3, raw: '3' },
],
function: expect.any(Object),
optional: false,
},
},
],
function: expect.any(Object),
optional: false,
})
})
@ -1638,7 +1638,6 @@ describe('should recognise callExpresions in binaryExpressions', () => {
},
{ type: 'PipeSubstitution', start: 25, end: 26 },
],
function: expect.any(Object),
optional: false,
},
right: { type: 'Literal', value: 1, raw: '1', start: 30, end: 31 },

View File

@ -18,6 +18,20 @@ export class KCLError {
}
}
export class KCLLexicalError extends KCLError {
constructor(msg: string, sourceRanges: [number, number][]) {
super('lexical', msg, sourceRanges)
Object.setPrototypeOf(this, KCLSyntaxError.prototype)
}
}
export class KCLInternalError extends KCLError {
constructor(msg: string, sourceRanges: [number, number][]) {
super('internal', msg, sourceRanges)
Object.setPrototypeOf(this, KCLSyntaxError.prototype)
}
}
export class KCLSyntaxError extends KCLError {
constructor(msg: string, sourceRanges: [number, number][]) {
super('syntax', msg, sourceRanges)

View File

@ -1,5 +1,5 @@
import { getNodePathFromSourceRange, getNodeFromPath } from './queryAst'
import { Identifier, parse, initPromise } from './wasm'
import { Identifier, parse, initPromise, Parameter } from './wasm'
beforeAll(() => initPromise)
@ -46,7 +46,7 @@ const b1 = cube([0,0], 10)`
const ast = parse(code)
const nodePath = getNodePathFromSourceRange(ast, sourceRange)
const node = getNodeFromPath<Identifier>(ast, nodePath).node
const node = getNodeFromPath<Parameter>(ast, nodePath).node
expect(nodePath).toEqual([
['body', ''],
@ -57,8 +57,8 @@ const b1 = cube([0,0], 10)`
['params', 'FunctionExpression'],
[0, 'index'],
])
expect(node.type).toBe('Identifier')
expect(node.name).toBe('pos')
expect(node.type).toBe('Parameter')
expect(node.identifier.name).toBe('pos')
})
it('gets path right for deep within function definition body', () => {
const code = `fn cube = (pos, scale) => {

View File

@ -310,7 +310,7 @@ export function extrudeSketch(
const name = findUniqueName(node, 'part')
const VariableDeclaration = createVariableDeclaration(name, extrudeCall)
let showCallIndex = getShowIndex(_node)
if (showCallIndex == -1) {
if (showCallIndex === -1) {
// We didn't find a show, so let's just append everything
showCallIndex = _node.body.length
}
@ -480,21 +480,6 @@ export function createCallExpressionStdLib(
end: 0,
name,
},
function: {
type: 'StdLib',
func: {
// We only need the name here to map it back when it serializes
// to rust, don't worry about the rest.
name,
summary: '',
description: '',
tags: [],
returnValue: { type: '', required: false, name: '', schema: {} },
args: [],
unpublished: false,
deprecated: false,
},
},
optional: false,
arguments: args,
}
@ -514,9 +499,6 @@ export function createCallExpression(
end: 0,
name,
},
function: {
type: 'InMemory',
},
optional: false,
arguments: args,
}

View File

@ -247,10 +247,10 @@ function moreNodePathFromSourceRange(
if (_node.type === 'FunctionExpression' && isInRange) {
for (let i = 0; i < _node.params.length; i++) {
const param = _node.params[i]
if (param.start <= start && param.end >= end) {
if (param.identifier.start <= start && param.identifier.end >= end) {
path.push(['params', 'FunctionExpression'])
path.push([i, 'index'])
return moreNodePathFromSourceRange(param, sourceRange, path)
return moreNodePathFromSourceRange(param.identifier, sourceRange, path)
}
}
if (_node.body.start <= start && _node.body.end >= end) {

View File

@ -2,5 +2,5 @@ The std is as expected, tools that are provided with the language.
For this language that means functions.
However because programatically changing the source code is a first class citizen in this lang, there needs to be helpes for adding and modifying these function calls,
However because programmatically changing the source code is a first class citizen in this lang, there needs to be helpers for adding and modifying these function calls,
So it makes sense to group some of these together.

View File

@ -51,7 +51,7 @@ type ClientMetrics = Models['ClientMetrics_type']
// EngineConnection encapsulates the connection(s) to the Engine
// for the EngineCommandManager; namely, the underlying WebSocket
// and WebRTC connections.
export class EngineConnection {
class EngineConnection {
websocket?: WebSocket
pc?: RTCPeerConnection
unreliableDataChannel?: RTCDataChannel
@ -216,6 +216,26 @@ export class EngineConnection {
}
})
this.pc.addEventListener('icecandidateerror', (_event) => {
const event = _event as RTCPeerConnectionIceErrorEvent
console.error(
`ICE candidate returned an error: ${event.errorCode}: ${event.errorText} for ${event.url}`
)
})
this.pc.addEventListener('connectionstatechange', (event) => {
if (this.pc?.iceConnectionState === 'connected') {
if (this.shouldTrace()) {
iceSpan.resolve?.()
}
} else if (this.pc?.iceConnectionState === 'failed') {
// failed is a terminal state; let's explicitly kill the
// connection to the server at this point.
console.log('failed to negotiate ice connection; restarting')
this.close()
}
})
this.websocket.addEventListener('open', (event) => {
if (this.shouldTrace()) {
websocketSpan.resolve?.()
@ -351,19 +371,6 @@ export class EngineConnection {
// until the end of this function is setup of our end of the
// PeerConnection and waiting for events to fire our callbacks.
this.pc.addEventListener('connectionstatechange', (event) => {
if (this.pc?.iceConnectionState === 'connected') {
if (this.shouldTrace()) {
iceSpan.resolve?.()
}
} else if (this.pc?.iceConnectionState === 'failed') {
// failed is a terminal state; let's explicitly kill the
// connection to the server at this point.
console.log('failed to negotiate ice connection; restarting')
this.close()
}
})
this.pc.addEventListener('icecandidate', (event) => {
if (!this.pc || !this.websocket) return
if (event.candidate !== null) {
@ -583,12 +590,35 @@ interface Subscription<T extends ModelTypes> {
) => void
}
export type CommandLog =
| {
type: 'send-modeling'
data: EngineCommand
}
| {
type: 'send-scene'
data: EngineCommand
}
| {
type: 'receive-reliable'
data: WebSocketResponse
id: string
cmd_type?: string
}
| {
type: 'execution-done'
data: null
}
export class EngineCommandManager {
artifactMap: ArtifactMap = {}
lastArtifactMap: ArtifactMap = {}
outSequence = 1
inSequence = 1
engineConnection?: EngineConnection
defaultPlanes: DefaultPlanes = { xy: '', yz: '', xz: '' }
_commandLogs: CommandLog[] = []
_commandLogCallBack: (command: CommandLog[]) => void = () => {}
// Folks should realize that wait for ready does not get called _everytime_
// the connection resets and restarts, it only gets called the first time.
// Be careful what you put here.
@ -633,7 +663,10 @@ export class EngineCommandManager {
// If we already have an engine connection, just need to resize the stream.
if (this.engineConnection) {
this.handleResize({ streamWidth: width, streamHeight: height })
this.handleResize({
streamWidth: width,
streamHeight: height,
})
return
}
@ -664,7 +697,7 @@ export class EngineCommandManager {
},
})
// Inisialize the planes.
// Initialize the planes.
this.initPlanes().then(() => {
// We execute the code here to make sure if the stream was to
// restart in a session, we want to make sure to execute the code.
@ -776,11 +809,17 @@ export class EngineCommandManager {
return
}
const modelingResponse = message.data.modeling_response
const command = this.artifactMap[id]
this.addCommandLog({
type: 'receive-reliable',
data: message,
id,
cmd_type: command?.commandType || this.lastArtifactMap[id]?.commandType,
})
Object.values(this.subscriptions[modelingResponse.type] || {}).forEach(
(callback) => callback(modelingResponse)
)
const command = this.artifactMap[id]
if (command && command.type === 'pending') {
const resolve = command.resolve
this.artifactMap[id] = {
@ -844,6 +883,7 @@ export class EngineCommandManager {
this.engineConnection?.tearDown()
}
startNewSession() {
this.lastArtifactMap = this.artifactMap
this.artifactMap = {}
}
subscribeTo<T extends ModelTypes>({
@ -887,8 +927,9 @@ export class EngineCommandManager {
}
endSession() {
// TODO: instead of sending a single command with `object_ids: Object.keys(this.artifactMap)`
// we need to loop over them each individualy because if the engine doesn't recognise a single
// we need to loop over them each individually because if the engine doesn't recognise a single
// id the whole command fails.
const artifactsToDelete: any = {}
Object.entries(this.artifactMap).forEach(([id, artifact]) => {
const artifactTypesToDelete: ArtifactMap[string]['commandType'][] = [
// 'start_path' creates a new scene object for the path, which is why it needs to be deleted,
@ -898,7 +939,9 @@ export class EngineCommandManager {
'start_path',
]
if (!artifactTypesToDelete.includes(artifact.commandType)) return
artifactsToDelete[id] = artifact
})
Object.keys(artifactsToDelete).forEach((id) => {
const deletCmd: EngineCommand = {
type: 'modeling_cmd_req',
cmd_id: uuidv4(),
@ -910,6 +953,21 @@ export class EngineCommandManager {
this.engineConnection?.send(deletCmd)
})
}
addCommandLog(message: CommandLog) {
if (this._commandLogs.length > 500) {
this._commandLogs.shift()
}
this._commandLogs.push(message)
this._commandLogCallBack([...this._commandLogs])
}
clearCommandLogs() {
this._commandLogs = []
this._commandLogCallBack(this._commandLogs)
}
registerCommandLogCallback(callback: (command: CommandLog[]) => void) {
this._commandLogCallBack = callback
}
sendSceneCommand(command: EngineCommand): Promise<any> {
if (this.engineConnection === undefined) {
return Promise.resolve()
@ -919,6 +977,20 @@ export class EngineCommandManager {
return Promise.resolve()
}
if (
!(
command.type === 'modeling_cmd_req' &&
(command.cmd.type === 'highlight_set_entity' ||
command.cmd.type === 'mouse_move')
)
) {
// highlight_set_entity and mouse_move are sent over the unreliable channel and are too noisy
this.addCommandLog({
type: 'send-scene',
data: command,
})
}
if (
command.type === 'modeling_cmd_req' &&
command.cmd.type !== lastMessage
@ -974,6 +1046,17 @@ export class EngineCommandManager {
if (!this.engineConnection?.isReady()) {
return Promise.resolve()
}
if (typeof command !== 'string') {
this.addCommandLog({
type: 'send-modeling',
data: command,
})
} else {
this.addCommandLog({
type: 'send-modeling',
data: JSON.parse(command),
})
}
this.engineConnection?.send(command)
if (typeof command !== 'string' && command.type === 'modeling_cmd_req') {
return this.handlePendingCommand(id, command?.cmd, range)
@ -982,7 +1065,7 @@ export class EngineCommandManager {
if (parseCommand.type === 'modeling_cmd_req')
return this.handlePendingCommand(id, parseCommand?.cmd, range)
}
throw 'shouldnt reach here'
throw Error('shouldnt reach here')
}
handlePendingCommand(
id: string,

View File

@ -20,7 +20,6 @@ import {
import { isLiteralArrayOrStatic } from './sketchcombos'
import { toolTips, ToolTip } from '../../useStore'
import { createPipeExpression, splitPathAtPipeExpression } from '../modifyAst'
import { generateUuidFromHashSeed } from '../../lib/uuid'
import { SketchLineHelper, ModifyAstBase, TransformCallback } from './stdTypes'
@ -92,18 +91,12 @@ export function createFirstArg(
throw new Error('all sketch line types should have been covered')
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
type LineData = {
from: [number, number, number]
to: [number, number, number]
}
function makeId(seed: string | any) {
if (typeof seed === 'string') {
return generateUuidFromHashSeed(seed)
}
return generateUuidFromHashSeed(JSON.stringify(seed))
}
export const lineTo: SketchLineHelper = {
add: ({
node,
@ -250,9 +243,6 @@ export const line: SketchLineHelper = {
])
if (callExpression.arguments?.[0].type === 'ObjectExpression') {
const toProp = callExpression.arguments?.[0].properties?.find(
({ key }) => key.name === 'to'
)
mutateObjExpProp(callExpression.arguments?.[0], toArrExp, 'to')
} else {
mutateArrExp(callExpression.arguments?.[0], toArrExp)
@ -956,7 +946,7 @@ export function compareVec2Epsilon(
) {
const compareEpsilon = 0.015625 // or 2^-6
const xDifference = Math.abs(vec1[0] - vec2[0])
const yDifference = Math.abs(vec1[0] - vec2[0])
const yDifference = Math.abs(vec1[1] - vec2[1])
return xDifference < compareEpsilon && yDifference < compareEpsilon
}
@ -974,14 +964,12 @@ export function addNewSketchLn({
const node = JSON.parse(JSON.stringify(_node))
const { add, updateArgs } = sketchLineHelperMap?.[fnName] || {}
if (!add || !updateArgs) throw new Error('not a sketch line helper')
const { node: varDec } = getNodeFromPath<VariableDeclarator>(
getNodeFromPath<VariableDeclarator>(node, pathToNode, 'VariableDeclarator')
getNodeFromPath<PipeExpression | CallExpression>(
node,
pathToNode,
'VariableDeclarator'
'PipeExpression'
)
const { node: pipeExp, shallowPath: pipePath } = getNodeFromPath<
PipeExpression | CallExpression
>(node, pathToNode, 'PipeExpression')
return add({
node,
previousProgramMemory,

View File

@ -50,7 +50,7 @@ async function testingSwapSketchFnCall({
}
}
describe('testing swaping out sketch calls with xLine/xLineTo', () => {
describe('testing swapping out sketch calls with xLine/xLineTo', () => {
const bigExampleArr = [
`const part001 = startSketchOn('XY')`,
` |> startProfileAt([0, 0], %)`,
@ -178,7 +178,7 @@ describe('testing swaping out sketch calls with xLine/xLineTo', () => {
constraintType: 'horizontal',
})
const expectedLine = "xLine({ length: -0.86, tag: 'abc4' }, %)"
// hmm "-0.86" is correct since the angle is 104, but need to make sure this is compatiable `-myVar`
// hmm "-0.86" is correct since the angle is 104, but need to make sure this is compatible `-myVar`
expect(newCode).toContain(expectedLine)
// new line should start at the same place as the old line
expect(originalRange[0]).toBe(newCode.indexOf(expectedLine))
@ -268,7 +268,7 @@ describe('testing swaping out sketch calls with xLine/xLineTo', () => {
})
})
describe('testing swaping out sketch calls with xLine/xLineTo while keeping variable/identifiers intact', () => {
describe('testing swapping out sketch calls with xLine/xLineTo while keeping variable/identifiers intact', () => {
// Enable rotations #152
const variablesExampleArr = [
`const lineX = -1`,

View File

@ -108,7 +108,7 @@ const part001 = startSketchOn('XY')
|> line([myVar, 1], %) // ln-should use legLen for y
|> line([myVar, -1], %) // ln-legLen but negative
|> line([-0.62, -1.54], %) // ln-should become angledLine
|> angledLine([myVar, 1.04], %) // ln-use segLen for secound arg
|> angledLine([myVar, 1.04], %) // ln-use segLen for second arg
|> angledLine([45, 1.04], %) // ln-segLen again
|> angledLineOfXLength([54, 2.35], %) // ln-should be transformed to angledLine
|> angledLineOfXLength([50, myVar], %) // ln-should use legAngX to calculate angle
@ -163,7 +163,7 @@ const part001 = startSketchOn('XY')
-legLen(segLen('seg01', %), myVar)
], %) // ln-legLen but negative
|> angledLine([-112, segLen('seg01', %)], %) // ln-should become angledLine
|> angledLine([myVar, segLen('seg01', %)], %) // ln-use segLen for secound arg
|> angledLine([myVar, segLen('seg01', %)], %) // ln-use segLen for second arg
|> angledLine([45, segLen('seg01', %)], %) // ln-segLen again
|> angledLine([54, segLen('seg01', %)], %) // ln-should be transformed to angledLine
|> angledLineOfXLength([
@ -471,7 +471,7 @@ async function helperThing(
}
describe('testing getConstraintLevelFromSourceRange', () => {
it('should devide up lines into free, partial and fully contrained', () => {
it('should divide up lines into free, partial and fully contrained', () => {
const code = `const baseLength = 3
const baseThick = 1
const armThick = 0.5

View File

@ -334,10 +334,7 @@ const setAbsDistanceForAngleLineCreateNode =
): TransformInfo['createNode'] =>
({ tag, forceValueUsedInTransform, varValA }) => {
return (args, referencedSegment) => {
const valueUsedInTransform = roundOff(
getArgLiteralVal(args?.[1]) - (referencedSegment?.to?.[index] || 0),
2
)
const valueUsedInTransform = roundOff(getArgLiteralVal(args?.[1]), 2)
const val =
(forceValueUsedInTransform as BinaryPart) ||
createLiteral(valueUsedInTransform)

View File

@ -20,6 +20,7 @@ export type { ObjectExpression } from '../wasm-lib/kcl/bindings/ObjectExpression
export type { MemberExpression } from '../wasm-lib/kcl/bindings/MemberExpression'
export type { PipeExpression } from '../wasm-lib/kcl/bindings/PipeExpression'
export type { VariableDeclaration } from '../wasm-lib/kcl/bindings/VariableDeclaration'
export type { Parameter } from '../wasm-lib/kcl/bindings/Parameter'
export type { PipeSubstitution } from '../wasm-lib/kcl/bindings/PipeSubstitution'
export type { Identifier } from '../wasm-lib/kcl/bindings/Identifier'
export type { UnaryExpression } from '../wasm-lib/kcl/bindings/UnaryExpression'

View File

@ -15,7 +15,11 @@ export const browserSaveFile = async (blob: Blob, suggestedName: string) => {
}
})()
// If the File System Access API is supported…
if (supportsFileSystemAccess && window.showSaveFilePicker) {
if (
supportsFileSystemAccess &&
window.showSaveFilePicker &&
!(window as any).playwrightSkipFilePicker
) {
try {
// Show the file save dialog.
const handle = await window.showSaveFilePicker({
@ -30,8 +34,8 @@ export const browserSaveFile = async (blob: Blob, suggestedName: string) => {
// Fail silently if the user has simply canceled the dialog.
if (err.name !== 'AbortError') {
console.error(err.name, err.message)
return
}
return
}
}
// Fallback if the File System Access API is not supported…

View File

@ -26,5 +26,4 @@ const bracket = startSketchOn('XY')
|> close(%)
|> extrude(width, %)
show(bracket)
`

View File

@ -34,7 +34,7 @@ export async function exportSave(data: ArrayBuffer) {
// Create a new blob.
const blob = new Blob([new Uint8Array(file.contents)])
// Save the file.
browserSaveFile(blob, file.name)
await browserSaveFile(blob, file.name)
}
}
} catch (e) {

View File

@ -8,6 +8,9 @@ import { kclManager } from 'lang/KclSinglton'
import { SelectionRange } from '@uiw/react-codemirror'
import { isOverlap } from 'lib/utils'
export const X_AXIS_UUID = 'ad792545-7fd3-482a-a602-a93924e3055b'
export const Y_AXIS_UUID = '680fd157-266f-4b8a-984f-cdf46b8bdf01'
/*
How selections work is complex due to the nature that we rely on the engine
to tell what has been selected after we send a click command. But than the
@ -15,7 +18,7 @@ app needs these selections to be based on cursors, therefore the app must
be in control of selections. On top of that because we need to set cursor
positions in code-mirror for selections, both from app logic, and still
allow the user to add multiple cursors like a normal editor, it's best to
let code mirror control cursor positions and assosiate those source ranges
let code mirror control cursor positions and associate those source ranges
with entity ids from code-mirror events later.
So it's a lot of back and forth. conceptually the back and forth is:
@ -43,7 +46,7 @@ In detail:
1) Click commands are mostly sent in stream.tsx search for
"select_with_point"
2) The handler for when the engine sends back entitiy ids calls
2) The handler for when the engine sends back entity ids calls
getEventForSelectWithPoint, it fires an XState event to update our
selections is xstate context
3 and 4) The XState handler for the above uses handleSelectionBatch and
@ -102,14 +105,23 @@ export async function getEventForSelectWithPoint(
Models['OkModelingCmdResponse_type'],
{ type: 'select_with_point' }
>,
{ sketchEnginePathId }: { sketchEnginePathId: string }
): Promise<ModelingMachineEvent> {
{ sketchEnginePathId }: { sketchEnginePathId?: string }
): Promise<ModelingMachineEvent | null> {
if (!data?.entity_id) {
return {
type: 'Set selection',
data: { selectionType: 'singleCodeCursor' },
}
}
if ([X_AXIS_UUID, Y_AXIS_UUID].includes(data.entity_id)) {
return {
type: 'Set selection',
data: {
selectionType: 'otherSelection',
selection: X_AXIS_UUID === data.entity_id ? 'x-axis' : 'y-axis',
},
}
}
const sourceRange = engineCommandManager.artifactMap[data.entity_id]?.range
if (engineCommandManager.artifactMap[data.entity_id]) {
return {
@ -120,6 +132,7 @@ export async function getEventForSelectWithPoint(
},
}
}
if (!sketchEnginePathId) return null
// selected a vertex
const res = await engineCommandManager.sendSceneCommand({
type: 'modeling_cmd_req',
@ -163,6 +176,7 @@ export function handleSelectionBatch({
}): {
selectionRangeTypeMap: SelectionRangeTypeMap
codeMirrorSelection?: EditorSelection
otherSelections: Axis[]
} {
const ranges: ReturnType<typeof EditorSelection.cursor>[] = []
const selectionRangeTypeMap: SelectionRangeTypeMap = {}
@ -179,43 +193,74 @@ export function handleSelectionBatch({
ranges,
selections.codeBasedSelections.length - 1
),
otherSelections: selections.otherSelections,
}
return {
selectionRangeTypeMap,
otherSelections: selections.otherSelections,
}
}
export function handleSelectionWithShift({
codeSelection,
currestSelections,
otherSelection,
currentSelections,
isShiftDown,
}: {
codeSelection?: Selection
currestSelections: Selections
otherSelection?: Axis
currentSelections: Selections
isShiftDown: boolean
}): {
selectionRangeTypeMap: SelectionRangeTypeMap
otherSelections: Axis[]
codeMirrorSelection?: EditorSelection
} {
const code = kclManager.code
if (!codeSelection)
if (codeSelection && otherSelection) {
throw new Error('cannot have both code and other selection')
}
if (!codeSelection && !otherSelection) {
return handleSelectionBatch({
selections: {
otherSelections: currestSelections.otherSelections,
otherSelections: [],
codeBasedSelections: [
{
range: [0, code.length ? code.length - 1 : 0],
range: [0, code.length ? code.length : 0],
type: 'default',
},
],
},
})
}
if (otherSelection) {
console.log('otherSelection in handleSelectionWithShift', otherSelection)
return handleSelectionBatch({
selections: {
codeBasedSelections: isShiftDown
? currentSelections.codeBasedSelections
: [
{
range: [0, code.length ? code.length : 0],
type: 'default',
},
],
otherSelections: [otherSelection],
},
})
}
const isEndOfFileDumbySelection =
currentSelections.codeBasedSelections.length === 1 &&
currentSelections.codeBasedSelections[0].range[0] === kclManager.code.length
const newCodeBasedSelections = !isShiftDown
? [codeSelection!]
: isEndOfFileDumbySelection
? [codeSelection!]
: [...currentSelections.codeBasedSelections, codeSelection!]
const selections: Selections = {
...currestSelections,
codeBasedSelections: isShiftDown
? [...currestSelections.codeBasedSelections, codeSelection]
: [codeSelection],
otherSelections: isShiftDown ? currentSelections.otherSelections : [],
codeBasedSelections: newCodeBasedSelections,
}
return handleSelectionBatch({ selections })
}
@ -226,10 +271,12 @@ export function processCodeMirrorRanges({
codeMirrorRanges,
selectionRanges,
selectionRangeTypeMap,
isShiftDown,
}: {
codeMirrorRanges: readonly SelectionRange[]
selectionRanges: Selections
selectionRangeTypeMap: SelectionRangeTypeMap
isShiftDown: boolean
}): null | {
modelingEvent: ModelingMachineEvent
engineEvents: Models['WebSocketRequest_type'][]
@ -290,7 +337,7 @@ export function processCodeMirrorRanges({
data: {
selectionType: 'mirrorCodeMirrorSelections',
selection: {
...selectionRanges,
otherSelections: isShiftDown ? selectionRanges.otherSelections : [],
codeBasedSelections,
},
},
@ -299,11 +346,11 @@ export function processCodeMirrorRanges({
}
}
export function resetAndSetEngineEntitySelectionCmds(
function resetAndSetEngineEntitySelectionCmds(
selections: SelectionToEngine[]
): Models['WebSocketRequest_type'][] {
if (!engineCommandManager.engineConnection?.isReady()) {
console.log('engine connection isnt ready')
console.log('engine connection is not ready')
return []
}
return [

View File

@ -9,7 +9,6 @@ import { documentDir, homeDir, sep } from '@tauri-apps/api/path'
import { isTauri } from './isTauri'
import { ProjectWithEntryPointMetadata } from '../Router'
import { metadata } from 'tauri-plugin-fs-extra-api'
import { bracket } from './exampleKcl'
const PROJECT_FOLDER = 'kittycad-modeling-projects'
export const FILE_EXT = '.kcl'
@ -211,7 +210,8 @@ export function sortProject(project: FileEntry[]): FileEntry[] {
// Creates a new file in the default directory with the default project name
// Returns the path to the new file
export async function createNewProject(
path: string
path: string,
initCode = ''
): Promise<ProjectWithEntryPointMetadata> {
if (!isTauri) {
throw new Error('createNewProject() can only be called from a Tauri app')
@ -225,10 +225,12 @@ export async function createNewProject(
})
}
await writeTextFile(path + sep + PROJECT_ENTRYPOINT, bracket).catch((err) => {
console.error('Error creating new file:', err)
throw err
})
await writeTextFile(path + sep + PROJECT_ENTRYPOINT, initCode).catch(
(err) => {
console.error('Error creating new file:', err)
throw err
}
)
const m = await metadata(path)

View File

@ -9,6 +9,7 @@ import { v4 as uuidv4 } from 'uuid'
type WebSocketResponse = Models['OkWebSocketResponseData_type']
class MockEngineCommandManager {
// eslint-disable-next-line @typescript-eslint/no-useless-constructor
constructor(mockParams: {
setIsStreamReady: (isReady: boolean) => void
setMediaStream: (stream: MediaStream) => void

File diff suppressed because one or more lines are too long

View File

@ -5,11 +5,15 @@
'@@xstate/typegen': true;
internalEvents: {
"": { type: "" };
"done.invoke.get-abs-x-info": { type: "done.invoke.get-abs-x-info"; data: unknown; __tip: "See the XState TS docs to learn how to strongly type this." };
"done.invoke.get-abs-y-info": { type: "done.invoke.get-abs-y-info"; data: unknown; __tip: "See the XState TS docs to learn how to strongly type this." };
"done.invoke.get-angle-info": { type: "done.invoke.get-angle-info"; data: unknown; __tip: "See the XState TS docs to learn how to strongly type this." };
"done.invoke.get-horizontal-info": { type: "done.invoke.get-horizontal-info"; data: unknown; __tip: "See the XState TS docs to learn how to strongly type this." };
"done.invoke.get-length-info": { type: "done.invoke.get-length-info"; data: unknown; __tip: "See the XState TS docs to learn how to strongly type this." };
"done.invoke.get-perpendicular-distance-info": { type: "done.invoke.get-perpendicular-distance-info"; data: unknown; __tip: "See the XState TS docs to learn how to strongly type this." };
"done.invoke.get-vertical-info": { type: "done.invoke.get-vertical-info"; data: unknown; __tip: "See the XState TS docs to learn how to strongly type this." };
"error.platform.get-abs-x-info": { type: "error.platform.get-abs-x-info"; data: unknown };
"error.platform.get-abs-y-info": { type: "error.platform.get-abs-y-info"; data: unknown };
"error.platform.get-angle-info": { type: "error.platform.get-angle-info"; data: unknown };
"error.platform.get-horizontal-info": { type: "error.platform.get-horizontal-info"; data: unknown };
"error.platform.get-length-info": { type: "error.platform.get-length-info"; data: unknown };
@ -19,7 +23,9 @@
"xstate.stop": { type: "xstate.stop" };
};
invokeSrcNameMap: {
"Get angle info": "done.invoke.get-angle-info";
"Get ABS X info": "done.invoke.get-abs-x-info";
"Get ABS Y info": "done.invoke.get-abs-y-info";
"Get angle info": "done.invoke.get-angle-info";
"Get horizontal info": "done.invoke.get-horizontal-info";
"Get length info": "done.invoke.get-length-info";
"Get perpendicular distance info": "done.invoke.get-perpendicular-distance-info";
@ -29,7 +35,7 @@
actions: "AST add line segment" | "AST start new sketch" | "Modify AST" | "Set selection" | "Update code selection cursors" | "create path" | "set tool" | "show default planes" | "sketch exit execute" | "toast extrude failed";
delays: never;
guards: "Selection contains axis" | "Selection contains edge" | "Selection contains face" | "Selection contains line" | "Selection contains point" | "Selection is not empty" | "Selection is one face";
services: "Get angle info" | "Get horizontal info" | "Get length info" | "Get perpendicular distance info" | "Get vertical info";
services: "Get ABS X info" | "Get ABS Y info" | "Get angle info" | "Get horizontal info" | "Get length info" | "Get perpendicular distance info" | "Get vertical info";
};
eventsCausingActions: {
"AST add line segment": "Add point";
@ -42,19 +48,21 @@
"Constrain horizontally align": "Constrain horizontally align";
"Constrain parallel": "Constrain parallel";
"Constrain remove constraints": "Constrain remove constraints";
"Constrain snap to X": "Constrain snap to X";
"Constrain snap to Y": "Constrain snap to Y";
"Constrain vertically align": "Constrain vertically align";
"Make selection horizontal": "Make segment horizontal";
"Make selection vertical": "Make segment vertical";
"Modify AST": "Complete line";
"Remove from code-based selection": "Deselect edge" | "Deselect face" | "Deselect point";
"Remove from other selection": "Deselect axis";
"Set selection": "Set selection" | "done.invoke.get-angle-info" | "done.invoke.get-horizontal-info" | "done.invoke.get-length-info" | "done.invoke.get-perpendicular-distance-info" | "done.invoke.get-vertical-info";
"Set selection": "Set selection" | "done.invoke.get-abs-x-info" | "done.invoke.get-abs-y-info" | "done.invoke.get-angle-info" | "done.invoke.get-horizontal-info" | "done.invoke.get-length-info" | "done.invoke.get-perpendicular-distance-info" | "done.invoke.get-vertical-info";
"Update code selection cursors": "Complete line" | "Deselect all" | "Deselect axis" | "Deselect edge" | "Deselect face" | "Deselect point" | "Deselect segment" | "Select edge" | "Select face" | "Select point" | "Select segment";
"create path": "Select default plane";
"default_camera_disable_sketch_mode": "Cancel";
"edit mode enter": "Enter sketch" | "Re-execute";
"edit_mode_exit": "Cancel";
"equip select": "CancelSketch" | "Constrain equal length" | "Constrain horizontally align" | "Constrain parallel" | "Constrain remove constraints" | "Constrain vertically align" | "Deselect point" | "Deselect segment" | "Enter sketch" | "Make segment horizontal" | "Make segment vertical" | "Re-execute" | "Select default plane" | "Select point" | "Select segment" | "Set selection" | "done.invoke.get-angle-info" | "done.invoke.get-horizontal-info" | "done.invoke.get-length-info" | "done.invoke.get-perpendicular-distance-info" | "done.invoke.get-vertical-info" | "error.platform.get-angle-info" | "error.platform.get-horizontal-info" | "error.platform.get-length-info" | "error.platform.get-perpendicular-distance-info" | "error.platform.get-vertical-info";
"equip select": "CancelSketch" | "Constrain equal length" | "Constrain horizontally align" | "Constrain parallel" | "Constrain remove constraints" | "Constrain snap to X" | "Constrain snap to Y" | "Constrain vertically align" | "Deselect point" | "Deselect segment" | "Enter sketch" | "Make segment horizontal" | "Make segment vertical" | "Re-execute" | "Select default plane" | "Select point" | "Select segment" | "Set selection" | "done.invoke.get-abs-x-info" | "done.invoke.get-abs-y-info" | "done.invoke.get-angle-info" | "done.invoke.get-horizontal-info" | "done.invoke.get-length-info" | "done.invoke.get-perpendicular-distance-info" | "done.invoke.get-vertical-info" | "error.platform.get-abs-x-info" | "error.platform.get-abs-y-info" | "error.platform.get-angle-info" | "error.platform.get-horizontal-info" | "error.platform.get-length-info" | "error.platform.get-perpendicular-distance-info" | "error.platform.get-vertical-info";
"hide default planes": "Cancel" | "Select default plane" | "xstate.stop";
"reset sketch metadata": "Cancel" | "Select default plane";
"set default plane id": "Select default plane";
@ -73,6 +81,8 @@
};
eventsCausingGuards: {
"Can canstrain parallel": "Constrain parallel";
"Can constrain ABS X": "Constrain ABS X";
"Can constrain ABS Y": "Constrain ABS Y";
"Can constrain angle": "Constrain angle";
"Can constrain equal length": "Constrain equal length";
"Can constrain horizontal distance": "Constrain horizontal distance";
@ -80,6 +90,8 @@
"Can constrain length": "Constrain length";
"Can constrain perpendicular distance": "Constrain perpendicular distance";
"Can constrain remove constraints": "Constrain remove constraints";
"Can constrain snap to X": "Constrain snap to X";
"Can constrain snap to Y": "Constrain snap to Y";
"Can constrain vertical distance": "Constrain vertical distance";
"Can constrain vertically align": "Constrain vertically align";
"Can make selection horizontal": "Make segment horizontal";
@ -98,13 +110,15 @@
"is editing existing sketch": "";
};
eventsCausingServices: {
"Get angle info": "Constrain angle";
"Get ABS X info": "Constrain ABS X";
"Get ABS Y info": "Constrain ABS Y";
"Get angle info": "Constrain angle";
"Get horizontal info": "Constrain horizontal distance";
"Get length info": "Constrain length";
"Get perpendicular distance info": "Constrain perpendicular distance";
"Get vertical info": "Constrain vertical distance";
};
matchesStates: "Sketch" | "Sketch no face" | "Sketch.Await angle info" | "Sketch.Await horizontal distance info" | "Sketch.Await length info" | "Sketch.Await perpendicular distance info" | "Sketch.Await vertical distance info" | "Sketch.Line Tool" | "Sketch.Line Tool.Done" | "Sketch.Line Tool.Init" | "Sketch.Line Tool.No Points" | "Sketch.Line Tool.Point Added" | "Sketch.Line Tool.Segment Added" | "Sketch.Move Tool" | "Sketch.Move Tool.Move init" | "Sketch.Move Tool.Move with execute" | "Sketch.Move Tool.Move without re-execute" | "Sketch.Move Tool.No move" | "Sketch.SketchIdle" | "awaiting selection" | "checking selection" | "idle" | { "Sketch"?: "Await angle info" | "Await horizontal distance info" | "Await length info" | "Await perpendicular distance info" | "Await vertical distance info" | "Line Tool" | "Move Tool" | "SketchIdle" | { "Line Tool"?: "Done" | "Init" | "No Points" | "Point Added" | "Segment Added";
matchesStates: "Sketch" | "Sketch no face" | "Sketch.Await ABS X info" | "Sketch.Await ABS Y info" | "Sketch.Await angle info" | "Sketch.Await horizontal distance info" | "Sketch.Await length info" | "Sketch.Await perpendicular distance info" | "Sketch.Await vertical distance info" | "Sketch.Line Tool" | "Sketch.Line Tool.Done" | "Sketch.Line Tool.Init" | "Sketch.Line Tool.No Points" | "Sketch.Line Tool.Point Added" | "Sketch.Line Tool.Segment Added" | "Sketch.Move Tool" | "Sketch.Move Tool.Move init" | "Sketch.Move Tool.Move with execute" | "Sketch.Move Tool.Move without re-execute" | "Sketch.Move Tool.No move" | "Sketch.SketchIdle" | "awaiting selection" | "checking selection" | "idle" | { "Sketch"?: "Await ABS X info" | "Await ABS Y info" | "Await angle info" | "Await horizontal distance info" | "Await length info" | "Await perpendicular distance info" | "Await vertical distance info" | "Line Tool" | "Move Tool" | "SketchIdle" | { "Line Tool"?: "Done" | "Init" | "No Points" | "Point Added" | "Segment Added";
"Move Tool"?: "Move init" | "Move with execute" | "Move without re-execute" | "No move"; }; };
tags: never;
}

View File

@ -255,6 +255,7 @@ const Home = () => {
Element="button"
onClick={() => send('Create project')}
icon={{ icon: faPlus }}
data-testid="home-new-file"
>
New file
</ActionButton>

View File

@ -62,6 +62,7 @@ export default function Export() {
Element="button"
onClick={next}
icon={{ icon: faArrowRight }}
data-testid="onboarding-next"
>
Next: Sketching
</ActionButton>

View File

@ -43,7 +43,10 @@ function OnboardingWithNewFile() {
ONBOARDING_PROJECT_NAME,
nextIndex
)
const newFile = await createNewProject(defaultDirectory + sep + name)
const newFile = await createNewProject(
defaultDirectory + sep + name,
bracket
)
navigate(
`${paths.FILE}/${encodeURIComponent(
newFile.path + sep + PROJECT_ENTRYPOINT
@ -79,7 +82,7 @@ function OnboardingWithNewFile() {
<ActionButton
Element="button"
onClick={() => {
kclManager.setCode(bracket)
kclManager.setCodeAndExecute(bracket)
next()
}}
icon={{ icon: faArrowRight }}
@ -118,7 +121,7 @@ function OnboardingWithNewFile() {
Element="button"
onClick={() => {
createAndOpenNewProject()
kclManager.setCode(bracket)
kclManager.setCode(bracket, false)
dismiss()
}}
icon={{ icon: faArrowRight }}

View File

@ -57,6 +57,7 @@ export default function Sketching() {
Element="button"
onClick={next}
icon={{ icon: faArrowRight }}
data-testid="onboarding-next"
>
Next: Future Work
</ActionButton>

View File

@ -125,9 +125,9 @@ const Onboarding = () => {
useHotkeys('esc', dismiss)
return (
<>
<div className="content" data-testid="onboarding-content">
<Outlet />
</>
</div>
)
}

View File

@ -32,6 +32,7 @@ import {
} from 'lib/tauriFS'
import { ONBOARDING_PROJECT_NAME } from './Onboarding'
import { sep } from '@tauri-apps/api/path'
import { bracket } from 'lib/exampleKcl'
export const Settings = () => {
const loaderData =
@ -96,7 +97,10 @@ export const Settings = () => {
ONBOARDING_PROJECT_NAME,
nextIndex
)
const newFile = await createNewProject(defaultDirectory + sep + name)
const newFile = await createNewProject(
defaultDirectory + sep + name,
bracket
)
navigate(`${paths.FILE}/${encodeURIComponent(newFile.path)}`)
}

View File

@ -67,6 +67,7 @@ const SignIn = () => {
onClick={signInTauri}
icon={{ icon: faSignInAlt }}
className="w-fit mt-4"
data-testid="sign-in-button"
>
Sign in
</ActionButton>

View File

@ -253,11 +253,13 @@ export async function executeAst({
engineCommandManager,
defaultPlanes,
useFakeExecutor = false,
programMemoryOverride,
}: {
ast: Program
engineCommandManager: EngineCommandManager
defaultPlanes: DefaultPlanes
useFakeExecutor?: boolean
programMemoryOverride?: ProgramMemory
}): Promise<{
logs: string[]
errors: KCLError[]
@ -269,10 +271,13 @@ export async function executeAst({
engineCommandManager.startNewSession()
}
const programMemory = await (useFakeExecutor
? enginelessExecutor(ast, {
root: defaultProgramMemory,
return: null,
})
? enginelessExecutor(
ast,
programMemoryOverride || {
root: defaultProgramMemory,
return: null,
}
)
: _executor(
ast,
{

View File

@ -6,10 +6,10 @@
serial-integration = { max-threads = 4 }
[profile.default]
slow-timeout = { period = "60s", terminate-after = 1 }
slow-timeout = { period = "10s", terminate-after = 1 }
[profile.ci]
slow-timeout = { period = "120s", terminate-after = 10 }
slow-timeout = { period = "30s", terminate-after = 5 }
[[profile.default.overrides]]
filter = "test(serial_test_)"
@ -20,3 +20,7 @@ threads-required = 4
filter = "test(serial_test_)"
test-group = "serial-integration"
threads-required = 4
[[profile.default.overrides]]
filter = "test(parser::parser_impl::snapshot_tests)"
slow-timeout = { period = "1s", terminate-after = 5 }

273
src/wasm-lib/Cargo.lock generated
View File

@ -157,7 +157,7 @@ checksum = "5fd55a5ba1179988837d24ab4c7cc8ed6efdeff578ede0416b4225a5fca35bd0"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.38",
"syn 2.0.39",
]
[[package]]
@ -168,7 +168,7 @@ checksum = "a66537f1bb974b254c98ed142ff995236e81b9d0fe4db0575f46612cb15eb0f9"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.38",
"syn 2.0.39",
]
[[package]]
@ -447,9 +447,9 @@ dependencies = [
[[package]]
name = "clap"
version = "4.4.6"
version = "4.4.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d04704f56c2cde07f43e8e2c154b43f216dc5c92fc98ada720177362f953b956"
checksum = "2275f18819641850fa26c89acc84d465c1bf91ce57bc2748b28c420473352f64"
dependencies = [
"clap_builder",
"clap_derive",
@ -457,9 +457,9 @@ dependencies = [
[[package]]
name = "clap_builder"
version = "4.4.6"
version = "4.4.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0e231faeaca65ebd1ea3c737966bf858971cd38c3849107aa3ea7de90a804e45"
checksum = "07cdf1b148b25c1e1f7a42225e30a0d99a615cd4637eae7365548dd4529b95bc"
dependencies = [
"anstream",
"anstyle",
@ -472,21 +472,21 @@ dependencies = [
[[package]]
name = "clap_derive"
version = "4.4.2"
version = "4.4.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0862016ff20d69b84ef8247369fabf5c008a7417002411897d40ee1f4532b873"
checksum = "cf9804afaaf59a91e75b022a30fb7229a7901f60c755489cc61c9b423b836442"
dependencies = [
"heck",
"proc-macro2",
"quote",
"syn 2.0.38",
"syn 2.0.39",
]
[[package]]
name = "clap_lex"
version = "0.5.1"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cd7cc57abe963c6d3b9d8be5b06ba7c8957a930305ca90304f24ef040aa6f961"
checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1"
[[package]]
name = "color_quant"
@ -681,6 +681,29 @@ version = "2.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c2e66c9d817f1720209181c316d28635c050fa304f9c79e47a520882661b7308"
[[package]]
name = "databake"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "82175d72e69414ceafbe2b49686794d3a8bed846e0d50267355f83ea8fdd953a"
dependencies = [
"databake-derive",
"proc-macro2",
"quote",
]
[[package]]
name = "databake-derive"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "377af281d8f23663862a7c84623bc5dcf7f8c44b13c7496a590bdc157f941a43"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.39",
"synstructure",
]
[[package]]
name = "deranged"
version = "0.3.9"
@ -702,7 +725,7 @@ dependencies = [
"quote",
"serde",
"serde_tokenstream",
"syn 2.0.38",
"syn 2.0.39",
]
[[package]]
@ -716,7 +739,7 @@ dependencies = [
"quote",
"serde",
"serde_tokenstream",
"syn 2.0.38",
"syn 2.0.39",
]
[[package]]
@ -799,6 +822,16 @@ dependencies = [
"windows-sys 0.48.0",
]
[[package]]
name = "execution-plan"
version = "0.1.0"
dependencies = [
"bytes",
"kittycad",
"serde",
"thiserror",
]
[[package]]
name = "expectorate"
version = "1.1.0"
@ -921,9 +954,9 @@ checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c"
[[package]]
name = "futures"
version = "0.3.28"
version = "0.3.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "23342abe12aba583913b2e62f22225ff9c950774065e4bfb61a19cd9770fec40"
checksum = "da0290714b38af9b4a7b094b8a37086d1b4e61f2df9122c3cad2577669145335"
dependencies = [
"futures-channel",
"futures-core",
@ -936,9 +969,9 @@ dependencies = [
[[package]]
name = "futures-channel"
version = "0.3.28"
version = "0.3.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2"
checksum = "ff4dd66668b557604244583e3e1e1eada8c5c2e96a6d0d6653ede395b78bbacb"
dependencies = [
"futures-core",
"futures-sink",
@ -946,15 +979,15 @@ dependencies = [
[[package]]
name = "futures-core"
version = "0.3.28"
version = "0.3.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c"
checksum = "eb1d22c66e66d9d72e1758f0bd7d4fd0bee04cad842ee34587d68c07e45d088c"
[[package]]
name = "futures-executor"
version = "0.3.28"
version = "0.3.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ccecee823288125bd88b4d7f565c9e58e41858e47ab72e8ea2d64e93624386e0"
checksum = "0f4fb8693db0cf099eadcca0efe2a5a22e4550f98ed16aba6c48700da29597bc"
dependencies = [
"futures-core",
"futures-task",
@ -963,38 +996,38 @@ dependencies = [
[[package]]
name = "futures-io"
version = "0.3.28"
version = "0.3.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964"
checksum = "8bf34a163b5c4c52d0478a4d757da8fb65cabef42ba90515efee0f6f9fa45aaa"
[[package]]
name = "futures-macro"
version = "0.3.28"
version = "0.3.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72"
checksum = "53b153fd91e4b0147f4aced87be237c98248656bb01050b96bf3ee89220a8ddb"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.38",
"syn 2.0.39",
]
[[package]]
name = "futures-sink"
version = "0.3.28"
version = "0.3.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e"
checksum = "e36d3378ee38c2a36ad710c5d30c2911d752cb941c00c72dbabfb786a7970817"
[[package]]
name = "futures-task"
version = "0.3.28"
version = "0.3.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65"
checksum = "efd193069b0ddadc69c46389b740bbccdd97203899b48d09c5f7969591d6bae2"
[[package]]
name = "futures-util"
version = "0.3.28"
version = "0.3.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533"
checksum = "a19526d624e703a3179b3d322efec918b6246ea0fa51d41124525f00f1cc8104"
dependencies = [
"futures-channel",
"futures-core",
@ -1305,6 +1338,20 @@ dependencies = [
"serde",
]
[[package]]
name = "insta"
version = "1.34.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5d64600be34b2fcfc267740a243fa7744441bb4947a619ac4e5bb6507f35fbfc"
dependencies = [
"console",
"lazy_static",
"linked-hash-map",
"serde",
"similar",
"yaml-rust",
]
[[package]]
name = "instant"
version = "0.1.12"
@ -1345,9 +1392,9 @@ dependencies = [
[[package]]
name = "itertools"
version = "0.11.0"
version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57"
checksum = "25db6b064527c5d482d0423354fcd07a89a2dfe07b67892e62411946db7f07b0"
dependencies = [
"either",
]
@ -1369,9 +1416,9 @@ dependencies = [
[[package]]
name = "js-sys"
version = "0.3.64"
version = "0.3.65"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c5f195fe497f702db0f318b07fdd68edb16955aed830df8363d837542f8f935a"
checksum = "54c0c35952f67de54bb584e9fd912b3023117cbafc0a77d8f3dee1fb5f572fe8"
dependencies = [
"wasm-bindgen",
]
@ -1389,7 +1436,7 @@ dependencies = [
[[package]]
name = "kcl-lib"
version = "0.1.35"
version = "0.1.40"
dependencies = [
"anyhow",
"async-recursion",
@ -1398,10 +1445,12 @@ dependencies = [
"clap",
"criterion",
"dashmap",
"databake",
"derive-docs 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
"expectorate",
"futures",
"itertools 0.11.0",
"insta",
"itertools 0.12.0",
"js-sys",
"kittycad",
"lazy_static",
@ -1423,11 +1472,23 @@ dependencies = [
"winnow",
]
[[package]]
name = "kcl-macros"
version = "0.1.0"
dependencies = [
"databake",
"kcl-lib",
"pretty_assertions",
"proc-macro2",
"quote",
"syn 2.0.39",
]
[[package]]
name = "kittycad"
version = "0.2.41"
version = "0.2.43"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "874914cd40bfd43674406683bb3f0924d41780698a4ade96f2e180a73678bdd1"
checksum = "41ab6de34cc4ab06519d65a613d4030ade14036ac619d8fee5ce6f35d1766c11"
dependencies = [
"anyhow",
"async-trait",
@ -1732,7 +1793,7 @@ checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575"
[[package]]
name = "openapitor"
version = "0.0.9"
source = "git+https://github.com/KittyCAD/kittycad.rs?branch=main#57b4d8b1688f6d13f91b538b0f107866b17bba84"
source = "git+https://github.com/KittyCAD/kittycad.rs?branch=main#920ba7c69fa167d74e4cc1be4f2ed96635893e89"
dependencies = [
"Inflector",
"anyhow",
@ -1878,7 +1939,7 @@ dependencies = [
"regex",
"regex-syntax 0.7.5",
"structmeta",
"syn 2.0.38",
"syn 2.0.39",
]
[[package]]
@ -1902,7 +1963,7 @@ dependencies = [
"bincode",
"either",
"fnv",
"itertools 0.11.0",
"itertools 0.10.5",
"lazy_static",
"nom",
"quick-xml",
@ -1931,7 +1992,7 @@ checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.38",
"syn 2.0.39",
]
[[package]]
@ -2041,9 +2102,9 @@ dependencies = [
[[package]]
name = "proc-macro2"
version = "1.0.69"
version = "1.0.70"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da"
checksum = "39278fbbf5fb4f646ce651690877f89d1c5811a3d4acb27700c1cb3cdb78fd3b"
dependencies = [
"unicode-ident",
]
@ -2383,9 +2444,9 @@ dependencies = [
[[package]]
name = "rustix"
version = "0.38.20"
version = "0.38.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "67ce50cb2e16c2903e30d1cbccfd8387a74b9d4c938b6a4c5ec6cc7556f7a8a0"
checksum = "2b426b0506e5d50a7d8dafcf2e81471400deb602392c7dd110815afb4eaf02a3"
dependencies = [
"bitflags 2.4.1",
"errno",
@ -2469,9 +2530,9 @@ dependencies = [
[[package]]
name = "schemars"
version = "0.8.15"
version = "0.8.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1f7b0ce13155372a76ee2e1c5ffba1fe61ede73fbea5630d61eee6fac4929c0c"
checksum = "45a28f4c49489add4ce10783f7911893516f15afe45d015608d41faca6bc4d29"
dependencies = [
"bigdecimal",
"bytes",
@ -2486,9 +2547,9 @@ dependencies = [
[[package]]
name = "schemars_derive"
version = "0.8.15"
version = "0.8.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e85e2a16b12bdb763244c69ab79363d71db2b4b918a2def53f80b02e0574b13c"
checksum = "c767fd6fa65d9ccf9cf026122c1b555f2ef9a4f0cea69da4d7dbc3e258d30967"
dependencies = [
"proc-macro2",
"quote",
@ -2543,9 +2604,9 @@ checksum = "836fa6a3e1e547f9a2c4040802ec865b5d85f4014efe00555d7090a3dcaa1090"
[[package]]
name = "serde"
version = "1.0.189"
version = "1.0.193"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e422a44e74ad4001bdc8eede9a4570ab52f71190e9c076d14369f38b9200537"
checksum = "25dd9975e68d0cb5aa1120c288333fc98731bd1dd12f561e468ea4728c042b89"
dependencies = [
"serde_derive",
]
@ -2561,13 +2622,13 @@ dependencies = [
[[package]]
name = "serde_derive"
version = "1.0.189"
version = "1.0.193"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e48d1f918009ce3145511378cf68d613e3b3d9137d67272562080d68a2b32d5"
checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.38",
"syn 2.0.39",
]
[[package]]
@ -2583,9 +2644,9 @@ dependencies = [
[[package]]
name = "serde_json"
version = "1.0.107"
version = "1.0.108"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6b420ce6e3d8bd882e9b243c6eed35dbc9a6110c9769e74b584e0d68d1f20c65"
checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b"
dependencies = [
"indexmap 2.0.2",
"itoa",
@ -2601,7 +2662,7 @@ checksum = "8725e1dfadb3a50f7e5ce0b1a540466f6ed3fe7a0fca2ac2b8b831d31316bd00"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.38",
"syn 2.0.39",
]
[[package]]
@ -2622,7 +2683,7 @@ dependencies = [
"proc-macro2",
"quote",
"serde",
"syn 2.0.38",
"syn 2.0.39",
]
[[package]]
@ -2833,7 +2894,7 @@ dependencies = [
"proc-macro2",
"quote",
"structmeta-derive",
"syn 2.0.38",
"syn 2.0.39",
]
[[package]]
@ -2844,7 +2905,7 @@ checksum = "a60bcaff7397072dca0017d1db428e30d5002e00b6847703e2e42005c95fbe00"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.38",
"syn 2.0.39",
]
[[package]]
@ -2882,15 +2943,27 @@ dependencies = [
[[package]]
name = "syn"
version = "2.0.38"
version = "2.0.39"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e96b79aaa137db8f61e26363a0c9b47d8b4ec75da28b7d1d614c2303e232408b"
checksum = "23e78b90f2fcf45d3e842032ce32e3f2d1545ba6636271dcbf24fa306d87be7a"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "synstructure"
version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "285ba80e733fac80aa4270fbcdf83772a79b80aa35c97075320abfee4a915b06"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.39",
"unicode-xid",
]
[[package]]
name = "system-configuration"
version = "0.5.1"
@ -2993,7 +3066,7 @@ checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.38",
"syn 2.0.39",
]
[[package]]
@ -3075,9 +3148,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
[[package]]
name = "tokio"
version = "1.33.0"
version = "1.34.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4f38200e3ef7995e5ef13baec2f432a6da0aa9ac495b2c0e8f3b7eec2c92d653"
checksum = "d0c014766411e834f7af5b8f4cf46257aab4036ca95e9d2c144a10f59ad6f5b9"
dependencies = [
"backtrace",
"bytes",
@ -3094,13 +3167,13 @@ dependencies = [
[[package]]
name = "tokio-macros"
version = "2.1.0"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e"
checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.38",
"syn 2.0.39",
]
[[package]]
@ -3241,7 +3314,7 @@ checksum = "84fd902d4e0b9a4b27f2f440108dc034e1758628a9b702f8ec61ad66355422fa"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.38",
"syn 2.0.39",
]
[[package]]
@ -3269,7 +3342,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.38",
"syn 2.0.39",
]
[[package]]
@ -3355,7 +3428,7 @@ dependencies = [
"Inflector",
"proc-macro2",
"quote",
"syn 2.0.38",
"syn 2.0.39",
"termcolor",
]
@ -3440,6 +3513,12 @@ version = "0.1.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85"
[[package]]
name = "unicode-xid"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c"
[[package]]
name = "unsafe-libyaml"
version = "0.2.9"
@ -3478,9 +3557,9 @@ checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
[[package]]
name = "uuid"
version = "1.5.0"
version = "1.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "88ad59a7560b41a70d191093a945f0b87bc1deeda46fb237479708a1d6b6cdfc"
checksum = "5e395fcf16a7a3d8127ec99782007af141946b4795001f876d54fb0d55978560"
dependencies = [
"atomic",
"getrandom",
@ -3533,9 +3612,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
name = "wasm-bindgen"
version = "0.2.87"
version = "0.2.88"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342"
checksum = "7daec296f25a1bae309c0cd5c29c4b260e510e6d813c286b19eaadf409d40fce"
dependencies = [
"cfg-if",
"wasm-bindgen-macro",
@ -3543,24 +3622,24 @@ dependencies = [
[[package]]
name = "wasm-bindgen-backend"
version = "0.2.87"
version = "0.2.88"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd"
checksum = "e397f4664c0e4e428e8313a469aaa58310d302159845980fd23b0f22a847f217"
dependencies = [
"bumpalo",
"log",
"once_cell",
"proc-macro2",
"quote",
"syn 2.0.38",
"syn 2.0.39",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-futures"
version = "0.4.37"
version = "0.4.38"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c02dbc21516f9f1f04f187958890d7e6026df8d16540b7ad9492bc34a67cea03"
checksum = "9afec9963e3d0994cac82455b2b3502b81a7f40f9a0d32181f7528d9f4b43e02"
dependencies = [
"cfg-if",
"futures-core",
@ -3571,9 +3650,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-macro"
version = "0.2.87"
version = "0.2.88"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d"
checksum = "5961017b3b08ad5f3fe39f1e79877f8ee7c23c5e5fd5eb80de95abc41f1f16b2"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
@ -3581,22 +3660,22 @@ dependencies = [
[[package]]
name = "wasm-bindgen-macro-support"
version = "0.2.87"
version = "0.2.88"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b"
checksum = "c5353b8dab669f5e10f5bd76df26a9360c748f054f862ff5f3f8aae0c7fb3907"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.38",
"syn 2.0.39",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-shared"
version = "0.2.87"
version = "0.2.88"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1"
checksum = "0d046c5d029ba91a1ed14da14dca44b68bf2f124cfbaf741c54151fdb3e0750b"
[[package]]
name = "wasm-lib"
@ -3625,9 +3704,9 @@ dependencies = [
[[package]]
name = "wasm-streams"
version = "0.3.0"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b4609d447824375f43e1ffbc051b50ad8f4b3ae8219680c94452ea05eb240ac7"
checksum = "b65dc4c90b63b118468cf747d8bf3566c1913ef60be765b5730ead9e0a3ba129"
dependencies = [
"futures-util",
"js-sys",
@ -3653,9 +3732,9 @@ dependencies = [
[[package]]
name = "web-sys"
version = "0.3.64"
version = "0.3.65"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b85cbef8c220a6abc02aefd892dfc0fc23afb1c6a426316ec33253a3877249b"
checksum = "5db499c5f66323272151db0e666cd34f78617522fb0c1604d31a27c50c206a85"
dependencies = [
"js-sys",
"wasm-bindgen",
@ -3847,9 +3926,9 @@ checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
[[package]]
name = "winnow"
version = "0.5.17"
version = "0.5.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a3b801d0e0a6726477cc207f60162da452f3a95adb368399bef20a946e06f65c"
checksum = "829846f3e3db426d4cee4510841b71a8e58aa2a76b1132579487ae430ccd9c7b"
dependencies = [
"memchr",
]
@ -3905,7 +3984,7 @@ checksum = "6b6093bc6d5265ff40b479c834cdd25d8e20784781a2a29a8106327393d0a9ff"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.38",
"syn 2.0.39",
]
[[package]]

View File

@ -2,6 +2,8 @@
name = "wasm-lib"
version = "0.1.0"
edition = "2021"
repository = "https://github.com/KittyCAD/modeling-app"
rust-version = "1.73"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[lib]
@ -12,10 +14,10 @@ bson = { version = "2.7.0", features = ["uuid-1", "chrono"] }
gloo-utils = "0.2.0"
kcl-lib = { path = "kcl" }
kittycad = { workspace = true }
serde_json = "1.0.107"
uuid = { version = "1.5.0", features = ["v4", "js", "serde"] }
wasm-bindgen = "0.2.87"
wasm-bindgen-futures = "0.4.37"
serde_json = "1.0.108"
uuid = { version = "1.6.1", features = ["v4", "js", "serde"] }
wasm-bindgen = "0.2.88"
wasm-bindgen-futures = "0.4.38"
[dev-dependencies]
anyhow = "1"
@ -23,19 +25,19 @@ image = "0.24.7"
kittycad = { workspace = true, default-features = true }
pretty_assertions = "1.4.0"
reqwest = { version = "0.11.22", default-features = false }
tokio = { version = "1.33.0", features = ["rt-multi-thread", "macros", "time"] }
tokio = { version = "1.34.0", features = ["rt-multi-thread", "macros", "time"] }
twenty-twenty = "0.6.1"
uuid = { version = "1.5.0", features = ["v4", "js", "serde"] }
uuid = { version = "1.6.1", features = ["v4", "js", "serde"] }
[target.'cfg(target_arch = "wasm32")'.dependencies]
futures = "0.3.28"
js-sys = "0.3.64"
futures = "0.3.29"
js-sys = "0.3.65"
tower-lsp = { version = "0.20.0", default-features = false, features = ["runtime-agnostic"] }
wasm-bindgen-futures = { version = "0.4.37", features = ["futures-core-03-stream"] }
wasm-streams = "0.3.0"
wasm-streams = "0.4.0"
[target.'cfg(target_arch = "wasm32")'.dependencies.web-sys]
version = "0.3.57"
version = "0.3.65"
features = [
"console",
"HtmlTextAreaElement",
@ -51,10 +53,12 @@ debug = true
members = [
"derive-docs",
"kcl",
"kcl-macros",
"execution-plan",
]
[workspace.dependencies]
kittycad = { version = "0.2.41", default-features = false, features = ["js"] }
kittycad = { version = "0.2.43", default-features = false, features = ["js"] }
[[test]]
name = "executor"

View File

@ -4,6 +4,8 @@ description = "A tool for generating documentation from Rust derive macros"
version = "0.1.4"
edition = "2021"
license = "MIT"
repository = "https://github.com/KittyCAD/modeling-app"
rust-version = "1.73"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@ -14,9 +16,9 @@ proc-macro = true
convert_case = "0.6.0"
proc-macro2 = "1"
quote = "1"
serde = { version = "1.0.189", features = ["derive"] }
serde = { version = "1.0.193", features = ["derive"] }
serde_tokenstream = "0.2"
syn = { version = "2.0.38", features = ["full"] }
syn = { version = "2.0.39", features = ["full"] }
[dev-dependencies]
expectorate = "1.1.0"

View File

@ -0,0 +1,13 @@
[package]
name = "execution-plan"
version = "0.1.0"
edition = "2021"
repository = "https://github.com/KittyCAD/modeling-app"
rust-version = "1.73"
description = "A DSL for composing KittyCAD API queries"
[dependencies]
bytes = "1.5"
kittycad = { workspace = true, features = ["requests"] }
serde = { version = "1", features = ["derive"] }
thiserror = "1"

View File

@ -0,0 +1,26 @@
use crate::{ExecutionError, NumericValue, Value};
/// Types that can be written to or read from KCEP program memory,
/// but require multiple values to store.
/// They get laid out into multiple consecutive memory addresses.
pub trait Composite<const SIZE: usize>: Sized {
/// Store the value in memory.
fn into_parts(self) -> [Value; SIZE];
/// Read the value from memory.
fn from_parts(values: [Value; SIZE]) -> Result<Self, ExecutionError>;
}
impl Composite<3> for kittycad::types::Point3D {
fn into_parts(self) -> [Value; 3] {
let points = [self.x, self.y, self.z];
points.map(NumericValue::Float).map(Value::NumericValue)
}
fn from_parts(values: [Value; 3]) -> Result<Self, ExecutionError> {
let [x, y, z] = values;
let x = x.try_into()?;
let y = y.try_into()?;
let z = z.try_into()?;
Ok(Self { x, y, z })
}
}

View File

@ -0,0 +1,289 @@
//! A KittyCAD execution plan (KCEP) is a list of
//! - KittyCAD API requests to make
//! - Values to send in API requests
//! - Values to assign from API responses
//! - Computation to perform on values
//! You can think of it as a domain-specific language for making KittyCAD API calls and using
//! the results to make other API calls.
use composite::Composite;
use serde::{Deserialize, Serialize};
use std::{collections::HashMap, fmt};
mod composite;
#[cfg(test)]
mod tests;
/// KCEP's program memory. A flat, linear list of values.
#[derive(Default, Debug)]
#[cfg_attr(test, derive(PartialEq))]
pub struct Memory(HashMap<usize, Value>);
/// An address in KCEP's program memory.
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
pub struct Address(usize);
impl fmt::Display for Address {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0.fmt(f)
}
}
impl From<usize> for Address {
fn from(value: usize) -> Self {
Self(value)
}
}
impl Memory {
/// Get a value from KCEP's program memory.
pub fn get(&self, addr: &Address) -> Option<&Value> {
self.0.get(&addr.0)
}
/// Store a value in KCEP's program memory.
pub fn set(&mut self, addr: Address, value: Value) {
self.0.insert(addr.0, value);
}
/// Store a composite value (i.e. a value which takes up multiple addresses in memory).
/// Store its parts in consecutive memory addresses starting at `start`.
pub fn set_composite<T: Composite<{ N }>, const N: usize>(&mut self, composite_value: T, start: Address) {
let parts = composite_value.into_parts().into_iter();
for (value, addr) in parts.zip(start.0..) {
self.0.insert(addr, value);
}
}
/// Get a composite value (i.e. a value which takes up multiple addresses in memory).
/// Its parts are stored in consecutive memory addresses starting at `start`.
pub fn get_composite<T: Composite<{ N }>, const N: usize>(&self, start: Address) -> Result<T, ExecutionError> {
let addrs: [Address; N] = core::array::from_fn(|i| Address(i + start.0));
let values: [Value; N] = arr_res_to_res_array(addrs.map(|addr| {
self.get(&addr)
.map(|x| x.to_owned())
.ok_or(ExecutionError::MemoryEmpty { addr })
}))?;
T::from_parts(values)
}
}
/// A value stored in KCEP program memory.
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
pub enum Value {
String(String),
NumericValue(NumericValue),
}
impl TryFrom<Value> for f64 {
type Error = ExecutionError;
fn try_from(value: Value) -> Result<Self, Self::Error> {
if let Value::NumericValue(NumericValue::Float(x)) = value {
Ok(x)
} else {
Err(ExecutionError::MemoryWrongType {
expected: "float",
actual: format!("{value:?}"),
})
}
}
}
#[cfg(test)]
impl From<f64> for Value {
fn from(value: f64) -> Self {
Self::NumericValue(NumericValue::Float(value))
}
}
#[cfg(test)]
impl From<usize> for Value {
fn from(value: usize) -> Self {
Self::NumericValue(NumericValue::Integer(value))
}
}
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
pub enum NumericValue {
Integer(usize),
Float(f64),
}
/// One step of the execution plan.
#[derive(Serialize, Deserialize)]
pub enum Instruction {
/// Call the KittyCAD API.
ApiRequest {
/// Which ModelingCmd to call.
/// It's a composite value starting at the given address.
endpoint: Address,
/// Which address should the response be stored in?
store_response: Option<usize>,
/// Look up each API request in this register number.
arguments: Vec<Address>,
},
/// Set a value in memory.
Set {
/// Which memory address to set.
address: Address,
/// What value to set the memory address to.
value: Value,
},
/// Perform arithmetic on values in memory.
Arithmetic {
/// What to do.
arithmetic: Arithmetic,
/// Write the output to this memory address.
destination: Address,
},
}
/// Instruction to perform arithmetic on values in memory.
#[derive(Deserialize, Serialize)]
pub struct Arithmetic {
/// Apply this operation
pub operation: Operation,
/// First operand for the operation
pub operand0: Operand,
/// Second operand for the operation
pub operand1: Operand,
}
macro_rules! arithmetic_body {
($arith:ident, $mem:ident, $method:ident) => {
match (
$arith.operand0.eval(&$mem)?.clone(),
$arith.operand1.eval(&$mem)?.clone(),
) {
// If both operands are numeric, then do the arithmetic operation.
(Value::NumericValue(x), Value::NumericValue(y)) => {
let num = match (x, y) {
(NumericValue::Integer(x), NumericValue::Integer(y)) => NumericValue::Integer(x.$method(y)),
(NumericValue::Integer(x), NumericValue::Float(y)) => NumericValue::Float((x as f64).$method(y)),
(NumericValue::Float(x), NumericValue::Integer(y)) => NumericValue::Float(x.$method(y as f64)),
(NumericValue::Float(x), NumericValue::Float(y)) => NumericValue::Float(x.$method(y)),
};
Ok(Value::NumericValue(num))
}
// This operation can only be done on numeric types.
_ => Err(ExecutionError::CannotApplyOperation {
op: $arith.operation,
operands: vec![
$arith.operand0.eval(&$mem)?.clone().to_owned(),
$arith.operand1.eval(&$mem)?.clone().to_owned(),
],
}),
}
};
}
impl Arithmetic {
/// Calculate the the arithmetic equation.
/// May read values from the given memory.
fn calculate(self, mem: &Memory) -> Result<Value, ExecutionError> {
use std::ops::{Add, Div, Mul, Sub};
match self.operation {
Operation::Add => {
arithmetic_body!(self, mem, add)
}
Operation::Mul => {
arithmetic_body!(self, mem, mul)
}
Operation::Sub => {
arithmetic_body!(self, mem, sub)
}
Operation::Div => {
arithmetic_body!(self, mem, div)
}
}
}
}
/// Operations that can be applied to values in memory.
#[derive(Debug, Deserialize, Serialize)]
pub enum Operation {
Add,
Mul,
Sub,
Div,
}
impl fmt::Display for Operation {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Operation::Add => "+",
Operation::Mul => "*",
Operation::Sub => "-",
Operation::Div => "/",
}
.fmt(f)
}
}
/// Argument to an operation.
#[derive(Deserialize, Serialize)]
pub enum Operand {
Literal(Value),
Reference(Address),
}
impl Operand {
/// Evaluate the operand, getting its value.
fn eval(&self, mem: &Memory) -> Result<Value, ExecutionError> {
match self {
Operand::Literal(v) => Ok(v.to_owned()),
Operand::Reference(addr) => match mem.get(addr) {
None => Err(ExecutionError::MemoryEmpty { addr: *addr }),
Some(v) => Ok(v.to_owned()),
},
}
}
}
/// Execute the plan.
pub fn execute(mem: &mut Memory, plan: Vec<Instruction>) -> Result<(), ExecutionError> {
for step in plan {
match step {
Instruction::ApiRequest { .. } => todo!("Execute API calls"),
Instruction::Set { address, value } => {
mem.set(address, value);
}
Instruction::Arithmetic {
arithmetic,
destination,
} => {
let out = arithmetic.calculate(mem)?;
mem.set(destination, out);
}
}
}
Ok(())
}
/// Errors that could occur when executing a KittyCAD execution plan.
#[derive(Debug, thiserror::Error)]
pub enum ExecutionError {
#[error("Memory address {addr} was not set")]
MemoryEmpty { addr: Address },
#[error("Cannot apply operation {op} to operands {operands:?}")]
CannotApplyOperation { op: Operation, operands: Vec<Value> },
#[error("Tried to read a '{expected}' from KCEP program memory, found an '{actual}' instead")]
MemoryWrongType { expected: &'static str, actual: String },
#[error("Wrong size of memory trying to read value from KCEP program memory: got {actual} but wanted {expected}")]
MemoryWrongSize { expected: usize, actual: usize },
}
/// Take an array of result and return a result of array.
/// If all members of the array are Ok(T), returns Ok with an array of the T values.
/// If any member of the array was Err(E), return Err with the first E value.
fn arr_res_to_res_array<T, E, const N: usize>(arr: [Result<T, E>; N]) -> Result<[T; N], E> {
let mut out = core::array::from_fn(|_| None);
for (i, res) in arr.enumerate() {
out[i] = match res {
Ok(x) => x,
Err(e) => return Err(e),
};
}
Ok(out.map(|opt| opt.unwrap()))
}

View File

@ -0,0 +1,94 @@
use kittycad::types::Point3D;
use super::*;
#[test]
fn write_addr_to_memory() {
let plan = vec![Instruction::Set {
address: Address(0),
value: 3.4.into(),
}];
let mut mem = Memory::default();
execute(&mut mem, plan).unwrap();
assert_eq!(mem.get(&Address(0)), Some(&3.4.into()))
}
#[test]
fn add_literals() {
let plan = vec![Instruction::Arithmetic {
arithmetic: Arithmetic {
operation: Operation::Add,
operand0: Operand::Literal(3.into()),
operand1: Operand::Literal(2.into()),
},
destination: Address(1),
}];
let mut mem = Memory::default();
execute(&mut mem, plan).unwrap();
assert_eq!(mem.get(&Address(1)), Some(&5.into()))
}
#[test]
fn add_literal_to_reference() {
let plan = vec![
// Memory addr 0 contains 450
Instruction::Set {
address: Address(0),
value: 450.into(),
},
// Add 20 to addr 0
Instruction::Arithmetic {
arithmetic: Arithmetic {
operation: Operation::Add,
operand0: Operand::Reference(Address(0)),
operand1: Operand::Literal(20.into()),
},
destination: Address(1),
},
];
// 20 + 450 = 470
let mut mem = Memory::default();
execute(&mut mem, plan).unwrap();
assert_eq!(mem.get(&Address(1)), Some(&470.into()))
}
#[test]
fn add_to_composite_value() {
let mut mem = Memory::default();
// Write a point to memory.
let point_before = Point3D { x: 2.0, y: 3.0, z: 4.0 };
let start_addr = Address(100);
mem.set_composite(point_before, start_addr);
assert_eq!(
mem,
Memory(HashMap::from(
[(100, 2.0.into()), (101, 3.0.into()), (102, 4.0.into()),]
))
);
// Update the point's x-value in memory.
execute(
&mut mem,
vec![Instruction::Arithmetic {
arithmetic: Arithmetic {
operation: Operation::Add,
operand0: Operand::Reference(start_addr),
operand1: Operand::Literal(40.into()),
},
destination: start_addr,
}],
)
.unwrap();
// Read the point out of memory, validate it.
let point_after: Point3D = mem.get_composite(start_addr).unwrap();
assert_eq!(
point_after,
Point3D {
x: 42.0,
y: 3.0,
z: 4.0
}
)
}

View File

@ -0,0 +1,22 @@
[package]
name = "kcl-macros"
description = "Macro for compiling KCL to its AST during Rust compile-time"
version = "0.1.0"
edition = "2021"
license = "MIT"
repository = "https://github.com/KittyCAD/modeling-app"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[lib]
proc-macro = true
[dependencies]
databake = "0.1.7"
kcl-lib = { path = "../kcl" }
proc-macro2 = "1"
quote = "1"
syn = { version = "2.0.39", features = ["full"] }
[dev-dependencies]
pretty_assertions = "1.4.0"

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