Compare commits

...

118 Commits

Author SHA1 Message Date
cf10557f65 Merge branch 'main' into kurt-remove-clickable-gaps 2024-02-22 21:09:07 +11:00
af97256ef2 flix clipable gabs around code pane 2024-02-22 21:05:17 +11:00
40479d177f Bump ip from 1.1.8 to 1.1.9 (#1471)
Bumps [ip](https://github.com/indutny/node-ip) from 1.1.8 to 1.1.9.
- [Commits](https://github.com/indutny/node-ip/compare/v1.1.8...v1.1.9)

---
updated-dependencies:
- dependency-name: ip
  dependency-type: indirect
...

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>
2024-02-22 04:59:03 -05:00
b88359dee2 Move discord automation into ci.yml (#1479)
* updating ci.yml with discord release automation

* removed line
2024-02-21 17:35:35 -07:00
f4c0347104 Cut release v0.15.2 (#1467)
Co-authored-by: Kurt Hutten <k.hutten@protonmail.ch>
2024-02-22 08:36:38 +11:00
ad36b5f5fa add make release bash script (#1475)
* add make release bash script

* read me details

* uncomment git uncommited changes

* typo

* tweaks

* use package.json as source of truth, not git tags
2024-02-22 07:01:45 +11:00
b798cf19d3 Bump kcl-lib (#1477)
* bump version

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

* bump version

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

* Revert "bump version"

This reverts commit 44e0b6ac6e.

* bump version

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

---------

Signed-off-by: Jess Frazelle <github@jessfraz.com>
2024-02-21 11:05:10 -08:00
7cfa897561 add modulo and power operators (#1341)
* add modulo and power operators

* format

* point to main instead of serena branch

* reset cargo lock

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

---------

Co-authored-by: gserena <serena@zoo.dev>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-02-21 11:11:57 -05:00
0d8804005a improve vitest hang (#1470)
* improve vitest hang

* fmt

* make types happy

* fix types

* fix
2024-02-21 13:23:50 +11:00
cbd26d29fa updates for units (#1458)
* updates for units

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

* scene units

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

* start passing in units to tests

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

* units tests

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

* add more images

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

* hacky code for now

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

* get settings context outside of react

* fmt

* pull through settings

* fix

* fmt

* move camera with units (#1461)

* temp patch tsc

* update kittycad.rs

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

* trait

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

* fix compile

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

* update screenshots

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

* execute on settings change

* Update src/clientSideScene/sceneInfra.ts

* try zoom

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

* more shit image

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

* new screenshots

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

* udpates

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

* updates

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

* tests

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

* update cam

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

* new

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

* updates for units

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

* fixles

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

* ;scale

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

* fixes

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

* fmt

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

* tweak playwright draft segments test

* another test tweak

* last test tweak

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

* update default plane snapshot scale

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

* number tweaks for playwright flow checks

* up[date

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

---------

Signed-off-by: Jess Frazelle <github@jessfraz.com>
Co-authored-by: Kurt Hutten Irev-Dev <k.hutten@protonmail.ch>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-02-20 17:55:06 -08:00
e501a542ac Add arc icon, replace settings icon (#1469)
* Add icons for arc and settings

* Update arc icon in toolbar

* Use settings icon instead of gear for settings

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

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-02-20 18:34:03 +00:00
7cb4f4d101 deselect line bug (#1457) 2024-02-20 11:04:42 +11:00
1162f5f4c4 Bump kcl-lib (#1455)
* update version

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

* update version

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

* fix

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

* fix

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

---------

Signed-off-by: Jess Frazelle <github@jessfraz.com>
2024-02-19 14:23:16 -08:00
3975e6d8f5 auto complete test more robust (#1456) 2024-02-20 09:22:33 +11:00
d68d7a7e00 Cut release v0.15.1 (#1452)
cut release v0.15.1
2024-02-20 08:10:26 +11:00
b135b97de6 Code mirror plugin lsp interface (#1444)
* better named dirs

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

* move some stuff around

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

* more logging

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

* updates

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

* less logging

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

* add fs in

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

* updates

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

* file reader

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

* workspace

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

* start of workspace folders

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

* start of workspace folders

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

* cleanup workspace folders

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

* cleanup logs

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

* updates

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

---------

Signed-off-by: Jess Frazelle <github@jessfraz.com>
2024-02-19 12:33:16 -08:00
de5885ce0b Enable/disable "start sketch", "edit sketch" and "extrude" appropriately (#1449)
* test that fails for when to enable extrude and sketch features

* add fix to make test pass

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

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-02-19 17:23:03 +11:00
ad7c544754 draft line snapshots (#1445)
* draft line snapshots

Make sure they don't get broken at some point, visual regression is only way to test these really

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

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-02-19 13:18:31 +11:00
4d77875bdc mouse listners should be reset outside of sketch (#1442)
* mouse listners should be reset outside of sketch (only orbit controls are needed) and also check mouse button

* tweak
2024-02-19 12:41:36 +11:00
3377923dcb fix flacky auto complete test (#1443) 2024-02-19 12:15:57 +11:00
c6005660c8 jsxify svgs (#1441) 2024-02-19 10:20:02 +11:00
66e62c6037 cancel execution on file change (#1440) 2024-02-19 09:23:18 +11:00
0a4a517bb4 try arm latest (#1439) 2024-02-17 22:12:39 -08:00
70f3ded7e2 Cut release v0.15.0 (#1436)
Bump app version to v0.15.0

Co-authored-by: Jess Frazelle <jessfraz@users.noreply.github.com>
2024-02-16 22:43:28 -05:00
095108252b snapshot extrude on each default plane (#1438)
* snapshot extrude on each default plane

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

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-02-17 12:19:46 +11:00
20b1c93f12 no camera sketch on face (#1412)
* no camera sketch on face

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

* updates

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

* updates

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

* fix

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

* new screenshots

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

* fix

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

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

* updates

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

* fix tests

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

* fixes

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

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

This reverts commit e839d7101f.

* Revert "fixes"

This reverts commit 3df8b63e3a.

* updates

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

---------

Signed-off-by: Jess Frazelle <github@jessfraz.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-02-16 16:42:01 -08:00
3747a1b993 respect camera target (#1421)
* respect camera target

* make default planes scale

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

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-02-16 12:04:24 -08:00
198feb7d44 Bump syn from 2.0.48 to 2.0.49 in /src/wasm-lib (#1432)
Bumps [syn](https://github.com/dtolnay/syn) from 2.0.48 to 2.0.49.
- [Release notes](https://github.com/dtolnay/syn/releases)
- [Commits](https://github.com/dtolnay/syn/compare/2.0.48...2.0.49)

---
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>
2024-02-16 09:55:39 -08:00
c7a8b8313e Bump tauri-plugin-fs-extra from 67405ae to 01211ff in /src-tauri (#1430)
Bumps [tauri-plugin-fs-extra](https://github.com/tauri-apps/plugins-workspace) from `67405ae` to `01211ff`.
- [Release notes](https://github.com/tauri-apps/plugins-workspace/releases)
- [Commits](67405aed06...01211ff075)

---
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>
2024-02-16 09:55:21 -08:00
1576dc3256 Bump openapitor from 88b05a6 to 8db292e in /src/wasm-lib (#1433)
Bumps [openapitor](https://github.com/KittyCAD/kittycad.rs) from `88b05a6` to `8db292e`.
- [Release notes](https://github.com/KittyCAD/kittycad.rs/releases)
- [Commits](88b05a638f...8db292eaa7)

---
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>
2024-02-16 09:55:02 -08:00
341a3b7609 Bump kittycad from 0.2.50 to 0.2.53 in /src-tauri (#1431)
Bumps [kittycad](https://github.com/KittyCAD/kittycad.rs) from 0.2.50 to 0.2.53.
- [Release notes](https://github.com/KittyCAD/kittycad.rs/releases)
- [Commits](https://github.com/KittyCAD/kittycad.rs/compare/v0.2.50...v0.2.53)

---
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>
2024-02-16 09:54:48 -08:00
ecb42b89a6 check cam-far when adding sketch segments (#1434)
Co-authored-by: Frank Noirot <frank@kittycad.io>
2024-02-16 12:15:35 -05:00
f00ee3a44a Revert "File based settings (#1361)" (#1435)
This reverts commit 602e7afef6.
2024-02-16 09:09:58 -05:00
900e3b96ad CI for macOS on M1 runners (#1428)
* CI for macOS on M1 runners
Fixes #1427

* Install x86 target for Universal builds
2024-02-16 08:30:39 -05:00
15fae05659 Ghost text (#888)
* copilot

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

* updates

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

* refactor layout for copilot lsp

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

* start of server

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

* updates

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

* more clippy

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

* cleanup code

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

* fix

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

* compile wasm

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

* make work w wasm

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

* clippy

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

* cleanup

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

* cleanup

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

* cleanup unwraps

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

* updates

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

* updates

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

* tests

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

* point to correct things

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

* updates

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

* updates

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

* updates

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

* updates

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

* cleanup

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

* updates

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

* cleanup

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

* updaes

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

* updates

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

* updates

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

* fixes

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

* shared backend features

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

* framework for workspace

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

* updates;

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

* cleanup;

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

* updates;

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

* updates

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

* cleanup lints

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

* fmt

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

---------

Signed-off-by: Jess Frazelle <github@jessfraz.com>
2024-02-15 13:56:31 -08:00
2730b6d152 Prevent default on backspace when not in a text field (#1367) 2024-02-15 19:25:26 +00:00
602e7afef6 File based settings (#1361)
* Rename GlobalStateContext to SettingsAuthContext

* Naive initial impl of settings persistence to file system

* Update app identifier in tauri config

* Add "show in folder" tauri command

* Load from and save to file system in Tauri app

* Add documents drive to tauri permission scope

* Add recursive prop to default dir selection dialog

* Add success toast to web restore defaults action

* Add a way to validate read-in settings

* Update imports to use separate settings lib file

* Validate localStorage-loaded settings, combine error message

* Add a e2e test for validation

* Clean up state state bugs

* Reverse validation looping so new users don't error

* update settingsMachine typegen to remove conflicts

* Fmt

* Fix TS errors
2024-02-15 14:14:14 -05:00
d9bcadb062 fix animation (#1426)
* fix animation to vertical quaternion

* test tweak
2024-02-15 20:32:59 +11:00
19f669b94c fix zoom cam change (#1420) 2024-02-15 09:13:37 +11:00
d9ef471385 Clean up new artifact types (#1419)
* clear up circular pattern and upgrade lib

* clean up imported object
2024-02-15 07:24:54 +11:00
39f8b306a2 Update KNOWN-ISSUES.md 2024-02-13 15:25:49 -08:00
19925d22c1 rename scene classes for clarity (#1409)
* rename for clarity

* typo

* make coverage happ+
somewhat pointless since we don't use coverage because its not complete with both vitest and playwright

* local storage issue

* fmt

* fix
2024-02-14 08:03:20 +11:00
e1af4b4219 add known issues file (#1408)
* updates

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

* fixes

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

* Update KNOWN-ISSUES.md

---------

Signed-off-by: Jess Frazelle <github@jessfraz.com>
2024-02-13 12:20:32 -08:00
c699611f5b Pull Circular patterns through to App (#1405)
* debugging steps

* add testing

* Update src/wasm-lib/tests/executor/main.rs

* generate docs and fmt

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

---------

Signed-off-by: Jess Frazelle <github@jessfraz.com>
Co-authored-by: gserena <serena@zoo.dev>
Co-authored-by: Jess Frazelle <jessfraz@users.noreply.github.com>
Co-authored-by: Jess Frazelle <github@jessfraz.com>
2024-02-13 11:20:49 -08:00
00ede7ec1a clean up (#1407) 2024-02-14 06:19:52 +11:00
f30601bd2c cost part001 = startSketchOn(..) should be undone . . . (#1404)
* undo sketch if no lines have been created

* fix sketch axis bug

* fix wrong event origin bug

* race condition on animation ending

* remove logs

* codespell
2024-02-14 05:35:05 +11:00
cfbc77b62f Start end for sketch on face (#1406)
* updates

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

* add tests

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

* clippy

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

---------

Signed-off-by: Jess Frazelle <github@jessfraz.com>
2024-02-13 10:26:09 -08:00
808830d29e don't put scene commands in the artifact map (#1403) 2024-02-13 18:47:37 +11:00
e714103655 Bump winnow from 0.5.39 to 0.5.40 in /src/wasm-lib (#1402)
Bumps [winnow](https://github.com/winnow-rs/winnow) from 0.5.39 to 0.5.40.
- [Changelog](https://github.com/winnow-rs/winnow/blob/main/CHANGELOG.md)
- [Commits](https://github.com/winnow-rs/winnow/compare/v0.5.39...v0.5.40)

---
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>
2024-02-13 16:33:19 +11:00
fbcb96add5 Sketch on face (#1371)
* add extra metadata to extrudeGroup

* add boilerplate

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

* updates

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

* cleanup and generate docs

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

* change plane id to entity id

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

* generate docs

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

* get face id from extrude using segment tag

* cleanup a bit

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

* cleanup a bit

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

* fix doc comment

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

* get rid of face_id in geo_meta

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

* updates

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

* sketch on face test

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

* updates

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

* cleanup edge_id

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

* updates

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

* fix value

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

* fix test

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

* fix tests

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

* fix tests

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

---------

Signed-off-by: Jess Frazelle <github@jessfraz.com>
Co-authored-by: Kurt Hutten Irev-Dev <k.hutten@protonmail.ch>
2024-02-12 18:08:42 -08:00
7386ccf1bf Playwright README fix (#1345)
* Update playwright readme, and snaps
Will fix #1340

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

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-02-12 19:48:50 -05:00
6e73578933 fix bool (#1399)
fixes bool

Signed-off-by: Jess Frazelle <github@jessfraz.com>
2024-02-12 15:33:54 -08:00
b88d5c8799 Add keyboard shortcut to settings (#1368) 2024-02-12 18:11:47 -05:00
5430c1fa66 Propagate errors UI (#1369)
* Pass engine connection state to NetworkHealthIndicator

* Create the basis for styling and further work

* Add icons

* Update styles on network health indicator

* Cleanup styles and unused state

* Rename State to NetworkHealthState

* Update tests

* fmt

---------

Co-authored-by: 49lf <ircsurfer33@gmail.com>
2024-02-12 16:00:31 -05:00
c0d4bb6c9f clean up (#1398) 2024-02-13 07:41:37 +11:00
25260a88c3 Import files (#1393)
* initial shit

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

* make file system work locally or with tauri

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

* fixxes

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

* updates

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

* fix docs

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

* fixes

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

* updates

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

* fix docs

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

* tests

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

* updates;

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

* add tests

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

* updates

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

* add more tests

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

* better errrors

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

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

* better docs

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

* better docs

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

* cleanup

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

* fix

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

* better errors

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

* make no assign work

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

* cleanup

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

* updates

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

* updates

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

* closer

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

* cleanup

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

* more additions to passing around fs

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

* make work

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

* updates

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

* updates

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

* updates

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

---------

Signed-off-by: Jess Frazelle <github@jessfraz.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-02-12 12:18:37 -08:00
b6d6f0f4c1 Bump actions/checkout from 2 to 4 (#1397)
Bumps [actions/checkout](https://github.com/actions/checkout) from 2 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/v2...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>
2024-02-11 19:54:08 -08:00
b1276b2ed8 Bump actions/setup-python from 2 to 5 (#1396)
Bumps [actions/setup-python](https://github.com/actions/setup-python) from 2 to 5.
- [Release notes](https://github.com/actions/setup-python/releases)
- [Commits](https://github.com/actions/setup-python/compare/v2...v5)

---
updated-dependencies:
- dependency-name: actions/setup-python
  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>
2024-02-11 19:54:01 -08:00
5f0f3f40d0 Fix default memory so its always initialized in one place -> rust (#1395)
* initial redo

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

* default memory

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

* rename values

* find tricky case

* fix test

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

---------

Signed-off-by: Jess Frazelle <github@jessfraz.com>
Co-authored-by: Kurt Hutten Irev-Dev <k.hutten@protonmail.ch>
2024-02-11 18:26:09 -08:00
f1ea9b6ece small comment (#1394)
* small comment

* typo
2024-02-12 00:07:33 +00:00
b94c5be1af Linear patterns (#1362)
* add linear patterns

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

* updates

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

* fix clippy

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

* add failing test for serialisation issue

* cleanup tests

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

* cleanup memoryitem

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

* add test to serialize memory item from rust

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

* update test

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

* run cargo sort everywhere

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

* run fmt everywhere

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

* fix typo

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

* clean up linear paterns on re-execute

* selections fix for patterns

* fix clippy

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

* fixes

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

---------

Signed-off-by: Jess Frazelle <github@jessfraz.com>
Co-authored-by: Kurt Hutten Irev-Dev <k.hutten@protonmail.ch>
2024-02-11 15:08:54 -08:00
8378eb1e94 Bump image from 0.24.7 to 0.24.8 in /src/wasm-lib (#1392)
Bumps [image](https://github.com/image-rs/image) from 0.24.7 to 0.24.8.
- [Changelog](https://github.com/image-rs/image/blob/master/CHANGES.md)
- [Commits](https://github.com/image-rs/image/compare/v0.24.7...v0.24.8)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-02-11 12:30:24 -08:00
05f98a8c39 Bump tokio from 1.35.1 to 1.36.0 in /src/wasm-lib (#1391)
Bumps [tokio](https://github.com/tokio-rs/tokio) from 1.35.1 to 1.36.0.
- [Release notes](https://github.com/tokio-rs/tokio/releases)
- [Commits](https://github.com/tokio-rs/tokio/compare/tokio-1.35.1...tokio-1.36.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>
2024-02-11 12:10:17 -08:00
386571fa60 Bump uuid from 1.6.1 to 1.7.0 in /src/wasm-lib (#1390)
Bumps [uuid](https://github.com/uuid-rs/uuid) from 1.6.1 to 1.7.0.
- [Release notes](https://github.com/uuid-rs/uuid/releases)
- [Commits](https://github.com/uuid-rs/uuid/compare/1.6.1...1.7.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>
2024-02-11 12:10:08 -08:00
b0abdf4f70 Bump once_cell from 1.18.0 to 1.19.0 in /src/wasm-lib (#1389)
Bumps [once_cell](https://github.com/matklad/once_cell) from 1.18.0 to 1.19.0.
- [Changelog](https://github.com/matklad/once_cell/blob/master/CHANGELOG.md)
- [Commits](https://github.com/matklad/once_cell/compare/v1.18.0...v1.19.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-02-11 12:09:59 -08:00
81e70e139f Bump parse-display from 0.8.2 to 0.9.0 in /src/wasm-lib (#1388)
Bumps [parse-display](https://github.com/frozenlib/parse-display) from 0.8.2 to 0.9.0.
- [Changelog](https://github.com/frozenlib/parse-display/blob/master/CHANGELOG.md)
- [Commits](https://github.com/frozenlib/parse-display/compare/v0.8.2...v0.9.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-02-11 12:09:50 -08:00
d6bfc38d62 Bump wasm-bindgen-futures from 0.4.38 to 0.4.41 in /src/wasm-lib (#1387)
Bumps [wasm-bindgen-futures](https://github.com/rustwasm/wasm-bindgen) from 0.4.38 to 0.4.41.
- [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>
2024-02-11 12:09:40 -08:00
ada66de92d Bump futures from 0.3.29 to 0.3.30 in /src/wasm-lib (#1382)
Bumps [futures](https://github.com/rust-lang/futures-rs) from 0.3.29 to 0.3.30.
- [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.29...0.3.30)

---
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>
2024-02-11 19:54:02 +00:00
8f133f9662 Bump anyhow from 1.0.75 to 1.0.79 in /src/wasm-lib (#1386)
Bumps [anyhow](https://github.com/dtolnay/anyhow) from 1.0.75 to 1.0.79.
- [Release notes](https://github.com/dtolnay/anyhow/releases)
- [Commits](https://github.com/dtolnay/anyhow/compare/1.0.75...1.0.79)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-02-11 19:50:46 +00:00
b360dbb961 Bump async-trait from 0.1.74 to 0.1.77 in /src/wasm-lib (#1385)
Bumps [async-trait](https://github.com/dtolnay/async-trait) from 0.1.74 to 0.1.77.
- [Release notes](https://github.com/dtolnay/async-trait/releases)
- [Commits](https://github.com/dtolnay/async-trait/commits)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-02-11 19:50:42 +00:00
eca3dc2967 Bump proc-macro2 from 1.0.76 to 1.0.78 in /src/wasm-lib (#1384)
Bumps [proc-macro2](https://github.com/dtolnay/proc-macro2) from 1.0.76 to 1.0.78.
- [Release notes](https://github.com/dtolnay/proc-macro2/releases)
- [Commits](https://github.com/dtolnay/proc-macro2/compare/1.0.76...1.0.78)

---
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>
2024-02-11 19:50:34 +00:00
ae36ab6982 Bump regex from 1.10.2 to 1.10.3 in /src/wasm-lib (#1383)
Bumps [regex](https://github.com/rust-lang/regex) from 1.10.2 to 1.10.3.
- [Release notes](https://github.com/rust-lang/regex/releases)
- [Changelog](https://github.com/rust-lang/regex/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rust-lang/regex/compare/1.10.2...1.10.3)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-02-11 19:50:19 +00:00
8cb6cf1b8a Bump bson from 2.7.0 to 2.9.0 in /src/wasm-lib (#1381)
Bumps [bson](https://github.com/mongodb/bson-rust) from 2.7.0 to 2.9.0.
- [Release notes](https://github.com/mongodb/bson-rust/releases)
- [Commits](https://github.com/mongodb/bson-rust/compare/v2.7.0...v2.9.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-02-11 19:40:26 +00:00
3c235c890a Bump winnow from 0.5.19 to 0.5.39 in /src/wasm-lib (#1380)
Bumps [winnow](https://github.com/winnow-rs/winnow) from 0.5.19 to 0.5.39.
- [Changelog](https://github.com/winnow-rs/winnow/blob/main/CHANGELOG.md)
- [Commits](https://github.com/winnow-rs/winnow/compare/v0.5.19...v0.5.39)

---
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>
2024-02-11 19:40:19 +00:00
b6dfd30840 Bump reqwest from 0.11.22 to 0.11.24 in /src/wasm-lib (#1379)
Bumps [reqwest](https://github.com/seanmonstar/reqwest) from 0.11.22 to 0.11.24.
- [Release notes](https://github.com/seanmonstar/reqwest/releases)
- [Changelog](https://github.com/seanmonstar/reqwest/blob/master/CHANGELOG.md)
- [Commits](https://github.com/seanmonstar/reqwest/compare/v0.11.22...v0.11.24)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-02-11 19:40:06 +00:00
65d128eecd Bump web-sys from 0.3.65 to 0.3.68 in /src/wasm-lib (#1378)
Bumps [web-sys](https://github.com/rustwasm/wasm-bindgen) from 0.3.65 to 0.3.68.
- [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>
2024-02-11 19:39:53 +00:00
77b7c602f2 Bump thiserror from 1.0.56 to 1.0.57 in /src/wasm-lib (#1377)
Bumps [thiserror](https://github.com/dtolnay/thiserror) from 1.0.56 to 1.0.57.
- [Release notes](https://github.com/dtolnay/thiserror/releases)
- [Commits](https://github.com/dtolnay/thiserror/compare/1.0.56...1.0.57)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-02-11 19:30:34 +00:00
fa0e61a2be Bump itertools from 0.12.0 to 0.12.1 in /src/wasm-lib (#1376)
Bumps [itertools](https://github.com/rust-itertools/itertools) from 0.12.0 to 0.12.1.
- [Changelog](https://github.com/rust-itertools/itertools/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rust-itertools/itertools/compare/v0.12.0...v0.12.1)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-02-11 11:22:48 -08:00
1cf35a611e Bump kittycad-execution-plan from 186ea7c to 632b75a in /src/wasm-lib (#1375)
Bump kittycad-execution-plan in /src/wasm-lib

Bumps [kittycad-execution-plan](https://github.com/KittyCAD/modeling-api) from `186ea7c` to `632b75a`.
- [Commits](186ea7c04d...632b75a024)

---
updated-dependencies:
- dependency-name: kittycad-execution-plan
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-02-11 11:22:23 -08:00
952d0e4c7c Bump js-sys from 0.3.65 to 0.3.68 in /src/wasm-lib (#1355)
Bumps [js-sys](https://github.com/rustwasm/wasm-bindgen) from 0.3.65 to 0.3.68.
- [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>
2024-02-11 11:21:51 -08:00
0f85de9df8 Bump tokio-tungstenite from 0.20.1 to 0.21.0 in /src/wasm-lib (#1272)
Bumps [tokio-tungstenite](https://github.com/snapview/tokio-tungstenite) from 0.20.1 to 0.21.0.
- [Changelog](https://github.com/snapview/tokio-tungstenite/blob/master/CHANGELOG.md)
- [Commits](https://github.com/snapview/tokio-tungstenite/compare/v0.20.1...v0.21.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-02-11 19:19:37 +00:00
0e8eed3f82 Bump wasm-bindgen from 0.2.88 to 0.2.91 in /src/wasm-lib (#1354)
Bumps [wasm-bindgen](https://github.com/rustwasm/wasm-bindgen) from 0.2.88 to 0.2.91.
- [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.88...0.2.91)

---
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>
2024-02-11 11:10:52 -08:00
5b43a5075f Bump openapitor from 920ba7c to 88b05a6 in /src/wasm-lib (#1357)
Bumps [openapitor](https://github.com/KittyCAD/kittycad.rs) from `920ba7c` to `88b05a6`.
- [Release notes](https://github.com/KittyCAD/kittycad.rs/releases)
- [Commits](920ba7c69f...88b05a638f)

---
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>
2024-02-11 11:09:37 -08:00
f5ed4e37b2 Bump clap from 4.4.8 to 4.5.0 in /src/wasm-lib (#1363)
Bumps [clap](https://github.com/clap-rs/clap) from 4.4.8 to 4.5.0.
- [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/clap_complete-v4.4.8...clap_complete-v4.5.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-02-11 11:09:03 -08:00
19c8da1a86 Add Discord release updates automation (#1346)
adding Discord release updates automation
2024-02-11 11:08:00 -08:00
a25f89aaba Bump tokio from 1.34.0 to 1.36.0 in /src-tauri (#1342)
Bumps [tokio](https://github.com/tokio-rs/tokio) from 1.34.0 to 1.36.0.
- [Release notes](https://github.com/tokio-rs/tokio/releases)
- [Commits](https://github.com/tokio-rs/tokio/compare/tokio-1.34.0...tokio-1.36.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>
2024-02-11 11:03:55 -08:00
aeebe5416f Bump kittycad from 0.2.42 to 0.2.50 in /src-tauri (#1358)
Bumps [kittycad](https://github.com/KittyCAD/kittycad.rs) from 0.2.42 to 0.2.50.
- [Release notes](https://github.com/KittyCAD/kittycad.rs/releases)
- [Commits](https://github.com/KittyCAD/kittycad.rs/compare/v0.2.42...v0.2.50)

---
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>
2024-02-11 10:55:04 -08:00
661788b8b0 Bump serde from 1.0.193 to 1.0.196 in /src-tauri (#1373)
Bumps [serde](https://github.com/serde-rs/serde) from 1.0.193 to 1.0.196.
- [Release notes](https://github.com/serde-rs/serde/releases)
- [Commits](https://github.com/serde-rs/serde/compare/v1.0.193...v1.0.196)

---
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>
2024-02-11 10:54:32 -08:00
ac24563159 Bump serde_json from 1.0.108 to 1.0.113 in /src-tauri (#1374)
Bumps [serde_json](https://github.com/serde-rs/json) from 1.0.108 to 1.0.113.
- [Release notes](https://github.com/serde-rs/json/releases)
- [Commits](https://github.com/serde-rs/json/compare/v1.0.108...v1.0.113)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-02-11 10:54:23 -08:00
d17342dfb8 Bump anyhow from 1.0.75 to 1.0.79 in /src-tauri (#1372)
Bumps [anyhow](https://github.com/dtolnay/anyhow) from 1.0.75 to 1.0.79.
- [Release notes](https://github.com/dtolnay/anyhow/releases)
- [Commits](https://github.com/dtolnay/anyhow/compare/1.0.75...1.0.79)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-02-11 10:54:10 -08:00
2e93b58ae6 Bump tauri-plugin-fs-extra from 537053d to 67405ae in /src-tauri (#1370)
Bumps [tauri-plugin-fs-extra](https://github.com/tauri-apps/plugins-workspace) from `537053d` to `67405ae`.
- [Release notes](https://github.com/tauri-apps/plugins-workspace/releases)
- [Commits](537053d317...67405aed06)

---
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>
2024-02-11 10:25:57 -08:00
6593656b08 Bump google-github-actions/setup-gcloud from 2.0.0 to 2.1.0 (#1311)
Bumps [google-github-actions/setup-gcloud](https://github.com/google-github-actions/setup-gcloud) from 2.0.0 to 2.1.0.
- [Release notes](https://github.com/google-github-actions/setup-gcloud/releases)
- [Changelog](https://github.com/google-github-actions/setup-gcloud/blob/main/CHANGELOG.md)
- [Commits](https://github.com/google-github-actions/setup-gcloud/compare/v2.0.0...v2.1.0)

---
updated-dependencies:
- dependency-name: google-github-actions/setup-gcloud
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-02-11 10:24:54 -08:00
47be749ec7 Bump tauri from 1.5.3 to 1.5.4 in /src-tauri (#1245)
Bumps [tauri](https://github.com/tauri-apps/tauri) from 1.5.3 to 1.5.4.
- [Release notes](https://github.com/tauri-apps/tauri/releases)
- [Commits](https://github.com/tauri-apps/tauri/compare/tauri-v1.5.3...tauri-v1.5.4)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-02-11 10:24:29 -08:00
a03e7f5c41 Bump tauri-build from 1.5.0 to 1.5.1 in /src-tauri (#1244)
Bumps [tauri-build](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-build-v1.5...tauri-build-v1.5.1)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-02-11 10:24:10 -08:00
b78e9fa131 Bump google-github-actions/auth from 2.0.0 to 2.1.1 (#1347)
Bumps [google-github-actions/auth](https://github.com/google-github-actions/auth) from 2.0.0 to 2.1.1.
- [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/v2.0.0...v2.1.1)

---
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>
2024-02-11 10:23:49 -08:00
c629233eaa Bump google-github-actions/upload-cloud-storage from 2.0.0 to 2.1.0 (#1313)
Bumps [google-github-actions/upload-cloud-storage](https://github.com/google-github-actions/upload-cloud-storage) from 2.0.0 to 2.1.0.
- [Release notes](https://github.com/google-github-actions/upload-cloud-storage/releases)
- [Changelog](https://github.com/google-github-actions/upload-cloud-storage/blob/main/CHANGELOG.md)
- [Commits](https://github.com/google-github-actions/upload-cloud-storage/compare/v2.0.0...v2.1.0)

---
updated-dependencies:
- dependency-name: google-github-actions/upload-cloud-storage
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-02-11 10:23:32 -08:00
f640f7a5e0 Client sketch scene (#1271)
* updates

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

* updates

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

* updates

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

* make tsc happ

* better error msg

* fix control point issue

* basic code gen working for tangentialArc

* partical fix for move with arcs

* tangential arc move

* fix

* make eslint rules less annoying

* inital refactor of some xstate stuff

* more old tangential arc clean up stuff

* more tweaks

* add testing

* tweak xstate inspect

* temp remove test

* update formating for less conflicts

* fix state machine layout after merge

* shrug, something weird with xstate typegen

* renaming some xstate events

* tweak numbers to make CI playwright happ

* CI hacks

* more CI hacks

* more CI hacks

* new hack strategy

* run tests agian

* make cmd bar less flaky

* ci hacks

* CI hacks

* CI hacks

* CI hacks

* clean up

* fix

* still have constraint stuff to deal with

* progress on move rules

* update source ranges after no execute code-mod

* typo

* mvp working

* hide show sketch overlay

* match scaling

* update arrow head style

* animate line tool

* bypass xstate for animations, much smoother

* add new segment working with refactor needed for setup paper sketch

* refactor setup paper sketch

* tangantialArcTo drag animations working

* tangential arc polish

* cargo fmt

* clippy

* more clippy

* mock canvas

* last of clippy?

* typo

* more clippy stuff

* move util function so they are shareable with typescript

* migrate a bunch to rust and only rust

* add arc center point for draft tangential ac

* clippy tweak

* delete uneeded test

* Rough start to scaling arrow heads.

The tangent arrow heads are basically nuked and replaced while the
straight line sections are just rotated and repositioned, this means they
miss out on updating scaling number after a screen size changes.
Needs fixing

* fix bug with tool tips

* fix draft line start position

Having drag the end of teh path before selecting a tooltip would result in the draft line starting where the path used to end, stale data

* some progress with pan maybe

* fmt

* inital camera sync working

For perspective camera at least

* change three.js to use z-up

* add grid

* orthographic camera working with polish items TODO

* fix zoom level when swapping camera

* fix up camera/orbit changing on cam change (pan wasn't being respected)

* tidy up

* use orbit target instead of assuming scene center

* dynamic fov working

* animate orthographic to perspective and reverse

* fix import

* temp fix for batch commands

* initial client side scene sketch working

* remove hover log

* FOV adjust fix

* fix comment

* tear down sketch and small tweaks

* some progress with camera tweening

* combine dollyZoom engine commands

see
https://github.com/KittyCAD/modeling-api/compare/kurt-perspective-settings?expand=1
and
https://github.com/KittyCAD/engine/compare/kurt-perspective-settings?expand=1

* make tests happy (mocks)

* fix tween to vertical/camera-up bug

* tween to each axis with hacky solutions in there

* fix startSketchOn planes

* tidy startSketchOn

* tweening okay for now I think

* get sketching on default planes working

* allow editing on all default planes

* clean up enter and exit sketch logic

* tidy

* tidy

* remove more default plane stuff

* start of draft line

* remove some annoying parts of the paper.js implementation

* fix drag than equip line bug

* comment

* don't animate on skech tear down since it's used for draft line

* remove more default plane shit

* style draft line

* refine dashed line

* draft line set up and tear down mostly happy

* add on click logic ready for draft lines

* sketch mode with drag and draft mode working solidly now, straight segments only

* default planes match colors, hover and select still TODO

* hover and click logic working for default planes

Now just need the code mode to fire to 'startSketchOn(...)'

* select default planes

* remove some logs

* fix update infinite loop

* start of orbitControls port to Franks control guards

* hiding scenes at different times

* scene hide on camera move should be respected by scroll zoom

* basic hover working

* Hook up user camera settings to ClientSideScene (#1334)

* Refactor to not import utilities from Router.tsx

* Stop tracking changes or formatting *.typegen.ts

* Hook up cameraControls to ClientSideScene

* Remove camera controls toggle from temp debug panel

---------

Co-authored-by: Kurt Hutten Irev-Dev <k.hutten@protonmail.ch>

* add select segment moves cursor

* highlight segments yellow on hover

* cursor ranges effect 2d line colors

* fix constrainst i.e. make sure the sketch is rejiged

* selecting nothing should remove selections

* remove hardcoded strings

* update get_tangential_arc_to_info rust util

* initial drawing of tangential arcs in client scene

* fix tangentialArc arrow head direction

* correct userData types for tangential arcs

* get tangential arc updates working

Doesn't include draging the head of the tangential arc itself yet

* spot of clean up

* make selections work with tangential arcs

* get draft tangential segment animated

* fix initial click weirdness for adding new tangential line

* couple tweaks

* add grace pixels /threshold to raycast

* redo arc dashes so that they spawn from the ccenter of the arc

* fix multi drag bug

* fmt

* add temp solution for close

* add default axis hover colors, still needs select logic

* selection of axis works, just with out selection color

* get axis selection colors working

* fix outdate source ranges after drag problem

* update moreNodePathFromSourceRange

* fix ts-rs issue/workaround

* fix default plane weirdness

* fix tangential arc rounding issue

* review clean up part 1

* review clean up part 2

Big state-diagram cull

* clippy

* typo

* clippy

* fix xstate types with typegen

* fix types

* clippy

* catch error

* fix test import issue

Not sure exactly what was happening but guessing circular import that vite didn't like

* add axis/plane info to sketch group tests

* case changes because of rs-ts bug, can probably revert this later

* start of playwright test fixes

* reduce geo complexity for straight segments

* fix cam adjust tests

* Revert "Clean up vite build warnings (#1332)"

This reverts commit c1f661ab52.

* selection e2e test fixed<

* remove camToggle to allow playwright tests to pass

* remove drag test

too brittle and needs to be redone from the ground up anyway

* trigger CI

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

* fix last test

* clean up part 3

* clean up part 4

* clean up part 5

* clean up sketch enter exit logic

* fix engine side selections

* default plane should not be selected form 'onDragEnd'
i.e. rotating the camera should not mean the user acidently selects a plane

* clean up state diagram around animating to sketch mode

Embracing that the animation is async and puting the interdiate steps in the state diagram clean up some logic and solved some bugs at the same time

* add test for multiple sketches

* typo

* make highlight more robust

* type tweak

* scale segmenst with distance from camera so they have a consistent pixel size/ screenspace size

* Jess's advice

* tsc and fmt

* clean up part 6

remove integer from xstate names

* clean up part 7

* integrate sequency in to camera moves

* fix tests

* update snapshot e2e

* small snapshot change

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

* trigger ci

* Fix HomeLoaderData types

* update std stuff

* update kittycad rs client lib

---------

Signed-off-by: Jess Frazelle <github@jessfraz.com>
Co-authored-by: Jess Frazelle <github@jessfraz.com>
Co-authored-by: Frank Noirot <frank@kittycad.io>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-02-11 12:59:00 +11:00
64398381a9 Make command bar arguments skippable, configure Extrude selection to be skippable (#1353)
* Make commands able to be configured as 'skippable'

* Make command machine able to skip arguments

* Add support for skippable selections, which are not known until within input component

* Update extrude command config to skippable

* Use defaultValue to seed initial arg values, not payload

* Remove unused `payload` command config prop

* Make skip and defaultValue types more exact

* Remove console logs

* fmt

* Revert type tightening, not worth the headache
2024-02-08 12:59:01 -05:00
0bc5534056 Fix build-test-apps (ubuntu-latest) (#1360)
* Revert "add kcl-samples menu item (#1352)"

This reverts commit a5879ceeda.

* Reapply "add kcl-samples menu item (#1352)"

This reverts commit 1c0ab6c8a2.

* Fix broken attempt at having tauri-driver part of Cargo.toml
2024-02-08 06:24:13 -05:00
9fc1df7c1d Add app version to UI in Settings (#1351)
* Make package version available in app code

* Show app version in settings page with link

* fmt

* Replace Vite define with Vite plugin

* Don't use import.meta.env in bare TS file
2024-02-07 11:36:19 -05:00
a5879ceeda add kcl-samples menu item (#1352) 2024-02-06 16:38:06 -07:00
379c30824e Grackle: handle complicated nested computed objects (#1348)
Previously, Grackle could compile a single computed property like `array[x]`. But it couldn't handle:

- Nested properties like `array[x][y]`
- Nested objects like `obj[x][y]`
- Arrays nested in objects, like `{x: [1,2,3]}`
- Objects in arrays like `[{a: 1}]`

It was quite difficult to find a way to handle this, which is why I started the EP Debugger project. The debugger helped me understand the VM much better and figure out a better way to handle these cases (the two new instructions AddrOfMember and CopyLen). Now Grackle can compile those cases!
2024-02-06 22:44:30 +11:00
a4d3263b88 Fix tauri e2e tests (tauri-driver = 0.1.3) (#1344)
* Fix tauri e2e tests (driver update)
Fixes #1343

* Clean up

* Clean up
2024-02-05 05:14:05 -05:00
c1f661ab52 Clean up vite build warnings (#1332)
* WIP Clean up vite build warnings
Fixes #1014

* Fix lint

* Fix React Hooks dependencies
Clean up, use void when await not straightforward

* Clean up

* Fix missing deconstruction
2024-01-31 04:17:24 -05:00
7d887a1497 Grackle: computed properties of objects (#1337) 2024-01-30 17:10:16 +11:00
4ca341e132 Grackle: Store KCL objects as KCEP objects (#1333)
* Grackle: Store KCL objects as KCEP objects

* Remove KCL SingleValue

* Fix a test, update map bindings

* Fix tests
2024-01-30 15:18:45 +11:00
c6249f36d2 Grackle: Runtime computed array indices (#1331) 2024-01-29 17:36:29 +11:00
dcbe5d7f75 Fix tauri tests in build-test-apps (#1328) 2024-01-27 02:59:43 -05:00
390cb2d51d Grackle: Write array length before array (#1326)
This gives the Execution Plan virtual machine the information it needs to look up indices of arrays at runtime.
2024-01-26 08:07:29 +00:00
98f7a564ea Use named fields for EpBinding::Sequence (#1325) 2024-01-26 18:38:54 +11:00
05f9e3c290 Grackle: update execution-plan repo (#1324) 2024-01-26 07:16:19 +00:00
09760fc2e9 Grackle: Allow objects to be params into arrays (#1322) 2024-01-25 00:05:41 +00:00
18ffc43e89 Grackle: Allow arrays to be args to functions (#1321)
Includes two refactors:

- Move array binding into its own method
- Use EvalPlan instead of an equivalent tuple (instructions, binding)
2024-01-24 23:38:18 +00:00
de63e4f19f Grackle: Refactor: Move error types into their own module (#1319)
Refactor: Move error types into their own submodule
2024-01-24 05:47:56 +00:00
b70b271e6b Grackle: compile KCL bools to EP bools (#1318) 2024-01-24 05:36:09 +00:00
08b7cdc5f6 Grackle: pipeline expressions (#1315)
Grackle can now compile |> pipelines. This means that these two programs compile to identical execution plans:

```kcl
fn double = (x) => { return x * 2 }
fn triple = (x) => { return x * 3 }
let x = 1 |> double(%) |> triple(%) // should be 6
```
```kcl
fn double = (x) => { return x * 2 }
fn triple = (x) => { return x * 3 }
let x = triple(double(1)) // should be 6
```

This required adding passing "what should % actually resolve to" through the program. This required modifying every call site of `plan_to_bind` and `plan_to_compute` to pass the data. To avoid doing this again, I wrapped that data into a struct called `Context` so that when we have more data like it, we can just add a new field and won't need to change every call site.
2024-01-24 10:05:40 +11:00
6efe6b54c0 Fix typo in onboarding (#1316)
fix typo
2024-01-23 17:46:34 -05:00
69f72d62e0 Rework initial engine connection logic (#1205) (#1221)
Rework EngineConnection class (#1205)

Co-authored-by: lf94 <inbox@leefallat.ca>
2024-01-23 13:13:43 -05:00
279 changed files with 50739 additions and 7061 deletions

View File

@ -1,3 +1,3 @@
[codespell] [codespell]
ignore-words-list: crate,everytime ignore-words-list: crate,everytime,inout,co-ordinate,ot,nwo
skip: **/target,node_modules,build,**/Cargo.lock skip: **/target,node_modules,build,**/Cargo.lock

View File

@ -1 +1,2 @@
src/wasm-lib/* src/wasm-lib/*
*.typegen.ts

View File

@ -17,12 +17,12 @@
"never" "never"
], ],
"react-hooks/exhaustive-deps": "off", "react-hooks/exhaustive-deps": "off",
"@typescript-eslint/no-floating-promises": "warn"
}, },
"overrides": [ "overrides": [
{ {
"files": ["e2e/**/*.ts"], // Update the pattern based on your file structure "files": ["e2e/**/*.ts"], // Update the pattern based on your file structure
"rules": { "rules": {
"@typescript-eslint/no-floating-promises": "warn",
"testing-library/prefer-screen-queries": "off" "testing-library/prefer-screen-queries": "off"
} }
} }

View File

@ -46,6 +46,7 @@ jobs:
workspaces: './src/wasm-lib' workspaces: './src/wasm-lib'
- run: yarn build:wasm - run: yarn build:wasm
- run: yarn xstate:typegen
- run: yarn tsc - run: yarn tsc
@ -85,8 +86,6 @@ jobs:
- run: yarn test:nowatch - run: yarn test:nowatch
- run: yarn test:cov
prepare-json-files: prepare-json-files:
runs-on: ubuntu-latest # seperate job on Ubuntu for easy string manipulations (compared to Windows) runs-on: ubuntu-latest # seperate job on Ubuntu for easy string manipulations (compared to Windows)
@ -125,7 +124,7 @@ jobs:
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
os: [macos-latest, ubuntu-latest, windows-latest] os: [macos-14, ubuntu-latest, windows-latest]
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
@ -188,10 +187,10 @@ jobs:
- name: Fix format - name: Fix format
run: yarn fmt run: yarn fmt
- name: Install Universal target (MacOS only) - name: Install x86 target for Universal builds (MacOS only)
if: matrix.os == 'macos-latest' if: matrix.os == 'macos-14'
run: | run: |
rustup target add aarch64-apple-darwin rustup target add x86_64-apple-darwin
- name: Prepare certificate and variables (Windows only) - name: Prepare certificate and variables (Windows only)
if: ${{ matrix.os == 'windows-latest' && env.BUILD_RELEASE == 'true' }} if: ${{ matrix.os == 'windows-latest' && env.BUILD_RELEASE == 'true' }}
@ -225,7 +224,7 @@ jobs:
with: with:
includeRelease: false includeRelease: false
includeDebug: true includeDebug: true
args: ${{ matrix.os == 'macos-latest' && '--target universal-apple-darwin' || '' }} args: ${{ matrix.os == 'macos-14' && '--target universal-apple-darwin' || '' }}
- name: Build the app (release) and sign - name: Build the app (release) and sign
uses: tauri-apps/tauri-action@v0 uses: tauri-apps/tauri-action@v0
@ -241,12 +240,12 @@ jobs:
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }} APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
TAURI_CONF_ARGS: "--config ${{ matrix.os == 'windows-latest' && 'src-tauri\\tauri.release.conf.json' || 'src-tauri/tauri.release.conf.json' }}" TAURI_CONF_ARGS: "--config ${{ matrix.os == 'windows-latest' && 'src-tauri\\tauri.release.conf.json' || 'src-tauri/tauri.release.conf.json' }}"
with: with:
args: "${{ matrix.os == 'macos-latest' && '--target universal-apple-darwin' || '' }} ${{ env.TAURI_CONF_ARGS }}" args: "${{ matrix.os == 'macos-14' && '--target universal-apple-darwin' || '' }} ${{ env.TAURI_CONF_ARGS }}"
- uses: actions/upload-artifact@v3 - uses: actions/upload-artifact@v3
if: matrix.os != 'ubuntu-latest' if: matrix.os != 'ubuntu-latest'
env: env:
PREFIX: ${{ matrix.os == 'macos-latest' && 'src-tauri/target/universal-apple-darwin' || 'src-tauri/target' }} PREFIX: ${{ matrix.os == 'macos-14' && 'src-tauri/target/universal-apple-darwin' || 'src-tauri/target' }}
MODE: ${{ env.BUILD_RELEASE == 'true' && 'release' || 'debug' }} MODE: ${{ env.BUILD_RELEASE == 'true' && 'release' || 'debug' }}
with: with:
path: "${{ env.PREFIX }}/${{ env.MODE }}/bundle/*/*" path: "${{ env.PREFIX }}/${{ env.MODE }}/bundle/*/*"
@ -254,7 +253,7 @@ jobs:
- name: Run e2e tests (linux only) - name: Run e2e tests (linux only)
if: matrix.os == 'ubuntu-latest' if: matrix.os == 'ubuntu-latest'
run: | run: |
cargo install tauri-driver cargo install tauri-driver@0.1.3
source .env.${{ env.BUILD_RELEASE == 'true' && 'production' || 'development' }} source .env.${{ env.BUILD_RELEASE == 'true' && 'production' || 'development' }}
export VITE_KC_API_BASE_URL export VITE_KC_API_BASE_URL
xvfb-run yarn test:e2e:tauri xvfb-run yarn test:e2e:tauri
@ -337,17 +336,17 @@ jobs:
cat last_download.json cat last_download.json
- name: Authenticate to Google Cloud - name: Authenticate to Google Cloud
uses: 'google-github-actions/auth@v2.0.0' uses: 'google-github-actions/auth@v2.1.1'
with: with:
credentials_json: '${{ secrets.GOOGLE_CLOUD_DL_SA }}' credentials_json: '${{ secrets.GOOGLE_CLOUD_DL_SA }}'
- name: Set up Google Cloud SDK - name: Set up Google Cloud SDK
uses: google-github-actions/setup-gcloud@v2.0.0 uses: google-github-actions/setup-gcloud@v2.1.0
with: with:
project_id: kittycadapi project_id: kittycadapi
- name: Upload release files to public bucket - name: Upload release files to public bucket
uses: google-github-actions/upload-cloud-storage@v2.0.0 uses: google-github-actions/upload-cloud-storage@v2.1.0
with: with:
path: artifact path: artifact
glob: '*/Zoo*' glob: '*/Zoo*'
@ -355,13 +354,13 @@ jobs:
destination: ${{ env.BUCKET_DIR }}/${{ env.VERSION }} destination: ${{ env.BUCKET_DIR }}/${{ env.VERSION }}
- name: Upload update endpoint to public bucket - name: Upload update endpoint to public bucket
uses: google-github-actions/upload-cloud-storage@v2.0.0 uses: google-github-actions/upload-cloud-storage@v2.1.0
with: with:
path: last_update.json path: last_update.json
destination: ${{ env.BUCKET_DIR }} destination: ${{ env.BUCKET_DIR }}
- name: Upload download endpoint to public bucket - name: Upload download endpoint to public bucket
uses: google-github-actions/upload-cloud-storage@v2.0.0 uses: google-github-actions/upload-cloud-storage@v2.1.0
with: with:
path: last_download.json path: last_download.json
destination: ${{ env.BUCKET_DIR }} destination: ${{ env.BUCKET_DIR }}
@ -371,3 +370,27 @@ jobs:
uses: softprops/action-gh-release@v1 uses: softprops/action-gh-release@v1
with: with:
files: 'artifact/*/Zoo*' files: 'artifact/*/Zoo*'
announce_release:
needs: [publish-apps-release]
runs-on: ubuntu-latest
steps:
- name: Check out code
uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.x'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install requests
- name: Announce Release
env:
DISCORD_WEBHOOK_URL: ${{ secrets.DISCORD_WEBHOOK_URL }}
RELEASE_VERSION: ${{ github.event.release.tag_name }}
RELEASE_BODY: ${{ github.event.release.body}}
run: python public/announce_release.py

View File

@ -79,7 +79,7 @@ jobs:
playwright-macos: playwright-macos:
timeout-minutes: 60 timeout-minutes: 60
runs-on: macos-latest runs-on: macos-14
needs: playwright-ubuntu needs: playwright-ubuntu
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4

4
.gitignore vendored
View File

@ -50,3 +50,7 @@ e2e/playwright/export-snapshots/*embedded.gltf
/playwright-report/ /playwright-report/
/blob-report/ /blob-report/
/playwright/.cache/ /playwright/.cache/
## generated files
src/**/*.typegen.ts

View File

@ -10,4 +10,4 @@ src/wasm-lib/kcl/bindings
e2e/playwright/export-snapshots e2e/playwright/export-snapshots
# XState generated files # XState generated files
src/machines/modelingMachine.typegen.ts src/machines/**.typegen.ts

View File

@ -136,6 +136,11 @@ Before you submit a contribution PR to this repo, please ensure that:
VERSION=x.y.z yarn run bump-jsons VERSION=x.y.z yarn run bump-jsons
``` ```
Alternatively you can try the experimental `make-release.sh` bash script that will create the branch with the updated json files for you.
run `./make-release.sh` for a patch update
run `./make-release.sh "minor"` for minor
run `./make-release.sh "major"` for major
The PR may serve as a place to discuss the human-readable changelog and extra QA. A quick way of getting PR's merged since the last bump is to [use this PR filter](https://github.com/KittyCAD/modeling-app/pulls?q=is%3Apr+sort%3Aupdated-desc+is%3Amerged+), open up the browser console and past in the following The PR may serve as a place to discuss the human-readable changelog and extra QA. A quick way of getting PR's merged since the last bump is to [use this PR filter](https://github.com/KittyCAD/modeling-app/pulls?q=is%3Apr+sort%3Aupdated-desc+is%3Amerged+), open up the browser console and past in the following
```typescript ```typescript
@ -182,7 +187,7 @@ For more information on fuzzing you can check out
First time running plawright locally, you'll need to add the secrets file First time running plawright locally, you'll need to add the secrets file
```bash ```bash
touch ./e2e/playwright/playwright-secrets.env touch ./e2e/playwright/playwright-secrets.env
echo 'token="your-token"\nsnapshottoken="your-snapshot-token"' > ./e2e/playwright/playwright-secrets2.env printf 'token="your-token"\nsnapshottoken="your-snapshot-token"' > ./e2e/playwright/playwright-secrets.env
``` ```
then replace "your-token" with a dev token from dev.zoo.dev/account/api-tokens then replace "your-token" with a dev token from dev.zoo.dev/account/api-tokens

16
docs/kcl/KNOWN-ISSUES.md Normal file
View File

@ -0,0 +1,16 @@
# Known Issues
The following are bugs that are not in modeling-app or kcl itself. These bugs
once fixed in engine will just start working here with no language changes.
- **Sketch on Face**: If your sketch is outside the edges of the face (on which you
are sketching) you will get multiple models returned instead of one single
model for that sketch and its underlying 3D object.
- **Patterns**: If you try and pass a pattern to `hole` currently only the first
item in the pattern is being subtracted. This is an engine bug that is being
worked on.
- **Import**: Right now you can import a file, even if that file has brep data
you cannot edit it. You also cannot move or transform the imported objects at
all. In the future, after v1, the engine will account for this.

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 80 KiB

After

Width:  |  Height:  |  Size: 193 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 80 KiB

After

Width:  |  Height:  |  Size: 193 KiB

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 56 KiB

After

Width:  |  Height:  |  Size: 259 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 56 KiB

After

Width:  |  Height:  |  Size: 220 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 56 KiB

After

Width:  |  Height:  |  Size: 220 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 56 KiB

After

Width:  |  Height:  |  Size: 220 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 80 KiB

After

Width:  |  Height:  |  Size: 193 KiB

View File

@ -1,7 +1,7 @@
ISO-10303-21; ISO-10303-21;
HEADER; HEADER;
FILE_DESCRIPTION((('kittycad.io export')), '2;1'); FILE_DESCRIPTION((('zoo.dev export')), '2;1');
FILE_NAME('dump.step', '1970-01-01T00:00:00.0+00:00', ('Author unknown'), ('Organization unknown'), 'kittycad.io beta', 'kittycad.io', 'Authorization unknown'); FILE_NAME('dump.step', '1970-01-01T00:00:00.0+00:00', ('Author unknown'), ('Organization unknown'), 'zoo.dev beta', 'zoo.dev', 'Authorization unknown');
FILE_SCHEMA(('AP203_CONFIGURATION_CONTROLLED_3D_DESIGN_OF_MECHANICAL_PARTS_AND_ASSEMBLIES_MIM_LF')); FILE_SCHEMA(('AP203_CONFIGURATION_CONTROLLED_3D_DESIGN_OF_MECHANICAL_PARTS_AND_ASSEMBLIES_MIM_LF'));
ENDSEC; ENDSEC;
DATA; DATA;
@ -19,59 +19,59 @@ DATA;
); );
#4 = CARTESIAN_POINT('NONE', (0, 0, -0)); #4 = CARTESIAN_POINT('NONE', (0, 0, -0));
#5 = VERTEX_POINT('NONE', #4); #5 = VERTEX_POINT('NONE', #4);
#6 = CARTESIAN_POINT('NONE', (0, -0.0254, -0)); #6 = CARTESIAN_POINT('NONE', (0, -0.64516, -0));
#7 = VERTEX_POINT('NONE', #6); #7 = VERTEX_POINT('NONE', #6);
#8 = CARTESIAN_POINT('NONE', (0, -0.0254, 0.1016)); #8 = CARTESIAN_POINT('NONE', (0, -0.64516, 2.58064));
#9 = VERTEX_POINT('NONE', #8); #9 = VERTEX_POINT('NONE', #8);
#10 = CARTESIAN_POINT('NONE', (0, 0, 0.1016)); #10 = CARTESIAN_POINT('NONE', (0, 0, 2.58064));
#11 = VERTEX_POINT('NONE', #10); #11 = VERTEX_POINT('NONE', #10);
#12 = CARTESIAN_POINT('NONE', (0.07861346939195568, -0.0254, -0)); #12 = CARTESIAN_POINT('NONE', (1.996782122555674, -0.64516, -0));
#13 = VERTEX_POINT('NONE', #12); #13 = VERTEX_POINT('NONE', #12);
#14 = CARTESIAN_POINT('NONE', (0.07861346939195568, -0.0254, 0.1016)); #14 = CARTESIAN_POINT('NONE', (1.996782122555674, -0.64516, 2.58064));
#15 = VERTEX_POINT('NONE', #14); #15 = VERTEX_POINT('NONE', #14);
#16 = CARTESIAN_POINT('NONE', (0.1511633881344551, -0.07619999999999998, -0)); #16 = CARTESIAN_POINT('NONE', (3.839550058615159, -1.9354799999999992, -0));
#17 = VERTEX_POINT('NONE', #16); #17 = VERTEX_POINT('NONE', #16);
#18 = CARTESIAN_POINT('NONE', (0.1511633881344551, -0.07619999999999998, 0.1016)); #18 = CARTESIAN_POINT('NONE', (3.839550058615159, -1.9354799999999992, 2.58064));
#19 = VERTEX_POINT('NONE', #18); #19 = VERTEX_POINT('NONE', #18);
#20 = CARTESIAN_POINT('NONE', (0.2413, -0.0762, -0)); #20 = CARTESIAN_POINT('NONE', (6.12902, -1.93548, -0));
#21 = VERTEX_POINT('NONE', #20); #21 = VERTEX_POINT('NONE', #20);
#22 = CARTESIAN_POINT('NONE', (0.2413, -0.0762, 0.1016)); #22 = CARTESIAN_POINT('NONE', (6.12902, -1.93548, 2.58064));
#23 = VERTEX_POINT('NONE', #22); #23 = VERTEX_POINT('NONE', #22);
#24 = CARTESIAN_POINT('NONE', (0.2413, -0.0635, -0)); #24 = CARTESIAN_POINT('NONE', (6.12902, -1.6129, -0));
#25 = VERTEX_POINT('NONE', #24); #25 = VERTEX_POINT('NONE', #24);
#26 = CARTESIAN_POINT('NONE', (0.2413, -0.0635, 0.1016)); #26 = CARTESIAN_POINT('NONE', (6.12902, -1.6129, 2.58064));
#27 = VERTEX_POINT('NONE', #26); #27 = VERTEX_POINT('NONE', #26);
#28 = CARTESIAN_POINT('NONE', (0.1551676827532182, -0.0635, -0)); #28 = CARTESIAN_POINT('NONE', (3.9412591419317424, -1.6129, -0));
#29 = VERTEX_POINT('NONE', #28); #29 = VERTEX_POINT('NONE', #28);
#30 = CARTESIAN_POINT('NONE', (0.1551676827532182, -0.0635, 0.1016)); #30 = CARTESIAN_POINT('NONE', (3.9412591419317424, -1.6129, 2.58064));
#31 = VERTEX_POINT('NONE', #30); #31 = VERTEX_POINT('NONE', #30);
#32 = CARTESIAN_POINT('NONE', (0.06448028432509392, 0, -0)); #32 = CARTESIAN_POINT('NONE', (1.6377992218573856, 0, -0));
#33 = VERTEX_POINT('NONE', #32); #33 = VERTEX_POINT('NONE', #32);
#34 = CARTESIAN_POINT('NONE', (0.06448028432509392, 0, 0.1016)); #34 = CARTESIAN_POINT('NONE', (1.6377992218573856, 0, 2.58064));
#35 = VERTEX_POINT('NONE', #34); #35 = VERTEX_POINT('NONE', #34);
#36 = CARTESIAN_POINT('NONE', (0.14618599799650817, 0.03810000000000001, -0)); #36 = CARTESIAN_POINT('NONE', (3.7131243491113075, 0.9677400000000002, -0));
#37 = VERTEX_POINT('NONE', #36); #37 = VERTEX_POINT('NONE', #36);
#38 = CARTESIAN_POINT('NONE', (0.14618599799650817, 0.03810000000000001, 0.1016)); #38 = CARTESIAN_POINT('NONE', (3.7131243491113075, 0.9677400000000002, 2.58064));
#39 = VERTEX_POINT('NONE', #38); #39 = VERTEX_POINT('NONE', #38);
#40 = CARTESIAN_POINT('NONE', (0.2413, 0.0381, -0)); #40 = CARTESIAN_POINT('NONE', (6.12902, 0.9677399999999998, -0));
#41 = VERTEX_POINT('NONE', #40); #41 = VERTEX_POINT('NONE', #40);
#42 = CARTESIAN_POINT('NONE', (0.2413, 0.0381, 0.1016)); #42 = CARTESIAN_POINT('NONE', (6.12902, 0.9677399999999998, 2.58064));
#43 = VERTEX_POINT('NONE', #42); #43 = VERTEX_POINT('NONE', #42);
#44 = CARTESIAN_POINT('NONE', (0.2413, 0.0508, -0)); #44 = CARTESIAN_POINT('NONE', (6.12902, 1.29032, -0));
#45 = VERTEX_POINT('NONE', #44); #45 = VERTEX_POINT('NONE', #44);
#46 = CARTESIAN_POINT('NONE', (0.2413, 0.0508, 0.1016)); #46 = CARTESIAN_POINT('NONE', (6.12902, 1.29032, 2.58064));
#47 = VERTEX_POINT('NONE', #46); #47 = VERTEX_POINT('NONE', #46);
#48 = CARTESIAN_POINT('NONE', (0.14337047578094278, 0.0508, -0)); #48 = CARTESIAN_POINT('NONE', (3.6416100848359463, 1.29032, -0));
#49 = VERTEX_POINT('NONE', #48); #49 = VERTEX_POINT('NONE', #48);
#50 = CARTESIAN_POINT('NONE', (0.14337047578094278, 0.0508, 0.1016)); #50 = CARTESIAN_POINT('NONE', (3.6416100848359463, 1.29032, 2.58064));
#51 = VERTEX_POINT('NONE', #50); #51 = VERTEX_POINT('NONE', #50);
#52 = CARTESIAN_POINT('NONE', (0.08889999999999999, 0.0254, -0)); #52 = CARTESIAN_POINT('NONE', (2.2580599999999995, 0.64516, -0));
#53 = VERTEX_POINT('NONE', #52); #53 = VERTEX_POINT('NONE', #52);
#54 = CARTESIAN_POINT('NONE', (0.08889999999999999, 0.0254, 0.1016)); #54 = CARTESIAN_POINT('NONE', (2.2580599999999995, 0.64516, 2.58064));
#55 = VERTEX_POINT('NONE', #54); #55 = VERTEX_POINT('NONE', #54);
#56 = CARTESIAN_POINT('NONE', (0, 0.0254, -0)); #56 = CARTESIAN_POINT('NONE', (0, 0.64516, -0));
#57 = VERTEX_POINT('NONE', #56); #57 = VERTEX_POINT('NONE', #56);
#58 = CARTESIAN_POINT('NONE', (0, 0.0254, 0.1016)); #58 = CARTESIAN_POINT('NONE', (0, 0.64516, 2.58064));
#59 = VERTEX_POINT('NONE', #58); #59 = VERTEX_POINT('NONE', #58);
#60 = DIRECTION('NONE', (0, -1, 0)); #60 = DIRECTION('NONE', (0, -1, 0));
#61 = VECTOR('NONE', #60, 1); #61 = VECTOR('NONE', #60, 1);
@ -79,11 +79,11 @@ DATA;
#63 = LINE('NONE', #62, #61); #63 = LINE('NONE', #62, #61);
#64 = DIRECTION('NONE', (0, 0, 1)); #64 = DIRECTION('NONE', (0, 0, 1));
#65 = VECTOR('NONE', #64, 1); #65 = VECTOR('NONE', #64, 1);
#66 = CARTESIAN_POINT('NONE', (0, -0.0254, -0)); #66 = CARTESIAN_POINT('NONE', (0, -0.64516, -0));
#67 = LINE('NONE', #66, #65); #67 = LINE('NONE', #66, #65);
#68 = DIRECTION('NONE', (0, -1, 0)); #68 = DIRECTION('NONE', (0, -1, 0));
#69 = VECTOR('NONE', #68, 1); #69 = VECTOR('NONE', #68, 1);
#70 = CARTESIAN_POINT('NONE', (0, 0, 0.1016)); #70 = CARTESIAN_POINT('NONE', (0, 0, 2.58064));
#71 = LINE('NONE', #70, #69); #71 = LINE('NONE', #70, #69);
#72 = DIRECTION('NONE', (0, 0, 1)); #72 = DIRECTION('NONE', (0, 0, 1));
#73 = VECTOR('NONE', #72, 1); #73 = VECTOR('NONE', #72, 1);
@ -91,155 +91,155 @@ DATA;
#75 = LINE('NONE', #74, #73); #75 = LINE('NONE', #74, #73);
#76 = DIRECTION('NONE', (1, 0, 0)); #76 = DIRECTION('NONE', (1, 0, 0));
#77 = VECTOR('NONE', #76, 1); #77 = VECTOR('NONE', #76, 1);
#78 = CARTESIAN_POINT('NONE', (0, -0.0254, -0)); #78 = CARTESIAN_POINT('NONE', (0, -0.64516, -0));
#79 = LINE('NONE', #78, #77); #79 = LINE('NONE', #78, #77);
#80 = DIRECTION('NONE', (0, 0, 1)); #80 = DIRECTION('NONE', (0, 0, 1));
#81 = VECTOR('NONE', #80, 1); #81 = VECTOR('NONE', #80, 1);
#82 = CARTESIAN_POINT('NONE', (0.07861346939195568, -0.0254, -0)); #82 = CARTESIAN_POINT('NONE', (1.996782122555674, -0.64516, -0));
#83 = LINE('NONE', #82, #81); #83 = LINE('NONE', #82, #81);
#84 = DIRECTION('NONE', (1, 0, 0)); #84 = DIRECTION('NONE', (1, 0, 0));
#85 = VECTOR('NONE', #84, 1); #85 = VECTOR('NONE', #84, 1);
#86 = CARTESIAN_POINT('NONE', (0, -0.0254, 0.1016)); #86 = CARTESIAN_POINT('NONE', (0, -0.64516, 2.58064));
#87 = LINE('NONE', #86, #85); #87 = LINE('NONE', #86, #85);
#88 = DIRECTION('NONE', (0.8191520442889919, -0.5735764363510459, 0)); #88 = DIRECTION('NONE', (0.819152044288992, -0.5735764363510459, 0));
#89 = VECTOR('NONE', #88, 1); #89 = VECTOR('NONE', #88, 1);
#90 = CARTESIAN_POINT('NONE', (0.07861346939195568, -0.0254, -0)); #90 = CARTESIAN_POINT('NONE', (1.996782122555674, -0.64516, -0));
#91 = LINE('NONE', #90, #89); #91 = LINE('NONE', #90, #89);
#92 = DIRECTION('NONE', (0, 0, 1)); #92 = DIRECTION('NONE', (0, 0, 1));
#93 = VECTOR('NONE', #92, 1); #93 = VECTOR('NONE', #92, 1);
#94 = CARTESIAN_POINT('NONE', (0.1511633881344551, -0.07619999999999998, -0)); #94 = CARTESIAN_POINT('NONE', (3.839550058615159, -1.9354799999999992, -0));
#95 = LINE('NONE', #94, #93); #95 = LINE('NONE', #94, #93);
#96 = DIRECTION('NONE', (0.8191520442889919, -0.5735764363510459, 0)); #96 = DIRECTION('NONE', (0.819152044288992, -0.5735764363510459, 0));
#97 = VECTOR('NONE', #96, 1); #97 = VECTOR('NONE', #96, 1);
#98 = CARTESIAN_POINT('NONE', (0.07861346939195568, -0.0254, 0.1016)); #98 = CARTESIAN_POINT('NONE', (1.996782122555674, -0.64516, 2.58064));
#99 = LINE('NONE', #98, #97); #99 = LINE('NONE', #98, #97);
#100 = DIRECTION('NONE', (1, -0.0000000000000003079278779307945, 0)); #100 = DIRECTION('NONE', (1, -0.00000000000000038794063361359933, 0));
#101 = VECTOR('NONE', #100, 1); #101 = VECTOR('NONE', #100, 1);
#102 = CARTESIAN_POINT('NONE', (0.1511633881344551, -0.07619999999999998, -0)); #102 = CARTESIAN_POINT('NONE', (3.839550058615159, -1.9354799999999992, -0));
#103 = LINE('NONE', #102, #101); #103 = LINE('NONE', #102, #101);
#104 = DIRECTION('NONE', (0, 0, 1)); #104 = DIRECTION('NONE', (0, 0, 1));
#105 = VECTOR('NONE', #104, 1); #105 = VECTOR('NONE', #104, 1);
#106 = CARTESIAN_POINT('NONE', (0.2413, -0.0762, -0)); #106 = CARTESIAN_POINT('NONE', (6.12902, -1.93548, -0));
#107 = LINE('NONE', #106, #105); #107 = LINE('NONE', #106, #105);
#108 = DIRECTION('NONE', (1, -0.0000000000000003079278779307945, 0)); #108 = DIRECTION('NONE', (1, -0.00000000000000038794063361359933, 0));
#109 = VECTOR('NONE', #108, 1); #109 = VECTOR('NONE', #108, 1);
#110 = CARTESIAN_POINT('NONE', (0.1511633881344551, -0.07619999999999998, 0.1016)); #110 = CARTESIAN_POINT('NONE', (3.839550058615159, -1.9354799999999992, 2.58064));
#111 = LINE('NONE', #110, #109); #111 = LINE('NONE', #110, #109);
#112 = DIRECTION('NONE', (0, 1, 0)); #112 = DIRECTION('NONE', (0, 1, 0));
#113 = VECTOR('NONE', #112, 1); #113 = VECTOR('NONE', #112, 1);
#114 = CARTESIAN_POINT('NONE', (0.2413, -0.0762, -0)); #114 = CARTESIAN_POINT('NONE', (6.12902, -1.93548, -0));
#115 = LINE('NONE', #114, #113); #115 = LINE('NONE', #114, #113);
#116 = DIRECTION('NONE', (0, 0, 1)); #116 = DIRECTION('NONE', (0, 0, 1));
#117 = VECTOR('NONE', #116, 1); #117 = VECTOR('NONE', #116, 1);
#118 = CARTESIAN_POINT('NONE', (0.2413, -0.0635, -0)); #118 = CARTESIAN_POINT('NONE', (6.12902, -1.6129, -0));
#119 = LINE('NONE', #118, #117); #119 = LINE('NONE', #118, #117);
#120 = DIRECTION('NONE', (0, 1, 0)); #120 = DIRECTION('NONE', (0, 1, 0));
#121 = VECTOR('NONE', #120, 1); #121 = VECTOR('NONE', #120, 1);
#122 = CARTESIAN_POINT('NONE', (0.2413, -0.0762, 0.1016)); #122 = CARTESIAN_POINT('NONE', (6.12902, -1.93548, 2.58064));
#123 = LINE('NONE', #122, #121); #123 = LINE('NONE', #122, #121);
#124 = DIRECTION('NONE', (-1, 0, 0)); #124 = DIRECTION('NONE', (-1, 0, 0));
#125 = VECTOR('NONE', #124, 1); #125 = VECTOR('NONE', #124, 1);
#126 = CARTESIAN_POINT('NONE', (0.2413, -0.0635, -0)); #126 = CARTESIAN_POINT('NONE', (6.12902, -1.6129, -0));
#127 = LINE('NONE', #126, #125); #127 = LINE('NONE', #126, #125);
#128 = DIRECTION('NONE', (0, 0, 1)); #128 = DIRECTION('NONE', (0, 0, 1));
#129 = VECTOR('NONE', #128, 1); #129 = VECTOR('NONE', #128, 1);
#130 = CARTESIAN_POINT('NONE', (0.1551676827532182, -0.0635, -0)); #130 = CARTESIAN_POINT('NONE', (3.9412591419317424, -1.6129, -0));
#131 = LINE('NONE', #130, #129); #131 = LINE('NONE', #130, #129);
#132 = DIRECTION('NONE', (-1, 0, 0)); #132 = DIRECTION('NONE', (-1, 0, 0));
#133 = VECTOR('NONE', #132, 1); #133 = VECTOR('NONE', #132, 1);
#134 = CARTESIAN_POINT('NONE', (0.2413, -0.0635, 0.1016)); #134 = CARTESIAN_POINT('NONE', (6.12902, -1.6129, 2.58064));
#135 = LINE('NONE', #134, #133); #135 = LINE('NONE', #134, #133);
#136 = DIRECTION('NONE', (-0.8191520442889919, 0.573576436351046, 0)); #136 = DIRECTION('NONE', (-0.8191520442889919, 0.573576436351046, 0));
#137 = VECTOR('NONE', #136, 1); #137 = VECTOR('NONE', #136, 1);
#138 = CARTESIAN_POINT('NONE', (0.1551676827532182, -0.0635, -0)); #138 = CARTESIAN_POINT('NONE', (3.9412591419317424, -1.6129, -0));
#139 = LINE('NONE', #138, #137); #139 = LINE('NONE', #138, #137);
#140 = DIRECTION('NONE', (0, 0, 1)); #140 = DIRECTION('NONE', (0, 0, 1));
#141 = VECTOR('NONE', #140, 1); #141 = VECTOR('NONE', #140, 1);
#142 = CARTESIAN_POINT('NONE', (0.06448028432509392, 0, -0)); #142 = CARTESIAN_POINT('NONE', (1.6377992218573856, 0, -0));
#143 = LINE('NONE', #142, #141); #143 = LINE('NONE', #142, #141);
#144 = DIRECTION('NONE', (-0.8191520442889919, 0.573576436351046, 0)); #144 = DIRECTION('NONE', (-0.8191520442889919, 0.573576436351046, 0));
#145 = VECTOR('NONE', #144, 1); #145 = VECTOR('NONE', #144, 1);
#146 = CARTESIAN_POINT('NONE', (0.1551676827532182, -0.0635, 0.1016)); #146 = CARTESIAN_POINT('NONE', (3.9412591419317424, -1.6129, 2.58064));
#147 = LINE('NONE', #146, #145); #147 = LINE('NONE', #146, #145);
#148 = DIRECTION('NONE', (0.90630778703665, 0.4226182617406993, 0)); #148 = DIRECTION('NONE', (0.90630778703665, 0.4226182617406992, 0));
#149 = VECTOR('NONE', #148, 1); #149 = VECTOR('NONE', #148, 1);
#150 = CARTESIAN_POINT('NONE', (0.06448028432509392, 0, -0)); #150 = CARTESIAN_POINT('NONE', (1.6377992218573856, 0, -0));
#151 = LINE('NONE', #150, #149); #151 = LINE('NONE', #150, #149);
#152 = DIRECTION('NONE', (0, 0, 1)); #152 = DIRECTION('NONE', (0, 0, 1));
#153 = VECTOR('NONE', #152, 1); #153 = VECTOR('NONE', #152, 1);
#154 = CARTESIAN_POINT('NONE', (0.14618599799650817, 0.03810000000000001, -0)); #154 = CARTESIAN_POINT('NONE', (3.7131243491113075, 0.9677400000000002, -0));
#155 = LINE('NONE', #154, #153); #155 = LINE('NONE', #154, #153);
#156 = DIRECTION('NONE', (0.90630778703665, 0.4226182617406993, 0)); #156 = DIRECTION('NONE', (0.90630778703665, 0.4226182617406992, 0));
#157 = VECTOR('NONE', #156, 1); #157 = VECTOR('NONE', #156, 1);
#158 = CARTESIAN_POINT('NONE', (0.06448028432509392, 0, 0.1016)); #158 = CARTESIAN_POINT('NONE', (1.6377992218573856, 0, 2.58064));
#159 = LINE('NONE', #158, #157); #159 = LINE('NONE', #158, #157);
#160 = DIRECTION('NONE', (1, -0.00000000000000007295344279228718, 0)); #160 = DIRECTION('NONE', (1, -0.0000000000000001378647737807002, 0));
#161 = VECTOR('NONE', #160, 1); #161 = VECTOR('NONE', #160, 1);
#162 = CARTESIAN_POINT('NONE', (0.14618599799650817, 0.03810000000000001, -0)); #162 = CARTESIAN_POINT('NONE', (3.7131243491113075, 0.9677400000000002, -0));
#163 = LINE('NONE', #162, #161); #163 = LINE('NONE', #162, #161);
#164 = DIRECTION('NONE', (0, 0, 1)); #164 = DIRECTION('NONE', (0, 0, 1));
#165 = VECTOR('NONE', #164, 1); #165 = VECTOR('NONE', #164, 1);
#166 = CARTESIAN_POINT('NONE', (0.2413, 0.0381, -0)); #166 = CARTESIAN_POINT('NONE', (6.12902, 0.9677399999999998, -0));
#167 = LINE('NONE', #166, #165); #167 = LINE('NONE', #166, #165);
#168 = DIRECTION('NONE', (1, -0.00000000000000007295344279228718, 0)); #168 = DIRECTION('NONE', (1, -0.0000000000000001378647737807002, 0));
#169 = VECTOR('NONE', #168, 1); #169 = VECTOR('NONE', #168, 1);
#170 = CARTESIAN_POINT('NONE', (0.14618599799650817, 0.03810000000000001, 0.1016)); #170 = CARTESIAN_POINT('NONE', (3.7131243491113075, 0.9677400000000002, 2.58064));
#171 = LINE('NONE', #170, #169); #171 = LINE('NONE', #170, #169);
#172 = DIRECTION('NONE', (0, 1, 0)); #172 = DIRECTION('NONE', (0, 1, 0));
#173 = VECTOR('NONE', #172, 1); #173 = VECTOR('NONE', #172, 1);
#174 = CARTESIAN_POINT('NONE', (0.2413, 0.0381, -0)); #174 = CARTESIAN_POINT('NONE', (6.12902, 0.9677399999999998, -0));
#175 = LINE('NONE', #174, #173); #175 = LINE('NONE', #174, #173);
#176 = DIRECTION('NONE', (0, 0, 1)); #176 = DIRECTION('NONE', (0, 0, 1));
#177 = VECTOR('NONE', #176, 1); #177 = VECTOR('NONE', #176, 1);
#178 = CARTESIAN_POINT('NONE', (0.2413, 0.0508, -0)); #178 = CARTESIAN_POINT('NONE', (6.12902, 1.29032, -0));
#179 = LINE('NONE', #178, #177); #179 = LINE('NONE', #178, #177);
#180 = DIRECTION('NONE', (0, 1, 0)); #180 = DIRECTION('NONE', (0, 1, 0));
#181 = VECTOR('NONE', #180, 1); #181 = VECTOR('NONE', #180, 1);
#182 = CARTESIAN_POINT('NONE', (0.2413, 0.0381, 0.1016)); #182 = CARTESIAN_POINT('NONE', (6.12902, 0.9677399999999998, 2.58064));
#183 = LINE('NONE', #182, #181); #183 = LINE('NONE', #182, #181);
#184 = DIRECTION('NONE', (-1, 0, 0)); #184 = DIRECTION('NONE', (-1, 0, 0));
#185 = VECTOR('NONE', #184, 1); #185 = VECTOR('NONE', #184, 1);
#186 = CARTESIAN_POINT('NONE', (0.2413, 0.0508, -0)); #186 = CARTESIAN_POINT('NONE', (6.12902, 1.29032, -0));
#187 = LINE('NONE', #186, #185); #187 = LINE('NONE', #186, #185);
#188 = DIRECTION('NONE', (0, 0, 1)); #188 = DIRECTION('NONE', (0, 0, 1));
#189 = VECTOR('NONE', #188, 1); #189 = VECTOR('NONE', #188, 1);
#190 = CARTESIAN_POINT('NONE', (0.14337047578094278, 0.0508, -0)); #190 = CARTESIAN_POINT('NONE', (3.6416100848359463, 1.29032, -0));
#191 = LINE('NONE', #190, #189); #191 = LINE('NONE', #190, #189);
#192 = DIRECTION('NONE', (-1, 0, 0)); #192 = DIRECTION('NONE', (-1, 0, 0));
#193 = VECTOR('NONE', #192, 1); #193 = VECTOR('NONE', #192, 1);
#194 = CARTESIAN_POINT('NONE', (0.2413, 0.0508, 0.1016)); #194 = CARTESIAN_POINT('NONE', (6.12902, 1.29032, 2.58064));
#195 = LINE('NONE', #194, #193); #195 = LINE('NONE', #194, #193);
#196 = DIRECTION('NONE', (-0.90630778703665, -0.42261826174069944, 0)); #196 = DIRECTION('NONE', (-0.90630778703665, -0.4226182617406995, 0));
#197 = VECTOR('NONE', #196, 1); #197 = VECTOR('NONE', #196, 1);
#198 = CARTESIAN_POINT('NONE', (0.14337047578094278, 0.0508, -0)); #198 = CARTESIAN_POINT('NONE', (3.6416100848359463, 1.29032, -0));
#199 = LINE('NONE', #198, #197); #199 = LINE('NONE', #198, #197);
#200 = DIRECTION('NONE', (0, 0, 1)); #200 = DIRECTION('NONE', (0, 0, 1));
#201 = VECTOR('NONE', #200, 1); #201 = VECTOR('NONE', #200, 1);
#202 = CARTESIAN_POINT('NONE', (0.08889999999999999, 0.0254, -0)); #202 = CARTESIAN_POINT('NONE', (2.2580599999999995, 0.64516, -0));
#203 = LINE('NONE', #202, #201); #203 = LINE('NONE', #202, #201);
#204 = DIRECTION('NONE', (-0.90630778703665, -0.42261826174069944, 0)); #204 = DIRECTION('NONE', (-0.90630778703665, -0.4226182617406995, 0));
#205 = VECTOR('NONE', #204, 1); #205 = VECTOR('NONE', #204, 1);
#206 = CARTESIAN_POINT('NONE', (0.14337047578094278, 0.0508, 0.1016)); #206 = CARTESIAN_POINT('NONE', (3.6416100848359463, 1.29032, 2.58064));
#207 = LINE('NONE', #206, #205); #207 = LINE('NONE', #206, #205);
#208 = DIRECTION('NONE', (-1, 0, 0)); #208 = DIRECTION('NONE', (-1, 0, 0));
#209 = VECTOR('NONE', #208, 1); #209 = VECTOR('NONE', #208, 1);
#210 = CARTESIAN_POINT('NONE', (0.08889999999999999, 0.0254, -0)); #210 = CARTESIAN_POINT('NONE', (2.2580599999999995, 0.64516, -0));
#211 = LINE('NONE', #210, #209); #211 = LINE('NONE', #210, #209);
#212 = DIRECTION('NONE', (0, 0, 1)); #212 = DIRECTION('NONE', (0, 0, 1));
#213 = VECTOR('NONE', #212, 1); #213 = VECTOR('NONE', #212, 1);
#214 = CARTESIAN_POINT('NONE', (0, 0.0254, -0)); #214 = CARTESIAN_POINT('NONE', (0, 0.64516, -0));
#215 = LINE('NONE', #214, #213); #215 = LINE('NONE', #214, #213);
#216 = DIRECTION('NONE', (-1, 0, 0)); #216 = DIRECTION('NONE', (-1, 0, 0));
#217 = VECTOR('NONE', #216, 1); #217 = VECTOR('NONE', #216, 1);
#218 = CARTESIAN_POINT('NONE', (0.08889999999999999, 0.0254, 0.1016)); #218 = CARTESIAN_POINT('NONE', (2.2580599999999995, 0.64516, 2.58064));
#219 = LINE('NONE', #218, #217); #219 = LINE('NONE', #218, #217);
#220 = DIRECTION('NONE', (0, -1, 0)); #220 = DIRECTION('NONE', (0, -1, 0));
#221 = VECTOR('NONE', #220, 1); #221 = VECTOR('NONE', #220, 1);
#222 = CARTESIAN_POINT('NONE', (0, 0.0254, -0)); #222 = CARTESIAN_POINT('NONE', (0, 0.64516, -0));
#223 = LINE('NONE', #222, #221); #223 = LINE('NONE', #222, #221);
#224 = DIRECTION('NONE', (0, -1, 0)); #224 = DIRECTION('NONE', (0, -1, 0));
#225 = VECTOR('NONE', #224, 1); #225 = VECTOR('NONE', #224, 1);
#226 = CARTESIAN_POINT('NONE', (0, 0.0254, 0.1016)); #226 = CARTESIAN_POINT('NONE', (0, 0.64516, 2.58064));
#227 = LINE('NONE', #226, #225); #227 = LINE('NONE', #226, #225);
#228 = EDGE_CURVE('NONE', #5, #7, #63, .T.); #228 = EDGE_CURVE('NONE', #5, #7, #63, .T.);
#229 = EDGE_CURVE('NONE', #7, #9, #67, .T.); #229 = EDGE_CURVE('NONE', #7, #9, #67, .T.);
@ -383,67 +383,67 @@ DATA;
#367 = ORIENTED_EDGE('NONE', *, *, #267, .T.); #367 = ORIENTED_EDGE('NONE', *, *, #267, .T.);
#368 = ORIENTED_EDGE('NONE', *, *, #269, .T.); #368 = ORIENTED_EDGE('NONE', *, *, #269, .T.);
#369 = EDGE_LOOP('NONE', (#355, #356, #357, #358, #359, #360, #361, #362, #363, #364, #365, #366, #367, #368)); #369 = EDGE_LOOP('NONE', (#355, #356, #357, #358, #359, #360, #361, #362, #363, #364, #365, #366, #367, #368));
#370 = CARTESIAN_POINT('NONE', (0, -0.0127, 0.0508)); #370 = CARTESIAN_POINT('NONE', (0, -0.3225799999999985, 1.2903199999999995));
#371 = DIRECTION('NONE', (-1, 0, -0)); #371 = DIRECTION('NONE', (-1, -0, 0));
#372 = AXIS2_PLACEMENT_3D('NONE', #370, #371, $); #372 = AXIS2_PLACEMENT_3D('NONE', #370, #371, $);
#373 = PLANE('NONE', #372); #373 = PLANE('NONE', #372);
#374 = CARTESIAN_POINT('NONE', (0.039306734695977924, -0.025399999999999995, 0.0508)); #374 = CARTESIAN_POINT('NONE', (0.9983910612778368, -0.6451599999999998, 1.2903199999999997));
#375 = DIRECTION('NONE', (0, -1, -0)); #375 = DIRECTION('NONE', (0, -1, 0));
#376 = AXIS2_PLACEMENT_3D('NONE', #374, #375, $); #376 = AXIS2_PLACEMENT_3D('NONE', #374, #375, $);
#377 = PLANE('NONE', #376); #377 = PLANE('NONE', #376);
#378 = CARTESIAN_POINT('NONE', (0.11488842876320533, -0.05079999999999996, 0.05079999999999999)); #378 = CARTESIAN_POINT('NONE', (2.918166090585415, -1.2903199999999988, 1.2903199999999997));
#379 = DIRECTION('NONE', (-0.5735764363510459, -0.819152044288992, 0)); #379 = DIRECTION('NONE', (-0.5735764363510459, -0.8191520442889919, 0));
#380 = AXIS2_PLACEMENT_3D('NONE', #378, #379, $); #380 = AXIS2_PLACEMENT_3D('NONE', #378, #379, $);
#381 = PLANE('NONE', #380); #381 = PLANE('NONE', #380);
#382 = CARTESIAN_POINT('NONE', (0.19623169406722757, -0.07619999999999999, 0.0508)); #382 = CARTESIAN_POINT('NONE', (4.984285029307579, -1.9354799999999992, 1.2903199999999997));
#383 = DIRECTION('NONE', (0, -1, -0)); #383 = DIRECTION('NONE', (0, -1, 0));
#384 = AXIS2_PLACEMENT_3D('NONE', #382, #383, $); #384 = AXIS2_PLACEMENT_3D('NONE', #382, #383, $);
#385 = PLANE('NONE', #384); #385 = PLANE('NONE', #384);
#386 = CARTESIAN_POINT('NONE', (0.2413, -0.06985, 0.0508)); #386 = CARTESIAN_POINT('NONE', (6.129019999999999, -1.7741899999999997, 1.2903199999999997));
#387 = DIRECTION('NONE', (1, 0, -0)); #387 = DIRECTION('NONE', (1, -0, 0));
#388 = AXIS2_PLACEMENT_3D('NONE', #386, #387, $); #388 = AXIS2_PLACEMENT_3D('NONE', #386, #387, $);
#389 = PLANE('NONE', #388); #389 = PLANE('NONE', #388);
#390 = CARTESIAN_POINT('NONE', (0.19823384137660915, -0.0635, 0.0508)); #390 = CARTESIAN_POINT('NONE', (5.035139570965871, -1.6128999999999998, 1.2903199999999997));
#391 = DIRECTION('NONE', (0, 1, -0)); #391 = DIRECTION('NONE', (0, 1, -0));
#392 = AXIS2_PLACEMENT_3D('NONE', #390, #391, $); #392 = AXIS2_PLACEMENT_3D('NONE', #390, #391, $);
#393 = PLANE('NONE', #392); #393 = PLANE('NONE', #392);
#394 = CARTESIAN_POINT('NONE', (0.10982398353915601, -0.03174999999999997, 0.0508)); #394 = CARTESIAN_POINT('NONE', (2.7895291818945633, -0.8064499999999998, 1.2903199999999995));
#395 = DIRECTION('NONE', (0.573576436351046, 0.8191520442889918, -0)); #395 = DIRECTION('NONE', (0.5735764363510459, 0.8191520442889918, -0));
#396 = AXIS2_PLACEMENT_3D('NONE', #394, #395, $); #396 = AXIS2_PLACEMENT_3D('NONE', #394, #395, $);
#397 = PLANE('NONE', #396); #397 = PLANE('NONE', #396);
#398 = CARTESIAN_POINT('NONE', (0.105333141160801, 0.019049999999999987, 0.0508)); #398 = CARTESIAN_POINT('NONE', (2.6754617854843468, 0.4838700000000003, 1.2903199999999997));
#399 = DIRECTION('NONE', (0.4226182617406993, -0.90630778703665, -0)); #399 = DIRECTION('NONE', (0.4226182617406992, -0.90630778703665, 0));
#400 = AXIS2_PLACEMENT_3D('NONE', #398, #399, $); #400 = AXIS2_PLACEMENT_3D('NONE', #398, #399, $);
#401 = PLANE('NONE', #400); #401 = PLANE('NONE', #400);
#402 = CARTESIAN_POINT('NONE', (0.19374299899825406, 0.0381, 0.0508)); #402 = CARTESIAN_POINT('NONE', (4.921072174555653, 0.9677399999999998, 1.2903199999999995));
#403 = DIRECTION('NONE', (0, -1, -0)); #403 = DIRECTION('NONE', (0, -1, 0));
#404 = AXIS2_PLACEMENT_3D('NONE', #402, #403, $); #404 = AXIS2_PLACEMENT_3D('NONE', #402, #403, $);
#405 = PLANE('NONE', #404); #405 = PLANE('NONE', #404);
#406 = CARTESIAN_POINT('NONE', (0.2413, 0.044449999999999996, 0.0508)); #406 = CARTESIAN_POINT('NONE', (6.129019999999998, 1.1290299999999989, 1.2903199999999995));
#407 = DIRECTION('NONE', (1, 0, -0)); #407 = DIRECTION('NONE', (1, -0, 0));
#408 = AXIS2_PLACEMENT_3D('NONE', #406, #407, $); #408 = AXIS2_PLACEMENT_3D('NONE', #406, #407, $);
#409 = PLANE('NONE', #408); #409 = PLANE('NONE', #408);
#410 = CARTESIAN_POINT('NONE', (0.19233523789047138, 0.0508, 0.0508)); #410 = CARTESIAN_POINT('NONE', (4.8853150424179725, 1.2903199999999997, 1.2903199999999997));
#411 = DIRECTION('NONE', (0, 1, -0)); #411 = DIRECTION('NONE', (0, 1, -0));
#412 = AXIS2_PLACEMENT_3D('NONE', #410, #411, $); #412 = AXIS2_PLACEMENT_3D('NONE', #410, #411, $);
#413 = PLANE('NONE', #412); #413 = PLANE('NONE', #412);
#414 = CARTESIAN_POINT('NONE', (0.11613523789047137, 0.0381, 0.05079999999999999)); #414 = CARTESIAN_POINT('NONE', (2.9498350424179733, 0.9677399999999998, 1.2903199999999997));
#415 = DIRECTION('NONE', (-0.42261826174069966, 0.90630778703665, -0)); #415 = DIRECTION('NONE', (-0.42261826174069933, 0.9063077870366499, -0));
#416 = AXIS2_PLACEMENT_3D('NONE', #414, #415, $); #416 = AXIS2_PLACEMENT_3D('NONE', #414, #415, $);
#417 = PLANE('NONE', #416); #417 = PLANE('NONE', #416);
#418 = CARTESIAN_POINT('NONE', (0.044449999999999996, 0.0254, 0.0508)); #418 = CARTESIAN_POINT('NONE', (1.1290299999999998, 0.6451599999999998, 1.29032));
#419 = DIRECTION('NONE', (0, 1, -0)); #419 = DIRECTION('NONE', (0, 1, -0));
#420 = AXIS2_PLACEMENT_3D('NONE', #418, #419, $); #420 = AXIS2_PLACEMENT_3D('NONE', #418, #419, $);
#421 = PLANE('NONE', #420); #421 = PLANE('NONE', #420);
#422 = CARTESIAN_POINT('NONE', (0, 0.0127, 0.0508)); #422 = CARTESIAN_POINT('NONE', (0, 0.32257999999999987, 1.2903199999999995));
#423 = DIRECTION('NONE', (-1, 0, -0)); #423 = DIRECTION('NONE', (-1, -0, 0));
#424 = AXIS2_PLACEMENT_3D('NONE', #422, #423, $); #424 = AXIS2_PLACEMENT_3D('NONE', #422, #423, $);
#425 = PLANE('NONE', #424); #425 = PLANE('NONE', #424);
#426 = CARTESIAN_POINT('NONE', (0, 0, -0)); #426 = CARTESIAN_POINT('NONE', (0, 0, -0));
#427 = DIRECTION('NONE', (0, 0, 1)); #427 = DIRECTION('NONE', (0, 0, 1));
#428 = AXIS2_PLACEMENT_3D('NONE', #426, #427, $); #428 = AXIS2_PLACEMENT_3D('NONE', #426, #427, $);
#429 = PLANE('NONE', #428); #429 = PLANE('NONE', #428);
#430 = CARTESIAN_POINT('NONE', (0, 0, 0.1016)); #430 = CARTESIAN_POINT('NONE', (0, 0, 2.58064));
#431 = DIRECTION('NONE', (0, 0, 1)); #431 = DIRECTION('NONE', (0, 0, 1));
#432 = AXIS2_PLACEMENT_3D('NONE', #430, #431, $); #432 = AXIS2_PLACEMENT_3D('NONE', #430, #431, $);
#433 = PLANE('NONE', #432); #433 = PLANE('NONE', #432);
@ -475,7 +475,7 @@ DATA;
#459 = ADVANCED_FACE('NONE', (#458), #421, .T.); #459 = ADVANCED_FACE('NONE', (#458), #421, .T.);
#460 = FACE_OUTER_BOUND('NONE', #339, .T.); #460 = FACE_OUTER_BOUND('NONE', #339, .T.);
#461 = ADVANCED_FACE('NONE', (#460), #425, .T.); #461 = ADVANCED_FACE('NONE', (#460), #425, .T.);
#462 = FACE_OUTER_BOUND('NONE', #354, .T.); #462 = FACE_OUTER_BOUND('NONE', #354, .F.);
#463 = ADVANCED_FACE('NONE', (#462), #429, .F.); #463 = ADVANCED_FACE('NONE', (#462), #429, .F.);
#464 = FACE_OUTER_BOUND('NONE', #369, .T.); #464 = FACE_OUTER_BOUND('NONE', #369, .T.);
#465 = ADVANCED_FACE('NONE', (#464), #433, .T.); #465 = ADVANCED_FACE('NONE', (#464), #433, .T.);

Binary file not shown.

Before

Width:  |  Height:  |  Size: 57 KiB

After

Width:  |  Height:  |  Size: 221 KiB

View File

@ -1,478 +1,478 @@
solid unnamed solid unnamed
facet normal -1 0 0 facet normal -1 0 0
outer loop outer loop
vertex 0 -4 0 vertex 0 -101.600006 0
vertex 0 -0 0 vertex 0 -0 0
vertex 0 -4 -1 vertex 0 -101.600006 -25.400002
endloop endloop
endfacet endfacet
facet normal -1 0 0 facet normal -1 0 0
outer loop outer loop
vertex 0 -4 -1 vertex 0 -101.600006 -25.400002
vertex 0 -0 0 vertex 0 -0 0
vertex 0 -0 -1 vertex 0 -0 -25.400002
endloop endloop
endfacet endfacet
facet normal 0 0 -1 facet normal 0 0 -1
outer loop outer loop
vertex 0 -4 -1 vertex 0 -101.600006 -25.400002
vertex 0 -0 -1 vertex 0 -0 -25.400002
vertex 3.0950184 -4 -1 vertex 78.613464 -101.600006 -25.400002
endloop endloop
endfacet endfacet
facet normal 0 0 -1 facet normal 0 0 -1
outer loop outer loop
vertex 3.0950184 -4 -1 vertex 78.613464 -101.600006 -25.400002
vertex 0 -0 -1 vertex 0 -0 -25.400002
vertex 3.0950184 -0 -1 vertex 78.613464 -0 -25.400002
endloop endloop
endfacet endfacet
facet normal -0.57357645 0 -0.81915206 facet normal -0.5735764 0 -0.8191522
outer loop outer loop
vertex 3.0950184 -4 -1 vertex 78.613464 -101.600006 -25.400002
vertex 3.0950184 -0 -1 vertex 78.613464 -0 -25.400002
vertex 5.9513144 -4 -3 vertex 151.16339 -101.600006 -76.2
endloop endloop
endfacet endfacet
facet normal -0.57357645 0 -0.81915206 facet normal -0.5735764 0 -0.8191522
outer loop outer loop
vertex 5.9513144 -4 -3 vertex 151.16339 -101.600006 -76.2
vertex 3.0950184 -0 -1 vertex 78.613464 -0 -25.400002
vertex 5.9513144 -0 -3 vertex 151.16339 -0 -76.2
endloop endloop
endfacet endfacet
facet normal 0 0 -1 facet normal 0 0 -1
outer loop outer loop
vertex 5.9513144 -4 -3 vertex 151.16339 -101.600006 -76.2
vertex 5.9513144 -0 -3 vertex 151.16339 -0 -76.2
vertex 9.5 -4 -3 vertex 241.3 -101.600006 -76.2
endloop endloop
endfacet endfacet
facet normal 0 0 -1 facet normal 0 0 -1
outer loop outer loop
vertex 9.5 -4 -3 vertex 241.3 -101.600006 -76.2
vertex 5.9513144 -0 -3 vertex 151.16339 -0 -76.2
vertex 9.5 -0 -3 vertex 241.3 -0 -76.2
endloop endloop
endfacet endfacet
facet normal 1 0 0 facet normal 1 0 0
outer loop outer loop
vertex 9.5 -4 -3 vertex 241.3 -101.600006 -76.2
vertex 9.5 -0 -3 vertex 241.3 -0 -76.2
vertex 9.5 -4 -2.5 vertex 241.3 -101.600006 -63.5
endloop endloop
endfacet endfacet
facet normal 1 -0 0 facet normal 1 -0 0
outer loop outer loop
vertex 9.5 -4 -2.5 vertex 241.3 -101.600006 -63.5
vertex 9.5 -0 -3 vertex 241.3 -0 -76.2
vertex 9.5 -0 -2.5 vertex 241.3 -0 -63.5
endloop
endfacet
facet normal 0 -0 1
outer loop
vertex 241.3 -101.600006 -63.5
vertex 241.3 -0 -63.5
vertex 155.16768 -101.600006 -63.5
endloop
endfacet
facet normal 0 0 1
outer loop
vertex 155.16768 -101.600006 -63.5
vertex 241.3 -0 -63.5
vertex 155.16768 -0 -63.5
endloop
endfacet
facet normal 0.5735765 0 0.81915194
outer loop
vertex 87.15214 -101.600006 -15.875
vertex 109.82398 -101.600006 -31.75
vertex 109.82398 -0 -31.75
endloop
endfacet
facet normal 0.57357645 0 0.819152
outer loop
vertex 109.82398 -101.600006 -31.75
vertex 155.16768 -101.600006 -63.5
vertex 155.16768 -0 -63.5
endloop
endfacet
facet normal 0.57357645 0 0.81915206
outer loop
vertex 87.15214 -0 -15.875
vertex 64.480286 -101.600006 0
vertex 87.15214 -101.600006 -15.875
endloop
endfacet
facet normal 0.5735765 0 0.81915194
outer loop
vertex 109.82398 -0 -31.75
vertex 87.15214 -0 -15.875
vertex 87.15214 -101.600006 -15.875
endloop
endfacet
facet normal 0.57357645 -0 0.819152
outer loop
vertex 109.82398 -101.600006 -31.75
vertex 155.16768 -0 -63.5
vertex 109.82398 -0 -31.75
endloop
endfacet
facet normal 0.57357645 -0 0.81915206
outer loop
vertex 64.480286 -101.600006 0
vertex 87.15214 -0 -15.875
vertex 64.480286 -0 0
endloop
endfacet
facet normal 0.4226182 0 -0.9063078
outer loop
vertex 84.906715 -101.600006 9.525
vertex 64.480286 -101.600006 0
vertex 64.480286 -0 0
endloop
endfacet
facet normal 0.42261833 0 -0.90630776
outer loop
vertex 105.33314 -101.600006 19.05
vertex 84.906715 -101.600006 9.525
vertex 84.906715 -0 9.525
endloop
endfacet
facet normal 0.4226182 0 -0.9063078
outer loop
vertex 84.906715 -0 9.525
vertex 84.906715 -101.600006 9.525
vertex 64.480286 -0 0
endloop
endfacet
facet normal 0.4226183 0 -0.9063078
outer loop
vertex 105.33314 -0 19.05
vertex 146.18599 -101.600006 38.1
vertex 105.33314 -101.600006 19.05
endloop
endfacet
facet normal 0.42261833 0 -0.90630776
outer loop
vertex 105.33314 -101.600006 19.05
vertex 84.906715 -0 9.525
vertex 105.33314 -0 19.05
endloop
endfacet
facet normal 0.4226183 0 -0.9063078
outer loop
vertex 146.18599 -101.600006 38.1
vertex 105.33314 -0 19.05
vertex 146.18599 -0 38.1
endloop
endfacet
facet normal 0 0 -1
outer loop
vertex 146.18599 -101.600006 38.1
vertex 146.18599 -0 38.1
vertex 241.3 -101.600006 38.1
endloop
endfacet
facet normal 0 0 -1
outer loop
vertex 241.3 -101.600006 38.1
vertex 146.18599 -0 38.1
vertex 241.3 -0 38.1
endloop
endfacet
facet normal 1 0 0
outer loop
vertex 241.3 -101.600006 38.1
vertex 241.3 -0 38.1
vertex 241.3 -101.600006 50.800003
endloop
endfacet
facet normal 1 -0 0
outer loop
vertex 241.3 -101.600006 50.800003
vertex 241.3 -0 38.1
vertex 241.3 -0 50.800003
endloop endloop
endfacet endfacet
facet normal 0 -0 0.99999994 facet normal 0 -0 0.99999994
outer loop outer loop
vertex 9.5 -4 -2.5 vertex 241.3 -101.600006 50.800003
vertex 9.5 -0 -2.5 vertex 241.3 -0 50.800003
vertex 6.108964 -4 -2.5 vertex 143.37048 -101.600006 50.800003
endloop endloop
endfacet endfacet
facet normal 0 0 0.99999994 facet normal 0 0 0.99999994
outer loop outer loop
vertex 6.108964 -4 -2.5 vertex 143.37048 -101.600006 50.800003
vertex 9.5 -0 -2.5 vertex 241.3 -0 50.800003
vertex 6.108964 -0 -2.5 vertex 143.37048 -0 50.800003
endloop endloop
endfacet endfacet
facet normal 0.5735763 0 0.8191522 facet normal -0.42261827 0 0.9063078
outer loop outer loop
vertex 3.4311862 -4 -0.625 vertex 143.37048 -101.600006 50.800003
vertex 4.323779 -4 -1.25 vertex 143.37048 -0 50.800003
vertex 4.323779 -0 -1.25 vertex 88.9 -101.600006 25.400002
endloop endloop
endfacet endfacet
facet normal 0.57357645 0 0.819152 facet normal -0.42261827 0 0.9063078
outer loop outer loop
vertex 4.323779 -4 -1.25 vertex 88.9 -101.600006 25.400002
vertex 6.108964 -4 -2.5 vertex 143.37048 -0 50.800003
vertex 6.108964 -0 -2.5 vertex 88.9 -0 25.400002
endloop
endfacet
facet normal 0.57357645 0 0.819152
outer loop
vertex 3.4311862 -0 -0.625
vertex 2.5385938 -0 0
vertex 2.5385938 -4 0
endloop
endfacet
facet normal 0.57357645 -0 0.819152
outer loop
vertex 3.4311862 -4 -0.625
vertex 3.4311862 -0 -0.625
vertex 2.5385938 -4 0
endloop
endfacet
facet normal 0.57357645 -0 0.819152
outer loop
vertex 4.323779 -4 -1.25
vertex 6.108964 -0 -2.5
vertex 4.323779 -0 -1.25
endloop
endfacet
facet normal 0.5735763 0 0.8191522
outer loop
vertex 3.4311862 -0 -0.625
vertex 3.4311862 -4 -0.625
vertex 4.323779 -0 -1.25
endloop
endfacet
facet normal 0.42261824 0 -0.9063078
outer loop
vertex 3.342784 -4 0.375
vertex 2.5385938 -4 0
vertex 2.5385938 -0 0
endloop
endfacet
facet normal 0.42261824 0 -0.9063078
outer loop
vertex 4.146974 -4 0.75
vertex 3.342784 -4 0.375
vertex 3.342784 -0 0.375
endloop
endfacet
facet normal 0.42261824 0 -0.9063078
outer loop
vertex 3.342784 -0 0.375
vertex 4.146974 -0 0.75
vertex 4.146974 -4 0.75
endloop
endfacet
facet normal 0.42261833 0 -0.90630776
outer loop
vertex 4.146974 -0 0.75
vertex 5.755354 -0 1.5
vertex 5.755354 -4 1.5
endloop
endfacet
facet normal 0.42261824 0 -0.9063078
outer loop
vertex 3.342784 -4 0.375
vertex 2.5385938 -0 0
vertex 3.342784 -0 0.375
endloop
endfacet
facet normal 0.42261833 0 -0.90630776
outer loop
vertex 5.755354 -4 1.5
vertex 4.146974 -4 0.75
vertex 4.146974 -0 0.75
endloop
endfacet
facet normal 0 0 -1
outer loop
vertex 5.755354 -4 1.5
vertex 5.755354 -0 1.5
vertex 9.5 -4 1.5
endloop
endfacet
facet normal 0 0 -1
outer loop
vertex 9.5 -4 1.5
vertex 5.755354 -0 1.5
vertex 9.5 -0 1.5
endloop
endfacet
facet normal 1 0 0
outer loop
vertex 9.5 -4 1.5
vertex 9.5 -0 1.5
vertex 9.5 -4 2
endloop
endfacet
facet normal 1 -0 0
outer loop
vertex 9.5 -4 2
vertex 9.5 -0 1.5
vertex 9.5 -0 2
endloop endloop
endfacet endfacet
facet normal 0 -0 1 facet normal 0 -0 1
outer loop outer loop
vertex 9.5 -4 2 vertex 88.9 -101.600006 25.400002
vertex 9.5 -0 2 vertex 88.9 -0 25.400002
vertex 5.644507 -4 2 vertex 0 -101.600006 25.400002
endloop endloop
endfacet endfacet
facet normal 0 0 1 facet normal 0 0 1
outer loop outer loop
vertex 5.644507 -4 2 vertex 0 -101.600006 25.400002
vertex 9.5 -0 2 vertex 88.9 -0 25.400002
vertex 5.644507 -0 2 vertex 0 -0 25.400002
endloop
endfacet
facet normal -0.42261824 0 0.90630776
outer loop
vertex 5.644507 -4 2
vertex 5.644507 -0 2
vertex 3.5 -4 1
endloop
endfacet
facet normal -0.42261824 0 0.90630776
outer loop
vertex 3.5 -4 1
vertex 5.644507 -0 2
vertex 3.5 -0 1
endloop
endfacet
facet normal 0 -0 1
outer loop
vertex 3.5 -4 1
vertex 3.5 -0 1
vertex 0 -4 1
endloop
endfacet
facet normal 0 0 1
outer loop
vertex 0 -4 1
vertex 3.5 -0 1
vertex 0 -0 1
endloop endloop
endfacet endfacet
facet normal -1 0 0 facet normal -1 0 0
outer loop outer loop
vertex 0 -4 1 vertex 0 -101.600006 25.400002
vertex 0 -0 1 vertex 0 -0 25.400002
vertex 0 -4 0 vertex 0 -101.600006 0
endloop endloop
endfacet endfacet
facet normal -1 0 0 facet normal -1 0 0
outer loop outer loop
vertex 0 -4 0 vertex 0 -101.600006 0
vertex 0 -0 1 vertex 0 -0 25.400002
vertex 0 -0 0 vertex 0 -0 0
endloop endloop
endfacet endfacet
facet normal 0 1 -0 facet normal 0 1 -0
outer loop outer loop
vertex 3.342784 -0 0.375 vertex 84.906715 -0 9.525
vertex 2.5385938 -0 0 vertex 64.480286 -0 0
vertex 3.5 -0 1 vertex 88.9 -0 25.400002
endloop endloop
endfacet endfacet
facet normal 0 1 0 facet normal 0 1 0
outer loop outer loop
vertex 3.4311862 -0 -0.625 vertex 105.33314 -0 19.05
vertex 4.323779 -0 -1.25 vertex 84.906715 -0 9.525
vertex 3.0950184 -0 -1 vertex 88.9 -0 25.400002
endloop endloop
endfacet endfacet
facet normal 0 1 0 facet normal 0 1 0
outer loop outer loop
vertex 3.342784 -0 0.375 vertex 87.15214 -0 -15.875
vertex 3.5 -0 1 vertex 109.82398 -0 -31.75
vertex 4.146974 -0 0.75 vertex 78.613464 -0 -25.400002
endloop
endfacet
facet normal 0 1 0
outer loop
vertex 105.33314 -0 19.05
vertex 143.37048 -0 50.800003
vertex 146.18599 -0 38.1
endloop
endfacet
facet normal -0 1 0
outer loop
vertex 0 -0 25.400002
vertex 88.9 -0 25.400002
vertex 64.480286 -0 0
endloop
endfacet
facet normal 0 1 0
outer loop
vertex 0 -0 25.400002
vertex 64.480286 -0 0
vertex 0 -0 0
endloop
endfacet
facet normal -0 1 0
outer loop
vertex 143.37048 -0 50.800003
vertex 241.3 -0 50.800003
vertex 146.18599 -0 38.1
endloop
endfacet
facet normal 0 1 0
outer loop
vertex 241.3 -0 50.800003
vertex 241.3 -0 38.1
vertex 146.18599 -0 38.1
endloop
endfacet
facet normal 0 1 -0
outer loop
vertex 105.33314 -0 19.05
vertex 88.9 -0 25.400002
vertex 143.37048 -0 50.800003
endloop endloop
endfacet endfacet
facet normal 0 0.99999994 0 facet normal 0 0.99999994 0
outer loop outer loop
vertex 4.323779 -0 -1.25 vertex 64.480286 -0 0
vertex 5.9513144 -0 -3 vertex 87.15214 -0 -15.875
vertex 3.0950184 -0 -1 vertex 78.613464 -0 -25.400002
endloop endloop
endfacet endfacet
facet normal 0 1 0 facet normal 0 1 0
outer loop outer loop
vertex 0 -0 -1 vertex 109.82398 -0 -31.75
vertex 2.5385938 -0 0 vertex 151.16339 -0 -76.2
vertex 3.0950184 -0 -1 vertex 78.613464 -0 -25.400002
endloop endloop
endfacet endfacet
facet normal 0 1 0 facet normal 0 1 0
outer loop outer loop
vertex 0 -0 -1 vertex 155.16768 -0 -63.5
vertex 151.16339 -0 -76.2
vertex 109.82398 -0 -31.75
endloop
endfacet
facet normal 0 1 0
outer loop
vertex 241.3 -0 -63.5
vertex 241.3 -0 -76.2
vertex 155.16768 -0 -63.5
endloop
endfacet
facet normal 0 1 0
outer loop
vertex 155.16768 -0 -63.5
vertex 241.3 -0 -76.2
vertex 151.16339 -0 -76.2
endloop
endfacet
facet normal 0 1 0
outer loop
vertex 64.480286 -0 0
vertex 78.613464 -0 -25.400002
vertex 0 -0 -25.400002
endloop
endfacet
facet normal 0 1 0
outer loop
vertex 0 -0 -25.400002
vertex 0 -0 0 vertex 0 -0 0
vertex 2.5385938 -0 0 vertex 64.480286 -0 0
endloop
endfacet
facet normal 0 0.99999994 -0
outer loop
vertex 9.5 -0 -3
vertex 6.108964 -0 -2.5
vertex 9.5 -0 -2.5
endloop
endfacet
facet normal 0 1 0
outer loop
vertex 9.5 -0 -3
vertex 5.9513144 -0 -3
vertex 6.108964 -0 -2.5
endloop
endfacet
facet normal 0 1 -0
outer loop
vertex 5.9513144 -0 -3
vertex 4.323779 -0 -1.25
vertex 6.108964 -0 -2.5
endloop
endfacet
facet normal 0 1 0
outer loop
vertex 5.644507 -0 2
vertex 5.755354 -0 1.5
vertex 4.146974 -0 0.75
endloop
endfacet
facet normal 0 0.99999994 -0
outer loop
vertex 3.0950184 -0 -1
vertex 2.5385938 -0 0
vertex 3.4311862 -0 -0.625
endloop
endfacet
facet normal 0 1 -0
outer loop
vertex 4.146974 -0 0.75
vertex 3.5 -0 1
vertex 5.644507 -0 2
endloop
endfacet
facet normal 0 1 -0
outer loop
vertex 9.5 -0 1.5
vertex 5.755354 -0 1.5
vertex 9.5 -0 2
endloop
endfacet
facet normal 0 1 -0
outer loop
vertex 5.755354 -0 1.5
vertex 5.644507 -0 2
vertex 9.5 -0 2
endloop
endfacet
facet normal 0 1 0
outer loop
vertex 2.5385938 -0 0
vertex 0 -0 0
vertex 0 -0 1
endloop
endfacet
facet normal 0 1 0
outer loop
vertex 3.5 -0 1
vertex 2.5385938 -0 0
vertex 0 -0 1
endloop endloop
endfacet endfacet
facet normal -0 -1 0 facet normal -0 -1 0
outer loop outer loop
vertex 3.342784 -4 0.375 vertex 84.906715 -101.600006 9.525
vertex 3.5 -4 1 vertex 88.9 -101.600006 25.400002
vertex 2.5385938 -4 0 vertex 64.480286 -101.600006 0
endloop endloop
endfacet endfacet
facet normal -0 -1 0 facet normal -0 -1 0
outer loop outer loop
vertex 4.146974 -4 0.75 vertex 105.33314 -101.600006 19.05
vertex 3.5 -4 1 vertex 88.9 -101.600006 25.400002
vertex 3.342784 -4 0.375 vertex 84.906715 -101.600006 9.525
endloop endloop
endfacet endfacet
facet normal 0 -1 -0 facet normal 0 -1 -0
outer loop outer loop
vertex 3.4311862 -4 -0.625 vertex 87.15214 -101.600006 -15.875
vertex 3.0950184 -4 -1 vertex 78.613464 -101.600006 -25.400002
vertex 4.323779 -4 -1.25 vertex 109.82398 -101.600006 -31.75
endloop
endfacet
facet normal 0 -1 0
outer loop
vertex 105.33314 -101.600006 19.05
vertex 146.18599 -101.600006 38.1
vertex 143.37048 -101.600006 50.800003
endloop
endfacet
facet normal 0 -1 0
outer loop
vertex 0 -101.600006 25.400002
vertex 64.480286 -101.600006 0
vertex 88.9 -101.600006 25.400002
endloop
endfacet
facet normal 0 -1 0
outer loop
vertex 0 -101.600006 25.400002
vertex 0 -101.600006 0
vertex 64.480286 -101.600006 0
endloop
endfacet
facet normal 0 -1 0
outer loop
vertex 143.37048 -101.600006 50.800003
vertex 146.18599 -101.600006 38.1
vertex 241.3 -101.600006 50.800003
endloop
endfacet
facet normal 0 -1 -0
outer loop
vertex 241.3 -101.600006 50.800003
vertex 146.18599 -101.600006 38.1
vertex 241.3 -101.600006 38.1
endloop
endfacet
facet normal 0 -1 0
outer loop
vertex 105.33314 -101.600006 19.05
vertex 143.37048 -101.600006 50.800003
vertex 88.9 -101.600006 25.400002
endloop endloop
endfacet endfacet
facet normal 0 -0.99999994 0 facet normal 0 -0.99999994 0
outer loop outer loop
vertex 4.146974 -4 0.75 vertex 64.480286 -101.600006 0
vertex 5.755354 -4 1.5 vertex 78.613464 -101.600006 -25.400002
vertex 5.644507 -4 2 vertex 87.15214 -101.600006 -15.875
endloop endloop
endfacet endfacet
facet normal 0 -1 0 facet normal -0 -1 -0
outer loop outer loop
vertex 0 -4 1 vertex 109.82398 -101.600006 -31.75
vertex 2.5385938 -4 0 vertex 78.613464 -101.600006 -25.400002
vertex 3.5 -4 1 vertex 151.16339 -101.600006 -76.2
endloop
endfacet
facet normal 0 -1 0
outer loop
vertex 0 -4 1
vertex 0 -4 0
vertex 2.5385938 -4 0
endloop
endfacet
facet normal 0 -1 0
outer loop
vertex 5.644507 -4 2
vertex 5.755354 -4 1.5
vertex 9.5 -4 2
endloop
endfacet
facet normal 0 -1 -0
outer loop
vertex 9.5 -4 2
vertex 5.755354 -4 1.5
vertex 9.5 -4 1.5
endloop
endfacet
facet normal 0 -1 0
outer loop
vertex 4.146974 -4 0.75
vertex 5.644507 -4 2
vertex 3.5 -4 1
endloop
endfacet
facet normal 0 -0.99999994 0
outer loop
vertex 2.5385938 -4 0
vertex 3.0950184 -4 -1
vertex 3.4311862 -4 -0.625
endloop
endfacet
facet normal -0 -0.99999994 -0
outer loop
vertex 4.323779 -4 -1.25
vertex 3.0950184 -4 -1
vertex 5.9513144 -4 -3
endloop endloop
endfacet endfacet
facet normal -0 -1 0 facet normal -0 -1 0
outer loop outer loop
vertex 6.108964 -4 -2.5 vertex 155.16768 -101.600006 -63.5
vertex 4.323779 -4 -1.25 vertex 109.82398 -101.600006 -31.75
vertex 5.9513144 -4 -3 vertex 151.16339 -101.600006 -76.2
endloop endloop
endfacet endfacet
facet normal -0 -0.99999994 -0 facet normal -0 -1 -0
outer loop outer loop
vertex 9.5 -4 -2.5 vertex 241.3 -101.600006 -63.5
vertex 6.108964 -4 -2.5 vertex 155.16768 -101.600006 -63.5
vertex 9.5 -4 -3 vertex 241.3 -101.600006 -76.2
endloop endloop
endfacet endfacet
facet normal 0 -1 -0 facet normal 0 -1 -0
outer loop outer loop
vertex 6.108964 -4 -2.5 vertex 155.16768 -101.600006 -63.5
vertex 5.9513144 -4 -3 vertex 151.16339 -101.600006 -76.2
vertex 9.5 -4 -3 vertex 241.3 -101.600006 -76.2
endloop endloop
endfacet endfacet
facet normal 0 -1 -0 facet normal 0 -1 -0
outer loop outer loop
vertex 2.5385938 -4 0 vertex 64.480286 -101.600006 0
vertex 0 -4 -1 vertex 0 -101.600006 -25.400002
vertex 3.0950184 -4 -1 vertex 78.613464 -101.600006 -25.400002
endloop endloop
endfacet endfacet
facet normal 0 -1 0 facet normal 0 -1 0
outer loop outer loop
vertex 0 -4 -1 vertex 0 -101.600006 -25.400002
vertex 2.5385938 -4 0 vertex 64.480286 -101.600006 0
vertex 0 -4 0 vertex 0 -101.600006 0
endloop endloop
endfacet endfacet
endsolid unnamed endsolid unnamed

Binary file not shown.

Before

Width:  |  Height:  |  Size: 57 KiB

After

Width:  |  Height:  |  Size: 221 KiB

View File

@ -1,7 +1,5 @@
import { test, expect } from '@playwright/test' import { test, expect } from '@playwright/test'
import { secrets } from './secrets' import { secrets } from './secrets'
import { EngineCommand } from '../../src/lang/std/engineConnection'
import { v4 as uuidv4 } from 'uuid'
import { getUtils } from './test-utils' import { getUtils } from './test-utils'
import waitOn from 'wait-on' import waitOn from 'wait-on'
import { Themes } from '../../src/lib/theme' import { Themes } from '../../src/lib/theme'
@ -53,40 +51,38 @@ test('Basic sketch', async ({ page }) => {
await page.goto('/') await page.goto('/')
await u.waitForAuthSkipAppStart() await u.waitForAuthSkipAppStart()
await u.openDebugPanel() await u.openDebugPanel()
await u.waitForDefaultPlanesVisibilityChange()
await expect(page.getByRole('button', { name: 'Start Sketch' })).toBeVisible() await expect(page.getByRole('button', { name: 'Start Sketch' })).toBeVisible()
// click on "Start Sketch" button // click on "Start Sketch" button
await u.clearCommandLogs() await u.clearCommandLogs()
await Promise.all([ await u.doAndWaitForImageDiff(
u.doAndWaitForImageDiff( () => page.getByRole('button', { name: 'Start Sketch' }).click(),
() => page.getByRole('button', { name: 'Start Sketch' }).click(), 200
200 )
),
u.waitForDefaultPlanesVisibilityChange(),
])
// select a plane // select a plane
await u.doAndWaitForCmd(() => page.mouse.click(700, 200), 'edit_mode_enter') await page.mouse.click(700, 200)
await u.waitForCmdReceive('set_tool')
await u.doAndWaitForCmd( await expect(page.locator('.cm-content')).toHaveText(
() => page.getByRole('button', { name: 'Line' }).click(), `const part001 = startSketchOn('-XZ')`
'set_tool'
) )
await page.waitForTimeout(300) // TODO detect animation ending, or disable animation
const startXPx = 600 const startXPx = 600
await u.doAndWaitForCmd( await page.mouse.click(startXPx + PUR * 10, 500 - PUR * 10)
() => page.mouse.click(startXPx + PUR * 10, 500 - PUR * 10), const startAt = '[23.74, -32.03]'
'mouse_click', await expect(page.locator('.cm-content'))
false .toHaveText(`const part001 = startSketchOn('-XZ')
) |> startProfileAt(${startAt}, %)`)
await page.waitForTimeout(100)
await u.closeDebugPanel()
await page.mouse.click(startXPx + PUR * 20, 500 - PUR * 10) await page.mouse.click(startXPx + PUR * 20, 500 - PUR * 10)
await page.waitForTimeout(100)
const startAt = '[18.26, -24.63]' const num = 23.97
const num = '18.43'
await expect(page.locator('.cm-content')) await expect(page.locator('.cm-content'))
.toHaveText(`const part001 = startSketchOn('-XZ') .toHaveText(`const part001 = startSketchOn('-XZ')
|> startProfileAt(${startAt}, %) |> startProfileAt(${startAt}, %)
@ -104,20 +100,15 @@ test('Basic sketch', async ({ page }) => {
|> startProfileAt(${startAt}, %) |> startProfileAt(${startAt}, %)
|> line([${num}, 0], %) |> line([${num}, 0], %)
|> line([0, ${num}], %) |> line([0, ${num}], %)
|> line([-36.69, 0], %)`) |> line([-47.71, 0], %)`)
// deselect line tool // deselect line tool
await u.doAndWaitForCmd( await page.getByRole('button', { name: 'Line' }).click()
() => page.getByRole('button', { name: 'Line' }).click(), await page.waitForTimeout(100)
'set_tool'
)
// click between first two clicks to get center of the line // click between first two clicks to get center of the line
await u.doAndWaitForCmd( await page.mouse.click(startXPx + PUR * 15, 500 - PUR * 10)
() => page.mouse.click(startXPx + PUR * 15, 500 - PUR * 10), await page.waitForTimeout(100)
'select_with_point'
)
await u.closeDebugPanel()
// hold down shift // hold down shift
await page.keyboard.down('Shift') await page.keyboard.down('Shift')
@ -206,7 +197,7 @@ test('if you write invalid kcl you get inlined errors', async ({ page }) => {
test('executes on load', async ({ page, context }) => { test('executes on load', async ({ page, context }) => {
const u = getUtils(page) const u = getUtils(page)
await context.addInitScript(async (token) => { await context.addInitScript(async () => {
localStorage.setItem( localStorage.setItem(
'persistCode', 'persistCode',
`const part001 = startSketchOn('-XZ') `const part001 = startSketchOn('-XZ')
@ -271,59 +262,41 @@ test('Can create sketches on all planes and their back sides', async ({
await page.goto('/') await page.goto('/')
await u.waitForAuthSkipAppStart() await u.waitForAuthSkipAppStart()
await u.openDebugPanel() await u.openDebugPanel()
await u.waitForDefaultPlanesVisibilityChange()
const camCmd: EngineCommand = { const camPos: [number, number, number] = [100, 100, 100]
type: 'modeling_cmd_req',
cmd_id: uuidv4(),
cmd: {
type: 'default_camera_look_at',
center: { x: 15, y: 0, z: 0 },
up: { x: 0, y: 0, z: 1 },
vantage: { x: 30, y: 30, z: 30 },
},
}
const TestSinglePlane = async ({ const TestSinglePlane = async ({
viewCmd, viewCmd,
expectedCode, expectedCode,
clickCoords, clickCoords,
}: { }: {
viewCmd: EngineCommand viewCmd: [number, number, number]
expectedCode: string expectedCode: string
clickCoords: { x: number; y: number } clickCoords: { x: number; y: number }
}) => { }) => {
await u.openDebugPanel() await u.openDebugPanel()
await u.sendCustomCmd(viewCmd)
await u.updateCamPosition(viewCmd)
await u.clearCommandLogs() await u.clearCommandLogs()
// await page.waitForTimeout(200)
await page.getByRole('button', { name: 'Start Sketch' }).click() await page.getByRole('button', { name: 'Start Sketch' }).click()
await u.waitForDefaultPlanesVisibilityChange()
await u.closeDebugPanel() await u.closeDebugPanel()
await page.mouse.click(clickCoords.x, clickCoords.y) await page.mouse.click(clickCoords.x, clickCoords.y)
await u.openDebugPanel() await page.waitForTimeout(300) // wait for animation
await expect(page.getByRole('button', { name: 'Line' })).toBeVisible() await expect(page.getByRole('button', { name: 'Line' })).toBeVisible()
// draw a line // draw a line
const startXPx = 600 const startXPx = 600
await u.clearCommandLogs()
await page.getByRole('button', { name: 'Line' }).click()
await u.waitForCmdReceive('set_tool')
await u.clearCommandLogs()
await u.closeDebugPanel() await u.closeDebugPanel()
await page.mouse.click(startXPx + PUR * 10, 500 - PUR * 10) await page.mouse.click(startXPx + PUR * 10, 500 - PUR * 10)
await u.openDebugPanel()
await u.waitForCmdReceive('mouse_click')
await u.closeDebugPanel()
await page.mouse.click(startXPx + PUR * 20, 500 - PUR * 10)
await u.openDebugPanel()
await expect(page.locator('.cm-content')).toHaveText(expectedCode) await expect(page.locator('.cm-content')).toHaveText(expectedCode)
await page.getByRole('button', { name: 'Line' }).click() await page.getByRole('button', { name: 'Line' }).click()
await u.clearCommandLogs() await u.openAndClearDebugPanel()
await page.getByRole('button', { name: 'Exit Sketch' }).click() await page.getByRole('button', { name: 'Exit Sketch' }).click()
await u.expectCmdLog('[data-message-type="execution-done"]') await u.expectCmdLog('[data-message-type="execution-done"]')
@ -333,51 +306,41 @@ test('Can create sketches on all planes and their back sides', async ({
const codeTemplate = ( const codeTemplate = (
plane = 'XY', plane = 'XY',
sign = '' rounded = false,
otherThing = '1'
) => `const part001 = startSketchOn('${plane}') ) => `const part001 = startSketchOn('${plane}')
|> startProfileAt([${sign}6.88, -9.29], %) |> startProfileAt([28.9${otherThing}, -39${rounded ? '' : '.01'}], %)`
|> line([${sign}6.95, 0], %)`
await TestSinglePlane({ await TestSinglePlane({
viewCmd: camCmd, viewCmd: camPos,
expectedCode: codeTemplate('XY'), expectedCode: codeTemplate('XY'),
clickCoords: { x: 700, y: 350 }, // red plane clickCoords: { x: 600, y: 388 }, // red plane
// clickCoords: { x: 600, y: 400 }, // red plane // clicks grid helper and that causes problems, should fix so that these coords work too.
}) })
await TestSinglePlane({ await TestSinglePlane({
viewCmd: camCmd, viewCmd: camPos,
expectedCode: codeTemplate('YZ'), expectedCode: codeTemplate('YZ', true),
clickCoords: { x: 1000, y: 200 }, // green plane clickCoords: { x: 700, y: 300 }, // green plane
}) })
await TestSinglePlane({ await TestSinglePlane({
viewCmd: camCmd, viewCmd: camPos,
expectedCode: codeTemplate('XZ', '-'), expectedCode: codeTemplate('XZ'),
clickCoords: { x: 630, y: 130 }, // blue plane clickCoords: { x: 700, y: 80 }, // blue plane
}) })
const camCmdBackSide: [number, number, number] = [-100, -100, -100]
// new camera angle to click the back side of all three planes
const camCmdBackSide: EngineCommand = {
type: 'modeling_cmd_req',
cmd_id: uuidv4(),
cmd: {
type: 'default_camera_look_at',
center: { x: -15, y: 0, z: 0 },
up: { x: 0, y: 0, z: 1 },
vantage: { x: -30, y: -30, z: -30 },
},
}
await TestSinglePlane({ await TestSinglePlane({
viewCmd: camCmdBackSide, viewCmd: camCmdBackSide,
expectedCode: codeTemplate('-XY', '-'), expectedCode: codeTemplate('-XY', false, '3'),
clickCoords: { x: 705, y: 136 }, // back of red plane clickCoords: { x: 601, y: 118 }, // back of red plane
}) })
await TestSinglePlane({ await TestSinglePlane({
viewCmd: camCmdBackSide, viewCmd: camCmdBackSide,
expectedCode: codeTemplate('-YZ', '-'), expectedCode: codeTemplate('-YZ'),
clickCoords: { x: 1000, y: 350 }, // back of green plane clickCoords: { x: 730, y: 219 }, // back of green plane
}) })
await TestSinglePlane({ await TestSinglePlane({
viewCmd: camCmdBackSide, viewCmd: camCmdBackSide,
expectedCode: codeTemplate('-XZ'), expectedCode: codeTemplate('-XZ', true),
clickCoords: { x: 600, y: 400 }, // back of blue plane clickCoords: { x: 680, y: 427 }, // back of blue plane
}) })
}) })
@ -387,7 +350,6 @@ test('Auto complete works', async ({ page }) => {
await page.setViewportSize({ width: 1200, height: 500 }) await page.setViewportSize({ width: 1200, height: 500 })
await page.goto('/') await page.goto('/')
await u.waitForAuthSkipAppStart() await u.waitForAuthSkipAppStart()
await u.waitForDefaultPlanesVisibilityChange()
// this test might be brittle as we add and remove functions // this test might be brittle as we add and remove functions
// but should also be easy to update. // but should also be easy to update.
@ -405,6 +367,7 @@ test('Auto complete works', async ({ page }) => {
await page.keyboard.type(' |> startProfi') await page.keyboard.type(' |> startProfi')
// expect there be a single auto complete option that we can just hit enter on // expect there be a single auto complete option that we can just hit enter on
await expect(page.locator('.cm-completionLabel')).toBeVisible() await expect(page.locator('.cm-completionLabel')).toBeVisible()
await page.waitForTimeout(100)
await page.keyboard.press('Enter') // accepting the auto complete, not a new line await page.keyboard.press('Enter') // accepting the auto complete, not a new line
await page.keyboard.type('([0,0], %)') await page.keyboard.type('([0,0], %)')
@ -412,6 +375,7 @@ test('Auto complete works', async ({ page }) => {
await page.keyboard.type(' |> lin') await page.keyboard.type(' |> lin')
await expect(page.locator('.cm-tooltip-autocomplete')).toBeVisible() await expect(page.locator('.cm-tooltip-autocomplete')).toBeVisible()
await page.waitForTimeout(100)
// press arrow down twice then enter to accept xLine // press arrow down twice then enter to accept xLine
await page.keyboard.press('ArrowDown') await page.keyboard.press('ArrowDown')
await page.keyboard.press('ArrowDown') await page.keyboard.press('ArrowDown')
@ -478,38 +442,36 @@ test('Selections work on fresh and edited sketch', async ({ page }) => {
await page.goto('/') await page.goto('/')
await u.waitForAuthSkipAppStart() await u.waitForAuthSkipAppStart()
await u.openDebugPanel() await u.openDebugPanel()
await u.waitForDefaultPlanesVisibilityChange()
const xAxisClick = () => page.mouse.click(700, 250) const xAxisClick = () =>
const emptySpaceClick = () => page.mouse.click(700, 300) page.mouse.click(700, 250).then(() => page.waitForTimeout(100))
const topHorzSegmentClick = () => page.mouse.click(700, 285) const emptySpaceClick = () =>
const bottomHorzSegmentClick = () => page.mouse.click(750, 393) page.mouse.click(728, 343).then(() => page.waitForTimeout(100))
const topHorzSegmentClick = () =>
page.mouse.click(709, 289).then(() => page.waitForTimeout(100))
const bottomHorzSegmentClick = () =>
page.mouse.click(767, 396).then(() => page.waitForTimeout(100))
await u.clearCommandLogs() await u.clearCommandLogs()
await page.getByRole('button', { name: 'Start Sketch' }).click() await page.getByRole('button', { name: 'Start Sketch' }).click()
await u.waitForDefaultPlanesVisibilityChange()
// select a plane // select a plane
await u.doAndWaitForCmd(() => page.mouse.click(700, 200), 'edit_mode_enter') await page.mouse.click(700, 200)
await u.waitForCmdReceive('set_tool') await page.waitForTimeout(700) // wait for animation
await u.doAndWaitForCmd(
() => page.getByRole('button', { name: 'Line' }).click(),
'set_tool'
)
const startXPx = 600 const startXPx = 600
await u.doAndWaitForCmd( await page.mouse.click(startXPx + PUR * 10, 500 - PUR * 10)
() => page.mouse.click(startXPx + PUR * 10, 500 - PUR * 10), const startAt = '[23.74, -32.03]'
'mouse_click', await expect(page.locator('.cm-content'))
false .toHaveText(`const part001 = startSketchOn('-XZ')
) |> startProfileAt(${startAt}, %)`)
await u.closeDebugPanel()
await page.mouse.click(startXPx + PUR * 20, 500 - PUR * 10) await page.mouse.click(startXPx + PUR * 20, 500 - PUR * 10)
const startAt = '[18.26, -24.63]' const num = 23.97
const num = '18.43' const num2 = '47.71'
const num2 = '36.69'
await expect(page.locator('.cm-content')) await expect(page.locator('.cm-content'))
.toHaveText(`const part001 = startSketchOn('-XZ') .toHaveText(`const part001 = startSketchOn('-XZ')
|> startProfileAt(${startAt}, %) |> startProfileAt(${startAt}, %)
@ -530,10 +492,7 @@ test('Selections work on fresh and edited sketch', async ({ page }) => {
|> line([-${num2}, 0], %)`) |> line([-${num2}, 0], %)`)
// deselect line tool // deselect line tool
await u.doAndWaitForCmd( await page.getByRole('button', { name: 'Line' }).click()
() => page.getByRole('button', { name: 'Line' }).click(),
'set_tool'
)
await u.closeDebugPanel() await u.closeDebugPanel()
const selectionSequence = async () => { const selectionSequence = async () => {
@ -555,79 +514,73 @@ test('Selections work on fresh and edited sketch', async ({ page }) => {
// now check clicking works including axis // now check clicking works including axis
// click a segment hold shift and click an axis, see that a relevant constraint is enabled // click a segment hold shift and click an axis, see that a relevant constraint is enabled
await u.doAndWaitForCmd(topHorzSegmentClick, 'select_with_point', false) await topHorzSegmentClick()
await page.keyboard.down('Shift') await page.keyboard.down('Shift')
const absYButton = page.getByRole('button', { name: 'ABS Y' }) const absYButton = page.getByRole('button', { name: 'ABS Y' })
await expect(absYButton).toBeDisabled() await expect(absYButton).toBeDisabled()
await u.doAndWaitForCmd(xAxisClick, 'select_with_point', false) await xAxisClick()
await page.keyboard.up('Shift') await page.keyboard.up('Shift')
await absYButton.and(page.locator(':not([disabled])')).waitFor() await absYButton.and(page.locator(':not([disabled])')).waitFor()
await expect(absYButton).not.toBeDisabled() await expect(absYButton).not.toBeDisabled()
// clear selection by clicking on nothing // clear selection by clicking on nothing
await u.doAndWaitForCmd(emptySpaceClick, 'select_clear', false) await emptySpaceClick()
// same selection but click the axis first // same selection but click the axis first
await u.doAndWaitForCmd(xAxisClick, 'select_with_point', false) await xAxisClick()
await expect(absYButton).toBeDisabled() await expect(absYButton).toBeDisabled()
await page.keyboard.down('Shift') await page.keyboard.down('Shift')
await u.doAndWaitForCmd(topHorzSegmentClick, 'select_with_point', false) await topHorzSegmentClick()
await page.keyboard.up('Shift') await page.keyboard.up('Shift')
await expect(absYButton).not.toBeDisabled() await expect(absYButton).not.toBeDisabled()
// clear selection by clicking on nothing // clear selection by clicking on nothing
await u.doAndWaitForCmd(emptySpaceClick, 'select_clear', false) await emptySpaceClick()
// check the same selection again by putting cursor in code first then selecting axis // check the same selection again by putting cursor in code first then selecting axis
await u.doAndWaitForCmd( await page.getByText(` |> line([-${num2}, 0], %)`).click()
() => page.getByText(` |> line([-${num2}, 0], %)`).click(),
'select_clear',
false
)
await page.keyboard.down('Shift') await page.keyboard.down('Shift')
await expect(absYButton).toBeDisabled() await expect(absYButton).toBeDisabled()
await u.doAndWaitForCmd(xAxisClick, 'select_with_point', false) await xAxisClick()
await page.keyboard.up('Shift') await page.keyboard.up('Shift')
await expect(absYButton).not.toBeDisabled() await expect(absYButton).not.toBeDisabled()
// clear selection by clicking on nothing // clear selection by clicking on nothing
await u.doAndWaitForCmd(emptySpaceClick, 'select_clear', false) await emptySpaceClick()
// select segment in editor than another segment in scene and check there are two cursors // select segment in editor than another segment in scene and check there are two cursors
await u.doAndWaitForCmd( await page.getByText(` |> line([-${num2}, 0], %)`).click()
() => page.getByText(` |> line([-${num2}, 0], %)`).click(), await page.waitForTimeout(300)
'select_clear',
false
)
await page.keyboard.down('Shift') await page.keyboard.down('Shift')
await expect(page.locator('.cm-cursor')).toHaveCount(1) await expect(page.locator('.cm-cursor')).toHaveCount(1)
await u.doAndWaitForCmd(bottomHorzSegmentClick, 'select_with_point', false) // another segment, bottom one await bottomHorzSegmentClick()
await page.keyboard.up('Shift') await page.keyboard.up('Shift')
await expect(page.locator('.cm-cursor')).toHaveCount(2) await expect(page.locator('.cm-cursor')).toHaveCount(2)
// clear selection by clicking on nothing // clear selection by clicking on nothing
await u.doAndWaitForCmd(emptySpaceClick, 'select_clear', false) await emptySpaceClick()
} }
await selectionSequence() await selectionSequence()
// hovering in fresh sketch worked, lets try exiting and re-entering // hovering in fresh sketch worked, lets try exiting and re-entering
await u.doAndWaitForCmd( await u.openAndClearDebugPanel()
() => page.getByRole('button', { name: 'Exit Sketch' }).click(), await page.getByRole('button', { name: 'Exit Sketch' }).click()
'edit_mode_exit' await page.waitForTimeout(200)
)
// wait for execution done // wait for execution done
await u.expectCmdLog('[data-message-type="execution-done"]') await u.expectCmdLog('[data-message-type="execution-done"]')
await u.closeDebugPanel()
// select a line // select a line
await u.doAndWaitForCmd(topHorzSegmentClick, 'select_clear', false) // await topHorzSegmentClick()
await page.getByText(startAt).click() // TODO remove this and reinstate // await topHorzSegmentClick()
await page.waitForTimeout(100)
// enter sketch again // enter sketch again
await u.doAndWaitForCmd( await page.getByRole('button', { name: 'Edit Sketch' }).click()
() => page.getByRole('button', { name: 'Start Sketch' }).click(), await page.waitForTimeout(300) // wait for animation
'edit_mode_enter',
false
)
// hover again and check it works // hover again and check it works
await selectionSequence() await selectionSequence()
@ -697,6 +650,8 @@ test('Can extrude from the command bar', async ({ page, context }) => {
await page.setViewportSize({ width: 1200, height: 500 }) await page.setViewportSize({ width: 1200, height: 500 })
await page.goto('/') await page.goto('/')
await u.waitForAuthSkipAppStart() await u.waitForAuthSkipAppStart()
await u.openDebugPanel()
await u.expectCmdLog('[data-message-type="execution-done"]')
let cmdSearchBar = page.getByPlaceholder('Search commands') let cmdSearchBar = page.getByPlaceholder('Search commands')
await page.keyboard.press('Meta+K') await page.keyboard.press('Meta+K')
@ -710,10 +665,7 @@ test('Can extrude from the command bar', async ({ page, context }) => {
await expect(page.getByRole('button', { name: 'selection' })).toBeDisabled() await expect(page.getByRole('button', { name: 'selection' })).toBeDisabled()
// Click to select face and set distance // Click to select face and set distance
await u.openAndClearDebugPanel()
await page.getByText('|> startProfileAt([-6.95, 4.98], %)').click() await page.getByText('|> startProfileAt([-6.95, 4.98], %)').click()
await u.waitForCmdReceive('select_add')
await u.closeDebugPanel()
await page.getByRole('button', { name: 'Continue' }).click() await page.getByRole('button', { name: 'Continue' }).click()
await expect(page.getByRole('button', { name: 'distance' })).toBeDisabled() await expect(page.getByRole('button', { name: 'distance' })).toBeDisabled()
await page.keyboard.press('Enter') await page.keyboard.press('Enter')
@ -735,3 +687,330 @@ test('Can extrude from the command bar', async ({ page, context }) => {
|> extrude(5, %)` |> extrude(5, %)`
) )
}) })
test('Can add multiple sketches', async ({ page }) => {
const u = getUtils(page)
await page.setViewportSize({ width: 1200, height: 500 })
const PUR = 400 / 37.5 //pixeltoUnitRatio
await page.goto('/')
await u.waitForAuthSkipAppStart()
await u.openDebugPanel()
await expect(page.getByRole('button', { name: 'Start Sketch' })).toBeVisible()
// click on "Start Sketch" button
await u.clearCommandLogs()
await u.doAndWaitForImageDiff(
() => page.getByRole('button', { name: 'Start Sketch' }).click(),
200
)
// select a plane
await page.mouse.click(700, 200)
await expect(page.locator('.cm-content')).toHaveText(
`const part001 = startSketchOn('-XZ')`
)
await page.waitForTimeout(500) // TODO detect animation ending, or disable animation
const startXPx = 600
await page.mouse.click(startXPx + PUR * 10, 500 - PUR * 10)
const startAt = '[23.74, -32.03]'
await expect(page.locator('.cm-content'))
.toHaveText(`const part001 = startSketchOn('-XZ')
|> startProfileAt(${startAt}, %)`)
await page.waitForTimeout(100)
await u.closeDebugPanel()
await page.mouse.click(startXPx + PUR * 20, 500 - PUR * 10)
await page.waitForTimeout(100)
const num = 23.97
await expect(page.locator('.cm-content'))
.toHaveText(`const part001 = startSketchOn('-XZ')
|> startProfileAt(${startAt}, %)
|> line([${num}, 0], %)`)
await page.mouse.click(startXPx + PUR * 20, 500 - PUR * 20)
await expect(page.locator('.cm-content'))
.toHaveText(`const part001 = startSketchOn('-XZ')
|> startProfileAt(${startAt}, %)
|> line([${num}, 0], %)
|> line([0, ${num}], %)`)
await page.mouse.click(startXPx, 500 - PUR * 20)
const finalCodeFirstSketch = `const part001 = startSketchOn('-XZ')
|> startProfileAt(${startAt}, %)
|> line([${num}, 0], %)
|> line([0, ${num}], %)
|> line([-47.71, 0], %)`
await expect(page.locator('.cm-content')).toHaveText(finalCodeFirstSketch)
// exit the sketch
await u.openAndClearDebugPanel()
await page.getByRole('button', { name: 'Exit Sketch' }).click()
await u.expectCmdLog('[data-message-type="execution-done"]')
await u.updateCamPosition([0, 100, 100])
// start a new sketch
await u.clearCommandLogs()
await page.getByRole('button', { name: 'Start Sketch' }).click()
await page.waitForTimeout(100)
await page.mouse.click(673, 384)
await page.waitForTimeout(500) // TODO detect animation ending, or disable animation
await u.clearAndCloseDebugPanel()
await page.mouse.click(startXPx + PUR * 10, 500 - PUR * 10)
const startAt2 = '[23.61, -31.85]'
await expect(
(await page.locator('.cm-content').innerText()).replace(/\s/g, '')
).toBe(
`${finalCodeFirstSketch}
const part002 = startSketchOn('XY')
|> startProfileAt(${startAt2}, %)`.replace(/\s/g, '')
)
await page.waitForTimeout(100)
await u.closeDebugPanel()
await page.mouse.click(startXPx + PUR * 20, 500 - PUR * 10)
await page.waitForTimeout(100)
const num2 = 23.83
await expect(
(await page.locator('.cm-content').innerText()).replace(/\s/g, '')
).toBe(
`${finalCodeFirstSketch}
const part002 = startSketchOn('XY')
|> startProfileAt(${startAt2}, %)
|> line([${num2}, 0], %)`.replace(/\s/g, '')
)
await page.mouse.click(startXPx + PUR * 20, 500 - PUR * 20)
await expect(
(await page.locator('.cm-content').innerText()).replace(/\s/g, '')
).toBe(
`${finalCodeFirstSketch}
const part002 = startSketchOn('XY')
|> startProfileAt(${startAt2}, %)
|> line([${num2}, 0], %)
|> line([0, ${num2}], %)`.replace(/\s/g, '')
)
await page.mouse.click(startXPx, 500 - PUR * 20)
await expect(
(await page.locator('.cm-content').innerText()).replace(/\s/g, '')
).toBe(
`${finalCodeFirstSketch}
const part002 = startSketchOn('XY')
|> startProfileAt(${startAt2}, %)
|> line([${num2}, 0], %)
|> line([0, ${num2}], %)
|> line([-47.44, 0], %)`.replace(/\s/g, '')
)
})
test('ProgramMemory can be serialised', async ({ page, context }) => {
const u = getUtils(page)
await context.addInitScript(async () => {
localStorage.setItem(
'persistCode',
`const part = startSketchOn('XY')
|> startProfileAt([0, 0], %)
|> line([0, 1], %)
|> line([1, 0], %)
|> line([0, -1], %)
|> close(%)
|> extrude(1, %)
|> patternLinear({
axis: [1, 0, 1],
repetitions: 3,
distance: 6
}, %)`
)
})
await page.setViewportSize({ width: 1000, height: 500 })
await page.goto('/')
const messages: string[] = []
// Listen for all console events and push the message text to an array
page.on('console', (message) => messages.push(message.text()))
await u.waitForAuthSkipAppStart()
// wait for execution done
await u.openDebugPanel()
await u.expectCmdLog('[data-message-type="execution-done"]')
const forbiddenMessages = ['cannot serialize tagged newtype variant']
forbiddenMessages.forEach((forbiddenMessage) => {
messages.forEach((message) => {
expect(message).not.toContain(forbiddenMessage)
})
})
})
test("Various pipe expressions should and shouldn't allow edit and or extrude", async ({
page,
context,
}) => {
const u = getUtils(page)
const selectionsSnippets = {
extrudeAndEditBlocked: '|> startProfileAt([10.81, 32.99], %)',
extrudeAndEditBlockedInFunction: '|> startProfileAt(pos, %)',
extrudeAndEditAllowed: '|> startProfileAt([15.72, 4.7], %)',
editOnly: '|> startProfileAt([15.79, -14.6], %)',
}
await context.addInitScript(
async ({
extrudeAndEditBlocked,
extrudeAndEditBlockedInFunction,
extrudeAndEditAllowed,
editOnly,
}: any) => {
localStorage.setItem(
'persistCode',
`const part001 = startSketchOn('-XZ')
${extrudeAndEditBlocked}
|> line([25.96, 2.93], %)
|> line([5.25, -5.72], %)
|> line([-2.01, -10.35], %)
|> line([-27.65, -2.78], %)
|> close(%)
|> extrude(5, %)
const part002 = startSketchOn('-XZ')
${extrudeAndEditAllowed}
|> line([10.32, 6.47], %)
|> line([9.71, -6.16], %)
|> line([-3.08, -9.86], %)
|> line([-12.02, -1.54], %)
|> close(%)
const part003 = startSketchOn('-XZ')
${editOnly}
|> line([27.55, -1.65], %)
|> line([4.95, -8], %)
|> line([-20.38, -10.12], %)
|> line([-15.79, 17.08], %)
fn yohey = (pos) => {
const part004 = startSketchOn('-XZ')
${extrudeAndEditBlockedInFunction}
|> line([27.55, -1.65], %)
|> line([4.95, -10.53], %)
|> line([-20.38, -8], %)
|> line([-15.79, 17.08], %)
return ''
}
yohey([15.79, -34.6])
`
)
},
selectionsSnippets
)
await page.setViewportSize({ width: 1200, height: 500 })
await page.goto('/')
await u.waitForAuthSkipAppStart()
// wait for execution done
await u.openDebugPanel()
await u.expectCmdLog('[data-message-type="execution-done"]')
await u.closeDebugPanel()
await page.getByText(selectionsSnippets.extrudeAndEditBlocked).click()
await expect(page.getByRole('button', { name: 'Extrude' })).toBeDisabled()
await expect(
page.getByRole('button', { name: 'Edit Sketch' })
).not.toBeVisible()
await page.getByText(selectionsSnippets.extrudeAndEditAllowed).click()
await expect(page.getByRole('button', { name: 'Extrude' })).not.toBeDisabled()
await expect(
page.getByRole('button', { name: 'Edit Sketch' })
).not.toBeDisabled()
await page.getByText(selectionsSnippets.editOnly).click()
await expect(page.getByRole('button', { name: 'Extrude' })).toBeDisabled()
await expect(
page.getByRole('button', { name: 'Edit Sketch' })
).not.toBeDisabled()
await page
.getByText(selectionsSnippets.extrudeAndEditBlockedInFunction)
.click()
await expect(page.getByRole('button', { name: 'Extrude' })).toBeDisabled()
await expect(
page.getByRole('button', { name: 'Edit Sketch' })
).not.toBeVisible()
// selecting an editable sketch but clicking "start sktech" should start a new sketch and not edit the existing one
await page.getByText(selectionsSnippets.extrudeAndEditAllowed).click()
await page.getByRole('button', { name: 'Start Sketch' }).click()
await page.mouse.click(700, 200)
// expect main content to contain `part005` i.e. started a new sketch
await expect(page.locator('.cm-content')).toHaveText(
/part005 = startSketchOn\('-XZ'\)/
)
})
test('Deselecting line tool should mean nothing happens on click', async ({
page,
}) => {
const u = getUtils(page)
await page.setViewportSize({ width: 1200, height: 500 })
const PUR = 400 / 37.5 //pixeltoUnitRatio
await page.goto('/')
await u.waitForAuthSkipAppStart()
await u.openDebugPanel()
await expect(page.getByRole('button', { name: 'Start Sketch' })).toBeVisible()
// click on "Start Sketch" button
await u.clearCommandLogs()
await u.doAndWaitForImageDiff(
() => page.getByRole('button', { name: 'Start Sketch' }).click(),
200
)
await page.mouse.click(700, 200)
await expect(page.locator('.cm-content')).toHaveText(
`const part001 = startSketchOn('-XZ')`
)
await page.waitForTimeout(300)
let previousCodeContent = await page.locator('.cm-content').innerText()
// deselect the line tool by clicking it
await page.getByRole('button', { name: 'Line' }).click()
await page.mouse.click(700, 200)
await page.waitForTimeout(100)
await page.mouse.click(700, 250)
await page.waitForTimeout(100)
await page.mouse.click(750, 200)
await page.waitForTimeout(100)
// expect no change
await expect(page.locator('.cm-content')).toHaveText(previousCodeContent)
// select line tool again
await page.getByRole('button', { name: 'Line' }).click()
await u.closeDebugPanel()
// line tool should work as expected again
await page.mouse.click(700, 200)
await expect(page.locator('.cm-content')).not.toHaveText(previousCodeContent)
previousCodeContent = await page.locator('.cm-content').innerText()
await page.mouse.click(700, 300)
await expect(page.locator('.cm-content')).not.toHaveText(previousCodeContent)
previousCodeContent = await page.locator('.cm-content').innerText()
await page.mouse.click(750, 300)
await expect(page.locator('.cm-content')).not.toHaveText(previousCodeContent)
previousCodeContent = await page.locator('.cm-content').innerText()
})

View File

@ -1,7 +1,5 @@
import { test, expect } from '@playwright/test' import { test, expect } from '@playwright/test'
import { secrets } from './secrets' import { secrets } from './secrets'
import { EngineCommand } from '../../src/lang/std/engineConnection'
import { v4 as uuidv4 } from 'uuid'
import { getUtils } from './test-utils' import { getUtils } from './test-utils'
import { Models } from '@kittycad/lib' import { Models } from '@kittycad/lib'
import fsp from 'fs/promises' import fsp from 'fs/promises'
@ -40,19 +38,8 @@ test('change camera, show planes', async ({ page, context }) => {
await u.waitForAuthSkipAppStart() await u.waitForAuthSkipAppStart()
await u.openAndClearDebugPanel() await u.openAndClearDebugPanel()
const camCmd: EngineCommand = { const camPos: [number, number, number] = [0, 85, 85]
type: 'modeling_cmd_req', await u.updateCamPosition(camPos)
cmd_id: uuidv4(),
cmd: {
type: 'default_camera_look_at',
center: { x: 0, y: 0, z: 0 },
up: { x: 0, y: 0, z: 1 },
vantage: { x: 0, y: 85, z: 85 },
},
}
await u.sendCustomCmd(camCmd)
await u.waitForCmdReceive('default_camera_look_at')
// rotate // rotate
await u.closeDebugPanel() await u.closeDebugPanel()
@ -62,13 +49,11 @@ test('change camera, show planes', async ({ page, context }) => {
await page.mouse.up({ button: 'right' }) await page.mouse.up({ button: 'right' })
await u.openDebugPanel() await u.openDebugPanel()
await u.waitForCmdReceive('camera_drag_end')
await page.waitForTimeout(500) await page.waitForTimeout(500)
await u.clearCommandLogs() await u.clearCommandLogs()
await page.getByRole('button', { name: 'Start Sketch' }).click() await page.getByRole('button', { name: 'Start Sketch' }).click()
await u.waitForDefaultPlanesVisibilityChange()
await u.closeDebugPanel() await u.closeDebugPanel()
await expect(page).toHaveScreenshot({ await expect(page).toHaveScreenshot({
@ -77,10 +62,8 @@ test('change camera, show planes', async ({ page, context }) => {
await u.openAndClearDebugPanel() await u.openAndClearDebugPanel()
await page.getByRole('button', { name: 'Exit Sketch' }).click() await page.getByRole('button', { name: 'Exit Sketch' }).click()
await u.waitForDefaultPlanesVisibilityChange()
await u.sendCustomCmd(camCmd) await u.updateCamPosition(camPos)
await u.waitForCmdReceive('default_camera_look_at')
await u.clearCommandLogs() await u.clearCommandLogs()
await u.closeDebugPanel() await u.closeDebugPanel()
@ -93,12 +76,10 @@ test('change camera, show planes', async ({ page, context }) => {
await page.keyboard.up('Shift') await page.keyboard.up('Shift')
await u.openDebugPanel() await u.openDebugPanel()
await u.waitForCmdReceive('camera_drag_end')
await page.waitForTimeout(300) await page.waitForTimeout(300)
await u.clearCommandLogs() await u.clearCommandLogs()
await page.getByRole('button', { name: 'Start Sketch' }).click() await page.getByRole('button', { name: 'Start Sketch' }).click()
await u.waitForDefaultPlanesVisibilityChange()
await u.closeDebugPanel() await u.closeDebugPanel()
await expect(page).toHaveScreenshot({ await expect(page).toHaveScreenshot({
@ -107,10 +88,8 @@ test('change camera, show planes', async ({ page, context }) => {
await u.openAndClearDebugPanel() await u.openAndClearDebugPanel()
await page.getByRole('button', { name: 'Exit Sketch' }).click() await page.getByRole('button', { name: 'Exit Sketch' }).click()
await u.waitForDefaultPlanesVisibilityChange()
await u.sendCustomCmd(camCmd) await u.updateCamPosition(camPos)
await u.waitForCmdReceive('default_camera_look_at')
await u.clearCommandLogs() await u.clearCommandLogs()
await u.closeDebugPanel() await u.closeDebugPanel()
@ -119,17 +98,15 @@ test('change camera, show planes', async ({ page, context }) => {
await page.keyboard.down('Control') await page.keyboard.down('Control')
await page.mouse.move(700, 400) await page.mouse.move(700, 400)
await page.mouse.down({ button: 'right' }) await page.mouse.down({ button: 'right' })
await page.mouse.move(700, 350) await page.mouse.move(700, 300)
await page.mouse.up({ button: 'right' }) await page.mouse.up({ button: 'right' })
await page.keyboard.up('Control') await page.keyboard.up('Control')
await u.openDebugPanel() await u.openDebugPanel()
await u.waitForCmdReceive('camera_drag_end')
await page.waitForTimeout(300) await page.waitForTimeout(300)
await u.clearCommandLogs() await u.clearCommandLogs()
await page.getByRole('button', { name: 'Start Sketch' }).click() await page.getByRole('button', { name: 'Start Sketch' }).click()
await u.waitForDefaultPlanesVisibilityChange()
await u.closeDebugPanel() await u.closeDebugPanel()
await expect(page).toHaveScreenshot({ await expect(page).toHaveScreenshot({
@ -164,11 +141,11 @@ const part001 = startSketchOn('-XZ')
|> xLineTo({ to: totalLen, tag: 'seg03' }, %) |> xLineTo({ to: totalLen, tag: 'seg03' }, %)
|> yLine({ length: -armThick, tag: 'seg01' }, %) |> yLine({ length: -armThick, tag: 'seg01' }, %)
|> angledLineThatIntersects({ |> angledLineThatIntersects({
angle: _180, angle: HALF_TURN,
offset: -armThick, offset: -armThick,
intersectTag: 'seg04' intersectTag: 'seg04'
}, %) }, %)
|> angledLineToY([segAng('seg04', %) + 180, _0], %) |> angledLineToY([segAng('seg04', %) + 180, ZERO], %)
|> angledLineToY({ |> angledLineToY({
angle: -bottomAng, angle: -bottomAng,
to: -totalHeightHalf - armThick, to: -totalHeightHalf - armThick,
@ -177,12 +154,12 @@ const part001 = startSketchOn('-XZ')
|> xLineTo(segEndX('seg03', %) + 0, %) |> xLineTo(segEndX('seg03', %) + 0, %)
|> yLine(-segLen('seg01', %), %) |> yLine(-segLen('seg01', %), %)
|> angledLineThatIntersects({ |> angledLineThatIntersects({
angle: _180, angle: HALF_TURN,
offset: -armThick, offset: -armThick,
intersectTag: 'seg02' intersectTag: 'seg02'
}, %) }, %)
|> angledLineToY([segAng('seg02', %) + 180, -baseHeight], %) |> angledLineToY([segAng('seg02', %) + 180, -baseHeight], %)
|> xLineTo(_0, %) |> xLineTo(ZERO, %)
|> close(%) |> close(%)
|> extrude(4, %)` |> extrude(4, %)`
) )
@ -191,7 +168,6 @@ const part001 = startSketchOn('-XZ')
await page.goto('/') await page.goto('/')
await u.waitForAuthSkipAppStart() await u.waitForAuthSkipAppStart()
await u.openDebugPanel() await u.openDebugPanel()
await u.waitForDefaultPlanesVisibilityChange()
await u.expectCmdLog('[data-message-type="execution-done"]') await u.expectCmdLog('[data-message-type="execution-done"]')
await u.waitForCmdReceive('extrude') await u.waitForCmdReceive('extrude')
await page.waitForTimeout(1000) await page.waitForTimeout(1000)
@ -386,3 +362,118 @@ const part001 = startSketchOn('-XZ')
}) })
} }
}) })
test('extrude on each default plane should be stable', async ({
page,
context,
}) => {
const u = getUtils(page)
const makeCode = (plane = 'XY') => `const part001 = startSketchOn('${plane}')
|> startProfileAt([0.70, 0.44], %)
|> line([0.66, -0.02], %)
|> line([0.28, 0.50], %)
|> line([-0.56, 0.44], %)
|> line([-0.54, -0.38], %)
|> close(%)
|> extrude(1.00, %)
`
await context.addInitScript(async (code) => {
localStorage.setItem('persistCode', code)
}, makeCode('XY'))
await page.setViewportSize({ width: 1200, height: 500 })
await page.goto('/')
await u.waitForAuthSkipAppStart()
// wait for execution done
await u.openDebugPanel()
await u.expectCmdLog('[data-message-type="execution-done"]')
await u.clearAndCloseDebugPanel()
await page.getByText('Code').click()
await expect(page).toHaveScreenshot({
maxDiffPixels: 100,
})
await page.getByText('Code').click()
const runSnapshotsForOtherPlanes = async (plane = 'XY') => {
// clear code
await u.removeCurrentCode()
// add makeCode('XZ')
await page.locator('.cm-content').fill(makeCode(plane))
// wait for execution done
await u.openDebugPanel()
await u.expectCmdLog('[data-message-type="execution-done"]')
await u.clearAndCloseDebugPanel()
await page.getByText('Code').click()
await expect(page).toHaveScreenshot({
maxDiffPixels: 100,
})
await page.getByText('Code').click()
}
await runSnapshotsForOtherPlanes('-XY')
await runSnapshotsForOtherPlanes('XZ')
await runSnapshotsForOtherPlanes('-XZ')
await runSnapshotsForOtherPlanes('YZ')
await runSnapshotsForOtherPlanes('-YZ')
})
test('Draft segments should look right', async ({ page }) => {
const u = getUtils(page)
await page.setViewportSize({ width: 1200, height: 500 })
const PUR = 400 / 37.5 //pixeltoUnitRatio
await page.goto('/')
await u.waitForAuthSkipAppStart()
await u.openDebugPanel()
await expect(page.getByRole('button', { name: 'Start Sketch' })).toBeVisible()
// click on "Start Sketch" button
await u.clearCommandLogs()
await u.doAndWaitForImageDiff(
() => page.getByRole('button', { name: 'Start Sketch' }).click(),
200
)
// select a plane
await page.mouse.click(700, 200)
await expect(page.locator('.cm-content')).toHaveText(
`const part001 = startSketchOn('-XZ')`
)
await page.waitForTimeout(300) // TODO detect animation ending, or disable animation
const startXPx = 600
await page.mouse.click(startXPx + PUR * 10, 500 - PUR * 10)
const startAt = '[23.74, -32.03]'
await expect(page.locator('.cm-content'))
.toHaveText(`const part001 = startSketchOn('-XZ')
|> startProfileAt(${startAt}, %)`)
await page.waitForTimeout(100)
await u.closeDebugPanel()
await page.mouse.move(startXPx + PUR * 20, 500 - PUR * 10)
await expect(page).toHaveScreenshot({
maxDiffPixels: 100,
})
await page.mouse.click(startXPx + PUR * 20, 500 - PUR * 10)
await page.waitForTimeout(100)
const num = 23.97
await expect(page.locator('.cm-content'))
.toHaveText(`const part001 = startSketchOn('-XZ')
|> startProfileAt(${startAt}, %)
|> line([${num}, 0], %)`)
await page.getByRole('button', { name: 'Tangential Arc' }).click()
await page.mouse.move(startXPx + PUR * 30, 500 - PUR * 20, { steps: 10 })
await expect(page).toHaveScreenshot({
maxDiffPixels: 100,
})
})

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 112 KiB

After

Width:  |  Height:  |  Size: 95 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 79 KiB

After

Width:  |  Height:  |  Size: 79 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 54 KiB

After

Width:  |  Height:  |  Size: 111 KiB

View File

@ -80,10 +80,21 @@ export function getUtils(page: Page) {
waitForAuthSkipAppStart: () => waitForPageLoad(page), waitForAuthSkipAppStart: () => waitForPageLoad(page),
removeCurrentCode: () => removeCurrentCode(page), removeCurrentCode: () => removeCurrentCode(page),
sendCustomCmd: (cmd: EngineCommand) => sendCustomCmd(page, cmd), sendCustomCmd: (cmd: EngineCommand) => sendCustomCmd(page, cmd),
updateCamPosition: async (xyz: [number, number, number]) => {
const fillInput = async () => {
await page.fill('[data-testid="cam-x-position"]', String(xyz[0]))
await page.fill('[data-testid="cam-y-position"]', String(xyz[1]))
await page.fill('[data-testid="cam-z-position"]', String(xyz[2]))
}
await fillInput()
await page.waitForTimeout(100)
await fillInput()
await page.waitForTimeout(100)
await fillInput()
await page.waitForTimeout(100)
},
clearCommandLogs: () => clearCommandLogs(page), clearCommandLogs: () => clearCommandLogs(page),
expectCmdLog: (locatorStr: string) => expectCmdLog(page, locatorStr), expectCmdLog: (locatorStr: string) => expectCmdLog(page, locatorStr),
waitForDefaultPlanesVisibilityChange: () =>
waitForDefaultPlanesToBeVisible(page),
openDebugPanel: () => openDebugPanel(page), openDebugPanel: () => openDebugPanel(page),
closeDebugPanel: () => closeDebugPanel(page), closeDebugPanel: () => closeDebugPanel(page),
openAndClearDebugPanel: async () => { openAndClearDebugPanel: async () => {

View File

@ -86,13 +86,11 @@ describe('ZMA (Tauri, Linux)', () => {
expect(await homeSection.getText()).toContain('project-000') expect(await homeSection.getText()).toContain('project-000')
}) })
it('opens the new file and expects an error on Linux', async () => { it('opens the new file and expects a loading stream', async () => {
const projectLink = await $('[data-testid="project-link"]') const projectLink = await $('[data-testid="project-link"]')
await click(projectLink) await click(projectLink)
const error = await $('h3') const loadingText = await $('[data-testid="loading-stream"]')
expect(await error.getText()).toContain( expect(await loadingText.getText()).toContain('Loading stream...')
"Can't find variable: RTCPeerConnection"
)
await browser.execute('window.location.href = "tauri://localhost/home"') await browser.execute('window.location.href = "tauri://localhost/home"')
}) })

69
make-release.sh Executable file
View File

@ -0,0 +1,69 @@
#!/bin/bash
if ! git diff-index --quiet HEAD --; then
echo "Please stash uncommitted changes before running release script"
exit 1
fi
git checkout main
git pull
git fetch --all
# Get the latest semver tag from git
latest_tag=$(jq -r '.version' package.json)
latest_tag="v$latest_tag"
# Print the latest semver tag
echo "Latest semver tag: $latest_tag"
# Function to bump version numbers
bump_version() {
local version=$1
local bump_type=$2
local major=$(echo $version | cut -d '.' -f 1 | sed 's/v//')
local minor=$(echo $version | cut -d '.' -f 2)
local patch=$(echo $version | cut -d '.' -f 3)
case "$bump_type" in
major)
major=$((major + 1))
minor=0
patch=0
;;
minor)
minor=$((minor + 1))
patch=0
;;
*)
patch=$((patch + 1))
;;
esac
echo "v${major}.${minor}.${patch}"
}
# Determine the type of bump based on the argument
bump_type=${1:-patch}
# Bump the version
new_version=$(bump_version $latest_tag $bump_type)
# Print the new semver tag
echo "New semver tag: $new_version"
new_version_number=${new_version:1}
echo "New version number without 'v': $new_version_number"
git checkout -b "cut-release-$new_version"
echo "$(jq --arg v "$new_version_number" '.version=$v' package.json --indent 2)" > package.json
echo "$(jq --arg v "$new_version_number" '.package.version=$v' src-tauri/tauri.conf.json --indent 2)" > src-tauri/tauri.conf.json
git add package.json src-tauri/tauri.conf.json
git commit -m "Cut release $new_version"
echo ""
echo "Versions has been bumped in relevant json files, a branch has been created and committed to."
echo ""
echo "What's left for you to do is, push the branch and make the release PR."
echo ""

View File

@ -1,6 +1,6 @@
{ {
"name": "untitled-app", "name": "untitled-app",
"version": "0.14.0", "version": "0.15.2",
"private": true, "private": true,
"dependencies": { "dependencies": {
"@codemirror/autocomplete": "^6.10.2", "@codemirror/autocomplete": "^6.10.2",
@ -10,7 +10,7 @@
"@fortawesome/react-fontawesome": "^0.2.0", "@fortawesome/react-fontawesome": "^0.2.0",
"@headlessui/react": "^1.7.17", "@headlessui/react": "^1.7.17",
"@headlessui/tailwindcss": "^0.2.0", "@headlessui/tailwindcss": "^0.2.0",
"@kittycad/lib": "^0.0.46", "@kittycad/lib": "^0.0.53",
"@lezer/javascript": "^1.4.9", "@lezer/javascript": "^1.4.9",
"@open-rpc/client-js": "^1.8.1", "@open-rpc/client-js": "^1.8.1",
"@react-hook/resize-observer": "^1.2.6", "@react-hook/resize-observer": "^1.2.6",
@ -21,6 +21,7 @@
"@testing-library/react": "^14.0.0", "@testing-library/react": "^14.0.0",
"@testing-library/user-event": "^14.5.1", "@testing-library/user-event": "^14.5.1",
"@ts-stack/markdown": "^1.5.0", "@ts-stack/markdown": "^1.5.0",
"@tweenjs/tween.js": "^23.1.1",
"@types/node": "^16.7.13", "@types/node": "^16.7.13",
"@types/react": "^18.2.41", "@types/react": "^18.2.41",
"@types/react-dom": "^18.0.0", "@types/react-dom": "^18.0.0",
@ -33,6 +34,7 @@
"fuse.js": "^7.0.0", "fuse.js": "^7.0.0",
"http-server": "^14.1.1", "http-server": "^14.1.1",
"json-rpc-2.0": "^1.6.0", "json-rpc-2.0": "^1.6.0",
"node-fetch": "^3.3.2",
"re-resizable": "^6.9.11", "re-resizable": "^6.9.11",
"react": "^18.2.0", "react": "^18.2.0",
"react-dom": "^18.2.0", "react-dom": "^18.2.0",
@ -45,11 +47,12 @@
"sketch-helpers": "^0.0.4", "sketch-helpers": "^0.0.4",
"swr": "^2.2.2", "swr": "^2.2.2",
"tauri-plugin-fs-extra-api": "https://github.com/tauri-apps/tauri-plugin-fs-extra#v1", "tauri-plugin-fs-extra-api": "https://github.com/tauri-apps/tauri-plugin-fs-extra#v1",
"three": "^0.160.0",
"toml": "^3.0.0", "toml": "^3.0.0",
"ts-node": "^10.9.1", "ts-node": "^10.9.1",
"typescript": "^5.2.2", "typescript": "^5.2.2",
"uuid": "^9.0.1", "uuid": "^9.0.1",
"vitest": "^0.34.6", "vitest": "^1.3.1",
"vscode-jsonrpc": "^8.1.0", "vscode-jsonrpc": "^8.1.0",
"vscode-languageserver-protocol": "^3.17.5", "vscode-languageserver-protocol": "^3.17.5",
"wasm-pack": "^0.12.1", "wasm-pack": "^0.12.1",
@ -70,7 +73,6 @@
"test": "vitest --mode development", "test": "vitest --mode development",
"test:nowatch": "vitest run --mode development", "test:nowatch": "vitest run --mode development",
"test:rust": "(cd src/wasm-lib && cargo test --all && cargo clippy --all --tests --benches)", "test:rust": "(cd src/wasm-lib && cargo test --all && cargo clippy --all --tests --benches)",
"test:cov": "vitest run --coverage --mode development",
"test:e2e:tauri": "E2E_TAURI_ENABLED=true TS_NODE_COMPILER_OPTIONS='{\"module\": \"commonjs\"}' wdio run wdio.conf.ts", "test:e2e:tauri": "E2E_TAURI_ENABLED=true TS_NODE_COMPILER_OPTIONS='{\"module\": \"commonjs\"}' wdio run wdio.conf.ts",
"simpleserver:ci": "yarn pretest && http-server ./public --cors -p 3000 &", "simpleserver:ci": "yarn pretest && http-server ./public --cors -p 3000 &",
"simpleserver": "yarn pretest && http-server ./public --cors -p 3000", "simpleserver": "yarn pretest && http-server ./public --cors -p 3000",
@ -82,7 +84,9 @@
"remove-importmeta": "sed -i 's/import.meta.url/window.location.origin/g' \"./src/wasm-lib/pkg/wasm_lib.js\"; sed -i '' 's/import.meta.url/window.location.origin/g' \"./src/wasm-lib/pkg/wasm_lib.js\" || echo \"sed for both mac and linux\"", "remove-importmeta": "sed -i 's/import.meta.url/window.location.origin/g' \"./src/wasm-lib/pkg/wasm_lib.js\"; sed -i '' 's/import.meta.url/window.location.origin/g' \"./src/wasm-lib/pkg/wasm_lib.js\" || echo \"sed for both mac and linux\"",
"wasm-prep": "rm -rf src/wasm-lib/pkg && mkdir src/wasm-lib/pkg && rm -rf src/wasm-lib/kcl/bindings", "wasm-prep": "rm -rf src/wasm-lib/pkg && mkdir src/wasm-lib/pkg && rm -rf src/wasm-lib/kcl/bindings",
"lint": "eslint --fix src", "lint": "eslint --fix src",
"bump-jsons": "echo \"$(jq --arg v \"$VERSION\" '.version=$v' package.json --indent 2)\" > package.json && echo \"$(jq --arg v \"$VERSION\" '.package.version=$v' src-tauri/tauri.conf.json --indent 2)\" > src-tauri/tauri.conf.json" "bump-jsons": "echo \"$(jq --arg v \"$VERSION\" '.version=$v' package.json --indent 2)\" > package.json && echo \"$(jq --arg v \"$VERSION\" '.package.version=$v' src-tauri/tauri.conf.json --indent 2)\" > src-tauri/tauri.conf.json",
"postinstall": "patch-package && yarn xstate:typegen",
"xstate:typegen": "yarn xstate typegen \"src/**/*.ts?(x)\""
}, },
"prettier": { "prettier": {
"trailingComma": "es5", "trailingComma": "es5",
@ -109,36 +113,40 @@
"@tauri-apps/cli": "^1.5.6", "@tauri-apps/cli": "^1.5.6",
"@types/crypto-js": "^4.1.1", "@types/crypto-js": "^4.1.1",
"@types/debounce-promise": "^3.1.8", "@types/debounce-promise": "^3.1.8",
"@types/isomorphic-fetch": "^0.0.36",
"@types/pixelmatch": "^5.2.6", "@types/pixelmatch": "^5.2.6",
"@types/pngjs": "^6.0.4", "@types/pngjs": "^6.0.4",
"@types/react-modal": "^3.16.3", "@types/react-modal": "^3.16.3",
"@types/three": "^0.160.0",
"@types/uuid": "^9.0.4", "@types/uuid": "^9.0.4",
"@types/wait-on": "^5.3.4", "@types/wait-on": "^5.3.4",
"@types/wicg-file-system-access": "^2020.9.6", "@types/wicg-file-system-access": "^2020.9.6",
"@types/ws": "^8.5.5", "@types/ws": "^8.5.5",
"@vitejs/plugin-react": "^4.1.1", "@vitejs/plugin-react": "^4.2.1",
"@vitest/coverage-istanbul": "^0.34.6",
"@wdio/cli": "^8.24.3", "@wdio/cli": "^8.24.3",
"@wdio/globals": "^8.24.3", "@wdio/globals": "^8.24.3",
"@wdio/local-runner": "^8.24.3", "@wdio/local-runner": "^8.24.3",
"@wdio/mocha-framework": "^8.24.3", "@wdio/mocha-framework": "^8.24.3",
"@wdio/spec-reporter": "^8.24.2", "@wdio/spec-reporter": "^8.24.2",
"@xstate/cli": "^0.5.17",
"autoprefixer": "^10.4.13", "autoprefixer": "^10.4.13",
"eslint": "^8.53.0", "eslint": "^8.53.0",
"eslint-config-react-app": "^7.0.1", "eslint-config-react-app": "^7.0.1",
"eslint-plugin-css-modules": "^2.12.0", "eslint-plugin-css-modules": "^2.12.0",
"happy-dom": "^10.8.0", "happy-dom": "^10.8.0",
"husky": "^8.0.3", "husky": "^8.0.3",
"patch-package": "^8.0.0",
"pixelmatch": "^5.3.0", "pixelmatch": "^5.3.0",
"pngjs": "^7.0.0", "pngjs": "^7.0.0",
"postcss": "^8.4.31", "postcss": "^8.4.31",
"postinstall-postinstall": "^2.1.0",
"prettier": "^2.8.0", "prettier": "^2.8.0",
"setimmediate": "^1.0.5", "setimmediate": "^1.0.5",
"tailwindcss": "^3.3.6", "tailwindcss": "^3.3.6",
"vite": "^4.5.2", "vite": "^5.1.3",
"vite-plugin-eslint": "^1.8.1", "vite-plugin-eslint": "^1.8.1",
"vite-tsconfig-paths": "^4.2.1", "vite-plugin-package-version": "^1.1.0",
"vite-tsconfig-paths": "^4.3.1",
"vitest-webgl-canvas-mock": "^1.1.0",
"wait-on": "^7.2.0", "wait-on": "^7.2.0",
"yarn": "^1.22.19" "yarn": "^1.22.19"
} }

138
patches/three+0.160.0.patch Normal file
View File

@ -0,0 +1,138 @@
diff --git a/node_modules/three/examples/jsm/controls/OrbitControls.js b/node_modules/three/examples/jsm/controls/OrbitControls.js
index f29e7fe..0ef636b 100644
--- a/node_modules/three/examples/jsm/controls/OrbitControls.js
+++ b/node_modules/three/examples/jsm/controls/OrbitControls.js
@@ -113,6 +113,25 @@ class OrbitControls extends EventDispatcher {
// public methods
//
+ this.interactionGuards = {
+ pan: {
+ description: 'Right click + Shift + drag or middle click + drag',
+ callback: (e) => e.button === 2 && !e.ctrlKey,
+ },
+ zoom: {
+ description: 'Scroll wheel or Right click + Ctrl + drag',
+ dragCallback: (e) => e.button === 2 && e.ctrlKey,
+ scrollCallback: () => true,
+ },
+ rotate: {
+ description: 'Right click + drag',
+ callback: (e) => e.button === 0,
+ },
+ }
+ this.setMouseGuards = (interactionGuards) => {
+ this.interactionGuards = interactionGuards
+ }
+
this.getPolarAngle = function () {
return spherical.phi;
@@ -1057,92 +1076,21 @@ class OrbitControls extends EventDispatcher {
function onMouseDown( event ) {
- let mouseAction;
-
- switch ( event.button ) {
-
- case 0:
-
- mouseAction = scope.mouseButtons.LEFT;
- break;
-
- case 1:
-
- mouseAction = scope.mouseButtons.MIDDLE;
- break;
-
- case 2:
-
- mouseAction = scope.mouseButtons.RIGHT;
- break;
-
- default:
-
- mouseAction = - 1;
-
- }
-
- switch ( mouseAction ) {
-
- case MOUSE.DOLLY:
-
- if ( scope.enableZoom === false ) return;
-
- handleMouseDownDolly( event );
-
- state = STATE.DOLLY;
-
- break;
-
- case MOUSE.ROTATE:
-
- if ( event.ctrlKey || event.metaKey || event.shiftKey ) {
-
- if ( scope.enablePan === false ) return;
-
- handleMouseDownPan( event );
-
- state = STATE.PAN;
-
- } else {
-
- if ( scope.enableRotate === false ) return;
-
- handleMouseDownRotate( event );
-
- state = STATE.ROTATE;
-
- }
-
- break;
-
- case MOUSE.PAN:
-
- if ( event.ctrlKey || event.metaKey || event.shiftKey ) {
-
- if ( scope.enableRotate === false ) return;
-
- handleMouseDownRotate( event );
-
- state = STATE.ROTATE;
-
- } else {
-
- if ( scope.enablePan === false ) return;
-
- handleMouseDownPan( event );
-
- state = STATE.PAN;
-
- }
-
- break;
-
- default:
-
- state = STATE.NONE;
-
- }
+ if (scope.interactionGuards.pan.callback(event)) {
+ if (scope.enablePan === false) return
+ handleMouseDownPan(event)
+ state = STATE.PAN
+ } else if (scope.interactionGuards.rotate.callback(event)) {
+ if (scope.enableRotate === false) return
+ handleMouseDownRotate(event)
+ state = STATE.ROTATE
+ } else if (scope.interactionGuards.zoom.dragCallback(event)) {
+ if (scope.enableZoom === false) return
+ handleMouseDownDolly(event)
+ state = STATE.DOLLY
+ } else {
+ return
+ }
if ( state !== STATE.NONE ) {

View File

@ -1,4 +1,4 @@
import { defineConfig, devices } from '@playwright/test'; import { defineConfig, devices } from '@playwright/test'
/** /**
* Read environment variables from file. * Read environment variables from file.
@ -78,5 +78,4 @@ export default defineConfig({
// url: 'http://127.0.0.1:3000', // url: 'http://127.0.0.1:3000',
reuseExistingServer: !process.env.CI, reuseExistingServer: !process.env.CI,
}, },
}); })

View File

@ -0,0 +1,26 @@
import requests
import os
webhook_url = os.getenv('DISCORD_WEBHOOK_URL')
release_version = os.getenv('RELEASE_VERSION')
release_body = os.getenv('RELEASE_BODY')
# message to send to Discord
data = {
"content":
f'''
**{release_version}** is now available! Check out the latest features and improvements here: https://zoo.dev/modeling-app/download
{release_body}
''',
"username": "Modeling App Release Updates",
"avatar_url": "https://raw.githubusercontent.com/KittyCAD/modeling-app/main/public/discord-avatar.png"
}
# POST request to the Discord webhook
response = requests.post(webhook_url, json=data)
# Check for success
if response.status_code == 204:
print("Successfully sent the message to Discord.")
else:
print("Failed to send the message to Discord.")

BIN
public/discord-avatar.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

104
src-tauri/Cargo.lock generated
View File

@ -67,9 +67,9 @@ dependencies = [
[[package]] [[package]]
name = "anyhow" name = "anyhow"
version = "1.0.75" version = "1.0.79"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" checksum = "080e9890a082662b09c1ad45f567faeeb47f22b5fb23895fbe1e651e718e25ca"
[[package]] [[package]]
name = "app" name = "app"
@ -95,7 +95,7 @@ checksum = "bc00ceb34980c03614e35a3a4e218276a0a824e911d07651cd0d858a51e8c0f0"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.33", "syn 2.0.48",
] ]
[[package]] [[package]]
@ -542,7 +542,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13b588ba4ac1a99f7f2964d24b3d896ddc6bf847ee3855dbd4366f058cfcd331" checksum = "13b588ba4ac1a99f7f2964d24b3d896ddc6bf847ee3855dbd4366f058cfcd331"
dependencies = [ dependencies = [
"quote", "quote",
"syn 2.0.33", "syn 2.0.48",
] ]
[[package]] [[package]]
@ -552,7 +552,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "30d2b3721e861707777e3195b0158f950ae6dc4a27e4d02ff9f67e3eb3de199e" checksum = "30d2b3721e861707777e3195b0158f950ae6dc4a27e4d02ff9f67e3eb3de199e"
dependencies = [ dependencies = [
"quote", "quote",
"syn 2.0.33", "syn 2.0.48",
] ]
[[package]] [[package]]
@ -582,7 +582,7 @@ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"strsim", "strsim",
"syn 2.0.33", "syn 2.0.48",
] ]
[[package]] [[package]]
@ -593,7 +593,7 @@ checksum = "29a358ff9f12ec09c3e61fef9b5a9902623a695a46a917b07f269bff1445611a"
dependencies = [ dependencies = [
"darling_core", "darling_core",
"quote", "quote",
"syn 2.0.33", "syn 2.0.48",
] ]
[[package]] [[package]]
@ -899,7 +899,7 @@ checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.33", "syn 2.0.48",
] ]
[[package]] [[package]]
@ -1664,9 +1664,9 @@ dependencies = [
[[package]] [[package]]
name = "kittycad" name = "kittycad"
version = "0.2.42" version = "0.2.53"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6aa554d86b6dbbd976a659c912ae25ce817b4378eb12a5684907e263410f0a7b" checksum = "a086e1a1bbddb3b38959c0f0ce6de6b3a3b7566e38e0b7d5fb101e91911beed4"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"async-trait", "async-trait",
@ -2215,7 +2215,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.33", "syn 2.0.48",
] ]
[[package]] [[package]]
@ -2368,7 +2368,7 @@ dependencies = [
"regex", "regex",
"regex-syntax 0.7.5", "regex-syntax 0.7.5",
"structmeta", "structmeta",
"syn 2.0.33", "syn 2.0.48",
] ]
[[package]] [[package]]
@ -2487,7 +2487,7 @@ dependencies = [
"phf_shared 0.11.2", "phf_shared 0.11.2",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.33", "syn 2.0.48",
] ]
[[package]] [[package]]
@ -2555,7 +2555,7 @@ checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.33", "syn 2.0.48",
] ]
[[package]] [[package]]
@ -2657,9 +2657,9 @@ checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068"
[[package]] [[package]]
name = "proc-macro2" name = "proc-macro2"
version = "1.0.67" version = "1.0.78"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d433d9f1a3e8c1263d9456598b16fec66f4acc9a74dacffd35c7bb09b3a1328" checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae"
dependencies = [ dependencies = [
"unicode-ident", "unicode-ident",
] ]
@ -2675,9 +2675,9 @@ dependencies = [
[[package]] [[package]]
name = "quote" name = "quote"
version = "1.0.33" version = "1.0.35"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
] ]
@ -3235,9 +3235,9 @@ dependencies = [
[[package]] [[package]]
name = "serde" name = "serde"
version = "1.0.193" version = "1.0.196"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "25dd9975e68d0cb5aa1120c288333fc98731bd1dd12f561e468ea4728c042b89" checksum = "870026e60fa08c69f064aa766c10f10b1d62db9ccd4d0abb206472bee0ce3b32"
dependencies = [ dependencies = [
"serde_derive", "serde_derive",
] ]
@ -3253,13 +3253,13 @@ dependencies = [
[[package]] [[package]]
name = "serde_derive" name = "serde_derive"
version = "1.0.193" version = "1.0.196"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3" checksum = "33c85360c95e7d137454dc81d9a4ed2b8efd8fbe19cee57357b32b9771fccb67"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.33", "syn 2.0.48",
] ]
[[package]] [[package]]
@ -3275,9 +3275,9 @@ dependencies = [
[[package]] [[package]]
name = "serde_json" name = "serde_json"
version = "1.0.108" version = "1.0.113"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b" checksum = "69801b70b1c3dac963ecb03a364ba0ceda9cf60c71cfe475e99864759c8b8a79"
dependencies = [ dependencies = [
"itoa 1.0.6", "itoa 1.0.6",
"ryu", "ryu",
@ -3302,7 +3302,7 @@ checksum = "bcec881020c684085e55a25f7fd888954d56609ef363479dc5a1305eb0d40cab"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.33", "syn 2.0.48",
] ]
[[package]] [[package]]
@ -3352,7 +3352,7 @@ dependencies = [
"darling", "darling",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.33", "syn 2.0.48",
] ]
[[package]] [[package]]
@ -3556,7 +3556,7 @@ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"structmeta-derive", "structmeta-derive",
"syn 2.0.33", "syn 2.0.48",
] ]
[[package]] [[package]]
@ -3567,7 +3567,7 @@ checksum = "a60bcaff7397072dca0017d1db428e30d5002e00b6847703e2e42005c95fbe00"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.33", "syn 2.0.48",
] ]
[[package]] [[package]]
@ -3605,9 +3605,9 @@ dependencies = [
[[package]] [[package]]
name = "syn" name = "syn"
version = "2.0.33" version = "2.0.48"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9caece70c63bfba29ec2fed841a09851b14a235c60010fa4de58089b6c025668" checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -3760,9 +3760,9 @@ dependencies = [
[[package]] [[package]]
name = "tauri" name = "tauri"
version = "1.5.3" version = "1.5.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32d563b672acde8d0cc4c1b1f5b855976923f67e8d6fe1eba51df0211e197be2" checksum = "fd27c04b9543776a972c86ccf70660b517ecabbeced9fb58d8b961a13ad129af"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"bytes", "bytes",
@ -3812,9 +3812,9 @@ dependencies = [
[[package]] [[package]]
name = "tauri-build" name = "tauri-build"
version = "1.5.0" version = "1.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "defbfc551bd38ab997e5f8e458f87396d2559d05ce32095076ad6c30f7fc5f9c" checksum = "e9914a4715e0b75d9f387a285c7e26b5bbfeb1249ad9f842675a82481565c532"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"cargo_toml", "cargo_toml",
@ -3831,9 +3831,9 @@ dependencies = [
[[package]] [[package]]
name = "tauri-codegen" name = "tauri-codegen"
version = "1.4.1" version = "1.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7b3475e55acec0b4a50fb96435f19631fb58cbcd31923e1a213de5c382536bbb" checksum = "a1554c5857f65dbc377cefb6b97c8ac77b1cb2a90d30d3448114d5d6b48a77fc"
dependencies = [ dependencies = [
"base64 0.21.2", "base64 0.21.2",
"brotli", "brotli",
@ -3857,9 +3857,9 @@ dependencies = [
[[package]] [[package]]
name = "tauri-macros" name = "tauri-macros"
version = "1.4.2" version = "1.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "acea6445eececebd72ed7720cfcca46eee3b5bad8eb408be8f7ef2e3f7411500" checksum = "277abf361a3a6993ec16bcbb179de0d6518009b851090a01adfea12ac89fa875"
dependencies = [ dependencies = [
"heck 0.4.1", "heck 0.4.1",
"proc-macro2", "proc-macro2",
@ -3872,7 +3872,7 @@ dependencies = [
[[package]] [[package]]
name = "tauri-plugin-fs-extra" name = "tauri-plugin-fs-extra"
version = "0.0.0" version = "0.0.0"
source = "git+https://github.com/tauri-apps/plugins-workspace?branch=v1#537053d3171a7374a1a86fed422523e7b45a4fb8" source = "git+https://github.com/tauri-apps/plugins-workspace?branch=v1#01211ff0759d578e0e9ac8c98c31fdf09077eb34"
dependencies = [ dependencies = [
"log", "log",
"serde", "serde",
@ -3883,9 +3883,9 @@ dependencies = [
[[package]] [[package]]
name = "tauri-runtime" name = "tauri-runtime"
version = "0.14.1" version = "0.14.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "07f8e9e53e00e9f41212c115749e87d5cd2a9eebccafca77a19722eeecd56d43" checksum = "cf2d0652aa2891ff3e9caa2401405257ea29ab8372cce01f186a5825f1bd0e76"
dependencies = [ dependencies = [
"gtk", "gtk",
"http", "http",
@ -3904,9 +3904,9 @@ dependencies = [
[[package]] [[package]]
name = "tauri-runtime-wry" name = "tauri-runtime-wry"
version = "0.14.2" version = "0.14.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "803a01101bc611ba03e13329951a1bde44287a54234189b9024b78619c1bc206" checksum = "6cae61fbc731f690a4899681c9052dde6d05b159b44563ace8186fc1bfb7d158"
dependencies = [ dependencies = [
"cocoa", "cocoa",
"gtk", "gtk",
@ -3924,9 +3924,9 @@ dependencies = [
[[package]] [[package]]
name = "tauri-utils" name = "tauri-utils"
version = "1.5.1" version = "1.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a52165bb340e6f6a75f1f5eeeab1bb49f861c12abe3a176067d53642b5454986" checksum = "ece74810b1d3d44f29f732a7ae09a63183d63949bbdd59c61f8ed2a1b70150db"
dependencies = [ dependencies = [
"brotli", "brotli",
"ctor", "ctor",
@ -4009,7 +4009,7 @@ checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.33", "syn 2.0.48",
] ]
[[package]] [[package]]
@ -4051,9 +4051,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
[[package]] [[package]]
name = "tokio" name = "tokio"
version = "1.34.0" version = "1.36.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d0c014766411e834f7af5b8f4cf46257aab4036ca95e9d2c144a10f59ad6f5b9" checksum = "61285f6515fa018fb2d1e46eb21223fff441ee8db5d0f1435e8ab4f5cdb80931"
dependencies = [ dependencies = [
"backtrace", "backtrace",
"bytes", "bytes",
@ -4193,7 +4193,7 @@ checksum = "0f57e3ca2a01450b1a921183a9c9cbfda207fd822cef4ccb00a65402cbba7a74"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.33", "syn 2.0.48",
] ]
[[package]] [[package]]
@ -4443,7 +4443,7 @@ dependencies = [
"once_cell", "once_cell",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.33", "syn 2.0.48",
"wasm-bindgen-shared", "wasm-bindgen-shared",
] ]
@ -4477,7 +4477,7 @@ checksum = "e128beba882dd1eb6200e1dc92ae6c5dbaa4311aa7bb211ca035779e5efc39f8"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.33", "syn 2.0.48",
"wasm-bindgen-backend", "wasm-bindgen-backend",
"wasm-bindgen-shared", "wasm-bindgen-shared",
] ]

View File

@ -12,17 +12,17 @@ rust-version = "1.60"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[build-dependencies] [build-dependencies]
tauri-build = { version = "1.5.0", features = [] } tauri-build = { version = "1.5.1", features = [] }
[dependencies] [dependencies]
anyhow = "1" anyhow = "1"
kittycad = "0.2.42" kittycad = "0.2.53"
oauth2 = "4.4.2" oauth2 = "4.4.2"
serde = { version = "1.0", features = ["derive"] } serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0" serde_json = "1.0"
tauri = { version = "1.5.3", features = [ "os-all", "dialog-all", "fs-all", "http-request", "path-all", "shell-open", "shell-open-api", "devtools"] } tauri = { version = "1.5.4", features = [ "os-all", "dialog-all", "fs-all", "http-request", "path-all", "shell-open", "shell-open-api", "devtools"] }
tauri-plugin-fs-extra = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "v1" } tauri-plugin-fs-extra = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "v1" }
tokio = { version = "1.34.0", features = ["time"] } tokio = { version = "1.36.0", features = ["time"] }
toml = "0.8.2" toml = "0.8.2"
[features] [features]

View File

@ -7,6 +7,7 @@ use std::io::Read;
use anyhow::Result; use anyhow::Result;
use oauth2::TokenResponse; use oauth2::TokenResponse;
use std::process::Command;
use tauri::{InvokeError, Manager}; use tauri::{InvokeError, Manager};
const DEFAULT_HOST: &str = "https://api.kittycad.io"; const DEFAULT_HOST: &str = "https://api.kittycad.io";

View File

@ -7,7 +7,7 @@
}, },
"package": { "package": {
"productName": "zoo-modeling-app", "productName": "zoo-modeling-app",
"version": "0.14.0" "version": "0.15.2"
}, },
"tauri": { "tauri": {
"allowlist": { "allowlist": {

View File

@ -1,73 +0,0 @@
import { render, screen } from '@testing-library/react'
import { App } from './App'
import { describe, test, vi } from 'vitest'
import {
Route,
RouterProvider,
createMemoryRouter,
createRoutesFromElements,
} from 'react-router-dom'
import { GlobalStateProvider } from './components/GlobalStateProvider'
import CommandBarProvider from 'components/CommandBar/CommandBar'
import ModelingMachineProvider from 'components/ModelingMachineProvider'
import { BROWSER_FILE_NAME } from 'Router'
let listener: ((rect: any) => void) | undefined = undefined
;(global as any).ResizeObserver = class ResizeObserver {
constructor(ls: ((rect: any) => void) | undefined) {
listener = ls
}
observe() {}
unobserve() {}
disconnect() {}
}
describe('App tests', () => {
test('Renders the modeling app screen, including "Variables" pane.', () => {
vi.mock('react-router-dom', async () => {
const actual = (await vi.importActual('react-router-dom')) as Record<
string,
any
>
return {
...actual,
useParams: () => ({ id: BROWSER_FILE_NAME }),
useLoaderData: () => ({ code: null }),
}
})
render(
<TestWrap>
<App />
</TestWrap>
)
const linkElement = screen.getByText(/Variables/i)
expect(linkElement).toBeInTheDocument()
vi.restoreAllMocks()
})
})
function TestWrap({ children }: { children: React.ReactNode }) {
// We have to use a memory router in the testing environment,
// and we have to use the createMemoryRouter function instead of <MemoryRouter /> as of react-router v6.4:
// https://reactrouter.com/en/6.16.0/routers/picking-a-router#using-v64-data-apis
const router = createMemoryRouter(
createRoutesFromElements(
<Route
path="/file/:id"
element={
<CommandBarProvider>
<GlobalStateProvider>
<ModelingMachineProvider>{children}</ModelingMachineProvider>
</GlobalStateProvider>
</CommandBarProvider>
}
/>
),
{
initialEntries: ['/file/new'],
initialIndex: 0,
}
)
return <RouterProvider router={router} />
}

View File

@ -19,21 +19,24 @@ import {
} from '@fortawesome/free-solid-svg-icons' } from '@fortawesome/free-solid-svg-icons'
import { useHotkeys } from 'react-hotkeys-hook' import { useHotkeys } from 'react-hotkeys-hook'
import { getNormalisedCoordinates } from './lib/utils' import { getNormalisedCoordinates } from './lib/utils'
import { useLoaderData } from 'react-router-dom' import { useLoaderData, useNavigate } from 'react-router-dom'
import { IndexLoaderData } from './Router' import { type IndexLoaderData } from 'lib/types'
import { paths } from 'lib/paths'
import { useGlobalStateContext } from 'hooks/useGlobalStateContext' import { useGlobalStateContext } from 'hooks/useGlobalStateContext'
import { onboardingPaths } from 'routes/Onboarding' import { onboardingPaths } from 'routes/Onboarding/paths'
import { cameraMouseDragGuards } from 'lib/cameraControls'
import { CameraDragInteractionType_type } from '@kittycad/lib/dist/types/src/models'
import { CodeMenu } from 'components/CodeMenu' import { CodeMenu } from 'components/CodeMenu'
import { TextEditor } from 'components/TextEditor' import { TextEditor } from 'components/TextEditor'
import { Themes, getSystemTheme } from 'lib/theme' import { Themes, getSystemTheme } from 'lib/theme'
import { useEngineConnectionSubscriptions } from 'hooks/useEngineConnectionSubscriptions' import { useEngineConnectionSubscriptions } from 'hooks/useEngineConnectionSubscriptions'
import { engineCommandManager } from './lang/std/engineConnection' import { engineCommandManager } from './lang/std/engineConnection'
import { useModelingContext } from 'hooks/useModelingContext' import { useModelingContext } from 'hooks/useModelingContext'
import { useAbsoluteFilePath } from 'hooks/useAbsoluteFilePath'
import { isTauri } from 'lib/isTauri'
export function App() { export function App() {
const { project, file } = useLoaderData() as IndexLoaderData const { project, file } = useLoaderData() as IndexLoaderData
const navigate = useNavigate()
const filePath = useAbsoluteFilePath()
useHotKeyListener() useHotKeyListener()
const { const {
@ -51,8 +54,7 @@ export function App() {
})) }))
const { settings } = useGlobalStateContext() const { settings } = useGlobalStateContext()
const { showDebugPanel, onboardingStatus, cameraControls, theme } = const { showDebugPanel, onboardingStatus, theme } = settings?.context || {}
settings?.context || {}
const { state, send } = useModelingContext() const { state, send } = useModelingContext()
const editorTheme = theme === Themes.System ? getSystemTheme() : theme const editorTheme = theme === Themes.System ? getSystemTheme() : theme
@ -71,6 +73,16 @@ export function App() {
useHotkeys('shift + e', () => togglePane('kclErrors')) useHotkeys('shift + e', () => togglePane('kclErrors'))
useHotkeys('shift + d', () => togglePane('debug')) useHotkeys('shift + d', () => togglePane('debug'))
useHotkeys('esc', () => send('Cancel')) useHotkeys('esc', () => send('Cancel'))
useHotkeys('backspace', (e) => {
e.preventDefault()
})
useHotkeys(
isTauri() ? 'mod + ,' : 'shift + mod + ,',
() => navigate(filePath + paths.SETTINGS),
{
splitKey: '|',
}
)
const paneOpacity = [onboardingPaths.CAMERA, onboardingPaths.STREAMING].some( const paneOpacity = [onboardingPaths.CAMERA, onboardingPaths.STREAMING].some(
(p) => p === onboardingStatus (p) => p === onboardingStatus
@ -84,9 +96,11 @@ export function App() {
const debounceSocketSend = throttle<EngineCommand>((message) => { const debounceSocketSend = throttle<EngineCommand>((message) => {
engineCommandManager.sendSceneCommand(message) engineCommandManager.sendSceneCommand(message)
}, 16) }, 1000 / 15)
const handleMouseMove: MouseEventHandler<HTMLDivElement> = (e) => { const handleMouseMove: MouseEventHandler<HTMLDivElement> = (e) => {
e.nativeEvent.preventDefault() if (state.matches('Sketch')) {
return
}
const { x, y } = getNormalisedCoordinates({ const { x, y } = getNormalisedCoordinates({
clientX: e.clientX, clientX: e.clientX,
@ -97,58 +111,11 @@ export function App() {
const newCmdId = uuidv4() const newCmdId = uuidv4()
if (buttonDownInStream === undefined) { if (buttonDownInStream === undefined) {
if (state.matches('Sketch.Line Tool')) {
debounceSocketSend({
type: 'modeling_cmd_req',
cmd_id: newCmdId,
cmd: {
type: 'mouse_move',
window: { x, y },
},
})
} else {
debounceSocketSend({
type: 'modeling_cmd_req',
cmd: {
type: 'highlight_set_entity',
selected_at_window: { x, y },
},
cmd_id: newCmdId,
})
}
} else {
if (state.matches('Sketch.Move Tool')) {
debounceSocketSend({
type: 'modeling_cmd_req',
cmd_id: newCmdId,
cmd: {
type: 'handle_mouse_drag_move',
window: { x, y },
},
})
return
}
const interactionGuards = cameraMouseDragGuards[cameraControls]
let interaction: CameraDragInteractionType_type
const eWithButton = { ...e, button: buttonDownInStream }
if (interactionGuards.pan.callback(eWithButton)) {
interaction = 'pan'
} else if (interactionGuards.rotate.callback(eWithButton)) {
interaction = 'rotate'
} else if (interactionGuards.zoom.dragCallback(eWithButton)) {
interaction = 'zoom'
} else {
return
}
debounceSocketSend({ debounceSocketSend({
type: 'modeling_cmd_req', type: 'modeling_cmd_req',
cmd: { cmd: {
type: 'camera_drag_move', type: 'highlight_set_entity',
interaction, selected_at_window: { x, y },
window: { x, y },
}, },
cmd_id: newCmdId, cmd_id: newCmdId,
}) })
@ -170,62 +137,71 @@ export function App() {
enableMenu={true} enableMenu={true}
/> />
<ModalContainer /> <ModalContainer />
<Resizable <div className="h-full">
className={ <div className="h-full flex">
'pointer-events-none h-full flex flex-col flex-1 z-10 my-5 ml-5 pr-1 transition-opacity transition-duration-75 ' + <div className="h-full w-5 z-10 pointer-events-auto"></div>
+paneOpacity <div className="flex flex-col">
} <div className="h-5 z-10 pointer-events-auto"></div>
defaultSize={{ <Resizable
width: '550px', className={
height: 'auto', 'pointer-events-none h-full flex flex-col flex-1 z-10 pr-1 transition-opacity transition-duration-75 ' +
}} +paneOpacity
minWidth={200} }
maxWidth={800} defaultSize={{
minHeight={'auto'} width: '550px',
maxHeight={'auto'} height: 'auto',
handleClasses={{ }}
right: minWidth={200}
'hover:bg-chalkboard-10/50 bg-transparent transition-colors duration-75 transition-ease-out delay-100 ' + maxWidth={800}
(buttonDownInStream || onboardingStatus === 'camera' minHeight={'auto'}
? 'pointer-events-none ' maxHeight={'auto'}
: 'pointer-events-auto'), handleClasses={{
}} right:
> 'hover:bg-chalkboard-10/50 bg-transparent transition-colors duration-75 transition-ease-out delay-100 ' +
<div (buttonDownInStream || onboardingStatus === 'camera'
id="code-pane" ? 'pointer-events-none '
className="h-full flex flex-col justify-between pointer-events-none" : 'pointer-events-auto'),
> }}
<CollapsiblePanel >
title="Code" <div
icon={faCode} id="code-pane"
className="open:!mb-2" className="h-full flex flex-col justify-between pointer-events-none"
open={openPanes.includes('code')} >
menu={<CodeMenu />} <CollapsiblePanel
> title="Code"
<TextEditor theme={editorTheme} /> icon={faCode}
</CollapsiblePanel> className="open:!mb-2"
<section className="flex flex-col"> open={openPanes.includes('code')}
<MemoryPanel menu={<CodeMenu />}
theme={editorTheme} >
open={openPanes.includes('variables')} <TextEditor theme={editorTheme} />
title="Variables" </CollapsiblePanel>
icon={faSquareRootVariable} <section className="flex flex-col">
/> <MemoryPanel
<Logs theme={editorTheme}
theme={editorTheme} open={openPanes.includes('variables')}
open={openPanes.includes('logs')} title="Variables"
title="Logs" icon={faSquareRootVariable}
icon={faCodeCommit} />
/> <Logs
<KCLErrors theme={editorTheme}
theme={editorTheme} open={openPanes.includes('logs')}
open={openPanes.includes('kclErrors')} title="Logs"
title="KCL Errors" icon={faCodeCommit}
iconClassNames={{ icon: 'group-open:text-destroy-30' }} />
/> <KCLErrors
</section> theme={editorTheme}
open={openPanes.includes('kclErrors')}
title="KCL Errors"
iconClassNames={{ icon: 'group-open:text-destroy-30' }}
/>
</section>
</div>
</Resizable>
<div className="h-5 z-10 pointer-events-auto"></div>
</div>
</div> </div>
</Resizable> </div>
<Stream className="absolute inset-0 z-0" /> <Stream className="absolute inset-0 z-0" />
{showDebugPanel && ( {showDebugPanel && (
<DebugPanel <DebugPanel
@ -238,6 +214,7 @@ export function App() {
open={openPanes.includes('debug')} open={openPanes.includes('debug')}
/> />
)} )}
{/* <CamToggle /> */}
</div> </div>
) )
} }

View File

@ -14,10 +14,7 @@ import {
import { useEffect } from 'react' import { useEffect } from 'react'
import { ErrorPage } from './components/ErrorPage' import { ErrorPage } from './components/ErrorPage'
import { Settings } from './routes/Settings' import { Settings } from './routes/Settings'
import Onboarding, { import Onboarding, { onboardingRoutes } from './routes/Onboarding'
onboardingRoutes,
onboardingPaths,
} from './routes/Onboarding'
import SignIn from './routes/SignIn' import SignIn from './routes/SignIn'
import { Auth } from './Auth' import { Auth } from './Auth'
import { isTauri } from './lib/isTauri' import { isTauri } from './lib/isTauri'
@ -29,7 +26,7 @@ import {
isProjectDirectory, isProjectDirectory,
PROJECT_ENTRYPOINT, PROJECT_ENTRYPOINT,
} from './lib/tauriFS' } from './lib/tauriFS'
import { metadata, type Metadata } from 'tauri-plugin-fs-extra-api' import { metadata } from 'tauri-plugin-fs-extra-api'
import DownloadAppBanner from './components/DownloadAppBanner' import DownloadAppBanner from './components/DownloadAppBanner'
import { WasmErrBanner } from './components/WasmErrBanner' import { WasmErrBanner } from './components/WasmErrBanner'
import { GlobalStateProvider } from './components/GlobalStateProvider' import { GlobalStateProvider } from './components/GlobalStateProvider'
@ -42,9 +39,12 @@ import CommandBarProvider from 'components/CommandBar/CommandBar'
import { TEST, VITE_KC_SENTRY_DSN } from './env' import { TEST, VITE_KC_SENTRY_DSN } from './env'
import * as Sentry from '@sentry/react' import * as Sentry from '@sentry/react'
import ModelingMachineProvider from 'components/ModelingMachineProvider' import ModelingMachineProvider from 'components/ModelingMachineProvider'
import { KclContextProvider, kclManager } from 'lang/KclSinglton' import { KclContextProvider, kclManager } from 'lang/KclSingleton'
import FileMachineProvider from 'components/FileMachineProvider' import FileMachineProvider from 'components/FileMachineProvider'
import { sep } from '@tauri-apps/api/path' import { sep } from '@tauri-apps/api/path'
import { paths } from 'lib/paths'
import { IndexLoaderData, HomeLoaderData } from 'lib/types'
import { fileSystemManager } from 'lang/std/fileSystemManager'
if (VITE_KC_SENTRY_DSN && !TEST) { if (VITE_KC_SENTRY_DSN && !TEST) {
Sentry.init({ Sentry.init({
@ -78,43 +78,8 @@ if (VITE_KC_SENTRY_DSN && !TEST) {
}) })
} }
const prependRoutes =
(routesObject: Record<string, string>) => (prepend: string) => {
return Object.fromEntries(
Object.entries(routesObject).map(([constName, path]) => [
constName,
prepend + path,
])
)
}
export const paths = {
INDEX: '/',
HOME: '/home',
FILE: '/file',
SETTINGS: '/settings',
SIGN_IN: '/signin',
ONBOARDING: prependRoutes(onboardingPaths)(
'/onboarding'
) as typeof onboardingPaths,
}
export const BROWSER_FILE_NAME = 'new' export const BROWSER_FILE_NAME = 'new'
export type IndexLoaderData = {
code: string | null
project?: ProjectWithEntryPointMetadata
file?: FileEntry
}
export type ProjectWithEntryPointMetadata = FileEntry & {
entrypointMetadata: Metadata
}
export type HomeLoaderData = {
projects: ProjectWithEntryPointMetadata[]
newDefaultDirectory?: string
}
type CreateBrowserRouterArg = Parameters<typeof createBrowserRouter>[0] type CreateBrowserRouterArg = Parameters<typeof createBrowserRouter>[0]
const addGlobalContextToElements = ( const addGlobalContextToElements = (
@ -146,18 +111,18 @@ const router = createBrowserRouter(
{ {
path: paths.FILE + '/:id', path: paths.FILE + '/:id',
element: ( element: (
<Auth> <KclContextProvider>
<FileMachineProvider> <Auth>
<KclContextProvider> <FileMachineProvider>
<ModelingMachineProvider> <ModelingMachineProvider>
<Outlet /> <Outlet />
<App /> <App />
</ModelingMachineProvider> </ModelingMachineProvider>
<WasmErrBanner /> <WasmErrBanner />
</KclContextProvider> </FileMachineProvider>
</FileMachineProvider> {!isTauri() && import.meta.env.PROD && <DownloadAppBanner />}
{!isTauri() && import.meta.env.PROD && <DownloadAppBanner />} </Auth>
</Auth> </KclContextProvider>
), ),
id: paths.FILE, id: paths.FILE,
loader: async ({ loader: async ({
@ -210,6 +175,10 @@ const router = createBrowserRouter(
const children = await readDir(projectPath, { recursive: true }) const children = await readDir(projectPath, { recursive: true })
kclManager.setCodeAndExecute(code, false) kclManager.setCodeAndExecute(code, false)
// Set the file system manager to the project path
// So that WASM gets an updated path for operations
fileSystemManager.dir = projectPath
return { return {
code, code,
project: { project: {
@ -249,7 +218,7 @@ const router = createBrowserRouter(
<Home /> <Home />
</Auth> </Auth>
), ),
loader: async () => { loader: async (): Promise<HomeLoaderData | Response> => {
if (!isTauri()) { if (!isTauri()) {
return redirect(paths.FILE + '/' + BROWSER_FILE_NAME) return redirect(paths.FILE + '/' + BROWSER_FILE_NAME)
} }

View File

@ -5,6 +5,8 @@ import { useModelingContext } from 'hooks/useModelingContext'
import { useCommandsContext } from 'hooks/useCommandsContext' import { useCommandsContext } from 'hooks/useCommandsContext'
import { ActionButton } from 'components/ActionButton' import { ActionButton } from 'components/ActionButton'
import usePlatform from 'hooks/usePlatform' import usePlatform from 'hooks/usePlatform'
import { isSingleCursorInPipe } from 'lang/queryAst'
import { kclManager } from 'lang/KclSingleton'
export const Toolbar = () => { export const Toolbar = () => {
const platform = usePlatform() const platform = usePlatform()
@ -13,14 +15,15 @@ export const Toolbar = () => {
const toolbarButtonsRef = useRef<HTMLUListElement>(null) const toolbarButtonsRef = useRef<HTMLUListElement>(null)
const bgClassName = const bgClassName =
'group-enabled:group-hover:bg-energy-10 group-pressed:bg-energy-10 dark:group-enabled:group-hover:bg-chalkboard-80 dark:group-pressed:bg-chalkboard-80' 'group-enabled:group-hover:bg-energy-10 group-pressed:bg-energy-10 dark:group-enabled:group-hover:bg-chalkboard-80 dark:group-pressed:bg-chalkboard-80'
const pathId = useMemo( const pathId = useMemo(() => {
() => if (!isSingleCursorInPipe(context.selectionRanges, kclManager.ast)) {
isCursorInSketchCommandRange( return false
engineCommandManager.artifactMap, }
context.selectionRanges return isCursorInSketchCommandRange(
), engineCommandManager.artifactMap,
[engineCommandManager.artifactMap, context.selectionRanges] context.selectionRanges
) )
}, [engineCommandManager.artifactMap, context.selectionRanges])
function handleToolbarButtonsWheelEvent(ev: WheelEvent<HTMLSpanElement>) { function handleToolbarButtonsWheelEvent(ev: WheelEvent<HTMLSpanElement>) {
const span = toolbarButtonsRef.current const span = toolbarButtonsRef.current
@ -50,7 +53,9 @@ export const Toolbar = () => {
<li className="contents"> <li className="contents">
<ActionButton <ActionButton
Element="button" Element="button"
onClick={() => send({ type: 'Enter sketch' })} onClick={() =>
send({ type: 'Enter sketch', data: { forceNewSketch: true } })
}
icon={{ icon={{
icon: 'sketch', icon: 'sketch',
bgClassName, bgClassName,
@ -89,44 +94,48 @@ export const Toolbar = () => {
</li> </li>
)} )}
{state.matches('Sketch') && !state.matches('idle') && ( {state.matches('Sketch') && !state.matches('idle') && (
<li className="contents"> <>
<ActionButton <li className="contents" key="line-button">
Element="button" <ActionButton
onClick={() => Element="button"
state.matches('Sketch.Line Tool') onClick={() =>
? send('CancelSketch') state?.matches('Sketch.Line tool')
: send('Equip tool') ? send('CancelSketch')
} : send('Equip Line tool')
aria-pressed={state.matches('Sketch.Line Tool')} }
className="pressed:bg-energy-10/20 dark:pressed:bg-energy-80" aria-pressed={state?.matches('Sketch.Line tool')}
icon={{ className="pressed:bg-energy-10/20 dark:pressed:bg-energy-80"
icon: 'line', icon={{
bgClassName, icon: 'line',
}} bgClassName,
> }}
Line >
</ActionButton> Line
</li> </ActionButton>
)} </li>
{state.matches('Sketch') && ( <li className="contents" key="tangential-arc-button">
<li className="contents"> <ActionButton
<ActionButton Element="button"
Element="button" onClick={() =>
onClick={() => state.matches('Sketch.Tangential arc to')
state.matches('Sketch.Move Tool') ? send('CancelSketch')
? send('CancelSketch') : send('Equip tangential arc to')
: send('Equip move tool') }
} aria-pressed={state.matches('Sketch.Tangential arc to')}
aria-pressed={state.matches('Sketch.Move Tool')} className="pressed:bg-energy-10/20 dark:pressed:bg-energy-80"
className="pressed:bg-energy-10/20 dark:pressed:bg-energy-80" icon={{
icon={{ icon: 'arc',
icon: 'move', bgClassName,
bgClassName, }}
}} disabled={
> !state.can('Equip tangential arc to') &&
Move !state.matches('Sketch.Tangential arc to')
</ActionButton> }
</li> >
Tangential Arc
</ActionButton>
</li>
</>
)} )}
{state.matches('Sketch.SketchIdle') && {state.matches('Sketch.SketchIdle') &&
state.nextEvents state.nextEvents
@ -151,7 +160,7 @@ export const Toolbar = () => {
return 0 return 0
}) })
.map((eventName) => ( .map((eventName) => (
<li className="contents"> <li className="contents" key={eventName}>
<ActionButton <ActionButton
Element="button" Element="button"
className="text-sm" className="text-sm"

View File

@ -0,0 +1,252 @@
import { useRef, useEffect, useState } from 'react'
import { useModelingContext } from 'hooks/useModelingContext'
import { cameraMouseDragGuards } from 'lib/cameraControls'
import { useGlobalStateContext } from 'hooks/useGlobalStateContext'
import { useStore } from 'useStore'
import {
DEBUG_SHOW_BOTH_SCENES,
ReactCameraProperties,
sceneInfra,
} from './sceneInfra'
import { throttle } from 'lib/utils'
function useShouldHideScene(): { hideClient: boolean; hideServer: boolean } {
const [isCamMoving, setIsCamMoving] = useState(false)
const [isTween, setIsTween] = useState(false)
const { state } = useModelingContext()
useEffect(() => {
sceneInfra.setIsCamMovingCallback((isMoving, isTween) => {
setIsCamMoving(isMoving)
setIsTween(isTween)
})
}, [])
if (DEBUG_SHOW_BOTH_SCENES || !isCamMoving)
return { hideClient: false, hideServer: false }
let hideServer = state.matches('Sketch')
if (isTween) {
hideServer = false
}
return { hideClient: !hideServer, hideServer }
}
export const ClientSideScene = ({
cameraControls,
}: {
cameraControls: ReturnType<
typeof useGlobalStateContext
>['settings']['context']['cameraControls']
}) => {
const canvasRef = useRef<HTMLDivElement>(null)
const { state, send } = useModelingContext()
const { hideClient, hideServer } = useShouldHideScene()
const { setHighlightRange } = useStore((s) => ({
setHighlightRange: s.setHighlightRange,
highlightRange: s.highlightRange,
}))
// Listen for changes to the camera controls setting
// and update the client-side scene's controls accordingly.
useEffect(() => {
sceneInfra.setInteractionGuards(cameraMouseDragGuards[cameraControls])
}, [cameraControls])
useEffect(() => {
sceneInfra.updateOtherSelectionColors(
state?.context?.selectionRanges?.otherSelections || []
)
}, [state?.context?.selectionRanges?.otherSelections])
useEffect(() => {
if (!canvasRef.current) return
const canvas = canvasRef.current
canvas.appendChild(sceneInfra.renderer.domElement)
sceneInfra.animate()
sceneInfra.setHighlightCallback(setHighlightRange)
canvas.addEventListener('mousemove', sceneInfra.onMouseMove, false)
canvas.addEventListener('mousedown', sceneInfra.onMouseDown, false)
canvas.addEventListener('mouseup', sceneInfra.onMouseUp, false)
sceneInfra.setSend(send)
return () => {
canvas?.removeEventListener('mousemove', sceneInfra.onMouseMove)
canvas?.removeEventListener('mousedown', sceneInfra.onMouseDown)
canvas?.removeEventListener('mouseup', sceneInfra.onMouseUp)
}
}, [])
return (
<div
ref={canvasRef}
className={`absolute inset-0 h-full w-full transition-all duration-300 ${
hideClient ? 'opacity-0' : 'opacity-100'
} ${hideServer ? 'bg-black' : ''} ${
!hideClient && !hideServer && state.matches('Sketch')
? 'bg-black/80'
: ''
}`}
></div>
)
}
const throttled = throttle((a: ReactCameraProperties) => {
if (a.type === 'perspective' && a.fov) {
sceneInfra.dollyZoom(a.fov)
}
}, 1000 / 15)
export const CamDebugSettings = () => {
const [camSettings, setCamSettings] = useState<ReactCameraProperties>({
type: 'perspective',
fov: 12,
position: [0, 0, 0],
quaternion: [0, 0, 0, 1],
})
const [fov, setFov] = useState(12)
useEffect(() => {
sceneInfra.setReactCameraPropertiesCallback(setCamSettings)
}, [sceneInfra])
useEffect(() => {
if (camSettings.type === 'perspective' && camSettings.fov) {
setFov(camSettings.fov)
}
}, [(camSettings as any)?.fov])
return (
<div>
<h3>cam settings</h3>
perspective cam
<input
type="checkbox"
checked={camSettings.type === 'perspective'}
onChange={(e) => {
if (camSettings.type === 'perspective') {
sceneInfra.useOrthographicCamera()
} else {
sceneInfra.usePerspectiveCamera()
}
}}
/>
{camSettings.type === 'perspective' && (
<input
type="range"
min="4"
max="90"
step={0.5}
value={fov}
onChange={(e) => {
setFov(parseFloat(e.target.value))
throttled({
...camSettings,
fov: parseFloat(e.target.value),
})
}}
className="w-full cursor-pointer pointer-events-auto"
/>
)}
{camSettings.type === 'perspective' && (
<div>
<span>fov</span>
<input
type="number"
value={camSettings.fov}
className="text-black w-16"
onChange={(e) => {
sceneInfra.setCam({
...camSettings,
fov: parseFloat(e.target.value),
})
}}
/>
</div>
)}
{camSettings.type === 'orthographic' && (
<>
<div>
<span>fov</span>
<input
type="number"
value={camSettings.zoom}
className="text-black w-16"
onChange={(e) => {
sceneInfra.setCam({
...camSettings,
zoom: parseFloat(e.target.value),
})
}}
/>
</div>
</>
)}
<div>
Position
<ul className="flex">
<li>
<span className="pl-2 pr-1">x:</span>
<input
type="number"
step={5}
data-testid="cam-x-position"
value={camSettings.position[0]}
className="text-black w-16"
onChange={(e) => {
sceneInfra.setCam({
...camSettings,
position: [
parseFloat(e.target.value),
camSettings.position[1],
camSettings.position[2],
],
})
}}
/>
</li>
<li>
<span className="pl-2 pr-1">y:</span>
<input
type="number"
step={5}
data-testid="cam-y-position"
value={camSettings.position[1]}
className="text-black w-16"
onChange={(e) => {
sceneInfra.setCam({
...camSettings,
position: [
camSettings.position[0],
parseFloat(e.target.value),
camSettings.position[2],
],
})
}}
/>
</li>
<li>
<span className="pl-2 pr-1">z:</span>
<input
type="number"
step={5}
data-testid="cam-z-position"
value={camSettings.position[2]}
className="text-black w-16"
onChange={(e) => {
sceneInfra.setCam({
...camSettings,
position: [
camSettings.position[0],
camSettings.position[1],
parseFloat(e.target.value),
],
})
}}
/>
</li>
</ul>
</div>
</div>
)
}

View File

@ -0,0 +1,33 @@
import {
GridHelper,
LineBasicMaterial,
OrthographicCamera,
PerspectiveCamera,
Group,
Mesh,
} from 'three'
export function createGridHelper({
size,
divisions,
}: {
size: number
divisions: number
}) {
const gridHelperMaterial = new LineBasicMaterial({
color: 0xaaaaaa,
transparent: true,
opacity: 0.5,
depthTest: false,
})
const gridHelper = new GridHelper(size, divisions, 0x0000ff, 0xffffff)
gridHelper.material = gridHelperMaterial
gridHelper.rotation.x = Math.PI / 2
return gridHelper
}
export const orthoScale = (cam: OrthographicCamera | PerspectiveCamera) =>
0.55 / cam.zoom
export const perspScale = (cam: PerspectiveCamera, group: Group | Mesh) =>
(group.position.distanceTo(cam.position) * cam.fov) / 4000

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,28 @@
import { Quaternion } from 'three'
import { isQuaternionVertical } from './sceneInfra'
describe('isQuaternionVertical', () => {
it('should identify vertical quaternions', () => {
const verticalQuaternions = [
new Quaternion(1, 0, 0, 0).normalize(), // bottom
new Quaternion(-0.7, 0.7, 0, 0).normalize(), // bottom 2
new Quaternion(0, 1, 0, 0).normalize(), // bottom 3
new Quaternion(0, 0, 0, 1).normalize(), // look from top
]
verticalQuaternions.forEach((quaternion) => {
expect(isQuaternionVertical(quaternion)).toBe(true)
})
})
it('should identify non-vertical quaternions', () => {
const nonVerticalQuaternions = [
new Quaternion(0.7, 0, 0, 0.7).normalize(), // front
new Quaternion(0, 0.7, 0.7, 0).normalize(), // back
new Quaternion(-0.5, 0.5, 0.5, -0.5).normalize(), // left side
new Quaternion(0.5, 0.5, 0.5, 0.5).normalize(), // right side
]
nonVerticalQuaternions.forEach((quaternion) => {
expect(isQuaternionVertical(quaternion)).toBe(false)
})
})
})

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,358 @@
import { Coords2d } from 'lang/std/sketch'
import {
BufferGeometry,
CatmullRomCurve3,
ConeGeometry,
CurvePath,
EllipseCurve,
ExtrudeGeometry,
Group,
LineCurve3,
Mesh,
MeshBasicMaterial,
NormalBufferAttributes,
Shape,
SphereGeometry,
Vector2,
Vector3,
} from 'three'
import { mergeGeometries } from 'three/examples/jsm/utils/BufferGeometryUtils.js'
import { PathToNode, SketchGroup, getTangentialArcToInfo } from 'lang/wasm'
import {
STRAIGHT_SEGMENT,
STRAIGHT_SEGMENT_BODY,
STRAIGHT_SEGMENT_DASH,
TANGENTIAL_ARC_TO_SEGMENT,
TANGENTIAL_ARC_TO_SEGMENT_BODY,
TANGENTIAL_ARC_TO__SEGMENT_DASH,
} from './sceneEntities'
import { getTangentPointFromPreviousArc } from 'lib/utils2d'
import { ARROWHEAD } from './sceneInfra'
export function straightSegment({
from,
to,
id,
pathToNode,
isDraftSegment,
scale = 1,
}: {
from: Coords2d
to: Coords2d
id: string
pathToNode: PathToNode
isDraftSegment?: boolean
scale?: number
}): Group {
const group = new Group()
const shape = new Shape()
shape.moveTo(0, -0.08 * scale)
shape.lineTo(0, 0.08 * scale) // The width of the line
let geometry
if (isDraftSegment) {
geometry = dashedStraight(from, to, shape, scale)
} else {
const line = new LineCurve3(
new Vector3(from[0], from[1], 0),
new Vector3(to[0], to[1], 0)
)
geometry = new ExtrudeGeometry(shape, {
steps: 2,
bevelEnabled: false,
extrudePath: line,
})
}
const body = new MeshBasicMaterial({ color: 0xffffff })
const mesh = new Mesh(geometry, body)
mesh.userData.type = isDraftSegment
? STRAIGHT_SEGMENT_DASH
: STRAIGHT_SEGMENT_BODY
mesh.name = STRAIGHT_SEGMENT_BODY
group.userData = {
type: STRAIGHT_SEGMENT,
id,
from,
to,
pathToNode,
isSelected: false,
}
const arrowGroup = createArrowhead(scale)
arrowGroup.position.set(to[0], to[1], 0)
const dir = new Vector3()
.subVectors(new Vector3(to[0], to[1], 0), new Vector3(from[0], from[1], 0))
.normalize()
arrowGroup.quaternion.setFromUnitVectors(new Vector3(0, 1, 0), dir)
group.add(mesh, arrowGroup)
return group
}
function createArrowhead(scale = 1): Group {
const arrowMaterial = new MeshBasicMaterial({ color: 0xffffff })
const arrowheadMesh = new Mesh(new ConeGeometry(0.31, 1.5, 12), arrowMaterial)
arrowheadMesh.position.set(0, -0.6, 0)
const sphereMesh = new Mesh(new SphereGeometry(0.27, 12, 12), arrowMaterial)
const arrowGroup = new Group()
arrowGroup.userData.type = ARROWHEAD
arrowGroup.name = ARROWHEAD
arrowGroup.add(arrowheadMesh, sphereMesh)
arrowGroup.lookAt(new Vector3(0, 1, 0))
arrowGroup.scale.set(scale, scale, scale)
return arrowGroup
}
export function tangentialArcToSegment({
prevSegment,
from,
to,
id,
pathToNode,
isDraftSegment,
scale = 1,
}: {
prevSegment: SketchGroup['value'][number]
from: Coords2d
to: Coords2d
id: string
pathToNode: PathToNode
isDraftSegment?: boolean
scale?: number
}): Group {
const group = new Group()
const previousPoint =
prevSegment?.type === 'TangentialArcTo'
? getTangentPointFromPreviousArc(
prevSegment.center,
prevSegment.ccw,
prevSegment.to
)
: prevSegment.from
const { center, radius, startAngle, endAngle, ccw } = getTangentialArcToInfo({
arcStartPoint: from,
arcEndPoint: to,
tanPreviousPoint: previousPoint,
obtuse: true,
})
const geometry = createArcGeometry({
center,
radius,
startAngle,
endAngle,
ccw,
isDashed: isDraftSegment,
scale,
})
const body = new MeshBasicMaterial({ color: 0xffffff })
const mesh = new Mesh(geometry, body)
mesh.userData.type = isDraftSegment
? TANGENTIAL_ARC_TO__SEGMENT_DASH
: TANGENTIAL_ARC_TO_SEGMENT_BODY
group.userData = {
type: TANGENTIAL_ARC_TO_SEGMENT,
id,
from,
to,
prevSegment,
pathToNode,
isSelected: false,
}
const arrowGroup = createArrowhead(scale)
arrowGroup.position.set(to[0], to[1], 0)
const arrowheadAngle = endAngle + (Math.PI / 2) * (ccw ? 1 : -1)
arrowGroup.quaternion.setFromUnitVectors(
new Vector3(0, 1, 0),
new Vector3(Math.cos(arrowheadAngle), Math.sin(arrowheadAngle), 0)
)
group.add(mesh, arrowGroup)
return group
}
export function createArcGeometry({
center,
radius,
startAngle,
endAngle,
ccw,
isDashed = false,
scale = 1,
}: {
center: Coords2d
radius: number
startAngle: number
endAngle: number
ccw: boolean
isDashed?: boolean
scale?: number
}): BufferGeometry {
const dashSize = 1.2 * scale
const gapSize = 1.2 * scale
const arcStart = new EllipseCurve(
center[0],
center[1],
radius,
radius,
startAngle,
endAngle,
!ccw,
0
)
const arcEnd = new EllipseCurve(
center[0],
center[1],
radius,
radius,
endAngle,
startAngle,
ccw,
0
)
const shape = new Shape()
shape.moveTo(0, -0.08 * scale)
shape.lineTo(0, 0.08 * scale) // The width of the line
if (!isDashed) {
const points = arcStart.getPoints(50)
const path = new CurvePath<Vector3>()
path.add(new CatmullRomCurve3(points.map((p) => new Vector3(p.x, p.y, 0))))
return new ExtrudeGeometry(shape, {
steps: 100,
bevelEnabled: false,
extrudePath: path,
})
}
const length = arcStart.getLength()
const totalDashes = length / (dashSize + gapSize) // rounding makes the dashes jittery since the new dash is suddenly appears instead of growing into place
const dashesAtEachEnd = Math.min(100, totalDashes / 2) // Assuming we want 50 dashes total, 25 at each end
const dashGeometries = []
// Function to create a dash at a specific t value (0 to 1 along the curve)
const createDashAt = (t: number, curve: EllipseCurve) => {
const startVec = curve.getPoint(t)
const endVec = curve.getPoint(Math.min(0.5, t + dashSize / length))
const midVec = curve.getPoint(Math.min(0.5, t + dashSize / length / 2))
const dashCurve = new CurvePath<Vector3>()
dashCurve.add(
new CatmullRomCurve3([
new Vector3(startVec.x, startVec.y, 0),
new Vector3(midVec.x, midVec.y, 0),
new Vector3(endVec.x, endVec.y, 0),
])
)
return new ExtrudeGeometry(shape, {
steps: 3,
bevelEnabled: false,
extrudePath: dashCurve,
})
}
// Create dashes at the start of the arc
for (let i = 0; i < dashesAtEachEnd; i++) {
const t = i / totalDashes
dashGeometries.push(createDashAt(t, arcStart))
dashGeometries.push(createDashAt(t, arcEnd))
}
// fill in the remaining arc
const remainingArcLength = length - dashesAtEachEnd * 2 * (dashSize + gapSize)
if (remainingArcLength > 0) {
const remainingArcStartT = dashesAtEachEnd / totalDashes
const remainingArcEndT = 1 - remainingArcStartT
const centerVec = new Vector2(center[0], center[1])
const remainingArcStartVec = arcStart.getPoint(remainingArcStartT)
const remainingArcEndVec = arcStart.getPoint(remainingArcEndT)
const remainingArcCurve = new EllipseCurve(
arcStart.aX,
arcStart.aY,
arcStart.xRadius,
arcStart.yRadius,
new Vector2().subVectors(centerVec, remainingArcStartVec).angle() +
Math.PI,
new Vector2().subVectors(centerVec, remainingArcEndVec).angle() + Math.PI,
!ccw
)
const remainingArcPoints = remainingArcCurve.getPoints(50)
const remainingArcPath = new CurvePath<Vector3>()
remainingArcPath.add(
new CatmullRomCurve3(
remainingArcPoints.map((p) => new Vector3(p.x, p.y, 0))
)
)
const remainingArcGeometry = new ExtrudeGeometry(shape, {
steps: 50,
bevelEnabled: false,
extrudePath: remainingArcPath,
})
dashGeometries.push(remainingArcGeometry)
}
const geo = dashGeometries.length
? mergeGeometries(dashGeometries)
: new BufferGeometry()
geo.userData.type = 'dashed'
return geo
}
export function dashedStraight(
from: Coords2d,
to: Coords2d,
shape: Shape,
scale = 1
): BufferGeometry<NormalBufferAttributes> {
const dashSize = 1.2 * scale
const gapSize = 1.2 * scale // todo: gabSize is not respected
const dashLine = new LineCurve3(
new Vector3(from[0], from[1], 0),
new Vector3(to[0], to[1], 0)
)
const length = dashLine.getLength()
const numberOfPoints = (length / (dashSize + gapSize)) * 2
const startOfLine = new Vector3(from[0], from[1], 0)
const endOfLine = new Vector3(to[0], to[1], 0)
const dashGeometries = []
const dashComponent = (xOrY: number, pointIndex: number) =>
((to[xOrY] - from[xOrY]) / numberOfPoints) * pointIndex + from[xOrY]
for (let i = 0; i < numberOfPoints; i += 2) {
const dashStart = new Vector3(dashComponent(0, i), dashComponent(1, i), 0)
let dashEnd = new Vector3(
dashComponent(0, i + 1),
dashComponent(1, i + 1),
0
)
if (startOfLine.distanceTo(dashEnd) > startOfLine.distanceTo(endOfLine))
dashEnd = endOfLine
if (dashEnd) {
const dashCurve = new LineCurve3(dashStart, dashEnd)
const dashGeometry = new ExtrudeGeometry(shape, {
steps: 1,
bevelEnabled: false,
extrudePath: dashCurve,
})
dashGeometries.push(dashGeometry)
}
}
const geo = dashGeometries.length
? mergeGeometries(dashGeometries)
: new BufferGeometry()
geo.userData.type = 'dashed'
return geo
}

View File

@ -1,6 +1,6 @@
import { ActionIcon, ActionIconProps } from './ActionIcon' import { ActionIcon, ActionIconProps } from './ActionIcon'
import React from 'react' import React from 'react'
import { paths } from '../Router' import { paths } from 'lib/paths'
import { Link } from 'react-router-dom' import { Link } from 'react-router-dom'
import type { LinkProps } from 'react-router-dom' import type { LinkProps } from 'react-router-dom'

View File

@ -1,6 +1,6 @@
import { Toolbar } from '../Toolbar' import { Toolbar } from '../Toolbar'
import UserSidebarMenu from './UserSidebarMenu' import UserSidebarMenu from './UserSidebarMenu'
import { IndexLoaderData } from '../Router' import { type IndexLoaderData } from 'lib/types'
import ProjectSidebarMenu from './ProjectSidebarMenu' import ProjectSidebarMenu from './ProjectSidebarMenu'
import { useGlobalStateContext } from 'hooks/useGlobalStateContext' import { useGlobalStateContext } from 'hooks/useGlobalStateContext'
import styles from './AppHeader.module.css' import styles from './AppHeader.module.css'

View File

@ -1,5 +1,5 @@
import { useModelingContext } from 'hooks/useModelingContext' import { useModelingContext } from 'hooks/useModelingContext'
import { kclManager } from 'lang/KclSinglton' import { kclManager } from 'lang/KclSingleton'
import { getNodeFromPath, getNodePathFromSourceRange } from 'lang/queryAst' import { getNodeFromPath, getNodePathFromSourceRange } from 'lang/queryAst'
import { useEffect, useRef, useState } from 'react' import { useEffect, useRef, useState } from 'react'
import { useStore } from 'useStore' import { useStore } from 'useStore'

View File

@ -8,7 +8,7 @@ import {
} from '../lang/modifyAst' } from '../lang/modifyAst'
import { findAllPreviousVariables, PrevVariable } from '../lang/queryAst' import { findAllPreviousVariables, PrevVariable } from '../lang/queryAst'
import { engineCommandManager } from '../lang/std/engineConnection' import { engineCommandManager } from '../lang/std/engineConnection'
import { kclManager, useKclContext } from 'lang/KclSinglton' import { kclManager, useKclContext } from 'lang/KclSingleton'
import { useModelingContext } from 'hooks/useModelingContext' import { useModelingContext } from 'hooks/useModelingContext'
import { executeAst } from 'useStore' import { executeAst } from 'useStore'
@ -148,7 +148,6 @@ export function useCalc({
executeAst({ executeAst({
ast, ast,
engineCommandManager, engineCommandManager,
defaultPlanes: kclManager.defaultPlanes,
useFakeExecutor: true, useFakeExecutor: true,
programMemoryOverride: JSON.parse( programMemoryOverride: JSON.parse(
JSON.stringify(kclManager.programMemory) JSON.stringify(kclManager.programMemory)

View File

@ -0,0 +1,75 @@
import { useState, useEffect } from 'react'
import { sceneInfra } from '../clientSideScene/sceneInfra'
import { engineCommandManager } from 'lang/std/engineConnection'
import { throttle, isReducedMotion } from 'lib/utils'
const updateDollyZoom = throttle(
(newFov: number) => sceneInfra.dollyZoom(newFov),
1000 / 15
)
export const CamToggle = () => {
const [isPerspective, setIsPerspective] = useState(true)
const [fov, setFov] = useState(40)
const [enableRotate, setEnableRotate] = useState(true)
useEffect(() => {
engineCommandManager.waitForReady.then(async () => {
sceneInfra.dollyZoom(fov)
})
}, [])
const toggleCamera = () => {
if (isPerspective) {
isReducedMotion()
? sceneInfra.useOrthographicCamera()
: sceneInfra.animateToOrthographic()
} else {
isReducedMotion()
? sceneInfra.usePerspectiveCamera()
: sceneInfra.animateToPerspective()
}
setIsPerspective(!isPerspective)
}
const handleFovChange = (newFov: number) => {
setFov(newFov)
updateDollyZoom(newFov)
}
return (
<div className="absolute right-14 bottom-3">
{isPerspective && (
<div className="">
<input
type="range"
min="4"
max="90"
step={0.5}
value={fov}
onChange={(e) => handleFovChange(Number(e.target.value))}
className="w-full cursor-pointer pointer-events-auto"
/>
</div>
)}
<button onClick={toggleCamera} className="">
{isPerspective
? 'Switch to Orthographic Camera'
: 'Switch to Perspective Camera'}
</button>
<button
onClick={() => {
if (enableRotate) {
sceneInfra.controls.enableRotate = false
} else {
sceneInfra.controls.enableRotate = true
}
setEnableRotate(!enableRotate)
}}
className=""
>
{enableRotate ? 'Disable Rotation' : 'Enable Rotation'}
</button>
</div>
)
}

View File

@ -9,7 +9,7 @@ import styles from './CodeMenu.module.css'
import { useConvertToVariable } from 'hooks/useToolbarGuards' import { useConvertToVariable } from 'hooks/useToolbarGuards'
import { editorShortcutMeta } from './TextEditor' import { editorShortcutMeta } from './TextEditor'
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { kclManager } from 'lang/KclSinglton' import { kclManager } from 'lang/KclSingleton'
export const CodeMenu = ({ children }: PropsWithChildren) => { export const CodeMenu = ({ children }: PropsWithChildren) => {
const { enable: convertToVarEnabled, handleClick: handleConvertToVarClick } = const { enable: convertToVarEnabled, handleClick: handleConvertToVarClick } =
@ -77,6 +77,24 @@ export const CodeMenu = ({ children }: PropsWithChildren) => {
</small> </small>
</a> </a>
</Menu.Item> </Menu.Item>
<Menu.Item>
<a
className={styles.button}
href="https://github.com/KittyCAD/kcl-samples"
target="_blank"
rel="noopener noreferrer"
>
<span>KCL samples</span>
<small>
On GitHub
<FontAwesomeIcon
icon={faArrowUpRightFromSquare}
className="ml-1 align-text-top"
width={12}
/>
</small>
</a>
</Menu.Item>
</Menu.Items> </Menu.Items>
</div> </div>
</Menu> </Menu>

View File

@ -27,6 +27,7 @@ export const CommandBarProvider = ({
}) => { }) => {
const { pathname } = useLocation() const { pathname } = useLocation()
const [commandBarState, commandBarSend] = useMachine(commandBarMachine, { const [commandBarState, commandBarSend] = useMachine(commandBarMachine, {
devTools: true,
guards: { guards: {
'Arguments are ready': (context, _) => { 'Arguments are ready': (context, _) => {
return context.selectedCommand?.args return context.selectedCommand?.args

View File

@ -1,6 +1,6 @@
import { useCommandsContext } from 'hooks/useCommandsContext' import { useCommandsContext } from 'hooks/useCommandsContext'
import { CustomIcon } from '../CustomIcon' import { CustomIcon } from '../CustomIcon'
import React, { useState } from 'react' import React, { ReactNode, useState } from 'react'
import { ActionButton } from '../ActionButton' import { ActionButton } from '../ActionButton'
import { Selections, getSelectionTypeDisplayText } from 'lib/selections' import { Selections, getSelectionTypeDisplayText } from 'lib/selections'
import { useHotkeys } from 'react-hotkeys-hook' import { useHotkeys } from 'react-hotkeys-hook'
@ -99,15 +99,7 @@ function CommandBarHeader({ children }: React.PropsWithChildren<{}>) {
) : typeof argumentsToSubmit[argName] === 'object' ? ( ) : typeof argumentsToSubmit[argName] === 'object' ? (
JSON.stringify(argumentsToSubmit[argName]) JSON.stringify(argumentsToSubmit[argName])
) : ( ) : (
argumentsToSubmit[argName] <em>{argumentsToSubmit[argName] as ReactNode}</em>
)
) : arg.payload ? (
arg.inputType === 'selection' ? (
getSelectionTypeDisplayText(arg.payload as Selections)
) : typeof arg.payload === 'object' ? (
JSON.stringify(arg.payload)
) : (
arg.payload
) )
) : ( ) : (
<em>{argName}</em> <em>{argName}</em>

View File

@ -1,6 +1,6 @@
import { useSelector } from '@xstate/react' import { useSelector } from '@xstate/react'
import { useCommandsContext } from 'hooks/useCommandsContext' import { useCommandsContext } from 'hooks/useCommandsContext'
import { useKclContext } from 'lang/KclSinglton' import { useKclContext } from 'lang/KclSingleton'
import { CommandArgument } from 'lib/commandTypes' import { CommandArgument } from 'lib/commandTypes'
import { import {
ResolvedSelectionType, ResolvedSelectionType,
@ -27,7 +27,7 @@ function CommandBarSelectionInput({
}) { }) {
const { code } = useKclContext() const { code } = useKclContext()
const inputRef = useRef<HTMLInputElement>(null) const inputRef = useRef<HTMLInputElement>(null)
const { commandBarSend } = useCommandsContext() const { commandBarState, commandBarSend } = useCommandsContext()
const [hasSubmitted, setHasSubmitted] = useState(false) const [hasSubmitted, setHasSubmitted] = useState(false)
const selection = useSelector(arg.actor, selectionSelector) const selection = useSelector(arg.actor, selectionSelector)
const [selectionsByType, setSelectionsByType] = useState< const [selectionsByType, setSelectionsByType] = useState<
@ -59,8 +59,16 @@ function CommandBarSelectionInput({
) )
}, [selection]) }, [selection])
// Fast-forward through this arg if it's marked as skippable
// and we have a valid selection already
useEffect(() => { useEffect(() => {
setCanSubmitSelection(canSubmitSelectionArg(selectionsByType, arg)) setCanSubmitSelection(canSubmitSelectionArg(selectionsByType, arg))
const argValue = commandBarState.context.argumentsToSubmit[arg.name]
if (canSubmitSelection && arg.skip && argValue === undefined) {
handleSubmit({
preventDefault: () => {},
} as React.FormEvent<HTMLFormElement>)
}
}, [selectionsByType, arg]) }, [selectionsByType, arg])
function handleChange() { function handleChange() {

View File

@ -48,6 +48,7 @@ function CommandComboBox({
(event.metaKey && event.key === 'k') || (event.metaKey && event.key === 'k') ||
(event.key === 'Backspace' && !event.currentTarget.value) (event.key === 'Backspace' && !event.currentTarget.value)
) { ) {
event.preventDefault()
commandBarSend({ type: 'Close' }) commandBarSend({ type: 'Close' })
} }
}} }}

View File

@ -1,9 +1,12 @@
export type CustomIconName = export type CustomIconName =
| 'arc'
| 'arrowDown' | 'arrowDown'
| 'arrowLeft' | 'arrowLeft'
| 'arrowRight' | 'arrowRight'
| 'arrowUp' | 'arrowUp'
| 'checkmark' | 'checkmark'
| 'clipboardPlus'
| 'clipboardCheckmark'
| 'close' | 'close'
| 'equal' | 'equal'
| 'extrude' | 'extrude'
@ -13,10 +16,14 @@ export type CustomIconName =
| 'folderPlus' | 'folderPlus'
| 'gear' | 'gear'
| 'horizontal' | 'horizontal'
| 'horizontalDash'
| 'line' | 'line'
| 'move' | 'move'
| 'network'
| 'networkCrossedOut'
| 'parallel' | 'parallel'
| 'search' | 'search'
| 'settings'
| 'sketch' | 'sketch'
| 'vertical' | 'vertical'
@ -27,6 +34,22 @@ export const CustomIcon = ({
name: CustomIconName name: CustomIconName
} & React.SVGProps<SVGSVGElement>) => { } & React.SVGProps<SVGSVGElement>) => {
switch (name) { switch (name) {
case 'arc':
return (
<svg
{...props}
viewBox="0 0 20 20"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M10 1.5C8.60217 1.5 7.22591 1.84474 5.99313 2.50367C4.76035 3.1626 3.70911 4.1154 2.93251 5.27765C2.15592 6.43991 1.67794 7.77575 1.54093 9.16685C1.40392 10.558 1.6121 11.9614 2.14703 13.2528C2.68195 14.5442 3.52712 15.6838 4.60766 16.5706C5.6882 17.4574 6.97076 18.064 8.34173 18.3367C9.71271 18.6094 11.1298 18.5398 12.4674 18.134C13.8051 17.7282 15.022 16.9988 16.0104 16.0104C16.3068 15.714 16.5796 15.3974 16.8273 15.0634L16.0241 14.4677C15.8055 14.7624 15.5648 15.0418 15.3033 15.3033C14.4312 16.1754 13.3574 16.819 12.1771 17.1771C10.9969 17.5351 9.74651 17.5965 8.53682 17.3559C7.32714 17.1153 6.19547 16.58 5.24205 15.7976C4.28863 15.0151 3.5429 14.0096 3.07091 12.8701C2.59891 11.7306 2.41522 10.4923 2.53612 9.26487C2.65701 8.03743 3.07875 6.85874 3.76398 5.83322C4.44921 4.8077 5.37678 3.967 6.46453 3.38559C7.55227 2.80418 8.76662 2.5 10 2.5C10.3699 2.5 10.7376 2.52734 11.1005 2.58117L11.2472 1.59199C10.836 1.53099 10.4192 1.5 10 1.5ZM13.2067 3.22008C13.5383 3.37691 13.8593 3.5585 14.1668 3.76398C14.4743 3.96946 14.7649 4.19652 15.0367 4.44286L15.7083 3.70191C15.4002 3.42271 15.0709 3.16538 14.7223 2.93251C14.3738 2.69964 14.0101 2.49384 13.6342 2.31609L13.2067 3.22008ZM16.433 6.14423C16.6216 6.45886 16.7876 6.78818 16.9291 7.12987C17.0706 7.47157 17.1861 7.82181 17.2752 8.17765L18.2453 7.93467C18.1443 7.53138 18.0134 7.13444 17.853 6.74719C17.6926 6.35995 17.5044 5.98672 17.2907 5.63012L16.433 6.14423ZM17.491 10.368C17.473 10.7344 17.428 11.1004 17.3559 11.4632C17.2837 11.8259 17.1852 12.1813 17.0616 12.5267L18.0031 12.8636C18.1432 12.4721 18.2549 12.0694 18.3367 11.6583C18.4184 11.2472 18.4694 10.8323 18.4898 10.4171L17.491 10.368Z"
fill="currentColor"
/>
</svg>
)
case 'arrowDown': case 'arrowDown':
return ( return (
<svg <svg
@ -107,6 +130,38 @@ export const CustomIcon = ({
/> />
</svg> </svg>
) )
case 'clipboardCheckmark':
return (
<svg
{...props}
viewBox="0 0 20 20"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M6.5 3H7L13 3L13.5 3V3.5V4.00001L15.5 4.00002L16 4.00002V4.50002V10.0351C15.6905 9.85609 15.3548 9.71733 15 9.62602V5.00002L13.5 5.00001V6.50001V7.00001L13 7.00001L7 7.00001L6.5 7.00001V6.50001V5.00001L5 5.00001V16H10.8773C11.2024 16.4055 11.6047 16.7463 12.062 17H4.5H4V16.5V4.50001V4.00001L4.5 4.00001L6.5 4.00001V3.5V3ZM15.938 17C15.9588 16.9885 15.9794 16.9768 16 16.9649V17H15.938ZM7.5 4V4.50001V6.00001L12.5 6.00001V4.50001V4L7.5 4ZM13 9H7V8H13V9ZM15.6855 11.5L13.2101 14.8005L12.2071 13.7975L11.5 14.5046L12.9107 15.9153L13.6642 15.8617L16.4855 12.1L15.6855 11.5Z"
fill="currentColor"
/>
</svg>
)
case 'clipboardPlus':
return (
<svg
{...props}
viewBox="0 0 20 20"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M6.5 3H7L13 3L13.5 3V3.5V4.00001L15.5 4.00002L16 4.00002V4.50002V10.0351C15.6905 9.85609 15.3548 9.71733 15 9.62602V5.00002L13.5 5.00001V6.50001V7.00001L13 7.00001L7 7.00001L6.5 7.00001V6.50001V5.00001L5 5.00001V16H10.8773C11.2024 16.4055 11.6047 16.7463 12.062 17H4.5H4V16.5V4.50001V4.00001L4.5 4.00001L6.5 4.00001V3.5V3ZM15.938 17C15.9588 16.9885 15.9794 16.9768 16 16.9649V17H15.938ZM7.5 4V4.50001V6.00001L12.5 6.00001V4.50001V4L7.5 4ZM13 9H7V8H13V9ZM13.5 11V13H11.5V14H13.5V16H14.5V14H16.5V13H14.5V11H13.5Z"
fill="currentColor"
/>
</svg>
)
case 'close': case 'close':
return ( return (
<svg <svg
@ -249,6 +304,22 @@ export const CustomIcon = ({
/> />
</svg> </svg>
) )
case 'horizontalDash':
return (
<svg
{...props}
viewBox="0 0 20 20"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M14 10.5H6V9.5H14V10.5Z"
fill="currentColor"
/>
</svg>
)
case 'line': case 'line':
return ( return (
<svg <svg
@ -281,6 +352,38 @@ export const CustomIcon = ({
/> />
</svg> </svg>
) )
case 'network':
return (
<svg
{...props}
viewBox="0 0 20 20"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M18 9.64741C17.1925 8.24871 16.0344 7.08457 14.6399 6.26971C13.2455 5.45486 11.6628 5.01742 10.0478 5.00051C8.4328 4.9836 6.84127 5.38779 5.43006 6.17326C4.01884 6.95873 2.83666 8.09837 2 9.47985L2.76881 9.94546C3.52456 8.69756 4.59243 7.66813 5.86718 6.95862C7.14193 6.2491 8.57955 5.88399 10.0384 5.89927C11.4972 5.91455 12.9269 6.30968 14.1865 7.04574C15.4461 7.7818 16.4922 8.83337 17.2216 10.0968L18 9.64741ZM15.2155 11.0953C14.6772 10.1628 13.9051 9.3867 12.9755 8.84347C12.0459 8.30023 10.9907 8.00861 9.91406 7.99733C8.8374 7.98606 7.77638 8.25552 6.83557 8.77917C5.89476 9.30281 5.10664 10.0626 4.54887 10.9836L5.34391 11.4651C5.81802 10.6822 6.48792 10.0364 7.28761 9.59132C8.0873 9.14622 8.98916 8.91718 9.90432 8.92676C10.8195 8.93635 11.7164 9.18423 12.5065 9.64598C13.2967 10.1077 13.953 10.7674 14.4106 11.56L15.2155 11.0953ZM10 14C10.8284 14 11.5 13.3284 11.5 12.5C11.5 11.6716 10.8284 11 10 11C9.17157 11 8.5 11.6716 8.5 12.5C8.5 13.3284 9.17157 14 10 14Z"
fill="currentColor"
/>
</svg>
)
case 'networkCrossedOut':
return (
<svg
{...props}
viewBox="0 0 20 20"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M4.35352 5.39647L14.253 15.296L14.9601 14.5889L5.06062 4.68936L4.35352 5.39647ZM12.5065 9.64599C11.9609 9.32713 11.3643 9.11025 10.746 9.00341L9.74058 7.99796C9.79835 7.99694 9.85618 7.99674 9.91406 7.99735C10.9907 8.00862 12.0459 8.30025 12.9755 8.84348C13.9051 9.38672 14.6772 10.1628 15.2155 11.0953L14.4106 11.56C13.953 10.7674 13.2967 10.1077 12.5065 9.64599ZM6.48788 8.98789L7.16295 9.66297C6.41824 10.1045 5.79317 10.7233 5.34391 11.4651L4.54887 10.9836C5.03646 10.1785 5.70009 9.49656 6.48788 8.98789ZM10.0384 5.89928C9.3134 5.89169 8.59366 5.97804 7.89655 6.15392L7.16867 5.42605C8.09637 5.13507 9.06776 4.99026 10.0478 5.00052C11.6628 5.01744 13.2455 5.45488 14.6399 6.26973C16.0344 7.08458 17.1925 8.24872 18 9.64742L17.2216 10.0968C16.4922 8.83338 15.4461 7.78181 14.1865 7.04575C12.9269 6.3097 11.4972 5.91456 10.0384 5.89928ZM5.00782 7.50783L4.36522 6.86524C3.42033 7.57557 2.61639 8.46208 2 9.47986L2.76881 9.94547C3.34775 8.98952 4.10986 8.16177 5.00782 7.50783ZM10 14C10.4142 14 10.7892 13.8321 11.0607 13.5607L8.93934 11.4394C8.66789 11.7108 8.5 12.0858 8.5 12.5C8.5 13.3284 9.17157 14 10 14Z"
fill="currentColor"
/>
</svg>
)
case 'parallel': case 'parallel':
return ( return (
<svg <svg
@ -313,6 +416,22 @@ export const CustomIcon = ({
/> />
</svg> </svg>
) )
case 'settings':
return (
<svg
{...props}
viewBox="0 0 20 20"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M8 5.5C8 5.77614 7.77614 6 7.5 6C7.22386 6 7 5.77614 7 5.5C7 5.22386 7.22386 5 7.5 5C7.77614 5 8 5.22386 8 5.5ZM6.08535 6C6.29127 6.5826 6.84689 7 7.5 7C8.32843 7 9 6.32843 9 5.5C9 4.67157 8.32843 4 7.5 4C6.84689 4 6.29127 4.4174 6.08535 5H5V6H6.08535ZM15 6H9.94999C9.98278 5.83844 10 5.67123 10 5.5C10 5.32877 9.98278 5.16155 9.94999 5H15V6ZM11 14.5C11 14.7761 10.7761 15 10.5 15C10.2239 15 10 14.7761 10 14.5C10 14.2239 10.2239 14 10.5 14C10.7761 14 11 14.2239 11 14.5ZM9.08535 15C9.29127 15.5826 9.84689 16 10.5 16C11.3284 16 12 15.3284 12 14.5C12 13.6716 11.3284 13 10.5 13C9.84689 13 9.29127 13.4174 9.08535 14H5V15H9.08535ZM15 15H12.95C12.9828 14.8384 13 14.6712 13 14.5C13 14.3288 12.9828 14.1616 12.95 14H15V15ZM11.5 10.5C11.7761 10.5 12 10.2761 12 10C12 9.72386 11.7761 9.5 11.5 9.5C11.2239 9.5 11 9.72386 11 10C11 10.2761 11.2239 10.5 11.5 10.5ZM11.5 8.5C12.1531 8.5 12.7087 8.9174 12.9146 9.5H15V10.5H12.9146C12.7087 11.0826 12.1531 11.5 11.5 11.5C10.6716 11.5 10 10.8284 10 10C10 9.17157 10.6716 8.5 11.5 8.5ZM9.05001 10.5C9.01722 10.3384 9 10.1712 9 10C9 9.82877 9.01722 9.66155 9.05001 9.5H5V10.5H9.05001Z"
fill="currentColor"
/>
</svg>
)
case 'sketch': case 'sketch':
return ( return (
<svg <svg

View File

@ -1,6 +1,7 @@
import { CollapsiblePanel, CollapsiblePanelProps } from './CollapsiblePanel' import { CollapsiblePanel, CollapsiblePanelProps } from './CollapsiblePanel'
import { AstExplorer } from './AstExplorer' import { AstExplorer } from './AstExplorer'
import { EngineCommands } from './EngineCommands' import { EngineCommands } from './EngineCommands'
import { CamDebugSettings } from 'clientSideScene/ClientSideSceneComp'
export const DebugPanel = ({ className, ...props }: CollapsiblePanelProps) => { export const DebugPanel = ({ className, ...props }: CollapsiblePanelProps) => {
return ( return (
@ -15,6 +16,7 @@ export const DebugPanel = ({ className, ...props }: CollapsiblePanelProps) => {
> >
<section className="p-4 flex flex-col gap-4"> <section className="p-4 flex flex-col gap-4">
<EngineCommands /> <EngineCommands />
<CamDebugSettings />
<div style={{ height: '400px' }} className="overflow-y-auto"> <div style={{ height: '400px' }} className="overflow-y-auto">
<AstExplorer /> <AstExplorer />
</div> </div>

View File

@ -0,0 +1,43 @@
import { MoveDesc } from 'machines/modelingMachine'
export const DragWarningToast = (moveDescs: MoveDesc[]) => {
if (moveDescs.length === 1) {
return (
<div className="flex items-center">
<div>🔒</div>
<div className="dark:bg-slate-950/50 bg-slate-400/50 p-1 px-3 rounded-xl text-sm">
move disabled: line{' '}
<span className="dark:text-energy-20 text-lime-600">
{moveDescs[0].line}
</span>
:{' '}
<pre>
<code className="dark:text-energy-20 text-lime-600">
{moveDescs[0].snippet}
</code>
</pre>{' '}
is fully constrained
</div>
</div>
)
} else if (moveDescs.length > 1) {
return (
<div className="dark:bg-slate-950/50 bg-slate-400/50 p-1 px-3 rounded-xl text-sm">
<div>Move disabled as The following lines are constrained</div>
{moveDescs.map((desc, i) => {
return (
<div key={i}>
line {desc.line}:{' '}
<pre className="inline-block">
<code className="dark:text-energy-20 text-lime-600">
{moveDescs[0].snippet}
</code>
</pre>{' '}
</div>
)
})}
</div>
)
}
return null
}

View File

@ -1,6 +1,7 @@
import { useMachine } from '@xstate/react' import { useMachine } from '@xstate/react'
import { useNavigate, useRouteLoaderData } from 'react-router-dom' import { useNavigate, useRouteLoaderData } from 'react-router-dom'
import { IndexLoaderData, paths } from '../Router' import { type IndexLoaderData } from 'lib/types'
import { paths } from 'lib/paths'
import React, { createContext } from 'react' import React, { createContext } from 'react'
import { toast } from 'react-hot-toast' import { toast } from 'react-hot-toast'
import { import {

View File

@ -1,4 +1,5 @@
import { IndexLoaderData, paths } from 'Router' import { type IndexLoaderData } from 'lib/types'
import { paths } from 'lib/paths'
import { ActionButton } from './ActionButton' import { ActionButton } from './ActionButton'
import Tooltip from './Tooltip' import Tooltip from './Tooltip'
import { FileEntry } from '@tauri-apps/api/fs' import { FileEntry } from '@tauri-apps/api/fs'

View File

@ -1,6 +1,6 @@
import { useMachine } from '@xstate/react' import { useMachine } from '@xstate/react'
import { useNavigate } from 'react-router-dom' import { useNavigate } from 'react-router-dom'
import { paths } from '../Router' import { paths } from 'lib/paths'
import { authMachine, TOKEN_PERSIST_KEY } from '../machines/authMachine' import { authMachine, TOKEN_PERSIST_KEY } from '../machines/authMachine'
import withBaseUrl from '../lib/withBaseURL' import withBaseUrl from '../lib/withBaseURL'
import React, { createContext, useEffect, useRef } from 'react' import React, { createContext, useEffect, useRef } from 'react'
@ -30,6 +30,12 @@ type GlobalContext = {
settings: MachineContext<typeof settingsMachine> settings: MachineContext<typeof settingsMachine>
} }
// a little hacky for sure, open to changing it
// this implies that we should only even have one instance of this provider mounted at any one time
// but I think that's a safe assumption
let settingsStateRef: (typeof settingsMachine)['context'] | undefined
export const getSettingsState = () => settingsStateRef
export const GlobalStateContext = createContext({} as GlobalContext) export const GlobalStateContext = createContext({} as GlobalContext)
export const GlobalStateProvider = ({ export const GlobalStateProvider = ({
@ -71,6 +77,7 @@ export const GlobalStateProvider = ({
}, },
}, },
}) })
settingsStateRef = settingsState.context
useStateMachineCommands({ useStateMachineCommands({
machineId: 'settings', machineId: 'settings',

View File

@ -2,7 +2,7 @@ import ReactJson from 'react-json-view'
import { useEffect } from 'react' import { useEffect } from 'react'
import { CollapsiblePanel, CollapsiblePanelProps } from './CollapsiblePanel' import { CollapsiblePanel, CollapsiblePanelProps } from './CollapsiblePanel'
import { Themes } from '../lib/theme' import { Themes } from '../lib/theme'
import { useKclContext } from 'lang/KclSinglton' import { useKclContext } from 'lang/KclSingleton'
const ReactJsonTypeHack = ReactJson as any const ReactJsonTypeHack = ReactJson as any

View File

@ -41,9 +41,9 @@ describe('processMemory', () => {
otherVar: 3, otherVar: 3,
theExtrude: [], theExtrude: [],
theSketch: [ theSketch: [
{ type: 'toPoint', to: [-3.35, 0.17], from: [0, 0], name: '' }, { type: 'ToPoint', to: [-3.35, 0.17], from: [0, 0], name: '' },
{ type: 'toPoint', to: [0.98, 5.16], from: [-3.35, 0.17], name: '' }, { type: 'ToPoint', to: [0.98, 5.16], from: [-3.35, 0.17], name: '' },
{ type: 'toPoint', to: [2.15, 4.32], from: [0.98, 5.16], name: '' }, { type: 'ToPoint', to: [2.15, 4.32], from: [0.98, 5.16], name: '' },
], ],
}) })
}) })

View File

@ -3,7 +3,7 @@ import { CollapsiblePanel, CollapsiblePanelProps } from './CollapsiblePanel'
import { useMemo } from 'react' import { useMemo } from 'react'
import { ProgramMemory, Path, ExtrudeSurface } from '../lang/wasm' import { ProgramMemory, Path, ExtrudeSurface } from '../lang/wasm'
import { Themes } from '../lib/theme' import { Themes } from '../lib/theme'
import { useKclContext } from 'lang/KclSinglton' import { useKclContext } from 'lang/KclSingleton'
interface MemoryPanelProps extends CollapsiblePanelProps { interface MemoryPanelProps extends CollapsiblePanelProps {
theme?: Exclude<Themes, Themes.System> theme?: Exclude<Themes, Themes.System>

View File

@ -13,23 +13,7 @@ import { useSetupEngineManager } from 'hooks/useSetupEngineManager'
import { useGlobalStateContext } from 'hooks/useGlobalStateContext' import { useGlobalStateContext } from 'hooks/useGlobalStateContext'
import { isCursorInSketchCommandRange } from 'lang/util' import { isCursorInSketchCommandRange } from 'lang/util'
import { engineCommandManager } from 'lang/std/engineConnection' import { engineCommandManager } from 'lang/std/engineConnection'
import { v4 as uuidv4 } from 'uuid' import { kclManager, useKclContext } from 'lang/KclSingleton'
import { addStartSketch } from 'lang/modifyAst'
import { roundOff } from 'lib/utils'
import {
recast,
parse,
Program,
PipeExpression,
CallExpression,
} from 'lang/wasm'
import { getNodeFromPath } from 'lang/queryAst'
import {
addCloseToPipe,
addNewSketchLn,
compareVec2Epsilon,
} from 'lang/std/sketch'
import { kclManager, useKclContext } from 'lang/KclSinglton'
import { applyConstraintHorzVertDistance } from './Toolbar/SetHorzVertDistance' import { applyConstraintHorzVertDistance } from './Toolbar/SetHorzVertDistance'
import { import {
angleBetweenInfo, angleBetweenInfo,
@ -49,6 +33,11 @@ import { applyConstraintIntersect } from './Toolbar/Intersect'
import { applyConstraintAbsDistance } from './Toolbar/SetAbsDistance' import { applyConstraintAbsDistance } from './Toolbar/SetAbsDistance'
import useStateMachineCommands from 'hooks/useStateMachineCommands' import useStateMachineCommands from 'hooks/useStateMachineCommands'
import { modelingMachineConfig } from 'lib/commandBarConfigs/modelingCommandConfig' import { modelingMachineConfig } from 'lib/commandBarConfigs/modelingCommandConfig'
import { sceneInfra } from 'clientSideScene/sceneInfra'
import { getSketchQuaternion } from 'clientSideScene/sceneEntities'
import { startSketchOnDefault } from 'lang/modifyAst'
import { Program } from 'lang/wasm'
import { isSingleCursorInPipe } from 'lang/queryAst'
type MachineContext<T extends AnyStateMachine> = { type MachineContext<T extends AnyStateMachine> = {
state: StateFrom<T> state: StateFrom<T>
@ -92,181 +81,10 @@ export const ModelingMachineProvider = ({
const [modelingState, modelingSend, modelingActor] = useMachine( const [modelingState, modelingSend, modelingActor] = useMachine(
modelingMachine, modelingMachine,
{ {
// context: persistedSettings,
actions: { actions: {
'Modify AST': () => {},
'Update code selection cursors': () => {},
'show default planes': () => {
kclManager.showPlanes()
},
'create path': assign({
sketchEnginePathId: () => {
const sketchUuid = uuidv4()
engineCommandManager.sendSceneCommand({
type: 'modeling_cmd_req',
cmd_id: sketchUuid,
cmd: {
type: 'start_path',
},
})
engineCommandManager.sendSceneCommand({
type: 'modeling_cmd_req',
cmd_id: uuidv4(),
cmd: {
type: 'edit_mode_enter',
target: sketchUuid,
},
})
return sketchUuid
},
}),
'AST start new sketch': assign(
({ sketchEnginePathId }, { data: { coords, axis, segmentId } }) => {
if (!axis) {
// Something really weird must have happened for this to happen.
console.error('axis is undefined for starting a new sketch')
return {}
}
if (!segmentId) {
// Something really weird must have happened for this to happen.
console.error('segmentId is undefined for starting a new sketch')
return {}
}
const _addStartSketch = addStartSketch(
kclManager.ast,
axis,
[roundOff(coords[0].x), roundOff(coords[0].y)],
[
roundOff(coords[1].x - coords[0].x),
roundOff(coords[1].y - coords[0].y),
]
)
const _modifiedAst = _addStartSketch.modifiedAst
const _pathToNode = _addStartSketch.pathToNode
const newCode = recast(_modifiedAst)
const astWithUpdatedSource = parse(newCode)
const updatedPipeNode = getNodeFromPath<PipeExpression>(
astWithUpdatedSource,
_pathToNode
).node
const startProfileAtCallExp = updatedPipeNode.body.find(
(exp) =>
exp.type === 'CallExpression' &&
exp.callee.name === 'startProfileAt'
)
if (startProfileAtCallExp)
engineCommandManager.artifactMap[sketchEnginePathId] = {
type: 'result',
range: [startProfileAtCallExp.start, startProfileAtCallExp.end],
commandType: 'start_path',
data: null,
raw: {} as any,
}
const lineCallExp = updatedPipeNode.body.find(
(exp) =>
exp.type === 'CallExpression' && exp.callee.name === 'line'
)
if (lineCallExp)
engineCommandManager.artifactMap[segmentId] = {
type: 'result',
range: [lineCallExp.start, lineCallExp.end],
commandType: 'extend_path',
parentId: sketchEnginePathId,
data: null,
raw: {} as any,
}
kclManager.executeAstMock(astWithUpdatedSource, true)
return {
sketchPathToNode: _pathToNode,
}
}
),
'AST add line segment': async (
{ sketchPathToNode, sketchEnginePathId },
{ data: { coords, segmentId } }
) => {
if (!sketchPathToNode) return
const lastCoord = coords[coords.length - 1]
const pathInfo = await engineCommandManager.sendSceneCommand({
type: 'modeling_cmd_req',
cmd_id: uuidv4(),
cmd: {
type: 'path_get_info',
path_id: sketchEnginePathId,
},
})
const firstSegment = pathInfo?.data?.data?.segments.find(
(seg: any) => seg.command === 'line_to'
)
const firstSegCoords = await engineCommandManager.sendSceneCommand({
type: 'modeling_cmd_req',
cmd_id: uuidv4(),
cmd: {
type: 'curve_get_control_points',
curve_id: firstSegment.command_id,
},
})
const startPathCoord = firstSegCoords?.data?.data?.control_points[0]
const isClose = compareVec2Epsilon(
[startPathCoord.x, startPathCoord.y],
[lastCoord.x, lastCoord.y]
)
let _modifiedAst: Program
if (!isClose) {
const newSketchLn = addNewSketchLn({
node: kclManager.ast,
programMemory: kclManager.programMemory,
to: [lastCoord.x, lastCoord.y],
from: [coords[0].x, coords[0].y],
fnName: 'line',
pathToNode: sketchPathToNode,
})
const _modifiedAst = newSketchLn.modifiedAst
kclManager.executeAstMock(_modifiedAst, true).then(() => {
const lineCallExp = getNodeFromPath<CallExpression>(
kclManager.ast,
newSketchLn.pathToNode
).node
if (segmentId)
engineCommandManager.artifactMap[segmentId] = {
type: 'result',
range: [lineCallExp.start, lineCallExp.end],
commandType: 'extend_path',
parentId: sketchEnginePathId,
data: null,
raw: {} as any,
}
})
} else {
_modifiedAst = addCloseToPipe({
node: kclManager.ast,
programMemory: kclManager.programMemory,
pathToNode: sketchPathToNode,
})
engineCommandManager.sendSceneCommand({
type: 'modeling_cmd_req',
cmd_id: uuidv4(),
cmd: { type: 'edit_mode_exit' },
})
engineCommandManager.sendSceneCommand({
type: 'modeling_cmd_req',
cmd_id: uuidv4(),
cmd: { type: 'default_camera_disable_sketch_mode' },
})
kclManager.executeAstMock(_modifiedAst, true)
// updateAst(_modifiedAst, true)
}
},
'sketch exit execute': () => { 'sketch exit execute': () => {
kclManager.executeAst() kclManager.executeAst()
}, },
'set tool': () => {}, // TODO
'Set selection': assign(({ selectionRanges }, event) => { 'Set selection': assign(({ selectionRanges }, event) => {
if (event.type !== 'Set selection') return {} // this was needed for ts after adding 'Set selection' action to on done modal events if (event.type !== 'Set selection') return {} // this was needed for ts after adding 'Set selection' action to on done modal events
const setSelections = event.data const setSelections = event.data
@ -274,36 +92,6 @@ export const ModelingMachineProvider = ({
if (setSelections.selectionType === 'mirrorCodeMirrorSelections') if (setSelections.selectionType === 'mirrorCodeMirrorSelections')
return { selectionRanges: setSelections.selection } return { selectionRanges: setSelections.selection }
else if (setSelections.selectionType === 'otherSelection') { else if (setSelections.selectionType === 'otherSelection') {
// TODO KittyCAD/engine/issues/1620: send axis highlight when it's working (if that's what we settle on)
// const axisAddCmd: EngineCommand = {
// type: 'modeling_cmd_req',
// cmd: {
// type: 'highlight_set_entities',
// entities: [
// setSelections.selection === 'x-axis'
// ? X_AXIS_UUID
// : Y_AXIS_UUID,
// ],
// },
// cmd_id: uuidv4(),
// }
// if (!isShiftDown) {
// engineCommandManager
// .sendSceneCommand({
// type: 'modeling_cmd_req',
// cmd: {
// type: 'select_clear',
// },
// cmd_id: uuidv4(),
// })
// .then(() => {
// engineCommandManager.sendSceneCommand(axisAddCmd)
// })
// } else {
// engineCommandManager.sendSceneCommand(axisAddCmd)
// }
const { const {
codeMirrorSelection, codeMirrorSelection,
selectionRangeTypeMap, selectionRangeTypeMap,
@ -384,12 +172,6 @@ export const ModelingMachineProvider = ({
}), }),
}, },
guards: { guards: {
'Selection contains axis': () => true,
'Selection contains edge': () => true,
'Selection contains face': () => true,
'Selection contains line': () => true,
'Selection contains point': () => true,
'Selection is not empty': () => true,
'has valid extrude selection': ({ selectionRanges }) => { 'has valid extrude selection': ({ selectionRanges }) => {
// A user can begin extruding if they either have 1+ faces selected or nothing selected // A user can begin extruding if they either have 1+ faces selected or nothing selected
// TODO: I believe this guard only allows for extruding a single face at a time // TODO: I believe this guard only allows for extruding a single face at a time
@ -401,7 +183,10 @@ export const ModelingMachineProvider = ({
return canExtrudeSelection(selectionRanges) return canExtrudeSelection(selectionRanges)
}, },
'Selection is one face': ({ selectionRanges }) => { 'Selection is on face': ({ selectionRanges }, { data }) => {
if (data?.forceNewSketch) return false
if (!isSingleCursorInPipe(selectionRanges, kclManager.ast))
return false
return !!isCursorInSketchCommandRange( return !!isCursorInSketchCommandRange(
engineCommandManager.artifactMap, engineCommandManager.artifactMap,
selectionRanges selectionRanges
@ -409,6 +194,41 @@ export const ModelingMachineProvider = ({
}, },
}, },
services: { services: {
'AST-undo-startSketchOn': async ({ sketchPathToNode }) => {
if (!sketchPathToNode) return
const newAst: Program = JSON.parse(JSON.stringify(kclManager.ast))
const varDecIndex = sketchPathToNode[1][0]
// remove body item at varDecIndex
newAst.body = newAst.body.filter((_, i) => i !== varDecIndex)
await kclManager.executeAstMock(newAst, { updates: 'code' })
sceneInfra.setCallbacks({
onClick: () => {},
onDrag: () => {},
})
},
'animate-to-face': async (_, { data: { plane, normal } }) => {
const { modifiedAst, pathToNode } = startSketchOnDefault(
kclManager.ast,
plane
)
await kclManager.updateAst(modifiedAst, false)
const quaternion = getSketchQuaternion(pathToNode, normal)
await sceneInfra.tweenCameraToQuaternion(quaternion)
return {
sketchPathToNode: pathToNode,
sketchNormalBackUp: normal,
}
},
'animate-to-sketch': async ({
sketchPathToNode,
sketchNormalBackUp,
}) => {
const quaternion = getSketchQuaternion(
sketchPathToNode || [],
sketchNormalBackUp
)
await sceneInfra.tweenCameraToQuaternion(quaternion)
},
'Get horizontal info': async ({ 'Get horizontal info': async ({
selectionRanges, selectionRanges,
}): Promise<SetSelections> => { }): Promise<SetSelections> => {
@ -542,17 +362,6 @@ export const ModelingMachineProvider = ({
} }
) )
useEffect(() => {
engineCommandManager.onPlaneSelected((plane_id: string) => {
if (modelingState.nextEvents.includes('Select default plane')) {
modelingSend({
type: 'Select default plane',
data: { planeId: plane_id },
})
}
})
}, [modelingSend, modelingState.nextEvents])
useEffect(() => { useEffect(() => {
kclManager.registerExecuteCallback(() => { kclManager.registerExecuteCallback(() => {
modelingSend({ type: 'Re-execute' }) modelingSend({ type: 'Re-execute' })
@ -565,10 +374,7 @@ export const ModelingMachineProvider = ({
send: modelingSend, send: modelingSend,
actor: modelingActor, actor: modelingActor,
commandBarConfig: modelingMachineConfig, commandBarConfig: modelingMachineConfig,
onCancel: () => { onCancel: () => modelingSend({ type: 'Cancel' }),
console.log('firing onCancel!!')
modelingSend({ type: 'Cancel' })
},
}) })
return ( return (

View File

@ -3,8 +3,9 @@ import { BrowserRouter } from 'react-router-dom'
import { GlobalStateProvider } from './GlobalStateProvider' import { GlobalStateProvider } from './GlobalStateProvider'
import CommandBarProvider from './CommandBar/CommandBar' import CommandBarProvider from './CommandBar/CommandBar'
import { import {
NETWORK_CONTENT, NETWORK_HEALTH_TEXT,
NetworkHealthIndicator, NetworkHealthIndicator,
NetworkHealthState,
} from './NetworkHealthIndicator' } from './NetworkHealthIndicator'
function TestWrap({ children }: { children: React.ReactNode }) { function TestWrap({ children }: { children: React.ReactNode }) {
@ -28,8 +29,8 @@ describe('NetworkHealthIndicator tests', () => {
fireEvent.click(screen.getByTestId('network-toggle')) fireEvent.click(screen.getByTestId('network-toggle'))
expect(screen.getByTestId('network-good')).toHaveTextContent( expect(screen.getByTestId('network')).toHaveTextContent(
NETWORK_CONTENT.good NETWORK_HEALTH_TEXT[NetworkHealthState.Ok]
) )
}) })
@ -43,8 +44,8 @@ describe('NetworkHealthIndicator tests', () => {
fireEvent.offline(window) fireEvent.offline(window)
fireEvent.click(screen.getByTestId('network-toggle')) fireEvent.click(screen.getByTestId('network-toggle'))
expect(screen.getByTestId('network-bad')).toHaveTextContent( expect(screen.getByTestId('network')).toHaveTextContent(
NETWORK_CONTENT.bad NETWORK_HEALTH_TEXT[NetworkHealthState.Disconnected]
) )
}) })
}) })

View File

@ -1,41 +1,186 @@
import { faExclamation, faWifi } from '@fortawesome/free-solid-svg-icons'
import { Popover } from '@headlessui/react' import { Popover } from '@headlessui/react'
import { useEffect, useState } from 'react' import { useEffect, useState } from 'react'
import { ActionIcon } from './ActionIcon' import { ActionIcon, ActionIconProps } from './ActionIcon'
import {
ConnectingType,
ConnectingTypeGroup,
DisconnectingType,
engineCommandManager,
EngineConnectionState,
EngineConnectionStateType,
ErrorType,
initialConnectingTypeGroupState,
} from '../lang/std/engineConnection'
import Tooltip from './Tooltip'
export const NETWORK_CONTENT = { export enum NetworkHealthState {
good: 'Network health is good', Ok,
bad: 'Network issue', Issue,
Disconnected,
} }
const NETWORK_MESSAGES = { export const NETWORK_HEALTH_TEXT: Record<NetworkHealthState, string> = {
offline: 'You are offline', [NetworkHealthState.Ok]: 'Connected',
[NetworkHealthState.Issue]: 'Problem',
[NetworkHealthState.Disconnected]: 'Offline',
}
type IconColorConfig = {
icon: string
bg: string
}
const hasIssueToIcon: Record<
string | number | symbol,
ActionIconProps['icon']
> = {
true: 'close',
undefined: 'horizontalDash',
false: 'checkmark',
}
const hasIssueToIconColors: Record<string | number | symbol, IconColorConfig> =
{
true: {
icon: 'text-destroy-80 dark:text-destroy-10',
bg: 'bg-destroy-10 dark:bg-destroy-80',
},
undefined: {
icon: 'text-chalkboard-70 dark:text-chalkboard-30',
bg: 'bg-chalkboard-30 dark:bg-chalkboard-70',
},
false: {
icon: 'text-chalkboard-110 dark:!text-chalkboard-10',
bg: 'bg-transparent dark:bg-transparent',
},
}
const overallConnectionStateColor: Record<NetworkHealthState, IconColorConfig> =
{
[NetworkHealthState.Ok]: {
icon: 'text-energy-80 dark:text-energy-10',
bg: 'bg-energy-10/30 dark:bg-energy-80/50',
},
[NetworkHealthState.Issue]: {
icon: 'text-destroy-80 dark:text-destroy-10',
bg: 'bg-destroy-10 dark:bg-destroy-80/80',
},
[NetworkHealthState.Disconnected]: {
icon: 'text-destroy-80 dark:text-destroy-10',
bg: 'bg-destroy-10 dark:bg-destroy-80',
},
}
const overallConnectionStateIcon: Record<
NetworkHealthState,
ActionIconProps['icon']
> = {
[NetworkHealthState.Ok]: 'network',
[NetworkHealthState.Issue]: 'networkCrossedOut',
[NetworkHealthState.Disconnected]: 'networkCrossedOut',
} }
export const NetworkHealthIndicator = () => { export const NetworkHealthIndicator = () => {
const [networkIssues, setNetworkIssues] = useState<string[]>([]) const [steps, setSteps] = useState(initialConnectingTypeGroupState)
const hasIssues = [...networkIssues.values()].length > 0 const [internetConnected, setInternetConnected] = useState<boolean>(true)
const [overallState, setOverallState] = useState<NetworkHealthState>(
NetworkHealthState.Ok
)
const [hasCopied, setHasCopied] = useState<boolean>(false)
const [error, setError] = useState<ErrorType | undefined>(undefined)
const issues: Record<ConnectingTypeGroup, boolean> = {
[ConnectingTypeGroup.WebSocket]: steps[ConnectingTypeGroup.WebSocket].some(
(a: [ConnectingType, boolean | undefined]) => a[1] === false
),
[ConnectingTypeGroup.ICE]: steps[ConnectingTypeGroup.ICE].some(
(a: [ConnectingType, boolean | undefined]) => a[1] === false
),
[ConnectingTypeGroup.WebRTC]: steps[ConnectingTypeGroup.WebRTC].some(
(a: [ConnectingType, boolean | undefined]) => a[1] === false
),
}
const hasIssues: boolean =
issues[ConnectingTypeGroup.WebSocket] ||
issues[ConnectingTypeGroup.ICE] ||
issues[ConnectingTypeGroup.WebRTC]
useEffect(() => { useEffect(() => {
const offlineListener = () => setOverallState(
setNetworkIssues((issues) => { !internetConnected
return [ ? NetworkHealthState.Disconnected
...issues.filter((issue) => issue !== NETWORK_MESSAGES.offline), : hasIssues
NETWORK_MESSAGES.offline, ? NetworkHealthState.Issue
] : NetworkHealthState.Ok
}) )
window.addEventListener('offline', offlineListener) }, [hasIssues, internetConnected])
const onlineListener = () => useEffect(() => {
setNetworkIssues((issues) => { const cb1 = () => {
return [...issues.filter((issue) => issue !== NETWORK_MESSAGES.offline)] setSteps(initialConnectingTypeGroupState)
}) setInternetConnected(true)
window.addEventListener('online', onlineListener)
return () => {
window.removeEventListener('offline', offlineListener)
window.removeEventListener('online', onlineListener)
} }
const cb2 = () => {
setInternetConnected(false)
}
window.addEventListener('online', cb1)
window.addEventListener('offline', cb2)
return () => {
window.removeEventListener('online', cb1)
window.removeEventListener('offline', cb2)
}
}, [])
useEffect(() => {
engineCommandManager.onConnectionStateChange(
(engineConnectionState: EngineConnectionState) => {
let hasSetAStep = false
if (
engineConnectionState.type === EngineConnectionStateType.Connecting
) {
const groups = Object.values(steps)
for (let group of groups) {
for (let step of group) {
if (step[0] !== engineConnectionState.value.type) continue
step[1] = true
hasSetAStep = true
}
}
}
if (
engineConnectionState.type === EngineConnectionStateType.Disconnecting
) {
const groups = Object.values(steps)
for (let group of groups) {
for (let step of group) {
if (
engineConnectionState.value.type === DisconnectingType.Error
) {
if (
engineConnectionState.value.value.lastConnectingValue
?.type === step[0]
) {
step[1] = false
hasSetAStep = true
}
}
}
if (engineConnectionState.value.type === DisconnectingType.Error) {
setError(engineConnectionState.value.value)
}
}
}
if (hasSetAStep) {
setSteps(steps)
}
}
)
}, []) }, [])
return ( return (
@ -45,65 +190,94 @@ export const NetworkHealthIndicator = () => {
'p-0 border-none bg-transparent dark:bg-transparent relative ' + 'p-0 border-none bg-transparent dark:bg-transparent relative ' +
(hasIssues (hasIssues
? 'focus-visible:outline-destroy-80' ? 'focus-visible:outline-destroy-80'
: 'focus-visible:outline-succeed-80') : 'focus-visible:outline-energy-80')
} }
data-testid="network-toggle" data-testid="network-toggle"
> >
<span className="sr-only">Network Health</span> <span className="sr-only">Network Health</span>
<ActionIcon <ActionIcon
icon={faWifi} icon={overallConnectionStateIcon[overallState]}
className="p-1" className="p-1"
iconClassName={ iconClassName={overallConnectionStateColor[overallState].icon}
hasIssues
? 'text-destroy-80 dark:text-destroy-30'
: 'text-succeed-80 dark:text-succeed-30'
}
bgClassName={ bgClassName={
'bg-transparent dark:bg-transparent ' + 'rounded-sm ' + overallConnectionStateColor[overallState].bg
(hasIssues
? 'hover:bg-destroy-10/50 hover:dark:bg-destroy-80/50 rounded'
: 'hover:bg-succeed-10/50 hover:dark:bg-succeed-80/50 rounded')
} }
/> />
<Tooltip position="blockEnd" delay={750} className="ui-open:hidden">
Network Health ({NETWORK_HEALTH_TEXT[overallState]})
</Tooltip>
</Popover.Button> </Popover.Button>
<Popover.Panel className="absolute right-0 left-auto top-full mt-1 w-56 flex flex-col gap-1 divide-y divide-chalkboard-20 dark:divide-chalkboard-70 align-stretch py-2 bg-chalkboard-10 dark:bg-chalkboard-90 rounded shadow-lg border border-solid border-chalkboard-20/50 dark:border-chalkboard-80/50 text-sm"> <Popover.Panel className="absolute right-0 left-auto top-full mt-1 w-64 flex flex-col gap-1 align-stretch bg-chalkboard-10 dark:bg-chalkboard-90 rounded shadow-lg border border-solid border-chalkboard-20/50 dark:border-chalkboard-80/50 text-sm">
{!hasIssues ? ( <div
<span className={`flex items-center justify-between p-2 rounded-t-sm ${overallConnectionStateColor[overallState].bg} ${overallConnectionStateColor[overallState].icon}`}
className="flex items-center justify-center gap-1 px-4" >
data-testid="network-good" <h2 className="text-sm font-sans font-normal">Network health</h2>
<p
data-testid="network"
className="font-bold text-xs uppercase px-2 py-1 rounded-sm"
> >
<ActionIcon {NETWORK_HEALTH_TEXT[overallState]}
icon="checkmark" </p>
bgClassName={'bg-succeed-10/50 dark:bg-succeed-80/50 rounded-sm'} </div>
iconClassName={'text-succeed-80 dark:text-succeed-30'} <ul className="divide-y divide-chalkboard-20 dark:divide-chalkboard-80">
/> {Object.keys(steps).map((name) => (
{NETWORK_CONTENT.good} <li
</span> key={name}
) : ( className={'flex flex-col px-2 py-4 gap-1 last:mb-0 '}
<ul className="divide-y divide-chalkboard-20 dark:divide-chalkboard-80">
<span
className="font-bold text-xs uppercase text-destroy-60 dark:text-destroy-50 px-4"
data-testid="network-bad"
> >
{NETWORK_CONTENT.bad} <div className="flex items-center text-left gap-1">
{networkIssues.length > 1 ? 's' : ''} <p className="flex-1">{name}</p>
</span> {internetConnected ? (
{networkIssues.map((issue) => ( <ActionIcon
<li size="lg"
key={issue} icon={
className="flex items-center gap-1 py-2 my-2 last:mb-0" hasIssueToIcon[
> issues[name as ConnectingTypeGroup].toString()
<ActionIcon ]
icon={faExclamation} }
bgClassName={'bg-destroy-10/50 dark:bg-destroy-80/50 rounded'} iconClassName={
iconClassName={'text-destroy-80 dark:text-destroy-30'} hasIssueToIconColors[
className="ml-4" issues[name as ConnectingTypeGroup].toString()
/> ].icon
<p className="flex-1 mr-4">{issue}</p> }
</li> bgClassName={
))} 'rounded-sm ' +
</ul> hasIssueToIconColors[
)} issues[name as ConnectingTypeGroup].toString()
].bg
}
/>
) : (
<ActionIcon
icon={hasIssueToIcon.true}
bgClassName={hasIssueToIconColors.true.bg}
iconClassName={hasIssueToIconColors.true.icon}
/>
)}
</div>
{issues[name as ConnectingTypeGroup] && (
<button
onClick={async () => {
await navigator.clipboard.writeText(
JSON.stringify(error, null, 2) || ''
)
setHasCopied(true)
setTimeout(() => setHasCopied(false), 5000)
}}
className="flex w-fit gap-2 items-center bg-transparent text-sm p-1 py-0 my-0 -mx-1 text-destroy-80 dark:text-destroy-10 hover:bg-transparent border-transparent dark:border-transparent hover:border-destroy-80 dark:hover:border-destroy-80 dark:hover:bg-destroy-80"
>
{hasCopied ? 'Copied' : 'Copy Error'}
<ActionIcon
size="lg"
icon={hasCopied ? 'clipboardCheckmark' : 'clipboardPlus'}
iconClassName="text-inherit dark:text-inherit"
bgClassName="!bg-transparent"
/>
</button>
)}
</li>
))}
</ul>
</Popover.Panel> </Popover.Panel>
</Popover> </Popover>
) )

View File

@ -1,5 +1,6 @@
import { FormEvent, useEffect, useRef, useState } from 'react' import { FormEvent, useEffect, useRef, useState } from 'react'
import { type ProjectWithEntryPointMetadata, paths } from '../Router' import { type ProjectWithEntryPointMetadata } from 'lib/types'
import { paths } from 'lib/paths'
import { Link } from 'react-router-dom' import { Link } from 'react-router-dom'
import { ActionButton } from './ActionButton' import { ActionButton } from './ActionButton'
import { import {

View File

@ -1,7 +1,7 @@
import { fireEvent, render, screen } from '@testing-library/react' import { fireEvent, render, screen } from '@testing-library/react'
import { BrowserRouter } from 'react-router-dom' import { BrowserRouter } from 'react-router-dom'
import ProjectSidebarMenu from './ProjectSidebarMenu' import ProjectSidebarMenu from './ProjectSidebarMenu'
import { ProjectWithEntryPointMetadata } from '../Router' import { type ProjectWithEntryPointMetadata } from 'lib/types'
import { GlobalStateProvider } from './GlobalStateProvider' import { GlobalStateProvider } from './GlobalStateProvider'
import CommandBarProvider from './CommandBar/CommandBar' import CommandBarProvider from './CommandBar/CommandBar'
import { APP_NAME } from 'lib/constants' import { APP_NAME } from 'lib/constants'

View File

@ -1,7 +1,8 @@
import { Popover, Transition } from '@headlessui/react' import { Popover, Transition } from '@headlessui/react'
import { ActionButton } from './ActionButton' import { ActionButton } from './ActionButton'
import { faHome } from '@fortawesome/free-solid-svg-icons' import { faHome } from '@fortawesome/free-solid-svg-icons'
import { IndexLoaderData, paths } from '../Router' import { type IndexLoaderData } from 'lib/types'
import { paths } from 'lib/paths'
import { isTauri } from '../lib/isTauri' import { isTauri } from '../lib/isTauri'
import { Link } from 'react-router-dom' import { Link } from 'react-router-dom'
import { ExportButton } from './ExportButton' import { ExportButton } from './ExportButton'

View File

@ -11,16 +11,13 @@ import { getNormalisedCoordinates, throttle } from '../lib/utils'
import Loading from './Loading' import Loading from './Loading'
import { cameraMouseDragGuards } from 'lib/cameraControls' import { cameraMouseDragGuards } from 'lib/cameraControls'
import { useGlobalStateContext } from 'hooks/useGlobalStateContext' import { useGlobalStateContext } from 'hooks/useGlobalStateContext'
import { CameraDragInteractionType_type } from '@kittycad/lib/dist/types/src/models'
import { Models } from '@kittycad/lib' import { Models } from '@kittycad/lib'
import { getNodeFromPath } from 'lang/queryAst'
import { VariableDeclarator, recast, CallExpression } from 'lang/wasm'
import { engineCommandManager } from '../lang/std/engineConnection' import { engineCommandManager } from '../lang/std/engineConnection'
import { useModelingContext } from 'hooks/useModelingContext' import { useModelingContext } from 'hooks/useModelingContext'
import { kclManager, useKclContext } from 'lang/KclSinglton' import { useKclContext } from 'lang/KclSingleton'
import { changeSketchArguments } from 'lang/std/sketch' import { ClientSideScene } from 'clientSideScene/ClientSideSceneComp'
export const Stream = ({ className = '' }) => { export const Stream = ({ className = '' }: { className?: string }) => {
const [isLoading, setIsLoading] = useState(true) const [isLoading, setIsLoading] = useState(true)
const [clickCoords, setClickCoords] = useState<{ x: number; y: number }>() const [clickCoords, setClickCoords] = useState<{ x: number; y: number }>()
const videoRef = useRef<HTMLVideoElement>(null) const videoRef = useRef<HTMLVideoElement>(null)
@ -39,7 +36,7 @@ export const Stream = ({ className = '' }) => {
})) }))
const { settings } = useGlobalStateContext() const { settings } = useGlobalStateContext()
const cameraControls = settings?.context?.cameraControls const cameraControls = settings?.context?.cameraControls
const { send, state, context } = useModelingContext() const { state } = useModelingContext()
const { isExecuting } = useKclContext() const { isExecuting } = useKclContext()
useEffect(() => { useEffect(() => {
@ -53,8 +50,10 @@ export const Stream = ({ className = '' }) => {
videoRef.current.srcObject = mediaStream videoRef.current.srcObject = mediaStream
}, [mediaStream]) }, [mediaStream])
const handleMouseDown: MouseEventHandler<HTMLVideoElement> = (e) => { const handleMouseDown: MouseEventHandler<HTMLDivElement> = (e) => {
if (!videoRef.current) return if (!videoRef.current) return
if (state.matches('Sketch')) return
if (state.matches('Sketch no face')) return
const { x, y } = getNormalisedCoordinates({ const { x, y } = getNormalisedCoordinates({
clientX: e.clientX, clientX: e.clientX,
clientY: e.clientY, clientY: e.clientY,
@ -62,55 +61,6 @@ export const Stream = ({ className = '' }) => {
...streamDimensions, ...streamDimensions,
}) })
const newId = uuidv4()
const interactionGuards = cameraMouseDragGuards[cameraControls]
let interaction: CameraDragInteractionType_type = 'rotate'
if (
interactionGuards.pan.callback(e) ||
interactionGuards.pan.lenientDragStartButton === e.button
) {
interaction = 'pan'
} else if (
interactionGuards.rotate.callback(e) ||
interactionGuards.rotate.lenientDragStartButton === e.button
) {
interaction = 'rotate'
} else if (
interactionGuards.zoom.dragCallback(e) ||
interactionGuards.zoom.lenientDragStartButton === e.button
) {
interaction = 'zoom'
}
if (state.matches('Sketch.Move Tool')) {
if (
state.matches('Sketch.Move Tool.No move') ||
state.matches('Sketch.Move Tool.Move with execute')
) {
return
}
engineCommandManager.sendSceneCommand({
type: 'modeling_cmd_req',
cmd: {
type: 'handle_mouse_drag_start',
window: { x, y },
},
cmd_id: newId,
})
} else if (!state.matches('Sketch.Line Tool')) {
engineCommandManager.sendSceneCommand({
type: 'modeling_cmd_req',
cmd: {
type: 'camera_drag_start',
interaction,
window: { x, y },
},
cmd_id: newId,
})
}
setButtonDownInStream(e.button) setButtonDownInStream(e.button)
setClickCoords({ x, y }) setClickCoords({ x, y })
} }
@ -128,13 +78,15 @@ export const Stream = ({ className = '' }) => {
}) })
}, Math.round(1000 / fps)) }, Math.round(1000 / fps))
const handleMouseUp: MouseEventHandler<HTMLVideoElement> = ({ const handleMouseUp: MouseEventHandler<HTMLDivElement> = ({
clientX, clientX,
clientY, clientY,
ctrlKey, ctrlKey,
}) => { }) => {
if (!videoRef.current) return if (!videoRef.current) return
setButtonDownInStream(undefined) setButtonDownInStream(undefined)
if (state.matches('Sketch')) return
if (state.matches('Sketch no face')) return
const { x, y } = getNormalisedCoordinates({ const { x, y } = getNormalisedCoordinates({
clientX, clientX,
clientY, clientY,
@ -155,206 +107,19 @@ export const Stream = ({ className = '' }) => {
cmd_id: newCmdId, cmd_id: newCmdId,
} }
if (!didDragInStream && state.matches('Sketch no face')) { if (!didDragInStream) {
command.cmd = {
type: 'select_with_point',
selection_type: 'add',
selected_at_window: { x, y },
}
engineCommandManager.sendSceneCommand(command)
} else if (!didDragInStream && state.matches('Sketch.Line Tool')) {
command.cmd = {
type: 'mouse_click',
window: { x, y },
}
engineCommandManager.sendSceneCommand(command).then(async (resp) => {
const entities_modified = resp?.data?.data?.entities_modified
if (!entities_modified) return
if (state.matches('Sketch.Line Tool.No Points')) {
send('Add point')
} else if (state.matches('Sketch.Line Tool.Point Added')) {
const curve = await engineCommandManager.sendSceneCommand({
type: 'modeling_cmd_req',
cmd_id: uuidv4(),
cmd: {
type: 'curve_get_control_points',
curve_id: entities_modified[0],
},
})
const coords: { x: number; y: number }[] =
curve.data.data.control_points
// We need the normal for the plane we are on.
const plane = await engineCommandManager.sendSceneCommand({
type: 'modeling_cmd_req',
cmd_id: uuidv4(),
cmd: {
type: 'get_sketch_mode_plane',
},
})
const z_axis = plane.data.data.z_axis
// Get the current axis.
let currentAxis: 'xy' | 'xz' | 'yz' | '-xy' | '-xz' | '-yz' | null =
null
if (context.sketchPlaneId === kclManager.getPlaneId('xy')) {
if (z_axis.z === -1) {
currentAxis = '-xy'
} else {
currentAxis = 'xy'
}
} else if (context.sketchPlaneId === kclManager.getPlaneId('yz')) {
if (z_axis.x === -1) {
currentAxis = '-yz'
} else {
currentAxis = 'yz'
}
} else if (context.sketchPlaneId === kclManager.getPlaneId('xz')) {
if (z_axis.y === -1) {
currentAxis = '-xz'
} else {
currentAxis = 'xz'
}
}
send({
type: 'Add point',
data: {
coords,
axis: currentAxis,
segmentId: entities_modified[0],
},
})
} else if (state.matches('Sketch.Line Tool.Segment Added')) {
const curve = await engineCommandManager.sendSceneCommand({
type: 'modeling_cmd_req',
cmd_id: uuidv4(),
cmd: {
type: 'curve_get_control_points',
curve_id: entities_modified[0],
},
})
const coords: { x: number; y: number }[] =
curve.data.data.control_points
send({
type: 'Add point',
data: { coords, axis: null, segmentId: entities_modified[0] },
})
}
})
} else if (
!didDragInStream &&
(state.matches('Sketch.SketchIdle') || state.matches('idle'))
) {
command.cmd = {
type: 'select_with_point',
selected_at_window: { x, y },
selection_type: 'add',
}
engineCommandManager.sendSceneCommand(command)
} else if (!didDragInStream && state.matches('Sketch.Move Tool')) {
command.cmd = { command.cmd = {
type: 'select_with_point', type: 'select_with_point',
selected_at_window: { x, y }, selected_at_window: { x, y },
selection_type: 'add', selection_type: 'add',
} }
engineCommandManager.sendSceneCommand(command) engineCommandManager.sendSceneCommand(command)
} else if (didDragInStream && state.matches('Sketch.Move Tool')) { } else if (didDragInStream) {
command.cmd = { command.cmd = {
type: 'handle_mouse_drag_end', type: 'handle_mouse_drag_end',
window: { x, y }, window: { x, y },
} }
engineCommandManager.sendSceneCommand(command).then(async () => { void engineCommandManager.sendSceneCommand(command)
if (!context.sketchPathToNode) return
getNodeFromPath<VariableDeclarator>(
kclManager.ast,
context.sketchPathToNode,
'VariableDeclarator'
)
// Get the current plane string for plane we are on.
let currentPlaneString = ''
if (context.sketchPlaneId === kclManager.getPlaneId('xy')) {
currentPlaneString = 'XY'
} else if (context.sketchPlaneId === kclManager.getPlaneId('yz')) {
currentPlaneString = 'YZ'
} else if (context.sketchPlaneId === kclManager.getPlaneId('xz')) {
currentPlaneString = 'XZ'
}
// Do not supporting editing/moving lines on a non-default plane.
// Eventually we can support this but for now we will just throw an
// error.
if (currentPlaneString === '') return
const pathInfo = await engineCommandManager.sendSceneCommand({
type: 'modeling_cmd_req',
cmd_id: uuidv4(),
cmd: {
type: 'path_get_info',
path_id: context.sketchEnginePathId,
},
})
const segmentsWithMappings = (
pathInfo?.data?.data?.segments as { command_id: string }[]
)
.filter(({ command_id }) => {
return command_id && engineCommandManager.artifactMap[command_id]
})
.map(({ command_id }) => command_id)
const segment2dInfo = await Promise.all(
segmentsWithMappings.map(async (segmentId) => {
const response = await engineCommandManager.sendSceneCommand({
type: 'modeling_cmd_req',
cmd_id: uuidv4(),
cmd: {
type: 'curve_get_control_points',
curve_id: segmentId,
},
})
const controlPoints: [
{ x: number; y: number },
{ x: number; y: number }
] = response.data.data.control_points
return {
controlPoints,
segmentId,
}
})
)
let modifiedAst = { ...kclManager.ast }
let code = kclManager.code
for (const controlPoint of segment2dInfo) {
const range =
engineCommandManager.artifactMap[controlPoint.segmentId].range
if (!range) continue
const from = controlPoint.controlPoints[0]
const to = controlPoint.controlPoints[1]
const modded = changeSketchArguments(
modifiedAst,
kclManager.programMemory,
range,
[to.x, to.y],
[from.x, from.y]
)
modifiedAst = modded.modifiedAst
// update artifact map ranges now that we have updated the ast.
code = recast(modded.modifiedAst)
const astWithCurrentRanges = kclManager.safeParse(code)
if (!astWithCurrentRanges) return
const updateNode = getNodeFromPath<CallExpression>(
astWithCurrentRanges,
modded.pathToNode
).node
engineCommandManager.artifactMap[controlPoint.segmentId].range = [
updateNode.start,
updateNode.end,
]
}
kclManager.executeAstMock(modifiedAst, true)
})
} else { } else {
engineCommandManager.sendSceneCommand(command) engineCommandManager.sendSceneCommand(command)
} }
@ -364,6 +129,8 @@ export const Stream = ({ className = '' }) => {
} }
const handleMouseMove: MouseEventHandler<HTMLVideoElement> = (e) => { const handleMouseMove: MouseEventHandler<HTMLVideoElement> = (e) => {
if (state.matches('Sketch')) return
if (state.matches('Sketch no face')) return
if (!clickCoords) return if (!clickCoords) return
const delta = const delta =
@ -376,16 +143,19 @@ export const Stream = ({ className = '' }) => {
} }
return ( return (
<div id="stream" className={className}> <div
id="stream"
className={className}
onMouseUp={handleMouseUp}
onMouseDown={handleMouseDown}
onContextMenu={(e) => e.preventDefault()}
onContextMenuCapture={(e) => e.preventDefault()}
>
<video <video
ref={videoRef} ref={videoRef}
muted muted
autoPlay autoPlay
controls={false} controls={false}
onMouseDown={handleMouseDown}
onMouseUp={handleMouseUp}
onContextMenu={(e) => e.preventDefault()}
onContextMenuCapture={(e) => e.preventDefault()}
onWheel={handleScroll} onWheel={handleScroll}
onPlay={() => setIsLoading(false)} onPlay={() => setIsLoading(false)}
onMouseMoveCapture={handleMouseMove} onMouseMoveCapture={handleMouseMove}
@ -393,6 +163,7 @@ export const Stream = ({ className = '' }) => {
disablePictureInPicture disablePictureInPicture
style={{ transitionDuration: '200ms', transitionProperty: 'filter' }} style={{ transitionDuration: '200ms', transitionProperty: 'filter' }}
/> />
<ClientSideScene cameraControls={settings.context.cameraControls} />
{isLoading && ( {isLoading && (
<div className="text-center absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2"> <div className="text-center absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2">
<Loading> <Loading>

View File

@ -3,20 +3,20 @@ import ReactCodeMirror, {
ViewUpdate, ViewUpdate,
keymap, keymap,
} from '@uiw/react-codemirror' } from '@uiw/react-codemirror'
import { FromServer, IntoServer } from 'editor/lsp/codec' import { FromServer, IntoServer } from 'editor/plugins/lsp/codec'
import Server from '../editor/lsp/server' import Server from '../editor/plugins/lsp/server'
import Client from '../editor/lsp/client' import Client from '../editor/plugins/lsp/client'
import { TEST } from 'env' import { TEST } from 'env'
import { useCommandsContext } from 'hooks/useCommandsContext' import { useCommandsContext } from 'hooks/useCommandsContext'
import { useGlobalStateContext } from 'hooks/useGlobalStateContext' import { useGlobalStateContext } from 'hooks/useGlobalStateContext'
import { useConvertToVariable } from 'hooks/useToolbarGuards' import { useConvertToVariable } from 'hooks/useToolbarGuards'
import { Themes } from 'lib/theme' import { Themes } from 'lib/theme'
import { useMemo } from 'react' import { useMemo, useRef } from 'react'
import { linter, lintGutter } from '@codemirror/lint' import { linter, lintGutter } from '@codemirror/lint'
import { useStore } from 'useStore' import { useStore } from 'useStore'
import { processCodeMirrorRanges } from 'lib/selections' import { processCodeMirrorRanges } from 'lib/selections'
import { LanguageServerClient } from 'editor/lsp' import { LanguageServerClient } from 'editor/plugins/lsp'
import kclLanguage from 'editor/lsp/language' import kclLanguage from 'editor/plugins/lsp/kcl/language'
import { EditorView, lineHighlightField } from 'editor/highlightextension' import { EditorView, lineHighlightField } from 'editor/highlightextension'
import { roundOff } from 'lib/utils' import { roundOff } from 'lib/utils'
import { kclErrToDiagnostic } from 'lang/errors' import { kclErrToDiagnostic } from 'lang/errors'
@ -24,7 +24,12 @@ import { CSSRuleObject } from 'tailwindcss/types/config'
import { useModelingContext } from 'hooks/useModelingContext' import { useModelingContext } from 'hooks/useModelingContext'
import interact from '@replit/codemirror-interact' import interact from '@replit/codemirror-interact'
import { engineCommandManager } from '../lang/std/engineConnection' import { engineCommandManager } from '../lang/std/engineConnection'
import { kclManager, useKclContext } from 'lang/KclSinglton' import { kclManager, useKclContext } from 'lang/KclSingleton'
import { ModelingMachineEvent } from 'machines/modelingMachine'
import { sceneInfra } from 'clientSideScene/sceneInfra'
import { copilotPlugin } from 'editor/plugins/lsp/copilot'
import { isTauri } from 'lib/isTauri'
import type * as LSP from 'vscode-languageserver-protocol'
export const editorShortcutMeta = { export const editorShortcutMeta = {
formatCode: { formatCode: {
@ -37,6 +42,15 @@ export const editorShortcutMeta = {
}, },
} }
function getWorkspaceFolders(): LSP.WorkspaceFolder[] {
// We only use workspace folders in Tauri since that is where we use more than
// one file.
if (isTauri()) {
return [{ uri: 'file://', name: 'ProjectRoot' }]
}
return []
}
export const TextEditor = ({ export const TextEditor = ({
theme, theme,
}: { }: {
@ -44,25 +58,31 @@ export const TextEditor = ({
}) => { }) => {
const { const {
editorView, editorView,
isLSPServerReady, isKclLspServerReady,
isCopilotLspServerReady,
setEditorView, setEditorView,
setIsLSPServerReady, setIsKclLspServerReady,
setIsCopilotLspServerReady,
isShiftDown, isShiftDown,
} = useStore((s) => ({ } = useStore((s) => ({
editorView: s.editorView, editorView: s.editorView,
isLSPServerReady: s.isLSPServerReady, isKclLspServerReady: s.isKclLspServerReady,
isCopilotLspServerReady: s.isCopilotLspServerReady,
setEditorView: s.setEditorView, setEditorView: s.setEditorView,
setIsLSPServerReady: s.setIsLSPServerReady, setIsKclLspServerReady: s.setIsKclLspServerReady,
setIsCopilotLspServerReady: s.setIsCopilotLspServerReady,
isShiftDown: s.isShiftDown, isShiftDown: s.isShiftDown,
})) }))
const { code, errors } = useKclContext() const { code, errors } = useKclContext()
const lastEvent = useRef({ event: '', time: Date.now() })
const { const {
context: { selectionRanges, selectionRangeTypeMap }, context: { selectionRanges, selectionRangeTypeMap },
send, send,
state,
} = useModelingContext() } = useModelingContext()
const { settings: { context: { textWrapping } = {} } = {} } = const { settings: { context: { textWrapping } = {} } = {}, auth } =
useGlobalStateContext() useGlobalStateContext()
const { commandBarSend } = useCommandsContext() const { commandBarSend } = useCommandsContext()
const { enable: convertEnabled, handleClick: convertCallback } = const { enable: convertEnabled, handleClick: convertCallback } =
@ -71,20 +91,20 @@ export const TextEditor = ({
// So this is a bit weird, we need to initialize the lsp server and client. // So this is a bit weird, we need to initialize the lsp server and client.
// But the server happens async so we break this into two parts. // But the server happens async so we break this into two parts.
// Below is the client and server promise. // Below is the client and server promise.
const { lspClient } = useMemo(() => { const { lspClient: kclLspClient } = useMemo(() => {
const intoServer: IntoServer = new IntoServer() const intoServer: IntoServer = new IntoServer()
const fromServer: FromServer = FromServer.create() const fromServer: FromServer = FromServer.create()
const client = new Client(fromServer, intoServer) const client = new Client(fromServer, intoServer)
if (!TEST) { if (!TEST) {
Server.initialize(intoServer, fromServer).then((lspServer) => { Server.initialize(intoServer, fromServer).then((lspServer) => {
lspServer.start() lspServer.start('kcl')
setIsLSPServerReady(true) setIsKclLspServerReady(true)
}) })
} }
const lspClient = new LanguageServerClient({ client }) const lspClient = new LanguageServerClient({ client, name: 'kcl' })
return { lspClient } return { lspClient }
}, [setIsLSPServerReady]) }, [setIsKclLspServerReady])
// Here we initialize the plugin which will start the client. // Here we initialize the plugin which will start the client.
// When we have multi-file support the name of the file will be a dep of // When we have multi-file support the name of the file will be a dep of
@ -93,19 +113,57 @@ export const TextEditor = ({
// We do not want to restart the server, its just wasteful. // We do not want to restart the server, its just wasteful.
const kclLSP = useMemo(() => { const kclLSP = useMemo(() => {
let plugin = null let plugin = null
if (isLSPServerReady && !TEST) { if (isKclLspServerReady && !TEST) {
// Set up the lsp plugin. // Set up the lsp plugin.
const lsp = kclLanguage({ const lsp = kclLanguage({
// When we have more than one file, we'll need to change this. // When we have more than one file, we'll need to change this.
documentUri: `file:///we-just-have-one-file-for-now.kcl`, documentUri: `file:///we-just-have-one-file-for-now.kcl`,
workspaceFolders: null, workspaceFolders: getWorkspaceFolders(),
client: lspClient, client: kclLspClient,
}) })
plugin = lsp plugin = lsp
} }
return plugin return plugin
}, [lspClient, isLSPServerReady]) }, [kclLspClient, isKclLspServerReady])
const { lspClient: copilotLspClient } = useMemo(() => {
const intoServer: IntoServer = new IntoServer()
const fromServer: FromServer = FromServer.create()
const client = new Client(fromServer, intoServer)
if (!TEST) {
Server.initialize(intoServer, fromServer).then((lspServer) => {
const token = auth?.context?.token
lspServer.start('copilot', token)
setIsCopilotLspServerReady(true)
})
}
const lspClient = new LanguageServerClient({ client, name: 'copilot' })
return { lspClient }
}, [setIsCopilotLspServerReady])
// Here we initialize the plugin which will start the client.
// When we have multi-file support the name of the file will be a dep of
// this use memo, as well as the directory structure, which I think is
// a good setup because it will restart the client but not the server :)
// We do not want to restart the server, its just wasteful.
const copilotLSP = useMemo(() => {
let plugin = null
if (isCopilotLspServerReady && !TEST) {
// Set up the lsp plugin.
const lsp = copilotPlugin({
// When we have more than one file, we'll need to change this.
documentUri: `file:///we-just-have-one-file-for-now.kcl`,
workspaceFolders: getWorkspaceFolders(),
client: copilotLspClient,
allowHTMLContent: true,
})
plugin = lsp
}
return plugin
}, [copilotLspClient, isCopilotLspServerReady])
// const onChange = React.useCallback((value: string, viewUpdate: ViewUpdate) => { // const onChange = React.useCallback((value: string, viewUpdate: ViewUpdate) => {
const onChange = (newCode: string) => { const onChange = (newCode: string) => {
@ -115,6 +173,12 @@ export const TextEditor = ({
if (!editorView) { if (!editorView) {
setEditorView(viewUpdate.view) setEditorView(viewUpdate.view)
} }
if (sceneInfra.selected) return // mid drag
const ignoreEvents: ModelingMachineEvent['type'][] = [
'Equip Line tool',
'Equip tangential arc to',
]
if (ignoreEvents.includes(state.event.type)) return
const eventInfo = processCodeMirrorRanges({ const eventInfo = processCodeMirrorRanges({
codeMirrorRanges: viewUpdate.state.selection.ranges, codeMirrorRanges: viewUpdate.state.selection.ranges,
selectionRanges, selectionRanges,
@ -122,7 +186,20 @@ export const TextEditor = ({
isShiftDown, isShiftDown,
}) })
if (!eventInfo) return if (!eventInfo) return
const deterministicEventInfo = {
...eventInfo,
engineEvents: eventInfo.engineEvents.map((e) => ({
...e,
cmd_id: 'static',
})),
}
const stringEvent = JSON.stringify(deterministicEventInfo)
if (
stringEvent === lastEvent.current.event &&
Date.now() - lastEvent.current.time < 500
)
return // don't repeat events
lastEvent.current = { event: stringEvent, time: Date.now() }
send(eventInfo.modelingEvent) send(eventInfo.modelingEvent)
eventInfo.engineEvents.forEach((event) => eventInfo.engineEvents.forEach((event) =>
engineCommandManager.sendSceneCommand(event) engineCommandManager.sendSceneCommand(event)
@ -161,6 +238,7 @@ export const TextEditor = ({
] as Extension[] ] as Extension[]
if (kclLSP) extensions.push(kclLSP) if (kclLSP) extensions.push(kclLSP)
if (copilotLSP) extensions.push(copilotLSP)
// These extensions have proven to mess with vitest // These extensions have proven to mess with vitest
if (!TEST) { if (!TEST) {

View File

@ -11,7 +11,7 @@ import {
getTransformInfos, getTransformInfos,
PathToNodeMap, PathToNodeMap,
} from '../../lang/std/sketchcombos' } from '../../lang/std/sketchcombos'
import { kclManager } from 'lang/KclSinglton' import { kclManager } from 'lang/KclSingleton'
export function equalAngleInfo({ export function equalAngleInfo({
selectionRanges, selectionRanges,

View File

@ -11,7 +11,7 @@ import {
getTransformInfos, getTransformInfos,
PathToNodeMap, PathToNodeMap,
} from '../../lang/std/sketchcombos' } from '../../lang/std/sketchcombos'
import { kclManager } from 'lang/KclSinglton' import { kclManager } from 'lang/KclSingleton'
export function setEqualLengthInfo({ export function setEqualLengthInfo({
selectionRanges, selectionRanges,

View File

@ -10,7 +10,7 @@ import {
getTransformInfos, getTransformInfos,
transformAstSketchLines, transformAstSketchLines,
} from '../../lang/std/sketchcombos' } from '../../lang/std/sketchcombos'
import { kclManager } from 'lang/KclSinglton' import { kclManager } from 'lang/KclSingleton'
export function horzVertInfo( export function horzVertInfo(
selectionRanges: Selections, selectionRanges: Selections,

View File

@ -15,7 +15,7 @@ import {
import { GetInfoModal, createInfoModal } from '../SetHorVertDistanceModal' import { GetInfoModal, createInfoModal } from '../SetHorVertDistanceModal'
import { createVariableDeclaration } from '../../lang/modifyAst' import { createVariableDeclaration } from '../../lang/modifyAst'
import { removeDoubleNegatives } from '../AvailableVarsHelpers' import { removeDoubleNegatives } from '../AvailableVarsHelpers'
import { kclManager } from 'lang/KclSinglton' import { kclManager } from 'lang/KclSingleton'
const getModalInfo = createInfoModal(GetInfoModal) const getModalInfo = createInfoModal(GetInfoModal)

View File

@ -10,7 +10,7 @@ import {
getRemoveConstraintsTransforms, getRemoveConstraintsTransforms,
transformAstSketchLines, transformAstSketchLines,
} from '../../lang/std/sketchcombos' } from '../../lang/std/sketchcombos'
import { kclManager } from 'lang/KclSinglton' import { kclManager } from 'lang/KclSingleton'
export function removeConstrainingValuesInfo({ export function removeConstrainingValuesInfo({
selectionRanges, selectionRanges,

View File

@ -19,7 +19,7 @@ import {
createVariableDeclaration, createVariableDeclaration,
} from '../../lang/modifyAst' } from '../../lang/modifyAst'
import { removeDoubleNegatives } from '../AvailableVarsHelpers' import { removeDoubleNegatives } from '../AvailableVarsHelpers'
import { kclManager } from 'lang/KclSinglton' import { kclManager } from 'lang/KclSingleton'
const getModalInfo = createSetAngleLengthModal(SetAngleLengthModal) const getModalInfo = createSetAngleLengthModal(SetAngleLengthModal)
@ -139,7 +139,7 @@ export function applyConstraintAxisAlign({
constraint, constraint,
}).transforms }).transforms
let finalValue = createIdentifier('_0') let finalValue = createIdentifier('ZERO')
return transformAstSketchLines({ return transformAstSketchLines({
ast: JSON.parse(JSON.stringify(kclManager.ast)), ast: JSON.parse(JSON.stringify(kclManager.ast)),

View File

@ -14,7 +14,7 @@ import {
import { GetInfoModal, createInfoModal } from '../SetHorVertDistanceModal' import { GetInfoModal, createInfoModal } from '../SetHorVertDistanceModal'
import { createVariableDeclaration } from '../../lang/modifyAst' import { createVariableDeclaration } from '../../lang/modifyAst'
import { removeDoubleNegatives } from '../AvailableVarsHelpers' import { removeDoubleNegatives } from '../AvailableVarsHelpers'
import { kclManager } from 'lang/KclSinglton' import { kclManager } from 'lang/KclSingleton'
const getModalInfo = createInfoModal(GetInfoModal) const getModalInfo = createInfoModal(GetInfoModal)

View File

@ -13,7 +13,7 @@ import {
import { GetInfoModal, createInfoModal } from '../SetHorVertDistanceModal' import { GetInfoModal, createInfoModal } from '../SetHorVertDistanceModal'
import { createLiteral, createVariableDeclaration } from '../../lang/modifyAst' import { createLiteral, createVariableDeclaration } from '../../lang/modifyAst'
import { removeDoubleNegatives } from '../AvailableVarsHelpers' import { removeDoubleNegatives } from '../AvailableVarsHelpers'
import { kclManager } from 'lang/KclSinglton' import { kclManager } from 'lang/KclSingleton'
import { Selections } from 'lib/selections' import { Selections } from 'lib/selections'
const getModalInfo = createInfoModal(GetInfoModal) const getModalInfo = createInfoModal(GetInfoModal)

View File

@ -21,7 +21,7 @@ import {
} from '../../lang/modifyAst' } from '../../lang/modifyAst'
import { removeDoubleNegatives } from '../AvailableVarsHelpers' import { removeDoubleNegatives } from '../AvailableVarsHelpers'
import { normaliseAngle } from '../../lib/utils' import { normaliseAngle } from '../../lib/utils'
import { kclManager } from 'lang/KclSinglton' import { kclManager } from 'lang/KclSingleton'
const getModalInfo = createSetAngleLengthModal(SetAngleLengthModal) const getModalInfo = createSetAngleLengthModal(SetAngleLengthModal)
@ -89,12 +89,16 @@ export async function applyConstraintAngleLength({
isReferencingXAxis && angleOrLength === 'setAngle' isReferencingXAxis && angleOrLength === 'setAngle'
let forceVal = valueUsedInTransform || 0 let forceVal = valueUsedInTransform || 0
let calcIdentifier = createIdentifier('_0') let calcIdentifier = createIdentifier('ZERO')
if (isReferencingYAxisAngle) { if (isReferencingYAxisAngle) {
calcIdentifier = createIdentifier(forceVal < 0 ? '_270' : '_90') calcIdentifier = createIdentifier(
forceVal < 0 ? 'THREE_QUARTER_TURN' : 'QUARTER_TURN'
)
forceVal = normaliseAngle(forceVal + (forceVal < 0 ? 90 : -90)) forceVal = normaliseAngle(forceVal + (forceVal < 0 ? 90 : -90))
} else if (isReferencingXAxisAngle) { } else if (isReferencingXAxisAngle) {
calcIdentifier = createIdentifier(Math.abs(forceVal) > 90 ? '_180' : '_0') calcIdentifier = createIdentifier(
Math.abs(forceVal) > 90 ? 'HALF_TURN' : 'ZERO'
)
forceVal = forceVal =
Math.abs(forceVal) > 90 ? normaliseAngle(forceVal - 180) : forceVal Math.abs(forceVal) > 90 ? normaliseAngle(forceVal - 180) : forceVal
} }
@ -112,7 +116,7 @@ export async function applyConstraintAngleLength({
) )
if ( if (
isReferencingYAxisAngle || isReferencingYAxisAngle ||
(isReferencingXAxisAngle && calcIdentifier.name !== '_0') (isReferencingXAxisAngle && calcIdentifier.name !== 'ZERO')
) { ) {
finalValue = createBinaryExpressionWithUnary([calcIdentifier, finalValue]) finalValue = createBinaryExpressionWithUnary([calcIdentifier, finalValue])
} }

View File

@ -4,7 +4,7 @@ import { faBars, faBug, faSignOutAlt } from '@fortawesome/free-solid-svg-icons'
import { faGithub } from '@fortawesome/free-brands-svg-icons' import { faGithub } from '@fortawesome/free-brands-svg-icons'
import { useLocation, useNavigate } from 'react-router-dom' import { useLocation, useNavigate } from 'react-router-dom'
import { Fragment, useState } from 'react' import { Fragment, useState } from 'react'
import { paths } from '../Router' import { paths } from 'lib/paths'
import { Models } from '@kittycad/lib' import { Models } from '@kittycad/lib'
import { useGlobalStateContext } from 'hooks/useGlobalStateContext' import { useGlobalStateContext } from 'hooks/useGlobalStateContext'
import { useAbsoluteFilePath } from 'hooks/useAbsoluteFilePath' import { useAbsoluteFilePath } from 'hooks/useAbsoluteFilePath'
@ -117,7 +117,7 @@ const UserSidebarMenu = ({ user }: { user?: User }) => {
<div className="p-4 flex flex-col gap-2"> <div className="p-4 flex flex-col gap-2">
<ActionButton <ActionButton
Element="button" Element="button"
icon={{ icon: 'gear' }} icon={{ icon: 'settings' }}
className="border-transparent dark:border-transparent hover:bg-transparent" className="border-transparent dark:border-transparent hover:bg-transparent"
onClick={() => { onClick={() => {
// since /settings is a nested route the sidebar doesn't close // since /settings is a nested route the sidebar doesn't close

View File

@ -1,7 +1,7 @@
import { Dialog } from '@headlessui/react' import { Dialog } from '@headlessui/react'
import { useState } from 'react' import { useState } from 'react'
import { ActionButton } from './ActionButton' import { ActionButton } from './ActionButton'
import { useKclContext } from 'lang/KclSinglton' import { useKclContext } from 'lang/KclSingleton'
export function WasmErrBanner() { export function WasmErrBanner() {
const [isBannerDismissed, setBannerDismissed] = useState(false) const [isBannerDismissed, setBannerDismissed] = useState(false)

View File

@ -65,6 +65,7 @@ export default class Client extends jsrpc.JSONRPCServerAndClient {
afterInitializedHooks: (() => Promise<void>)[] = [] afterInitializedHooks: (() => Promise<void>)[] = []
#fromServer: FromServer #fromServer: FromServer
private serverCapabilities: LSP.ServerCapabilities<any> = {} private serverCapabilities: LSP.ServerCapabilities<any> = {}
private notifyFn: ((message: LSP.NotificationMessage) => void) | null = null
constructor(fromServer: FromServer, intoServer: IntoServer) { constructor(fromServer: FromServer, intoServer: IntoServer) {
super( super(
@ -167,9 +168,15 @@ export default class Client extends jsrpc.JSONRPCServerAndClient {
return this.serverCapabilities return this.serverCapabilities
} }
setNotifyFn(fn: (message: LSP.NotificationMessage) => void): void {
this.notifyFn = fn
}
async processNotifications(): Promise<void> { async processNotifications(): Promise<void> {
for await (const notification of this.#fromServer.notifications) { for await (const notification of this.#fromServer.notifications) {
await this.receiveAndSend(notification) if (this.notifyFn) {
this.notifyFn(notification)
}
} }
} }

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