Compare commits

...

101 Commits

Author SHA1 Message Date
da323e22d4 Cut release v0.21.0 (#2334) 2024-05-10 16:23:04 -04:00
8dc3628e9b Bump kittycad-modeling-cmds from 0.2.23 to 0.2.24 in /src/wasm-lib (#2330)
Bumps [kittycad-modeling-cmds](https://github.com/KittyCAD/modeling-api) from 0.2.23 to 0.2.24.
- [Commits](https://github.com/KittyCAD/modeling-api/compare/kittycad-modeling-cmds-0.2.23...kittycad-modeling-cmds-0.2.24)

---
updated-dependencies:
- dependency-name: kittycad-modeling-cmds
  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-05-09 11:38:43 -07:00
253744867b Franknoirot/sketch light mode (#2328) 2024-05-09 08:38:42 -04:00
c45eb1e3e3 clean up old imports (#2331) 2024-05-09 15:38:04 +10:00
758aac9328 fix unreliable channel (#2329)
* fix unreliable channel

* add test for hovering
2024-05-09 15:04:33 +10:00
309943cf2c Bump proc-macro2 from 1.0.81 to 1.0.82 in /src/wasm-lib (#2321)
Bumps [proc-macro2](https://github.com/dtolnay/proc-macro2) from 1.0.81 to 1.0.82.
- [Release notes](https://github.com/dtolnay/proc-macro2/releases)
- [Commits](https://github.com/dtolnay/proc-macro2/compare/1.0.81...1.0.82)

---
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-05-08 10:56:37 -07:00
b3d4ab91fc Bump serde from 1.0.200 to 1.0.201 in /src/wasm-lib (#2320)
Bumps [serde](https://github.com/serde-rs/serde) from 1.0.200 to 1.0.201.
- [Release notes](https://github.com/serde-rs/serde/releases)
- [Commits](https://github.com/serde-rs/serde/compare/v1.0.200...v1.0.201)

---
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-05-08 10:56:21 -07:00
5e73fa45f0 Remove useBackdropHighlight (#2323) 2024-05-08 12:27:41 -04:00
17d23a17db Move the command bar out to the right in the AppHeader (#2317)
* Move the command bar out to the right in the AppHeader

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

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

* trigger ci

* trigger ci

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Kurt Hutten <k.hutten@protonmail.ch>
2024-05-08 09:57:16 -04:00
0460f8eaee Cut release v0.20.2 (#2319)
Signed-off-by: Jess Frazelle <github@jessfraz.com>
2024-05-08 01:35:55 +00:00
2077cdb6fc remove code-pane stuff temporarily again (#2318)
Signed-off-by: Jess Frazelle <github@jessfraz.com>
2024-05-08 01:21:52 +00:00
cb0b7e8169 Make "Extrude from command bar" test selection via 3D scene, not code (#2313) 2024-05-07 20:01:52 -04:00
3a05211d30 Mac TestFlight in nightly runs only (#2312) 2024-05-07 12:33:04 -07:00
d12d103cba Franknoirot/refresh button add (#2314)
* Add a simple refresh button

* Add plausible event for when Refresh button is clicked
2024-05-07 14:33:11 -04:00
04f6d3dcc8 Bump syn from 2.0.60 to 2.0.61 in /src/wasm-lib (#2310)
Bumps [syn](https://github.com/dtolnay/syn) from 2.0.60 to 2.0.61.
- [Release notes](https://github.com/dtolnay/syn/releases)
- [Commits](https://github.com/dtolnay/syn/compare/2.0.60...2.0.61)

---
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-05-07 09:40:21 -07:00
9c9ffa0d03 Bump anyhow from 1.0.82 to 1.0.83 in /src/wasm-lib (#2309)
Bumps [anyhow](https://github.com/dtolnay/anyhow) from 1.0.82 to 1.0.83.
- [Release notes](https://github.com/dtolnay/anyhow/releases)
- [Commits](https://github.com/dtolnay/anyhow/compare/1.0.82...1.0.83)

---
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-05-07 09:40:13 -07:00
c62b9f1f04 Bump thiserror from 1.0.59 to 1.0.60 in /src/wasm-lib (#2307)
Bumps [thiserror](https://github.com/dtolnay/thiserror) from 1.0.59 to 1.0.60.
- [Release notes](https://github.com/dtolnay/thiserror/releases)
- [Commits](https://github.com/dtolnay/thiserror/compare/1.0.59...1.0.60)

---
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-05-07 09:40:06 -07:00
fcac3c72e4 Exit edit mode when selection input is up, re-enter when it's not. (#2306) 2024-05-06 18:52:17 -04:00
1e2f577a9f tell the save dialog the file extension (#2303)
Signed-off-by: Jess Frazelle <github@jessfraz.com>
2024-05-06 13:07:35 -07:00
1814f340fb Disable tauri e2e tests on release (#2299) 2024-05-06 11:25:08 +00:00
43928f88aa enable editor changes in sketch mode, refactor some of the code manager (#2287)
* Revert "Revert "client side sketch updating properly with direct changes to t… (#2286)"

This reverts commit e7ab645267.

* rejig out side of xstate

* test tweak

* more test tweak

* refactor some codeManager stuff

* tsc

* try and fix tests

* revert small uneeded change

* fix

* snapshot tweak

* more test tweak

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

* small tweak

* disable bad test

* fmt

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-05-06 19:28:30 +10:00
6959036688 Cut release v0.20.1 (#2292)
bump jsons

Signed-off-by: Jess Frazelle <github@jessfraz.com>
2024-05-03 15:18:10 -07:00
570d0473c6 switch to new sketch mode api (#2295)
* swtch to new api

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

* updates

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

* fix ts

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

* fix

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

* small changes

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-05-03 14:45:39 -07:00
44f0d7c25c turn on tauri app logging (#2296)
* turn on tauri app logging

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

* fixups

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

* switch everything to logs;

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

* remove other shit logs

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

* fix macos open

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

---------

Signed-off-by: Jess Frazelle <github@jessfraz.com>
2024-05-03 13:49:26 -07:00
3ccb04c4e7 Fix Create Release commit sha (#2293) 2024-05-03 05:37:27 -04:00
00058f699a Cut release v0.20.0 (#2284)
* Cut release v0.20.0

* Disable app store steps to see

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

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

* Bring back TAURI_CONF_ARGS

* Remove fileAssociations

* sketchy info.p;list

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

* turn back on app store

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

* remove entitlements

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

* fix clippy

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

* clippy;

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

* clupppy

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

* cluppy

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

* turn back on file associations

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

* cleanup general entitlements

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>
Co-authored-by: Jess Frazelle <github@jessfraz.com>
Co-authored-by: Jess Frazelle <jessfraz@users.noreply.github.com>
2024-05-02 20:37:00 -04:00
5a478fe0b3 log click opens from tauri otherwise i have zero visibiltiy (#2291)
log opens

Signed-off-by: Jess Frazelle <github@jessfraz.com>
2024-05-02 17:26:37 -07:00
723cf4f746 shebang hover (#2290)
* add a test

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

* plugoin

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

---------

Signed-off-by: Jess Frazelle <github@jessfraz.com>
2024-05-02 16:31:33 -07:00
3950de0a4d parse the shebang and make it work with recast (#2289)
* parse the shebang and make it work with recast

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

* updates

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

* fix playwright

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

* fix playwright

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

* fix playwright

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

---------

Signed-off-by: Jess Frazelle <github@jessfraz.com>
2024-05-02 15:13:00 -07:00
901d474986 Bump serde from 1.0.199 to 1.0.200 in /src/wasm-lib (#2280)
Bumps [serde](https://github.com/serde-rs/serde) from 1.0.199 to 1.0.200.
- [Release notes](https://github.com/serde-rs/serde/releases)
- [Commits](https://github.com/serde-rs/serde/compare/v1.0.199...v1.0.200)

---
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-05-02 09:47:52 -07:00
e7ab645267 Revert "client side sketch updating properly with direct changes to t… (#2286)
Revert "client side sketch updating properly with direct changes to the editor (#2283)"

This reverts commit cf830f9895.
2024-05-02 12:26:29 +00:00
cf830f9895 client side sketch updating properly with direct changes to the editor (#2283)
* client side sketch updating properly with direct changes to the editor

* try tweak
2024-05-02 20:53:57 +10:00
2c1f53f0f0 Update README.md (#2282) 2024-05-02 09:04:29 +00:00
d39e2502d0 makeTemplate test util (#2281)
* makeTemplate test util

* rename things

* fmt

* doc string

* clean up
2024-05-02 15:26:23 +10:00
51fed9c541 retain playw traces (#2278) 2024-05-02 01:06:05 +00:00
b3a09abe01 Bump kittycad-modeling-cmds from 0.2.21 to 0.2.22 in /src/wasm-lib (#2273)
Bumps [kittycad-modeling-cmds](https://github.com/KittyCAD/modeling-api) from 0.2.21 to 0.2.22.
- [Commits](https://github.com/KittyCAD/modeling-api/compare/kittycad-modeling-cmds-0.2.21...kittycad-modeling-cmds-0.2.22)

---
updated-dependencies:
- dependency-name: kittycad-modeling-cmds
  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-05-01 16:31:00 -07:00
cd3a2fea07 Bump base64 from 0.22.0 to 0.22.1 in /src/wasm-lib (#2274)
Bumps [base64](https://github.com/marshallpierce/rust-base64) from 0.22.0 to 0.22.1.
- [Changelog](https://github.com/marshallpierce/rust-base64/blob/master/RELEASE-NOTES.md)
- [Commits](https://github.com/marshallpierce/rust-base64/compare/v0.22.0...v0.22.1)

---
updated-dependencies:
- dependency-name: base64
  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-05-01 09:47:32 -07:00
c29c4a8567 Bring back tauri e2e tests (#2062)
* Bring back tauri e2e tests
Fixes #2061 once green

* Fix if

* Add bail mocha opt and more cleanup, disable second dir test

* Add mocha types and tsconfig

* Add 10sec delay for auth (worked in 22.04 local docker)

* Add back close settings click

* Disable open file

* Re-enable settings test

* Handle error page

* Back to brower.execute location.href

* Add --force to tauri-driver install (I think because of cache)

---------

Co-authored-by: Jess Frazelle <jessfraz@users.noreply.github.com>
2024-05-01 08:24:07 -04:00
39ccd94884 Remove tauri.release-macos.conf on debug builds (#2276)
* Remove tauri.release-macos.conf on debug builds

* BUILD_RELEASE: true

* Back to conditional BUILD_RELEASE for merge

* Remove tauri.release-macos.conf.json
2024-05-01 08:00:49 -04:00
d99ab22b56 Allow developers to override token behavior only for LSP plugin (#2223) 2024-04-30 19:13:46 -04:00
20a8f2aa6a Cut release vwhatever (not really cutting release, mucking with info.plist) (#2272)
* muck with info.plist

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

* updates

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

* handle urls

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

* fixups

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

* config args

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

* updates

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

* macos

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

* updates

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

* error on non relavent file

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>
2024-04-30 15:50:02 -07:00
93266a9819 Cut release v0.19.5 (not actually cutting a release, didn't bump version just testing macos app store things) (#2263)
* bump version

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

* fix

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

* updates

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

* 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>

* 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>

* 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>

* xcode version

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>

* updates

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

* entitlements

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

* entitlements

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

* entitlements

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

* updates

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

* specific config for app store

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

* add more geometry file formats

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

* add toml

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

* add LSHandlerRank

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

* field

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

* plist 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>

* line

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

* updates

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

* remove bad info.plist for now

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

* dont actually bump version

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

---------

Signed-off-by: Jess Frazelle <github@jessfraz.com>
2024-04-30 12:34:02 -07:00
a9c7a7cb13 Bump schemars from 0.8.16 to 0.8.17 in /src/wasm-lib (#2266)
Bumps [schemars](https://github.com/GREsau/schemars) from 0.8.16 to 0.8.17.
- [Release notes](https://github.com/GREsau/schemars/releases)
- [Changelog](https://github.com/GREsau/schemars/blob/master/CHANGELOG.md)
- [Commits](https://github.com/GREsau/schemars/compare/v0.8.16...v0.8.17)

---
updated-dependencies:
- dependency-name: schemars
  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-04-30 19:10:41 +00:00
8dd9b8d192 Bump serde from 1.0.198 to 1.0.199 in /src/wasm-lib (#2268)
Bumps [serde](https://github.com/serde-rs/serde) from 1.0.198 to 1.0.199.
- [Release notes](https://github.com/serde-rs/serde/releases)
- [Commits](https://github.com/serde-rs/serde/compare/v1.0.198...v1.0.199)

---
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-04-30 11:40:25 -07:00
23181d8144 Franknoirot/settings search (#2270)
* Search and highlight, no scroll yet

* Tweak toggle look

* Style search, fix state changes

* Separate out settings components

* Include description in results, minor style tweaks

* Fix tsc import

* Remove unused imports in Settings

* fmt
2024-04-30 14:37:32 -04:00
834967df6a Set max-bundle for local engine (#2271)
It's not permissible to transition the bundle policy during runtime, so
we need to maintain max-bundle, even if we know there's no ICE and it
won't matter any.

Signed-off-by: Paul R. Tagliamonte <paul@kittycad.io>
2024-04-30 12:39:06 -04:00
deacaac33a Update onboarding (#2269)
* Update onboarding

* update onboarding bracket png
2024-04-29 10:44:00 -07:00
c55603853b Change BundlePolicy to max-bundle (#2253)
Signed-off-by: Paul R. Tagliamonte <paul@kittycad.io>
2024-04-29 14:01:37 +00:00
93f652647e Add an E2E test for clicking all the way through the onboarding, restore code pane highlighting in onboarding (#2265)
* Add test clicking through each onboarding step

* Revert "get rid of code pane shit (#2259)"

This reverts commit d341681c0d.

* Fix the missing #code-pane id

* fmt
2024-04-26 10:20:03 -07:00
67cea620a6 Create a draft release on 'Cut release' PR merge (#2238)
* WIP: automate release, get PR info from commit

* Get version from PR title in script

* Add create release script call under comment, will wait for next release to test

* Test with v0.19.0

* Change to draft

* Clean up after v0.19.0 test

* Test for v0.19.1

* Clean up after test

---------

Co-authored-by: Jess Frazelle <jessfraz@users.noreply.github.com>
2024-04-26 05:04:58 -04:00
ed0c7d038d Deep links and app store pushing (#2256)
* start of deep links

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

* deep links

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

* deep links

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

* info.plist

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

* fix

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

* updates

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

* kcl

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

* mimetype

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>

* updates

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

* try half release

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>

* udates

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-04-25 22:02:11 -07:00
d3aa789761 Bump async-recursion from 1.1.0 to 1.1.1 in /src/wasm-lib (#2261)
Bumps [async-recursion](https://github.com/dcchut/async-recursion) from 1.1.0 to 1.1.1.
- [Release notes](https://github.com/dcchut/async-recursion/releases)
- [Commits](https://github.com/dcchut/async-recursion/compare/v1.1.0...v1.1.1)

---
updated-dependencies:
- dependency-name: async-recursion
  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-04-25 22:02:00 -07:00
cd68f80b71 bump version to 0.19.4 (#2262)
bump version

Signed-off-by: Jess Frazelle <github@jessfraz.com>
2024-04-25 21:33:05 -07:00
d341681c0d get rid of code pane shit (#2259)
Signed-off-by: Jess Frazelle <github@jessfraz.com>
2024-04-25 21:09:08 -07:00
0578e9d2a1 Bump actions/upload-artifact from 2 to 3 (#2260)
Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 2 to 3.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/v2...v3)

---
updated-dependencies:
- dependency-name: actions/upload-artifact
  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-04-25 21:00:59 -07:00
b413538e9e fetch wasm bundle locally (#2257) 2024-04-25 23:59:10 +00:00
c4e7754fc5 fix for relative path (#2252)
Signed-off-by: Jess Frazelle <github@jessfraz.com>
2024-04-25 22:53:44 +00:00
94515b5490 more speed up wasm build (#2254)
* more spped up wasm build

* try download again

* clean up

* rando change to rust to check it builds wasm

* make workflow more grokable/commented

* Revert "rando change to rust to check it builds wasm"

This reverts commit d2d9926b4b.
2024-04-25 22:27:28 +00:00
aa52407fda Cut release v0.19.3 (#2251) 2024-04-25 13:28:42 -07:00
e45be831d0 Pass the ?pool query param through to the backend. (#2246)
Pass the ?pool query param through to the backend.

This will slice off the ?pool= param and pass it to the WebSocket
request, which requests that the Zoo API use a particular pool of
engines. This isn't something any users of the zoo api require; but it's
needed for the internal engine Zoo development workflow. This may be
used in the future, but for now this'll be always enabled. Passing any
value in the production servers will result in a "no backend" error for
now.
2024-04-25 19:51:33 +00:00
005944f3a3 fix the updater (#2250)
Signed-off-by: Jess Frazelle <github@jessfraz.com>
2024-04-25 12:41:22 -07:00
755ef8ce7f download-wasm if there's no rust changes (#2234)
* download-wasm if there's no rust changes

* typo

* typo

* artifact stuff

* add needs

* permissions

* hmm

* more logic

* same for ubuntu
2024-04-26 05:37:32 +10:00
005d1f0ca7 Filter files and folders that start with a . (#2249) 2024-04-25 19:01:50 +00:00
e158f6f513 Better rust parsing of route uris for files (#2248)
* refactors

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

* updates

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

* fiex;

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

* fiex;

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>

* updates

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

---------

Signed-off-by: Jess Frazelle <github@jessfraz.com>
2024-04-25 18:55:11 +00:00
879d7ec4f4 Cut release v0.19.2 (#2247) 2024-04-25 14:38:25 -04:00
f6838b9b14 always ensure the dirs exist (#2245)
Signed-off-by: Jess Frazelle <github@jessfraz.com>
2024-04-25 17:07:24 +00:00
cb75c47631 fix env vars for lsp server to match other .env vars (#2243)
fix env vars for lsp

Signed-off-by: Jess Frazelle <github@jessfraz.com>
2024-04-25 16:41:39 +00:00
9b95ec1083 fix relevant extensions (#2241)
* 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>
2024-04-25 08:36:45 -07:00
a3eeff65c8 Cut release v0.19.1 (#2240) 2024-04-25 10:40:31 -04:00
fab3d2b130 Make FileTree a pane (desktop only) (#2232) 2024-04-25 09:56:55 -04:00
0a96dc6fd2 Project state improvements (#2239) 2024-04-25 12:52:08 +00:00
e123a00d4b Cut release v0.19.0 (#2206)
Co-authored-by: Kurt Hutten <k.hutten@protonmail.ch>
2024-04-25 07:30:49 -04:00
b950cc0583 able to go to and from app settings to executor settings for cli (#2236)
* settings from and into exefcutor settings

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

* updates

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

* settings

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

* ipdates

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

* ensure all have extrudes

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-04-25 09:31:18 +00:00
c89780a489 upload artifact to same action (#2235) 2024-04-25 17:43:48 +10:00
1afed68dd7 Settings move to rust (for read/write from files) (#2220)
* start of settings types

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

* updates

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

* add validator

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

* updates

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

* start of settings in rust

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

* fix wasm

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

* fix

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

* fix wasm

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>

* more tests

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

* derive docs

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

* configuration

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

* updates

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

* read and write functions with migration

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

* make more dry

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

* more parsing of app settings

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

* more things

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

* cleanup

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

* trim end

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

* project settings

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>

* fixes

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

* fixes

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

* cleanup tauri commands

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

* updates

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

* refactor

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

* refactor

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>

* change to files

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

* better

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

* cleanup more

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

* get rid of dead code

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

* fixed

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

* updates

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

* cleanup some more shit

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>

* add validation

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

* validation

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

* validate

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

* validate

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

* clippuy

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

* clippuy

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-04-25 07:13:09 +00:00
dcbed4f06f build and store wasm (#2233) 2024-04-25 07:01:42 +00:00
379f154a5c more parallel playw tests (#2179) 2024-04-25 07:00:41 +10:00
60c4969322 Adding dark edge lines in light mode + enabling SSAO (#2219)
* adding dark edge lines in light mode + enabling SSAO

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

* Rerun CI

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Frank Noirot <frank@zoo.dev>
Co-authored-by: Frank Noirot <frank@kittycad.io>
2024-04-24 16:59:25 -04:00
cc6dee8ad4 Make it impossible to crash app while extruding (#2224)
Co-authored-by: Kurt Hutten <k.hutten@protonmail.ch>
2024-04-24 16:34:56 -04:00
2fc7c0d5fd fix gizmo (#2221) 2024-04-24 20:09:39 +00:00
bf2dcd808f Bump tauri-plugin-http from 2.0.0-beta.5 to 2.0.0-beta.6 in /src-tauri (#2222)
Bumps [tauri-plugin-http](https://github.com/tauri-apps/plugins-workspace) from 2.0.0-beta.5 to 2.0.0-beta.6.
- [Release notes](https://github.com/tauri-apps/plugins-workspace/releases)
- [Commits](https://github.com/tauri-apps/plugins-workspace/compare/fs-v2.0.0-beta.5...fs-v2.0.0-beta.6)

---
updated-dependencies:
- dependency-name: tauri-plugin-http
  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-04-24 05:54:18 -04:00
ee21e486d4 math u32 fix (#2218)
* math fix

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

* updates

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

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

* empty

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

* empty

---------

Signed-off-by: Jess Frazelle <github@jessfraz.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-04-23 22:59:12 +00:00
b5a3eb9e9c Bump tauri-build from 2.0.0-beta.12 to 2.0.0-beta.13 in /src-tauri (#2204)
Bumps [tauri-build](https://github.com/tauri-apps/tauri) from 2.0.0-beta.12 to 2.0.0-beta.13.
- [Release notes](https://github.com/tauri-apps/tauri/releases)
- [Commits](https://github.com/tauri-apps/tauri/compare/tauri-build-v2.0.0-beta.12...tauri-build-v2.0.0-beta.13)

---
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-04-23 20:57:23 +00:00
c85645c9f2 Bump tauri-plugin-dialog from 2.0.0-beta.5 to 2.0.0-beta.6 in /src-tauri (#2203)
Bumps [tauri-plugin-dialog](https://github.com/tauri-apps/plugins-workspace) from 2.0.0-beta.5 to 2.0.0-beta.6.
- [Release notes](https://github.com/tauri-apps/plugins-workspace/releases)
- [Commits](https://github.com/tauri-apps/plugins-workspace/compare/fs-v2.0.0-beta.5...fs-v2.0.0-beta.6)

---
updated-dependencies:
- dependency-name: tauri-plugin-dialog
  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-04-23 18:48:00 +00:00
cfa4dd2e33 Bump kittycad-modeling-cmds from 0.2.20 to 0.2.21 in /src/wasm-lib (#2199)
Bumps [kittycad-modeling-cmds](https://github.com/KittyCAD/modeling-api) from 0.2.20 to 0.2.21.
- [Commits](https://github.com/KittyCAD/modeling-api/compare/kittycad-modeling-cmds-0.2.20...kittycad-modeling-cmds-0.2.21)

---
updated-dependencies:
- dependency-name: kittycad-modeling-cmds
  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-04-23 17:55:03 +00:00
c620f7269c Patterns fixes (2d) (#2197)
* updates

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

* patterns fixes

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

* fix docs

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

* better autocomplete

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-04-23 17:31:20 +00:00
2d8d29b345 Bump tauri-plugin-fs from 2.0.0-beta.5 to 2.0.0-beta.6 in /src-tauri (#2205)
Bumps [tauri-plugin-fs](https://github.com/tauri-apps/plugins-workspace) from 2.0.0-beta.5 to 2.0.0-beta.6.
- [Release notes](https://github.com/tauri-apps/plugins-workspace/releases)
- [Commits](https://github.com/tauri-apps/plugins-workspace/compare/fs-v2.0.0-beta.5...fs-v2.0.0-beta.6)

---
updated-dependencies:
- dependency-name: tauri-plugin-fs
  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-04-23 16:56:00 +00:00
00da062586 bump kittycad.rs (#2196)
* update lib

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

* fix tests

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

---------

Signed-off-by: Jess Frazelle <github@jessfraz.com>
2024-04-23 03:46:54 +00:00
aafbaf6c50 human speed completions (#2193)
* human speed completions

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

* add slowness

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

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

* empty

---------

Signed-off-by: Jess Frazelle <github@jessfraz.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-04-23 00:21:24 +00:00
2894c84a4e fix recast (#2194)
* fix recast

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

* fixes

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

---------

Signed-off-by: Jess Frazelle <github@jessfraz.com>
2024-04-22 17:14:20 -07:00
c01084feb0 Zoom to fit rust side (#2195)
* zoom to fit

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

* zoom to fit

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

* docs

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-04-22 17:14:10 -07:00
c461db5f54 fix const completion (#2192)
Signed-off-by: Jess Frazelle <github@jessfraz.com>
2024-04-22 21:53:49 +00:00
03fcb73aca Bump kittycad-modeling-cmds from 0.2.19 to 0.2.20 in /src/wasm-lib (#2186)
Bumps [kittycad-modeling-cmds](https://github.com/KittyCAD/modeling-api) from 0.2.19 to 0.2.20.
- [Commits](https://github.com/KittyCAD/modeling-api/compare/kittycad-modeling-cmds-0.2.19...kittycad-modeling-cmds-0.2.20)

---
updated-dependencies:
- dependency-name: kittycad-modeling-cmds
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Jess Frazelle <jessfraz@users.noreply.github.com>
2024-04-22 20:09:53 +00:00
8065e7e51a Bump thiserror from 1.0.58 to 1.0.59 in /src/wasm-lib (#2187)
Bumps [thiserror](https://github.com/dtolnay/thiserror) from 1.0.58 to 1.0.59.
- [Release notes](https://github.com/dtolnay/thiserror/releases)
- [Commits](https://github.com/dtolnay/thiserror/compare/1.0.58...1.0.59)

---
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>
Co-authored-by: Jess Frazelle <jessfraz@users.noreply.github.com>
2024-04-22 18:46:00 +00:00
2d0ac249df Cut release v0.18.1 (#2189)
* Cut release v0.18.1

* Fix release script
2024-04-22 09:47:10 -07:00
3d73b82c23 project global origin for sketches and use engine animations (#2113)
* use engine animations for sketch on face, but not default planes

* massage things

* fix type issue

* small problem in playwright test<

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

* some tests fixes

* more test tweaks

* more test tweaks

* clean up

* more tidy

* tests are a pain

* more test stuff

* test stuff again

* fix micro think axes in sketch mode

* more test shit

* more test shit more

* more test tweaks

* more test tweaks

* more test stuff

* trigger ci

* clean up

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-04-22 20:14:06 +10:00
0b235dc1cd Cut release v0.18.0 (#2177)
* Cut release v0.18.0

* Update src-tauri/tauri.conf.json

* Update src-tauri/tauri.conf.json

* Update src-tauri/tauri.conf.json

* Dumb tauri.conf.json issue

---------

Co-authored-by: Pierre Jacquier <pierrejacquier39@gmail.com>
2024-04-22 10:12:06 +02:00
0857415793 turn back on test (#2178)
* turn back on test

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

* format

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

---------

Signed-off-by: Jess Frazelle <github@jessfraz.com>
2024-04-20 01:16:33 +00:00
1da4fd03ef Bump @headlessui/react from 1.7.18 to 1.7.19 (#2172)
Bumps [@headlessui/react](https://github.com/tailwindlabs/headlessui/tree/HEAD/packages/@headlessui-react) from 1.7.18 to 1.7.19.
- [Release notes](https://github.com/tailwindlabs/headlessui/releases)
- [Changelog](https://github.com/tailwindlabs/headlessui/blob/main/packages/@headlessui-react/CHANGELOG.md)
- [Commits](https://github.com/tailwindlabs/headlessui/commits/@headlessui/react@v1.7.19/packages/@headlessui-react)

---
updated-dependencies:
- dependency-name: "@headlessui/react"
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-04-19 14:54:50 -07:00
39d84c12ab generate new images (#2176)
Signed-off-by: Jess Frazelle <github@jessfraz.com>
2024-04-19 21:27:40 +00:00
537d86c8ff Editor singleton to prevent re-renders (#2163)
* move editor data into a singleton

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

* debounce on update

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

* updates

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

* make select on extrude work

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

* highlight range

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

* highlight range

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

* updates

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

* fix errors

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

* updates

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

* almost forgot the error pane

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

* loint

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

* call out to codemirror

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

* updates

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

* fix tauri;

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

* updates

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

* more efficient

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

* create the modals in the hook

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

* Revert "create the modals in the hook"

This reverts commit bbeba85030763cf7235a09fa24247dbf120f2a64.

* change todo

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

---------

Signed-off-by: Jess Frazelle <github@jessfraz.com>
2024-04-19 21:24:40 +00:00
409 changed files with 14504 additions and 6277 deletions

View File

@ -3,3 +3,4 @@ VITE_KC_API_BASE_URL=https://api.dev.zoo.dev
VITE_KC_SITE_BASE_URL=https://dev.zoo.dev
VITE_KC_SKIP_AUTH=false
VITE_KC_CONNECTION_TIMEOUT_MS=5000
VITE_KC_DEV_TOKEN="your token from dev.zoo.dev should go in .env.development.local"

View File

@ -0,0 +1,33 @@
name: Build and Store WASM
on:
push:
branches:
- main
jobs:
build-and-upload:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version-file: '.nvmrc'
cache: 'yarn'
- name: Install dependencies
run: yarn
- name: Setup Rust
uses: dtolnay/rust-toolchain@stable
- name: Cache wasm
uses: Swatinem/rust-cache@v2
with:
workspaces: './src/wasm-lib'
- name: build wasm
run: yarn build:wasm
# Upload the WASM bundle as an artifact
- uses: actions/upload-artifact@v3
with:
name: wasm-bundle
path: src/wasm-lib/pkg

View File

@ -19,7 +19,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
dir: ['src/wasm-lib']
dir: ['src/wasm-lib', 'src-tauri']
steps:
- uses: actions/checkout@v4
- name: Install latest rust
@ -31,9 +31,22 @@ jobs:
- name: install dependencies
if: matrix.dir == 'src-tauri'
shell: bash
run: |
sudo apt-get update
sudo apt-get install -y libgtk-3-dev libwebkit2gtk-4.0-dev libappindicator3-dev librsvg2-dev patchelf
sudo apt-get install -y \
libgtk-3-dev \
libayatana-appindicator3-dev \
webkit2gtk-driver \
libsoup-3.0-dev \
libjavascriptcoregtk-4.1-dev \
libwebkit2gtk-4.1-dev \
at-spi2-core \
xvfb
yarn install
yarn build:wasm
yarn build:local
- name: Rust Cache
uses: Swatinem/rust-cache@v2.6.1

57
.github/workflows/cargo-test-tauri.yml vendored Normal file
View File

@ -0,0 +1,57 @@
on:
push:
branches:
- main
paths:
- 'src-tauri/**.rs'
- '**/Cargo.toml'
- '**/Cargo.lock'
- '**/rust-toolchain.toml'
- .github/workflows/cargo-test-tauri.yml
pull_request:
paths:
- 'src-tauri/**.rs'
- '**/Cargo.toml'
- '**/Cargo.lock'
- '**/rust-toolchain.toml'
- .github/workflows/cargo-test-tauri.yml
workflow_dispatch:
permissions: read-all
concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
cancel-in-progress: true
name: cargo test of tauri
jobs:
cargotest:
name: cargo test
runs-on: ubuntu-latest-8-cores
strategy:
matrix:
dir: ['src-tauri']
steps:
- uses: actions/checkout@v4
- name: Install latest rust
uses: actions-rs/toolchain@v1
with:
toolchain: stable
override: true
- name: install dependencies
if: matrix.dir == 'src-tauri'
run: |
sudo apt-get update
sudo apt-get install -y \
libgtk-3-dev \
libayatana-appindicator3-dev \
webkit2gtk-driver \
libsoup-3.0-dev \
libjavascriptcoregtk-4.1-dev \
libwebkit2gtk-4.1-dev \
at-spi2-core \
xvfb
- name: Rust Cache
uses: Swatinem/rust-cache@v2.6.1
- name: cargo test
shell: bash
run: |-
cd "${{ matrix.dir }}"
cargo test --all

View File

@ -13,7 +13,7 @@ on:
# Will checkout the last commit from the default branch (main as of 2023-10-04)
env:
BUILD_RELEASE: ${{ github.event_name == 'release' || github.event_name == 'schedule' || github.event_name == 'pull_request' && contains(github.event.pull_request.title, 'Cut release v') }}
BUILD_RELEASE: ${{ github.event_name == 'release' || github.event_name == 'schedule' || github.event_name == 'pull_request' && (contains(github.event.pull_request.title, 'Cut release v')) }}
concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
@ -50,7 +50,7 @@ jobs:
- run: yarn tsc
check-typos:
check-typos:
runs-on: ubuntu-latest
steps:
- name: Checkout
@ -98,7 +98,7 @@ jobs:
with:
node-version-file: '.nvmrc'
cache: 'yarn'
- name: Set nightly version
if: github.event_name == 'schedule'
run: |
@ -130,7 +130,9 @@ jobs:
matrix:
os: [macos-14, ubuntu-latest, windows-latest]
env:
# Specific Apple Universal target for macos
TAURI_ARGS_MACOS: ${{ matrix.os == 'macos-14' && '--target universal-apple-darwin' || '' }}
# Only build executable on linux (no appimage or deb)
TAURI_ARGS_UBUNTU: ${{ matrix.os == 'ubuntu-latest' && '--bundles' || '' }}
steps:
- uses: actions/checkout@v4
@ -143,21 +145,21 @@ jobs:
ls -l artifact
cp artifact/package.json package.json
cp artifact/src-tauri/tauri.conf.json src-tauri/tauri.conf.json
cp artifact/src-tauri/tauri.release.conf.json src-tauri/tauri.release.conf.json
cp artifact/src-tauri/tauri.release.conf.json src-tauri/tauri.release.conf.json
- name: Install ubuntu system dependencies
if: matrix.os == 'ubuntu-latest'
run: >
sudo apt-get update &&
sudo apt-get install -y
libgtk-3-dev
libayatana-appindicator3-dev
webkit2gtk-driver
libsoup-3.0-dev
libjavascriptcoregtk-4.1-dev
libwebkit2gtk-4.1-dev
at-spi2-core
xvfb
run: |
sudo apt-get update
sudo apt-get install -y \
libgtk-3-dev \
libayatana-appindicator3-dev \
webkit2gtk-driver \
libsoup-3.0-dev \
libjavascriptcoregtk-4.1-dev \
libwebkit2gtk-4.1-dev \
at-spi2-core \
xvfb
- name: Sync node version and setup cache
uses: actions/setup-node@v4
@ -237,6 +239,96 @@ jobs:
includeDebug: true
args: "${{ env.TAURI_ARGS_MACOS }} ${{ env.TAURI_ARGS_UBUNTU }}"
- name: Build for Mac TestFlight (nightly)
if: ${{ github.event_name == 'schedule' && matrix.os == 'macos-14' }}
shell: bash
run: |
unset APPLE_SIGNING_IDENTITY
unset APPLE_CERTIFICATE
sign_app="3rd Party Mac Developer Application: KittyCAD Inc (${APPLE_TEAM_ID})"
sign_install="3rd Party Mac Developer Installer: KittyCAD Inc (${APPLE_TEAM_ID})"
profile="src-tauri/entitlements/Mac_App_Distribution.provisionprofile"
mkdir -p src-tauri/entitlements
echo -n "${APPLE_STORE_PROVISIONING_PROFILE}" | base64 --decode -o "${profile}"
echo -n "${APPLE_STORE_DISTRIBUTION_CERT}" | base64 --decode -o "dist.cer"
echo -n "${APPLE_STORE_INSTALLER_CERT}" | base64 --decode -o "installer.cer"
KEYCHAIN_PATH=$RUNNER_TEMP/app-signing.keychain-db
KEYCHAIN_PASSWORD="password"
# create temporary keychain
security create-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH
security set-keychain-settings -lut 21600 $KEYCHAIN_PATH
security unlock-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH
# import certificate to keychain
security import "dist.cer" -P "$APPLE_STORE_P12_PASSWORD" -k $KEYCHAIN_PATH -f pkcs12 -t cert -A
security import "installer.cer" -P "$APPLE_STORE_P12_PASSWORD" -k $KEYCHAIN_PATH -f pkcs12 -t cert -A
security set-key-partition-list -S apple-tool:,apple: -k "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH
security list-keychain -d user -s $KEYCHAIN_PATH
target="universal-apple-darwin"
# Turn off the default target
# We don't want to install the updater for the apple store build
sed -i.bu "s/default =/# default =/" src-tauri/Cargo.toml
rm src-tauri/Cargo.toml.bu
git diff src-tauri/Cargo.toml
yarn tauri build --target "${target}" --verbose --config src-tauri/tauri.app-store.conf.json
app_path="src-tauri/target/${target}/release/bundle/macos/Zoo Modeling App.app"
build_name="src-tauri/target/${target}/release/bundle/macos/Zoo Modeling App.pkg"
cp_dir="src-tauri/target/${target}/release/bundle/macos/Zoo Modeling App.app/Contents/embedded.provisionprofile"
entitlements="src-tauri/entitlements/app-store.entitlements"
cp "${profile}" "${cp_dir}"
codesign --deep --force -s "${sign_app}" --entitlements "${entitlements}" "${app_path}"
productbuild --component "${app_path}" /Applications/ --sign "${sign_install}" "${build_name}"
# Undo the changes to the Cargo.toml
git checkout src-tauri/Cargo.toml
env:
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
APPLE_STORE_PROVISIONING_PROFILE: ${{ secrets.APPLE_STORE_PROVISIONING_PROFILE }}
APPLE_STORE_DISTRIBUTION_CERT: ${{ secrets.APPLE_STORE_DISTRIBUTION_CERT }}
APPLE_STORE_INSTALLER_CERT: ${{ secrets.APPLE_STORE_INSTALLER_CERT }}
APPLE_STORE_P12_PASSWORD: ${{ secrets.APPLE_STORE_P12_PASSWORD }}
- name: 'Upload to Mac TestFlight (nightly)'
uses: apple-actions/upload-testflight-build@v1
if: ${{ github.event_name == 'schedule' && matrix.os == 'macos-14' }}
with:
app-path: 'src-tauri/target/universal-apple-darwin/release/bundle/macos/Zoo Modeling App.pkg'
issuer-id: ${{ secrets.APPLE_STORE_ISSUER_ID }}
api-key-id: ${{ secrets.APPLE_STORE_API_KEY_ID }}
api-private-key: ${{ secrets.APPLE_STORE_API_PRIVATE_KEY }}
app-type: osx
- name: Clean up after Mac TestFlight (nightly)
if: ${{ github.event_name == 'schedule' && matrix.os == 'macos-14' }}
shell: bash
run: |
git status
# remove our target builds because we want to make sure the later build
# includes the updater, and that anything we changed with the target
# does not persist
rm -rf src-tauri/target
# Lets get rid of the info.plist for the normal mac builds since its
# being sketchy.
rm src-tauri/Info.plist
# We do this after the apple store because the apple store build is
# specific and we want to overwrite it with the this new build after and
# not upload the apple store build to the public bucket
- name: Build the app (release) and sign
uses: tauri-apps/tauri-action@v0
if: ${{ env.BUILD_RELEASE == 'true' }}
@ -261,11 +353,10 @@ jobs:
with:
path: "${{ env.PREFIX }}/${{ env.MODE }}/bundle/*/*"
# TODO: re-enable linux e2e tests when possible
- name: Run e2e tests (linux only)
if: false
if: ${{ matrix.os == 'ubuntu-latest' && github.event_name != 'release' && github.event_name != 'schedule' }}
run: |
cargo install tauri-driver
cargo install tauri-driver --force
source .env.${{ env.BUILD_RELEASE == 'true' && 'production' || 'development' }}
export VITE_KC_API_BASE_URL
xvfb-run yarn test:e2e:tauri
@ -383,7 +474,7 @@ jobs:
uses: softprops/action-gh-release@v2
with:
files: 'artifact/*/Zoo*'
announce_release:
needs: [publish-apps-release]
runs-on: ubuntu-latest
@ -391,17 +482,17 @@ jobs:
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 }}

37
.github/workflows/create-release.yml vendored Normal file
View File

@ -0,0 +1,37 @@
name: Create Release
on:
push:
branches:
- main
jobs:
create-release:
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: read
if: contains(github.event.head_commit.message, 'Cut release v')
steps:
- uses: actions/github-script@v7
name: Read Cut release PR info and create release
with:
script: |
const { owner, repo } = context.repo
const pulls = await github.rest.repos.listPullRequestsAssociatedWithCommit({
owner,
repo,
commit_sha: context.sha,
})
const { title, body } = pulls.data[0]
const version = title.split('Cut release ')[1]
const result = await github.rest.repos.createRelease({
owner,
repo,
body,
tag_name: version,
name: version,
draft: true,
})
console.log(result)

View File

@ -12,11 +12,31 @@ concurrency:
permissions:
contents: write
pull-requests: write
actions: read
jobs:
check-rust-changes:
runs-on: ubuntu-latest
outputs:
rust-changed: ${{ steps.filter.outputs.rust }}
steps:
- uses: actions/checkout@v4
- id: filter
name: Check for Rust changes
uses: dorny/paths-filter@v3
with:
filters: |
rust:
- 'src/wasm-lib/**'
playwright-ubuntu:
timeout-minutes: 60
runs-on: ubuntu-latest-8-cores
needs: check-rust-changes
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
@ -28,13 +48,38 @@ jobs:
run: yarn
- name: Install Playwright Browsers
run: yarn playwright install --with-deps
- name: Download Wasm Cache
id: download-wasm
if: needs.check-rust-changes.outputs.rust-changed == 'false'
uses: dawidd6/action-download-artifact@v3
continue-on-error: true
with:
github_token: ${{secrets.GITHUB_TOKEN}}
name: wasm-bundle
workflow: build-and-store-wasm.yml
branch: main
path: src/wasm-lib/pkg
- name: copy wasm blob
if: needs.check-rust-changes.outputs.rust-changed == 'false'
run: cp src/wasm-lib/pkg/wasm_lib_bg.wasm public
continue-on-error: true
- name: Setup Rust
uses: dtolnay/rust-toolchain@stable
- name: Cache wasm
- name: Cache Wasm (because rust diff)
if: needs.check-rust-changes.outputs.rust-changed == 'true'
uses: Swatinem/rust-cache@v2
with:
workspaces: './src/wasm-lib'
- name: build wasm
- name: OR Cache Wasm (because wasm cache failed)
if: steps.download-wasm.outcome == 'failure'
uses: Swatinem/rust-cache@v2
with:
workspaces: './src/wasm-lib'
- name: Build Wasm (because rust diff)
if: needs.check-rust-changes.outputs.rust-changed == 'true'
run: yarn build:wasm
- name: OR Build Wasm (because wasm cache failed)
if: steps.download-wasm.outcome == 'failure'
run: yarn build:wasm
- name: build web
run: yarn build:local
@ -89,6 +134,7 @@ jobs:
playwright-macos:
timeout-minutes: 60
runs-on: macos-14
needs: check-rust-changes
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
@ -99,13 +145,38 @@ jobs:
run: yarn
- name: Install Playwright Browsers
run: yarn playwright install --with-deps
- name: Download Wasm Cache
id: download-wasm
if: needs.check-rust-changes.outputs.rust-changed == 'false'
uses: dawidd6/action-download-artifact@v3
continue-on-error: true
with:
github_token: ${{secrets.GITHUB_TOKEN}}
name: wasm-bundle
workflow: build-and-store-wasm.yml
branch: main
path: src/wasm-lib/pkg
- name: copy wasm blob
if: needs.check-rust-changes.outputs.rust-changed == 'false'
run: cp src/wasm-lib/pkg/wasm_lib_bg.wasm public
continue-on-error: true
- name: Setup Rust
uses: dtolnay/rust-toolchain@stable
- name: Cache wasm
- name: Cache Wasm (because rust diff)
if: needs.check-rust-changes.outputs.rust-changed == 'true'
uses: Swatinem/rust-cache@v2
with:
workspaces: './src/wasm-lib'
- name: build wasm
- name: OR Cache Wasm (because wasm cache failed)
if: steps.download-wasm.outcome == 'failure'
uses: Swatinem/rust-cache@v2
with:
workspaces: './src/wasm-lib'
- name: Build Wasm (because rust diff)
if: needs.check-rust-changes.outputs.rust-changed == 'true'
run: yarn build:wasm
- name: OR Build Wasm (because wasm cache failed)
if: steps.download-wasm.outcome == 'failure'
run: yarn build:wasm
- name: build web
run: yarn build:local

1
.gitignore vendored
View File

@ -54,3 +54,4 @@ src/**/*.typegen.ts
src-tauri/gen
src/wasm-lib/grackle/stdlib_cube_partial.json
Mac_App_Distribution.provisionprofile

2
.nvmrc
View File

@ -1 +1 @@
v20.5.0
v21.7.1

View File

@ -59,6 +59,10 @@ followed by:
```
yarn build:wasm-dev
```
or if you have the gh cli installed
```
./get-latest-wasm-bundle.sh # this will download the latest main wasm bundle
```
That will build the WASM binary and put in the `public` dir (though gitignored)
@ -68,7 +72,13 @@ finally, to run the web app only, run:
yarn start
```
## Developing in Chrome
If you're not an KittyCAD employee you won't be able to access the dev environment, you should copy everything from `.env.production` to `.env.development` to make it point to production instead, then when you navigate to `localhost:3000` the easiest way to sign in is to paste `localStorage.setItem('TOKEN_PERSIST_KEY', "your-token-from-https://zoo.dev/account/api-tokens")` replacing the with a real token from https://zoo.dev/account/api-tokens ofcourse, then navigate to localhost:3000 again. Note that navigating to localhost:3000/signin removes your token so you will need to set the token again.
### Development environment variables
The Copilot LSP plugin in the editor requires a Zoo API token to run. In production, we authenticate this with a token via cookie in the browser and device auth token in the desktop environment, but this token is inaccessible in the dev browser version because the cookie is considered "cross-site" (from `localhost` to `dev.zoo.dev`). There is an optional environment variable called `VITE_KC_DEV_TOKEN` that you can populate with a dev token in a `.env.development.local` file to not check it into Git, which will use that token instead of other methods for the LSP service.
### Developing in Chrome
Chrome is in the process of rolling out a new default which
[blocks Third-Party Cookies](https://developer.chrome.com/en/docs/privacy-sandbox/third-party-cookie-phase-out/).

View File

@ -1,3 +1,3 @@
module.exports = {
presets: ["@babel/preset-env"],
presets: ['@babel/preset-env'],
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1,5 +1,5 @@
import { test, expect } from '@playwright/test'
import { getUtils } from './test-utils'
import { makeTemplate, getUtils } from './test-utils'
import waitOn from 'wait-on'
import { roundOff } from 'lib/utils'
import { SaveSettingsPayload } from 'lib/settings/settingsTypes'
@ -8,9 +8,11 @@ import {
TEST_SETTINGS,
TEST_SETTINGS_KEY,
TEST_SETTINGS_CORRUPTED,
TEST_SETTINGS_ONBOARDING,
TEST_SETTINGS_ONBOARDING_EXPORT,
TEST_SETTINGS_ONBOARDING_START,
} from './storageStates'
import * as TOML from '@iarna/toml'
import { Coords2d } from 'lang/std/sketch'
/*
debug helper: unfortunately we do rely on exact coord mouse clicks in a few places
@ -104,6 +106,7 @@ test('Basic sketch', async ({ page }) => {
|> startProfileAt(${commonPoints.startAt}, %)
|> line([${commonPoints.num1}, 0], %)
|> line([0, ${commonPoints.num1}], %)`)
await page.waitForTimeout(100)
await page.mouse.click(startXPx, 500 - PUR * 20)
await expect(page.locator('.cm-content'))
.toHaveText(`const part001 = startSketchOn('-XZ')
@ -277,7 +280,7 @@ test('if you write invalid kcl you get inlined errors', async ({ page }) => {
const bottomAng = 25
*/
await page.click('.cm-content')
await page.keyboard.type('# error')
await page.keyboard.type('$ error')
// press arrows to clear autocomplete
await page.keyboard.press('ArrowLeft')
@ -294,10 +297,10 @@ test('if you write invalid kcl you get inlined errors', async ({ page }) => {
// error text on hover
await page.hover('.cm-lint-marker-error')
await expect(page.getByText("found unknown token '#'")).toBeVisible()
await expect(page.getByText("found unknown token '$'")).toBeVisible()
// select the line that's causing the error and delete it
await page.getByText('# error').click()
await page.getByText('$ error').click()
await page.keyboard.press('End')
await page.keyboard.down('Shift')
await page.keyboard.press('Home')
@ -328,9 +331,7 @@ test('if you write invalid kcl you get inlined errors', async ({ page }) => {
await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible()
})
/* Ignore this test for now since its causing engine to crash
*
* test('if your kcl gets an error from the engine it is inlined', async ({
test('if your kcl gets an error from the engine it is inlined', async ({
page,
}) => {
const u = getUtils(page)
@ -349,7 +350,7 @@ const sketch001 = startSketchOn(box, "revolveAxis")
|> startProfileAt([5, 10], %)
|> line([0, -10], %)
|> line([2, 0], %)
|> line([0, 10], %)
|> line([0, -10], %)
|> close(%)
|> revolve({
axis: getEdge('revolveAxis', box),
@ -364,7 +365,7 @@ angle: 90
await u.waitForAuthSkipAppStart()
u.openDebugPanel()
await u.openDebugPanel()
await u.expectCmdLog('[data-message-type="execution-done"]')
await u.closeDebugPanel()
@ -378,7 +379,7 @@ angle: 90
'sketch profile must lie entirely on one side of the revolution axis'
)
).toBeVisible()
})*/
})
test('executes on load', async ({ page }) => {
const u = getUtils(page)
@ -528,6 +529,10 @@ test.describe('Can create sketches on all planes and their back sides', () => {
})
test('Auto complete works', async ({ page }) => {
test.skip(
true,
'CORS issue stopping the kcl lsp from working, enable again later'
)
const u = getUtils(page)
// const PUR = 400 / 37.5 //pixeltoUnitRatio
await page.setViewportSize({ width: 1200, height: 500 })
@ -566,7 +571,9 @@ test('Auto complete works', async ({ page }) => {
await page.keyboard.press('Tab')
await page.keyboard.type('12')
await page.waitForTimeout(100)
await page.keyboard.press('Tab')
await page.waitForTimeout(100)
await page.keyboard.press('Tab')
await page.keyboard.press('Tab')
await page.keyboard.press('Enter')
@ -595,13 +602,12 @@ test('Auto complete works', async ({ page }) => {
test('Stored settings are validated and fall back to defaults', async ({
page,
context,
}) => {
const u = getUtils(page)
// Override beforeEach test setup
// with corrupted settings
await context.addInitScript(
await page.addInitScript(
async ({ settingsKey, settings }) => {
localStorage.setItem(settingsKey, settings)
},
@ -618,18 +624,18 @@ test('Stored settings are validated and fall back to defaults', async ({
// Check the settings were reset
const storedSettings = TOML.parse(
await page.evaluate(
({ settingsKey }) => localStorage.getItem(settingsKey) || '{}',
({ settingsKey }) => localStorage.getItem(settingsKey) || '',
{ settingsKey: TEST_SETTINGS_KEY }
)
) as { settings: SaveSettingsPayload }
expect(storedSettings.settings.app?.theme).toBe('dark')
expect(storedSettings.settings?.app?.theme).toBe(undefined)
// Check that the invalid settings were removed
expect(storedSettings.settings.modeling?.defaultUnit).toBe(undefined)
expect(storedSettings.settings.modeling?.mouseControls).toBe(undefined)
expect(storedSettings.settings.app?.projectDirectory).toBe(undefined)
expect(storedSettings.settings.projects?.defaultProjectName).toBe(undefined)
expect(storedSettings.settings?.modeling?.defaultUnit).toBe(undefined)
expect(storedSettings.settings?.modeling?.mouseControls).toBe(undefined)
expect(storedSettings.settings?.app?.projectDirectory).toBe(undefined)
expect(storedSettings.settings?.projects?.defaultProjectName).toBe(undefined)
})
test('Project settings can be set and override user settings', async ({
@ -680,6 +686,45 @@ test('Project settings can be set and override user settings', async ({
await expect(page.locator('select[name="app-theme"]')).toHaveValue('light')
})
test('Click through each onboarding step', async ({ page }) => {
const u = getUtils(page)
// Override beforeEach test setup
await page.addInitScript(
async ({ settingsKey, settings }) => {
// Give no initial code, so that the onboarding start is shown immediately
localStorage.setItem('persistCode', '')
localStorage.setItem(settingsKey, settings)
},
{
settingsKey: TEST_SETTINGS_KEY,
settings: TOML.stringify({ settings: TEST_SETTINGS_ONBOARDING_START }),
}
)
await page.setViewportSize({ width: 1200, height: 1080 })
await page.goto('/')
await u.waitForAuthSkipAppStart()
// Test that the onboarding pane loaded
await expect(page.getByText('Welcome to Modeling App! This')).toBeVisible()
const nextButton = page.getByTestId('onboarding-next')
while ((await nextButton.innerText()) !== 'Finish') {
await expect(nextButton).toBeVisible()
await nextButton.click()
}
// Finish the onboarding
await expect(nextButton).toBeVisible()
await nextButton.click()
// Test that the onboarding pane is gone
await expect(page.getByTestId('onboarding-content')).not.toBeVisible()
await expect(page.url()).not.toContain('onboarding')
})
test('Onboarding redirects and code updating', async ({ page }) => {
const u = getUtils(page)
@ -692,7 +737,7 @@ test('Onboarding redirects and code updating', async ({ page }) => {
},
{
settingsKey: TEST_SETTINGS_KEY,
settings: TOML.stringify({ settings: TEST_SETTINGS_ONBOARDING }),
settings: TOML.stringify({ settings: TEST_SETTINGS_ONBOARDING_EXPORT }),
}
)
@ -736,7 +781,7 @@ test('Selections work on fresh and edited sketch', async ({ page }) => {
await u.openDebugPanel()
const xAxisClick = () =>
page.mouse.click(700, 250).then(() => page.waitForTimeout(100))
page.mouse.click(700, 253).then(() => page.waitForTimeout(100))
const emptySpaceClick = () =>
page.mouse.click(728, 343).then(() => page.waitForTimeout(100))
const topHorzSegmentClick = () =>
@ -761,6 +806,7 @@ test('Selections work on fresh and edited sketch', async ({ page }) => {
.toHaveText(`const part001 = startSketchOn('-XZ')
|> startProfileAt(${commonPoints.startAt}, %)`)
await page.waitForTimeout(100)
await page.mouse.click(startXPx + PUR * 20, 500 - PUR * 10)
await expect(page.locator('.cm-content'))
@ -768,12 +814,14 @@ test('Selections work on fresh and edited sketch', async ({ page }) => {
|> startProfileAt(${commonPoints.startAt}, %)
|> line([${commonPoints.num1}, 0], %)`)
await page.waitForTimeout(100)
await page.mouse.click(startXPx + PUR * 20, 500 - PUR * 20)
await expect(page.locator('.cm-content'))
.toHaveText(`const part001 = startSketchOn('-XZ')
|> startProfileAt(${commonPoints.startAt}, %)
|> line([${commonPoints.num1}, 0], %)
|> line([0, ${commonPoints.num1}], %)`)
await page.waitForTimeout(100)
await page.mouse.click(startXPx, 500 - PUR * 20)
await expect(page.locator('.cm-content'))
.toHaveText(`const part001 = startSketchOn('-XZ')
@ -786,10 +834,14 @@ test('Selections work on fresh and edited sketch', async ({ page }) => {
await page.getByRole('button', { name: 'Line' }).click()
await u.closeDebugPanel()
const selectionSequence = async () => {
const selectionSequence = async (isSecondTime = false) => {
await expect(page.getByTestId('hover-highlight')).not.toBeVisible()
await page.mouse.move(startXPx + PUR * 15, 500 - PUR * 10)
await page.waitForTimeout(100)
await page.mouse.move(
startXPx + PUR * 15,
isSecondTime ? 430 : 500 - PUR * 10
)
await expect(page.getByTestId('hover-highlight')).toBeVisible()
// bg-yellow-200 is more brittle than hover-highlight, but is closer to the user experience
@ -799,7 +851,10 @@ test('Selections work on fresh and edited sketch', async ({ page }) => {
// check mousing off, than mousing onto another line
await page.mouse.move(startXPx + PUR * 10, 500 - PUR * 15) // mouse off
await expect(page.getByTestId('hover-highlight')).not.toBeVisible()
await page.mouse.move(startXPx + PUR * 10, 500 - PUR * 20) // mouse onto another line
await page.mouse.move(
startXPx + PUR * 10,
isSecondTime ? 295 : 500 - PUR * 20
) // mouse onto another line
await expect(page.getByTestId('hover-highlight')).toBeVisible()
// now check clicking works including axis
@ -809,6 +864,7 @@ test('Selections work on fresh and edited sketch', async ({ page }) => {
await page.keyboard.down('Shift')
const absYButton = page.getByRole('button', { name: 'ABS Y' })
await expect(absYButton).toBeDisabled()
await page.waitForTimeout(100)
await xAxisClick()
await page.keyboard.up('Shift')
await absYButton.and(page.locator(':not([disabled])')).waitFor()
@ -817,10 +873,12 @@ test('Selections work on fresh and edited sketch', async ({ page }) => {
// clear selection by clicking on nothing
await emptySpaceClick()
await page.waitForTimeout(100)
// same selection but click the axis first
await xAxisClick()
await expect(absYButton).toBeDisabled()
await page.keyboard.down('Shift')
await page.waitForTimeout(100)
await topHorzSegmentClick()
await page.keyboard.up('Shift')
@ -833,6 +891,7 @@ test('Selections work on fresh and edited sketch', async ({ page }) => {
await page.getByText(` |> line([-${commonPoints.num2}, 0], %)`).click()
await page.keyboard.down('Shift')
await expect(absYButton).toBeDisabled()
await page.waitForTimeout(100)
await xAxisClick()
await page.keyboard.up('Shift')
await expect(absYButton).not.toBeDisabled()
@ -875,11 +934,16 @@ test('Selections work on fresh and edited sketch', async ({ page }) => {
await page.waitForTimeout(100)
// enter sketch again
await page.getByRole('button', { name: 'Edit Sketch' }).click()
await u.doAndWaitForCmd(
() => page.getByRole('button', { name: 'Edit Sketch' }).click(),
'default_camera_get_settings'
)
await page.waitForTimeout(150)
await page.waitForTimeout(300) // wait for animation
// hover again and check it works
await selectionSequence()
await selectionSequence(true)
})
test.describe('Command bar tests', () => {
@ -891,9 +955,8 @@ test.describe('Command bar tests', () => {
let cmdSearchBar = page.getByPlaceholder('Search commands')
// First try opening the command bar and closing it
// It has a different label on mac and windows/linux, "Meta+K" and "Ctrl+/" respectively
await page
.getByRole('button', { name: 'Ctrl+/' })
.getByRole('button', { name: 'Commands', exact: false })
.or(page.getByRole('button', { name: '⌘K' }))
.click()
await expect(cmdSearchBar).toBeVisible()
@ -938,13 +1001,13 @@ test.describe('Command bar tests', () => {
localStorage.setItem(
'persistCode',
`const distance = sqrt(20)
const part001 = startSketchOn('-XZ')
|> startProfileAt([-6.95, 4.98], %)
|> line([25.1, 0.41], %)
|> line([0.73, -14.93], %)
|> line([-23.44, 0.52], %)
|> close(%)
`
const part001 = startSketchOn('-XZ')
|> startProfileAt([-6.95, 10.98], %)
|> line([25.1, 0.41], %)
|> line([0.73, -20.93], %)
|> line([-23.44, 0.52], %)
|> close(%)
`
)
})
@ -961,7 +1024,6 @@ test.describe('Command bar tests', () => {
page.getByRole('button', { name: 'Start Sketch' })
).not.toBeDisabled()
await u.clearCommandLogs()
await page.getByText('|> line([0.73, -14.93], %)').click()
await page.getByRole('button', { name: 'Extrude' }).isEnabled()
let cmdSearchBar = page.getByPlaceholder('Search commands')
@ -971,6 +1033,12 @@ test.describe('Command bar tests', () => {
// Search for extrude command and choose it
await page.getByRole('option', { name: 'Extrude' }).click()
// Assert that we're on the selection step
await expect(page.getByRole('button', { name: 'selection' })).toBeDisabled()
// Select a face
await page.mouse.move(700, 200)
await page.mouse.click(700, 200)
// Assert that we're on the distance step
await expect(page.getByRole('button', { name: 'distance' })).toBeDisabled()
@ -1004,9 +1072,9 @@ test.describe('Command bar tests', () => {
`const distance = sqrt(20)
const distance001 = 5 + 7
const part001 = startSketchOn('-XZ')
|> startProfileAt([-6.95, 4.98], %)
|> startProfileAt([-6.95, 10.98], %)
|> line([25.1, 0.41], %)
|> line([0.73, -14.93], %)
|> line([0.73, -20.93], %)
|> line([-23.44, 0.52], %)
|> close(%)
|> extrude(distance001, %)`.replace(/(\r\n|\n|\r)/gm, '') // remove newlines
@ -1015,6 +1083,7 @@ const part001 = startSketchOn('-XZ')
})
test('Can add multiple sketches', async ({ page }) => {
test.skip(process.platform === 'darwin', 'Can add multiple sketches')
const u = getUtils(page)
await page.setViewportSize({ width: 1200, height: 500 })
const PUR = 400 / 37.5 //pixeltoUnitRatio
@ -1065,6 +1134,7 @@ test('Can add multiple sketches', async ({ page }) => {
|> startProfileAt(${commonPoints.startAt}, %)
|> line([${commonPoints.num1}, 0], %)
|> line([0, ${commonPoints.num1}], %)`)
await page.waitForTimeout(100)
await page.mouse.click(startXPx, 500 - PUR * 20)
const finalCodeFirstSketch = `const part001 = startSketchOn('-XZ')
|> startProfileAt(${commonPoints.startAt}, %)
@ -1080,24 +1150,33 @@ test('Can add multiple sketches', async ({ page }) => {
await u.expectCmdLog('[data-message-type="execution-done"]')
await u.updateCamPosition([0, 100, 100])
await u.updateCamPosition([100, 100, 100])
await page.waitForTimeout(250)
// 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(400)
await page.mouse.click(650, 450)
await page.waitForTimeout(500) // TODO detect animation ending, or disable animation
await u.clearAndCloseDebugPanel()
// on mock os there are issues with getting the camera to update
// it should not be selecting the 'XZ' plane here if the camera updated
// properly, but if we just role with it we can still verify everything
// in the rest of the test
const plane = process.platform === 'darwin' ? 'XZ' : 'XY'
await page.waitForTimeout(100)
await page.mouse.click(startXPx + PUR * 10, 500 - PUR * 10)
const startAt2 = '[0.93,-1.25]'
const startAt2 =
process.platform === 'darwin' ? '[9.75, -13.16]' : '[0.93, -1.25]'
await expect(
(await page.locator('.cm-content').innerText()).replace(/\s/g, '')
).toBe(
`${finalCodeFirstSketch}
const part002 = startSketchOn('XY')
const part002 = startSketchOn('${plane}')
|> startProfileAt(${startAt2}, %)`.replace(/\s/g, '')
)
await page.waitForTimeout(100)
@ -1106,12 +1185,12 @@ const part002 = startSketchOn('XY')
await page.mouse.click(startXPx + PUR * 20, 500 - PUR * 10)
await page.waitForTimeout(100)
const num2 = 0.94
const num2 = process.platform === 'darwin' ? 9.84 : 0.94
await expect(
(await page.locator('.cm-content').innerText()).replace(/\s/g, '')
).toBe(
`${finalCodeFirstSketch}
const part002 = startSketchOn('XY')
const part002 = startSketchOn('${plane}')
|> startProfileAt(${startAt2}, %)
|> line([${num2}, 0], %)`.replace(/\s/g, '')
)
@ -1121,21 +1200,29 @@ const part002 = startSketchOn('XY')
(await page.locator('.cm-content').innerText()).replace(/\s/g, '')
).toBe(
`${finalCodeFirstSketch}
const part002 = startSketchOn('XY')
const part002 = startSketchOn('${plane}')
|> startProfileAt(${startAt2}, %)
|> line([${num2}, 0], %)
|> line([0, ${roundOff(num2 - 0.01)}], %)`.replace(/\s/g, '')
|> line([0, ${roundOff(
num2 + (process.platform === 'darwin' ? 0.01 : -0.01)
)}], %)`.replace(/\s/g, '')
)
await page.waitForTimeout(100)
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')
const part002 = startSketchOn('${plane}')
|> startProfileAt(${startAt2}, %)
|> line([${num2}, 0], %)
|> line([0, ${roundOff(num2 - 0.01)}], %)
|> line([-1.87, 0], %)`.replace(/\s/g, '')
|> line([0, ${roundOff(
num2 + (process.platform === 'darwin' ? 0.01 : -0.01)
)}], %)
|> line([-${process.platform === 'darwin' ? 19.59 : 1.87}, 0], %)`.replace(
/\s/g,
''
)
)
})
@ -1178,6 +1265,72 @@ test('ProgramMemory can be serialised', async ({ page }) => {
})
})
test('Hovering over 3d features highlights code', async ({ page }) => {
const u = getUtils(page)
await page.addInitScript(async () => {
localStorage.setItem(
'persistCode',
`const part001 = startSketchOn('-XZ')
|> startProfileAt([20, 0], %)
|> line([7.13, 4 + 0], %)
|> angledLine({ angle: 3 + 0, length: 3.14 + 0 }, %)
|> lineTo([20.14 + 0, -0.14 + 0], %)
|> xLineTo(29 + 0, %)
|> yLine(-3.14 + 0, %, 'a')
|> xLine(1.63, %)
|> angledLineOfXLength({ angle: 3 + 0, length: 3.14 }, %)
|> angledLineOfYLength({ angle: 30, length: 3 + 0 }, %)
|> angledLineToX({ angle: 22.14 + 0, to: 12 }, %)
|> angledLineToY({ angle: 30, to: 11.14 }, %)
|> angledLineThatIntersects({
angle: 3.14,
intersectTag: 'a',
offset: 0
}, %)
|> tangentialArcTo([13.14 + 0, 13.14], %)
|> close(%)
|> extrude(5 + 7, %)
`
)
})
await page.setViewportSize({ width: 1000, height: 500 })
await page.goto('/')
await u.waitForAuthSkipAppStart()
const extrusionTop: Coords2d = [800, 240]
const flatExtrusionFace: Coords2d = [960, 160]
const arc: Coords2d = [840, 160]
const close: Coords2d = [720, 200]
const nothing: Coords2d = [600, 200]
await page.mouse.move(nothing[0], nothing[1])
await page.mouse.click(nothing[0], nothing[1])
await expect(page.getByTestId('hover-highlight')).not.toBeVisible()
await page.waitForTimeout(200)
await page.mouse.move(extrusionTop[0], extrusionTop[1])
await expect(page.getByTestId('hover-highlight')).toBeVisible()
await page.mouse.move(nothing[0], nothing[1])
await expect(page.getByTestId('hover-highlight')).not.toBeVisible()
await page.mouse.move(arc[0], arc[1])
await expect(page.getByTestId('hover-highlight')).toBeVisible()
await page.mouse.move(nothing[0], nothing[1])
await expect(page.getByTestId('hover-highlight')).not.toBeVisible()
await page.mouse.move(close[0], close[1])
await expect(page.getByTestId('hover-highlight')).toBeVisible()
await page.mouse.move(nothing[0], nothing[1])
await expect(page.getByTestId('hover-highlight')).not.toBeVisible()
await page.mouse.move(flatExtrusionFace[0], flatExtrusionFace[1])
await expect(page.getByTestId('hover-highlight')).toHaveCount(5) // multiple lines
await page.mouse.move(nothing[0], nothing[1])
await page.waitForTimeout(100)
await expect(page.getByTestId('hover-highlight')).not.toBeVisible()
})
test("Various pipe expressions should and shouldn't allow edit and or extrude", async ({
page,
}) => {
@ -1312,7 +1465,7 @@ test('Deselecting line tool should mean nothing happens on click', async ({
`const part001 = startSketchOn('-XZ')`
)
await page.waitForTimeout(300)
await page.waitForTimeout(600)
let previousCodeContent = await page.locator('.cm-content').innerText()
@ -1339,10 +1492,12 @@ test('Deselecting line tool should mean nothing happens on click', async ({
await expect(page.locator('.cm-content')).not.toHaveText(previousCodeContent)
previousCodeContent = await page.locator('.cm-content').innerText()
await page.waitForTimeout(100)
await page.mouse.click(700, 300)
await expect(page.locator('.cm-content')).not.toHaveText(previousCodeContent)
previousCodeContent = await page.locator('.cm-content').innerText()
await page.waitForTimeout(100)
await page.mouse.click(750, 300)
await expect(page.locator('.cm-content')).not.toHaveText(previousCodeContent)
previousCodeContent = await page.locator('.cm-content').innerText()
@ -1367,16 +1522,16 @@ test('Can edit segments by dragging their handles', async ({ page }) => {
page.getByRole('button', { name: 'Start Sketch' })
).not.toBeDisabled()
const startPX = [652, 418]
const lineEndPX = [794, 416]
const arcEndPX = [893, 318]
const startPX = [665, 458]
const lineEndPX = [842, 458]
const arcEndPX = [971, 342]
const dragPX = 30
await page.getByText('startProfileAt([4.61, -14.01], %)').click()
await expect(page.getByRole('button', { name: 'Edit Sketch' })).toBeVisible()
await page.getByRole('button', { name: 'Edit Sketch' }).click()
await page.waitForTimeout(100)
await page.waitForTimeout(400)
let prevContent = await page.locator('.cm-content').innerText()
const step5 = { steps: 5 }
@ -1386,7 +1541,7 @@ test('Can edit segments by dragging their handles', async ({ page }) => {
await page.mouse.down()
await page.mouse.move(startPX[0] + dragPX, startPX[1] - dragPX, step5)
await page.mouse.up()
await page.waitForTimeout(100)
await expect(page.locator('.cm-content')).not.toHaveText(prevContent)
prevContent = await page.locator('.cm-content').innerText()
@ -1414,9 +1569,9 @@ test('Can edit segments by dragging their handles', async ({ page }) => {
// expect the code to have changed
await expect(page.locator('.cm-content'))
.toHaveText(`const part001 = startSketchOn('-XZ')
|> startProfileAt([7.01, -11.79], %)
|> line([14.69, 2.73], %)
|> tangentialArcTo([27.6, -3.25], %)`)
|> startProfileAt([6.44, -12.07], %)
|> line([14.04, 2.03], %)
|> tangentialArcTo([27.19, -4.2], %)`)
})
const doSnapAtDifferentScales = async (
@ -1535,38 +1690,46 @@ test('Sketch on face', async ({ page }) => {
).not.toBeDisabled()
await page.getByRole('button', { name: 'Start Sketch' }).click()
await page.waitForTimeout(300)
let previousCodeContent = await page.locator('.cm-content').innerText()
await page.mouse.click(793, 133)
await u.openAndClearDebugPanel()
await u.doAndWaitForCmd(
() => page.mouse.click(793, 133),
'default_camera_get_settings',
true
)
await page.waitForTimeout(150)
const firstClickPosition = [612, 238]
const secondClickPosition = [661, 242]
const thirdClickPosition = [609, 267]
await page.waitForTimeout(300)
await page.mouse.click(firstClickPosition[0], firstClickPosition[1])
await expect(page.locator('.cm-content')).not.toHaveText(previousCodeContent)
previousCodeContent = await page.locator('.cm-content').innerText()
await page.waitForTimeout(100)
await page.mouse.click(secondClickPosition[0], secondClickPosition[1])
await expect(page.locator('.cm-content')).not.toHaveText(previousCodeContent)
previousCodeContent = await page.locator('.cm-content').innerText()
await page.waitForTimeout(100)
await page.mouse.click(thirdClickPosition[0], thirdClickPosition[1])
await expect(page.locator('.cm-content')).not.toHaveText(previousCodeContent)
previousCodeContent = await page.locator('.cm-content').innerText()
await page.waitForTimeout(100)
await page.mouse.click(firstClickPosition[0], firstClickPosition[1])
await expect(page.locator('.cm-content')).not.toHaveText(previousCodeContent)
previousCodeContent = await page.locator('.cm-content').innerText()
await expect(page.locator('.cm-content'))
.toContainText(`const part002 = startSketchOn(part001, 'seg01')
|> startProfileAt([1.03, 1.03], %)
|> line([4.18, -0.35], %)
|> line([-4.44, -2.13], %)
|> startProfileAt([-12.83, 6.7], %)
|> line([2.87, -0.23], %)
|> line([-3.05, -1.47], %)
|> close(%)`)
await u.openAndClearDebugPanel()
@ -1576,9 +1739,14 @@ test('Sketch on face', async ({ page }) => {
await u.updateCamPosition([1049, 239, 686])
await u.closeDebugPanel()
await page.getByText('startProfileAt([1.03, 1.03], %)').click()
await page.getByText('startProfileAt([-12.83, 6.7], %)').click()
await expect(page.getByRole('button', { name: 'Edit Sketch' })).toBeVisible()
await page.getByRole('button', { name: 'Edit Sketch' }).click()
await u.doAndWaitForCmd(
() => page.getByRole('button', { name: 'Edit Sketch' }).click(),
'default_camera_get_settings',
true
)
await page.waitForTimeout(150)
await page.setViewportSize({ width: 1200, height: 1200 })
await u.openAndClearDebugPanel()
await u.updateCamPosition([452, -152, 1166])
@ -1596,21 +1764,20 @@ test('Sketch on face', async ({ page }) => {
await expect(page.locator('.cm-content')).not.toHaveText(previousCodeContent)
previousCodeContent = await page.locator('.cm-content').innerText()
await expect(page.locator('.cm-content'))
.toContainText(`const part002 = startSketchOn(part001, 'seg01')
|> startProfileAt([1.03, 1.03], %)
|> line([${process?.env?.CI ? 2.74 : 2.93}, -${
process?.env?.CI ? 0.24 : 0.2
}], %)
|> line([-4.44, -2.13], %)
|> close(%)`)
const result = makeTemplate`const part002 = startSketchOn(part001, 'seg01')
|> startProfileAt([-12.83, 6.7], %)
|> line([${[2.28, 2.35]}, -${0.07}], %)
|> line([-3.05, -1.47], %)
|> close(%)`
await expect(page.locator('.cm-content')).toHaveText(result.regExp)
// exit sketch
await u.openAndClearDebugPanel()
await page.getByRole('button', { name: 'Exit Sketch' }).click()
await u.expectCmdLog('[data-message-type="execution-done"]')
await page.getByText('startProfileAt([1.03, 1.03], %)').click()
await page.getByText('startProfileAt([-12.83, 6.7], %)').click()
await expect(page.getByRole('button', { name: 'Extrude' })).not.toBeDisabled()
await page.getByRole('button', { name: 'Extrude' }).click()
@ -1622,15 +1789,9 @@ test('Sketch on face', async ({ page }) => {
await expect(page.getByText('Confirm Extrude')).toBeVisible()
await page.keyboard.press('Enter')
await expect(page.locator('.cm-content'))
.toContainText(`const part002 = startSketchOn(part001, 'seg01')
|> startProfileAt([1.03, 1.03], %)
|> line([${process?.env?.CI ? 2.74 : 2.93}, -${
process?.env?.CI ? 0.24 : 0.2
}], %)
|> line([-4.44, -2.13], %)
|> close(%)
|> extrude(5 + 7, %)`)
const result2 = result.genNext`
|> extrude(${[5, 5]} + 7, %)`
await expect(page.locator('.cm-content')).toHaveText(result2.regExp)
})
test('Can code mod a line length', async ({ page }) => {
@ -1661,11 +1822,11 @@ test('Can code mod a line length', async ({ page }) => {
// enter sketch again
await page.getByRole('button', { name: 'Edit Sketch' }).click()
await page.waitForTimeout(300) // wait for animation
await page.waitForTimeout(350) // wait for animation
const startXPx = 500
await page.mouse.move(startXPx + PUR * 15, 250 - PUR * 10)
await page.mouse.click(615, 133)
await page.mouse.click(615, 102)
await page.getByRole('button', { name: 'length', exact: true }).click()
await page.getByText('Add constraining value').click()
@ -1673,3 +1834,42 @@ test('Can code mod a line length', async ({ page }) => {
`const length001 = 20const part001 = startSketchOn('XY') |> startProfileAt([-10, -10], %) |> line([20, 0], %) |> line([0, 20], %) |> xLine(-length001, %)`
)
})
test('Extrude from command bar selects extrude line after', async ({
page,
}) => {
await page.addInitScript(async () => {
localStorage.setItem(
'persistCode',
`const part001 = startSketchOn('XY')
|> startProfileAt([-10, -10], %)
|> line([20, 0], %)
|> line([0, 20], %)
|> xLine(-20, %)
|> close(%)
`
)
})
const u = getUtils(page)
await page.setViewportSize({ width: 1200, height: 500 })
await page.goto('/')
await u.waitForAuthSkipAppStart()
await u.openDebugPanel()
await u.expectCmdLog('[data-message-type="execution-done"]')
await u.closeDebugPanel()
// Click the line of code for xLine.
await page.getByText(`close(%)`).click() // TODO remove this and reinstate // await topHorzSegmentClick()
await page.waitForTimeout(100)
await page.getByRole('button', { name: 'Extrude' }).click()
await page.waitForTimeout(100)
await page.keyboard.press('Enter')
await page.waitForTimeout(100)
await page.keyboard.press('Enter')
await page.waitForTimeout(100)
await expect(page.locator('.cm-activeLine')).toHaveText(
` |> extrude(5 + 7, %)`
)
})

View File

@ -507,7 +507,7 @@ test('Draft rectangles should look right', async ({ page, context }) => {
`const part001 = startSketchOn('-XZ')`
)
await page.waitForTimeout(300) // TODO detect animation ending, or disable animation
await page.waitForTimeout(500) // TODO detect animation ending, or disable animation
await u.closeDebugPanel()
const startXPx = 600
@ -597,12 +597,15 @@ test.describe('Client side scene scale should match engine scale', () => {
// exit sketch
await u.openAndClearDebugPanel()
await page.getByRole('button', { name: 'Exit Sketch' }).click()
await u.doAndWaitForImageDiff(
() => page.getByRole('button', { name: 'Exit Sketch' }).click(),
200
)
// wait for execution done
await u.expectCmdLog('[data-message-type="execution-done"]')
await u.clearAndCloseDebugPanel()
await page.waitForTimeout(200)
await page.waitForTimeout(300)
// second screen shot should look almost identical, i.e. scale should be the same.
await expect(page).toHaveScreenshot({
@ -696,12 +699,15 @@ test.describe('Client side scene scale should match engine scale', () => {
// exit sketch
await u.openAndClearDebugPanel()
await page.getByRole('button', { name: 'Exit Sketch' }).click()
await u.doAndWaitForImageDiff(
() => page.getByRole('button', { name: 'Exit Sketch' }).click(),
200
)
// wait for execution done
await u.expectCmdLog('[data-message-type="execution-done"]')
await u.clearAndCloseDebugPanel()
await page.waitForTimeout(200)
await page.waitForTimeout(300)
// second screen shot should look almost identical, i.e. scale should be the same.
await expect(page).toHaveScreenshot({

Binary file not shown.

Before

Width:  |  Height:  |  Size: 39 KiB

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 51 KiB

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 45 KiB

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 52 KiB

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 72 KiB

After

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 46 KiB

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 50 KiB

After

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 46 KiB

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 47 KiB

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 49 KiB

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 45 KiB

After

Width:  |  Height:  |  Size: 48 KiB

View File

@ -1,12 +1,13 @@
import { SaveSettingsPayload } from 'lib/settings/settingsTypes'
import { Themes } from 'lib/theme'
export const TEST_SETTINGS_KEY = '/user.toml'
export const TEST_SETTINGS_KEY = '/settings.toml'
export const TEST_SETTINGS = {
app: {
theme: Themes.Dark,
onboardingStatus: 'dismissed',
projectDirectory: '',
enableSSAO: false,
},
modeling: {
defaultUnit: 'in',
@ -21,9 +22,14 @@ export const TEST_SETTINGS = {
},
} satisfies Partial<SaveSettingsPayload>
export const TEST_SETTINGS_ONBOARDING = {
export const TEST_SETTINGS_ONBOARDING_EXPORT = {
...TEST_SETTINGS,
app: { ...TEST_SETTINGS.app, onboardingStatus: '/export ' },
app: { ...TEST_SETTINGS.app, onboardingStatus: '/export' },
} satisfies Partial<SaveSettingsPayload>
export const TEST_SETTINGS_ONBOARDING_START = {
...TEST_SETTINGS,
app: { ...TEST_SETTINGS.app, onboardingStatus: '' },
} satisfies Partial<SaveSettingsPayload>
export const TEST_SETTINGS_CORRUPTED = {

View File

@ -182,3 +182,76 @@ export function getUtils(page: Page) {
}),
}
}
type TemplateOptions = Array<number | Array<number>>
type makeTemplateReturn = {
regExp: RegExp
genNext: (
templateParts: TemplateStringsArray,
...options: TemplateOptions
) => makeTemplateReturn
}
const escapeRegExp = (string: string) => {
return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&') // $& means the whole matched string
}
const _makeTemplate = (
templateParts: TemplateStringsArray,
...options: TemplateOptions
) => {
const length = Math.max(...options.map((a) => (Array.isArray(a) ? a[0] : 0)))
let reExpTemplate = ''
for (let i = 0; i < length; i++) {
const currentStr = templateParts.map((str, index) => {
const currentOptions = options[index]
return (
escapeRegExp(str) +
String(
Array.isArray(currentOptions)
? currentOptions[i]
: typeof currentOptions === 'number'
? currentOptions
: ''
)
)
})
reExpTemplate += '|' + currentStr.join('')
}
return new RegExp(reExpTemplate)
}
/**
* Tool for making templates to match code snippets in the editor with some fudge factor,
* as there's some level of non-determinism.
*
* Usage is as such:
* ```typescript
* const result = makeTemplate`const myVar = aFunc(${[1, 2, 3]})`
* await expect(page.locator('.cm-content')).toHaveText(result.regExp)
* ```
* Where the value `1`, `2` or `3` are all valid and should make the test pass.
*
* The function also has a `genNext` function that allows you to chain multiple templates
* together without having to repeat previous parts of the template.
* ```typescript
* const result2 = result.genNext`const myVar2 = aFunc(${[4, 5, 6]})`
* ```
*/
export const makeTemplate: (
templateParts: TemplateStringsArray,
...values: TemplateOptions
) => makeTemplateReturn = (templateParts, ...options) => {
return {
regExp: _makeTemplate(templateParts, ...options),
genNext: (
nextTemplateParts: TemplateStringsArray,
...nextOptions: TemplateOptions
) =>
makeTemplate(
[...templateParts, ...nextTemplateParts] as any as TemplateStringsArray,
[...options, ...nextOptions] as any
),
}
}

View File

@ -2,7 +2,7 @@ import { browser, $, expect } from '@wdio/globals'
import fs from 'fs/promises'
const documentsDir = `${process.env.HOME}/Documents`
const userSettingsFile = `${process.env.HOME}/.config/dev.zoo.modeling-app/user.toml`
const userSettingsDir = `${process.env.HOME}/.config/dev.zoo.modeling-app`
const defaultProjectDir = `${documentsDir}/zoo-modeling-app-projects`
const newProjectDir = `${documentsDir}/a-different-directory`
const userCodeDir = '/tmp/kittycad_user_code'
@ -29,8 +29,10 @@ describe('ZMA (Tauri, Linux)', () => {
// Clean up filesystem from previous tests
await new Promise((resolve) => setTimeout(resolve, 100))
await fs.rm(defaultProjectDir, { force: true, recursive: true })
await fs.rm(newProjectDir, { force: true, recursive: true })
await fs.rm(userCodeDir, { force: true })
await fs.rm(userSettingsFile, { force: true })
await fs.rm(userSettingsDir, { force: true, recursive: true })
await fs.mkdir(defaultProjectDir, { recursive: true })
await fs.mkdir(newProjectDir, { recursive: true })
const signInButton = await $('[data-testid="sign-in-button"]')
@ -70,8 +72,9 @@ describe('ZMA (Tauri, Linux)', () => {
console.log(cr.status)
// Now should be signed in
await new Promise((resolve) => setTimeout(resolve, 10000))
const newFileButton = await $('[data-testid="home-new-file"]')
expect(await newFileButton.getText()).toEqual('New file')
expect(await newFileButton.getText()).toEqual('New project')
})
it('opens the settings page, checks filesystem settings, and closes the settings page', async () => {
@ -117,8 +120,8 @@ describe('ZMA (Tauri, Linux)', () => {
it('opens the new file and expects a loading stream', async () => {
const projectLink = await $('[data-testid="project-link"]')
await click(projectLink)
const loadingText = await $('[data-testid="loading-stream"]')
expect(await loadingText.getText()).toContain('Loading stream...')
const errorText = await $('[data-testid="unexpected-error"]')
expect(await errorText.getText()).toContain('unexpected error')
await browser.execute('window.location.href = "tauri://localhost/home"')
})

24
get-latest-wasm-bundle.sh Executable file
View File

@ -0,0 +1,24 @@
#!/bin/bash
# Set the repository owner and name
REPO_OWNER="KittyCAD"
REPO_NAME="modeling-app"
WORKFLOW_NAME="build-and-store-wasm.yml"
ARTIFACT_NAME="wasm-bundle"
# Fetch the latest completed workflow run ID for the specified workflow
# RUN_ID=$(gh api repos/$REPO_OWNER/$REPO_NAME/actions/workflows/$WORKFLOW_NAME/runs --paginate --jq '.workflow_runs[] | select(.status=="completed") | .id' | head -n 1)
RUN_ID=$(gh api repos/$REPO_OWNER/$REPO_NAME/actions/workflows/$WORKFLOW_NAME/runs --paginate --jq '.workflow_runs[] | select(.status=="completed" and .conclusion=="success") | .id' | head -n 1)
echo $RUN_ID
# Check if a valid RUN_ID was found
if [ -z "$RUN_ID" ]; then
echo "Failed to find a workflow run for $WORKFLOW_NAME."
exit 1
fi
gh run download $RUN_ID --repo $REPO_OWNER/$REPO_NAME --name $ARTIFACT_NAME --dir ./src/wasm-lib/pkg
cp src/wasm-lib/pkg/wasm_lib_bg.wasm public
echo "latest wasm copied to public folder"

View File

@ -15,7 +15,7 @@
<script
defer
data-domain="app.zoo.dev"
src="https://plausible.corp.zoo.dev/js/script.js"
src="https://plausible.corp.zoo.dev/js/script.tagged-events.js"
></script>
<title>Zoo Modeling App</title>
</head>

View File

@ -57,7 +57,7 @@ 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
echo "$(jq --arg v "$new_version_number" '.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"

View File

@ -1,6 +1,6 @@
{
"name": "untitled-app",
"version": "0.17.3",
"version": "0.21.0",
"private": true,
"dependencies": {
"@codemirror/autocomplete": "^6.16.0",
@ -8,9 +8,9 @@
"@fortawesome/free-brands-svg-icons": "^6.5.2",
"@fortawesome/free-solid-svg-icons": "^6.4.2",
"@fortawesome/react-fontawesome": "^0.2.0",
"@headlessui/react": "^1.7.18",
"@headlessui/react": "^1.7.19",
"@headlessui/tailwindcss": "^0.2.0",
"@kittycad/lib": "^0.0.58",
"@kittycad/lib": "^0.0.60",
"@lezer/javascript": "^1.4.9",
"@open-rpc/client-js": "^1.8.1",
"@react-hook/resize-observer": "^1.2.6",
@ -84,8 +84,9 @@
"test:e2e:tauri": "E2E_TAURI_ENABLED=true TS_NODE_COMPILER_OPTIONS='{\"module\": \"commonjs\"}' wdio run wdio.conf.ts",
"simpleserver:ci": "yarn pretest && http-server ./public --cors -p 3000 &",
"simpleserver": "yarn pretest && http-server ./public --cors -p 3000",
"fmt": "prettier --write ./src && prettier --write ./e2e",
"fmt-check": "prettier --check ./src && prettier --check ./e2e",
"fmt": "prettier --write ./src *.ts *.json *.js ./e2e",
"fmt-check": "prettier --check ./src *.ts *.json *.js ./e2e",
"fetch:wasm": "./get-latest-wasm-bundle.sh",
"build:wasm-dev": "(cd src/wasm-lib && wasm-pack build --dev --target web --out-dir pkg && cargo test -p kcl-lib export_bindings) && cp src/wasm-lib/pkg/wasm_lib_bg.wasm public && yarn fmt",
"build:wasm": "(cd src/wasm-lib && wasm-pack build --target web --out-dir pkg && cargo test -p kcl-lib export_bindings) && cp src/wasm-lib/pkg/wasm_lib_bg.wasm public && yarn fmt",
"build:wasm-clean": "yarn wasm-prep && yarn build:wasm",
@ -122,6 +123,7 @@
"@tauri-apps/cli": "^2.0.0-beta.13",
"@types/crypto-js": "^4.2.2",
"@types/debounce-promise": "^3.1.9",
"@types/mocha": "^10.0.6",
"@types/pixelmatch": "^5.2.6",
"@types/pngjs": "^6.0.4",
"@types/react-modal": "^3.16.3",
@ -132,6 +134,7 @@
"@types/wicg-file-system-access": "^2023.10.5",
"@types/ws": "^8.5.10",
"@vitejs/plugin-react": "^4.2.1",
"@vitest/web-worker": "^1.5.0",
"@wdio/cli": "^8.24.3",
"@wdio/globals": "^8.36.0",
"@wdio/local-runner": "^8.36.0",

View File

@ -27,7 +27,7 @@ export default defineConfig({
baseURL: 'http://localhost:3000',
/* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
trace: 'on-first-retry',
trace: 'retain-on-failure',
},
/* Configure projects for major browsers */
@ -49,8 +49,6 @@ export default defineConfig({
// use: { ...devices['Desktop Chrome'] },
// },
/* Test against mobile viewports. */
// {
// name: 'Mobile Chrome',
@ -78,4 +76,4 @@ export default defineConfig({
// url: 'http://127.0.0.1:3000',
reuseExistingServer: !process.env.CI,
},
})
})

View File

@ -0,0 +1,15 @@
{
"applinks": {
"details": [
{
"appIDs": ["92H8YB3B95.dev.zoo.modeling-app"],
"components": [
{
"/": "/file/*",
"comment": "Matches any URL whose path starts with /file/"
}
]
}
]
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 150 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 152 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 142 KiB

After

Width:  |  Height:  |  Size: 867 KiB

2760
src-tauri/Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -1,38 +1,44 @@
[package]
name = "app"
version = "0.1.0"
description = "A Tauri App"
authors = ["you"]
description = "The Zoo Modeling App"
authors = ["Zoo Engineers <eng@zoo.dev>"]
license = ""
repository = "https://github.com/KittyCAD/modeling-app"
default-run = "app"
edition = "2021"
rust-version = "1.70"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[build-dependencies]
tauri-build = { version = "2.0.0-beta.12", features = [] }
tauri-build = { version = "2.0.0-beta.13", features = [] }
[dependencies]
anyhow = "1"
kittycad = "0.2.67"
kcl-lib = { version = "0.1.53", path = "../src/wasm-lib/kcl" }
kittycad = "0.3.0"
log = "0.4.21"
oauth2 = "4.4.2"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
tauri = { version = "2.0.0-beta.15", features = [ "devtools", "unstable"] }
tauri-plugin-dialog = { version = "2.0.0-beta.5" }
tauri-plugin-fs = { version = "2.0.0-beta.5" }
tauri-plugin-http = { version = "2.0.0-beta.5" }
tauri-plugin-cli = { version = "2.0.0-beta.3" }
tauri-plugin-deep-link = { version = "2.0.0-beta.3" }
tauri-plugin-dialog = { version = "2.0.0-beta.6" }
tauri-plugin-fs = { version = "2.0.0-beta.6" }
tauri-plugin-http = { version = "2.0.0-beta.6" }
tauri-plugin-log = { version = "2.0.0-beta.4" }
tauri-plugin-os = { version = "2.0.0-beta.2" }
tauri-plugin-process = { version = "2.0.0-beta.2" }
tauri-plugin-shell = { version = "2.0.0-beta.2" }
tauri-plugin-updater = { version = "2.0.0-beta.4" }
tokio = { version = "1.37.0", features = ["time"] }
tokio = { version = "1.37.0", features = ["time", "fs", "process"] }
toml = "0.8.2"
url = "2.5.0"
[features]
default = ["updater"]
# this feature is used for production builds or when `devPath` points to the filesystem and the built-in dev server is disabled.
# If you use cargo directly instead of tauri's cli you can use this feature flag to switch between tauri's `dev` and `build` modes.
# DO NOT REMOVE!!
custom-protocol = ["tauri/custom-protocol"]
updater = []

376
src-tauri/Info.plist Normal file
View File

@ -0,0 +1,376 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>NSPrincipalClass</key>
<string>NSApplication</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>NSDesktopFolderUsageDescription</key>
<string>Zoo Modeling App accesses the Desktop to load and save your project files and/or exported files here</string>
<key>NSDocumentsFolderUsageDescription</key>
<string>Zoo Modeling App accesses the Documents folder to load and save your project files and/or exported files here</string>
<key>NSDownloadsFolderUsageDescription</key>
<string>Zoo Modeling App accesses the Downloads folder to load and save your project files and/or exported files here</string>
<key>ITSAppUsesNonExemptEncryption</key>
<false/>
<key>DTXcode</key>
<string>1501</string>
<key>DTXcodeBuild</key>
<string>15A507</string>
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleURLName</key>
<string>dev.zoo.modeling-app</string>
<key>CFBundleURLSchemes</key>
<array>
<string>zoo-modeling-app</string>
<string>zoo</string>
</array>
</dict>
</array>
<key>LSFileQuarantineEnabled</key>
<false/>
<key>CFBundleDocumentTypes</key>
<array>
<dict>
<key>LSItemContentTypes</key>
<array>
<string>dev.zoo.kcl</string>
</array>
<key>CFBundleTypeName</key>
<string>KCL</string>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>LSTypeIsPackage</key>
<false/>
<key>LSHandlerRank</key>
<string>Owner</string>
</dict>
<dict>
<key>LSItemContentTypes</key>
<array>
<string>dev.zoo.toml</string>
</array>
<key>CFBundleTypeName</key>
<string>TOML</string>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>LSTypeIsPackage</key>
<false/>
<key>LSHandlerRank</key>
<string>Default</string>
</dict>
<dict>
<key>LSItemContentTypes</key>
<array>
<string>dev.zoo.gltf</string>
</array>
<key>CFBundleTypeName</key>
<string>glTF</string>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>LSTypeIsPackage</key>
<false/>
<key>LSHandlerRank</key>
<string>Default</string>
</dict>
<dict>
<key>LSItemContentTypes</key>
<array>
<string>dev.zoo.glb</string>
</array>
<key>CFBundleTypeName</key>
<string>glb</string>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>LSTypeIsPackage</key>
<false/>
<key>LSHandlerRank</key>
<string>Default</string>
</dict>
<dict>
<key>LSItemContentTypes</key>
<array>
<string>dev.zoo.step</string>
</array>
<key>CFBundleTypeName</key>
<string>STEP</string>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>LSTypeIsPackage</key>
<false/>
<key>LSHandlerRank</key>
<string>Default</string>
</dict>
<dict>
<key>LSItemContentTypes</key>
<array>
<string>dev.zoo.fbx</string>
</array>
<key>CFBundleTypeName</key>
<string>FBX</string>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>LSTypeIsPackage</key>
<false/>
<key>LSHandlerRank</key>
<string>Default</string>
</dict>
<dict>
<key>LSItemContentTypes</key>
<array>
<string>dev.zoo.sldprt</string>
</array>
<key>CFBundleTypeName</key>
<string>Solidworks Part</string>
<key>CFBundleTypeRole</key>
<string>Viewer</string>
<key>LSTypeIsPackage</key>
<false/>
<key>LSHandlerRank</key>
<string>Default</string>
</dict>
<dict>
<key>LSItemContentTypes</key>
<array>
<string>public.geometry-definition-format</string>
</array>
<key>CFBundleTypeName</key>
<string>OBJ</string>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>LSTypeIsPackage</key>
<false/>
<key>LSHandlerRank</key>
<string>Default</string>
</dict>
<dict>
<key>LSItemContentTypes</key>
<array>
<string>public.polygon-file-format</string>
</array>
<key>CFBundleTypeName</key>
<string>PLY</string>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>LSTypeIsPackage</key>
<false/>
<key>LSHandlerRank</key>
<string>Default</string>
</dict>
<dict>
<key>LSItemContentTypes</key>
<array>
<string>public.standard-tesselated-geometry-format</string>
</array>
<key>CFBundleTypeName</key>
<string>STL</string>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>LSTypeIsPackage</key>
<false/>
<key>LSHandlerRank</key>
<string>Default</string>
</dict>
<dict>
<key>LSItemContentTypes</key>
<array>
<string>public.folder</string>
</array>
<key>CFBundleTypeName</key>
<string>Folders</string>
<key>CFBundleTypeRole</key>
<string>Viewer</string>
<key>LSHandlerRank</key>
<string>Alternate</string>
</dict>
</array>
<key>UTExportedTypeDeclarations</key>
<array>
<dict>
<key>UTTypeIdentifier</key>
<string>dev.zoo.kcl</string>
<key>UTTypeReferenceURL</key>
<string>https://zoo.dev/docs/kcl</string>
<key>UTTypeConformsTo</key>
<array>
<string>public.source-code</string>
<string>public.data</string>
<string>public.text</string>
<string>public.plain-text</string>
<string>public.3d-content</string>
<string>public.script</string>
</array>
<key>UTTypeDescription</key>
<string>KCL (KittyCAD Language) document</string>
<key>UTTypeTagSpecification</key>
<dict>
<key>public.filename-extension</key>
<array>
<string>kcl</string>
</array>
<key>public.mime-type</key>
<array>
<string>text/vnd.zoo.kcl</string>
</array>
</dict>
</dict>
<dict>
<key>UTTypeIdentifier</key>
<string>dev.zoo.gltf</string>
<key>UTTypeReferenceURL</key>
<string>https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html</string>
<key>UTTypeConformsTo</key>
<array>
<string>public.data</string>
<string>public.text</string>
<string>public.plain-text</string>
<string>public.3d-content</string>
<string>public.json</string>
</array>
<key>UTTypeDescription</key>
<string>Graphics Library Transmission Format (glTF)</string>
<key>UTTypeTagSpecification</key>
<dict>
<key>public.filename-extension</key>
<array>
<string>gltf</string>
</array>
<key>public.mime-type</key>
<array>
<string>model/gltf+json</string>
</array>
</dict>
</dict>
<dict>
<key>UTTypeIdentifier</key>
<string>dev.zoo.glb</string>
<key>UTTypeReferenceURL</key>
<string>https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html</string>
<key>UTTypeConformsTo</key>
<array>
<string>public.data</string>
<string>public.3d-content</string>
</array>
<key>UTTypeDescription</key>
<string>Graphics Library Transmission Format (glTF) binary</string>
<key>UTTypeTagSpecification</key>
<dict>
<key>public.filename-extension</key>
<array>
<string>glb</string>
</array>
<key>public.mime-type</key>
<array>
<string>model/gltf-binary</string>
</array>
</dict>
</dict>
<dict>
<key>UTTypeIdentifier</key>
<string>dev.zoo.step</string>
<key>UTTypeReferenceURL</key>
<string>https://www.loc.gov/preservation/digital/formats/fdd/fdd000448.shtml</string>
<key>UTTypeConformsTo</key>
<array>
<string>public.data</string>
<string>public.3d-content</string>
<string>public.text</string>
<string>public.plain-text</string>
</array>
<key>UTTypeDescription</key>
<string>STEP-file, ISO 10303-21</string>
<key>UTTypeTagSpecification</key>
<dict>
<key>public.filename-extension</key>
<array>
<string>step</string>
<string>stp</string>
</array>
<key>public.mime-type</key>
<array>
<string>model/step</string>
</array>
</dict>
</dict>
<dict>
<key>UTTypeIdentifier</key>
<string>dev.zoo.sldprt</string>
<key>UTTypeReferenceURL</key>
<string>https://docs.fileformat.com/cad/sldprt/</string>
<key>UTTypeConformsTo</key>
<array>
<string>public.data</string>
<string>public.3d-content</string>
</array>
<key>UTTypeDescription</key>
<string>Solidworks Part</string>
<key>UTTypeTagSpecification</key>
<dict>
<key>public.filename-extension</key>
<array>
<string>sldprt</string>
</array>
<key>public.mime-type</key>
<array>
<string>model/vnd.solidworks.sldprt</string>
</array>
</dict>
</dict>
<dict>
<key>UTTypeIdentifier</key>
<string>dev.zoo.fbx</string>
<key>UTTypeReferenceURL</key>
<string>https://en.wikipedia.org/wiki/FBX</string>
<key>UTTypeConformsTo</key>
<array>
<string>public.data</string>
<string>public.3d-content</string>
</array>
<key>UTTypeDescription</key>
<string>Autodesk Filmbox (FBX) format</string>
<key>UTTypeTagSpecification</key>
<dict>
<key>public.filename-extension</key>
<array>
<string>fbx</string>
<string>fbxb</string>
</array>
<key>public.mime-type</key>
<array>
<string>model/vnd.autodesk.fbx</string>
</array>
</dict>
</dict>
<dict>
<key>UTTypeIdentifier</key>
<string>dev.zoo.toml</string>
<key>UTTypeReferenceURL</key>
<string>https://toml.io/en/</string>
<key>UTTypeConformsTo</key>
<array>
<string>public.data</string>
<string>public.text</string>
<string>public.plain-text</string>
</array>
<key>UTTypeDescription</key>
<string>Tom's Obvious Minimal Language</string>
<key>UTTypeTagSpecification</key>
<dict>
<key>public.filename-extension</key>
<array>
<string>kcl</string>
</array>
<key>public.mime-type</key>
<array>
<string>text/toml</string>
</array>
</dict>
</dict>
</array>
</dict>
</plist>

View File

@ -7,6 +7,9 @@
"main"
],
"permissions": [
"cli:default",
"deep-link:default",
"log:default",
"path:default",
"event:default",
"window:default",
@ -23,7 +26,6 @@
"fs:allow-copy-file",
"fs:allow-mkdir",
"fs:allow-remove",
"fs:allow-remove",
"fs:allow-rename",
"fs:allow-exists",
"fs:allow-stat",

View File

@ -0,0 +1,24 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.app-sandbox</key>
<true/>
<key>com.apple.security.network.server</key>
<true/>
<key>com.apple.security.network.client</key>
<true/>
<key>com.apple.security.files.user-selected.read-write</key>
<true/>
<key>com.apple.security.files.downloads.read-write</key>
<true/>
<key>com.apple.application-identifier</key>
<string>92H8YB3B95.dev.zoo.modeling-app</string>
<key>com.apple.developer.team-identifier</key>
<string>92H8YB3B95</string>
<key>com.apple.developer.associated-domains</key>
<array>
<string>applinks:app.zoo.dev</string>
</array>
</dict>
</plist>

6
src-tauri/rustfmt.toml Normal file
View File

@ -0,0 +1,6 @@
max_width = 120
edition = "2018"
format_code_in_doc_comments = true
format_strings = false
imports_granularity = "Crate"
group_imports = "StdExternalCrate"

View File

@ -1,98 +1,232 @@
// Prevents additional console window on Windows in release, DO NOT REMOVE!!
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
use std::env;
use std::fs;
use std::io::Read;
use std::path::Path;
use std::path::PathBuf;
pub(crate) mod state;
use std::{
env,
path::{Path, PathBuf},
};
use anyhow::Result;
use kcl_lib::settings::types::{
file::{FileEntry, Project, ProjectRoute, ProjectState},
project::ProjectConfiguration,
Configuration,
};
use oauth2::TokenResponse;
use serde::Serialize;
use std::process::Command;
use tauri::ipc::InvokeError;
use tauri::{ipc::InvokeError, Manager};
use tauri_plugin_cli::CliExt;
use tauri_plugin_shell::ShellExt;
const DEFAULT_HOST: &str = "https://api.kittycad.io";
use tokio::process::Command;
const DEFAULT_HOST: &str = "https://api.zoo.dev";
const SETTINGS_FILE_NAME: &str = "settings.toml";
const PROJECT_SETTINGS_FILE_NAME: &str = "project.toml";
const PROJECT_FOLDER: &str = "zoo-modeling-app-projects";
/// This command returns the a json string parse from a toml file at the path.
#[tauri::command]
fn read_toml(path: &str) -> Result<String, InvokeError> {
let mut file = std::fs::File::open(path).map_err(|e| InvokeError::from_anyhow(e.into()))?;
let mut contents = String::new();
file.read_to_string(&mut contents)
.map_err(|e| InvokeError::from_anyhow(e.into()))?;
let value =
toml::from_str::<toml::Value>(&contents).map_err(|e| InvokeError::from_anyhow(e.into()))?;
let value = serde_json::to_string(&value).map_err(|e| InvokeError::from_anyhow(e.into()))?;
Ok(value)
fn get_initial_default_dir(app: tauri::AppHandle) -> Result<PathBuf, InvokeError> {
let dir = match app.path().document_dir() {
Ok(dir) => dir,
Err(_) => {
// for headless Linux (eg. Github Actions)
let home_dir = app.path().home_dir()?;
home_dir.join("Documents")
}
};
Ok(dir.join(PROJECT_FOLDER))
}
/// From https://github.com/tauri-apps/tauri/blob/1.x/core/tauri/src/api/dir.rs#L51
/// Removed from tauri v2
#[derive(Debug, Serialize)]
pub struct DiskEntry {
/// The path to the entry.
pub path: PathBuf,
/// The name of the entry (file name with extension or directory name).
pub name: Option<String>,
/// The children of this entry if it's a directory.
#[serde(skip_serializing_if = "Option::is_none")]
pub children: Option<Vec<DiskEntry>>,
}
/// From https://github.com/tauri-apps/tauri/blob/1.x/core/tauri/src/api/dir.rs#L51
/// Removed from tauri v2
fn is_dir<P: AsRef<Path>>(path: P) -> Result<bool> {
std::fs::metadata(path)
.map(|md| md.is_dir())
.map_err(Into::into)
}
/// From https://github.com/tauri-apps/tauri/blob/1.x/core/tauri/src/api/dir.rs#L51
/// Removed from tauri v2
#[tauri::command]
fn read_dir_recursive(path: &str) -> Result<Vec<DiskEntry>, InvokeError> {
let mut files_and_dirs: Vec<DiskEntry> = vec![];
// let path = path.as_ref();
for entry in fs::read_dir(path).map_err(|e| InvokeError::from_anyhow(e.into()))? {
let path = entry
.map_err(|e| InvokeError::from_anyhow(e.into()))?
.path();
async fn get_state(app: tauri::AppHandle) -> Result<Option<ProjectState>, InvokeError> {
let store = app.state::<state::Store>();
Ok(store.get().await)
}
if let Ok(flag) = is_dir(&path) {
files_and_dirs.push(DiskEntry {
path: path.clone(),
children: if flag {
Some(read_dir_recursive(path.to_str().expect("No path"))?)
} else {
None
},
name: path
.file_name()
.map(|name| name.to_string_lossy())
.map(|name| name.to_string()),
});
#[tauri::command]
async fn set_state(app: tauri::AppHandle, state: Option<ProjectState>) -> Result<(), InvokeError> {
let store = app.state::<state::Store>();
store.set(state).await;
Ok(())
}
async fn get_app_settings_file_path(app: &tauri::AppHandle) -> Result<PathBuf, InvokeError> {
let app_config_dir = app.path().app_config_dir()?;
// Ensure this directory exists.
if !app_config_dir.exists() {
tokio::fs::create_dir_all(&app_config_dir)
.await
.map_err(|e| InvokeError::from_anyhow(e.into()))?;
}
Ok(app_config_dir.join(SETTINGS_FILE_NAME))
}
#[tauri::command]
async fn read_app_settings_file(app: tauri::AppHandle) -> Result<Configuration, InvokeError> {
let mut settings_path = get_app_settings_file_path(&app).await?;
let mut needs_migration = false;
// Check if this file exists.
if !settings_path.exists() {
// Try the backwards compatible path.
// TODO: Remove this after a few releases.
let app_config_dir = app.path().app_config_dir()?;
settings_path = format!(
"{}user.toml",
app_config_dir.display().to_string().trim_end_matches('/')
)
.into();
needs_migration = true;
// Check if this path exists.
if !settings_path.exists() {
let mut default = Configuration::default();
default.settings.project.directory = get_initial_default_dir(app.clone())?;
// Return the default configuration.
return Ok(default);
}
}
Ok(files_and_dirs)
let contents = tokio::fs::read_to_string(&settings_path)
.await
.map_err(|e| InvokeError::from_anyhow(e.into()))?;
let mut parsed = Configuration::backwards_compatible_toml_parse(&contents).map_err(InvokeError::from_anyhow)?;
if parsed.settings.project.directory == PathBuf::new() {
parsed.settings.project.directory = get_initial_default_dir(app.clone())?;
}
// TODO: Remove this after a few releases.
if needs_migration {
write_app_settings_file(app, parsed.clone()).await?;
// Delete the old file.
tokio::fs::remove_file(settings_path)
.await
.map_err(|e| InvokeError::from_anyhow(e.into()))?;
}
Ok(parsed)
}
/// This command returns a string that is the contents of a file at the path.
#[tauri::command]
fn read_txt_file(path: &str) -> Result<String, InvokeError> {
let mut file = std::fs::File::open(path).map_err(|e| InvokeError::from_anyhow(e.into()))?;
let mut contents = String::new();
file.read_to_string(&mut contents)
async fn write_app_settings_file(app: tauri::AppHandle, configuration: Configuration) -> Result<(), InvokeError> {
let settings_path = get_app_settings_file_path(&app).await?;
let contents = toml::to_string_pretty(&configuration).map_err(|e| InvokeError::from_anyhow(e.into()))?;
tokio::fs::write(settings_path, contents.as_bytes())
.await
.map_err(|e| InvokeError::from_anyhow(e.into()))?;
Ok(contents)
Ok(())
}
async fn get_project_settings_file_path(
app_settings: Configuration,
project_name: &str,
) -> Result<PathBuf, InvokeError> {
let project_dir = app_settings.settings.project.directory.join(project_name);
if !project_dir.exists() {
tokio::fs::create_dir_all(&project_dir)
.await
.map_err(|e| InvokeError::from_anyhow(e.into()))?;
}
Ok(project_dir.join(PROJECT_SETTINGS_FILE_NAME))
}
#[tauri::command]
async fn read_project_settings_file(
app_settings: Configuration,
project_name: &str,
) -> Result<ProjectConfiguration, InvokeError> {
let settings_path = get_project_settings_file_path(app_settings, project_name).await?;
// Check if this file exists.
if !settings_path.exists() {
// Return the default configuration.
return Ok(ProjectConfiguration::default());
}
let contents = tokio::fs::read_to_string(&settings_path)
.await
.map_err(|e| InvokeError::from_anyhow(e.into()))?;
let parsed = ProjectConfiguration::backwards_compatible_toml_parse(&contents).map_err(InvokeError::from_anyhow)?;
Ok(parsed)
}
#[tauri::command]
async fn write_project_settings_file(
app_settings: Configuration,
project_name: &str,
configuration: ProjectConfiguration,
) -> Result<(), InvokeError> {
let settings_path = get_project_settings_file_path(app_settings, project_name).await?;
let contents = toml::to_string_pretty(&configuration).map_err(|e| InvokeError::from_anyhow(e.into()))?;
tokio::fs::write(settings_path, contents.as_bytes())
.await
.map_err(|e| InvokeError::from_anyhow(e.into()))?;
Ok(())
}
/// Initialize the directory that holds all the projects.
#[tauri::command]
async fn initialize_project_directory(configuration: Configuration) -> Result<PathBuf, InvokeError> {
configuration
.ensure_project_directory_exists()
.await
.map_err(InvokeError::from_anyhow)
}
/// Create a new project directory.
#[tauri::command]
async fn create_new_project_directory(
configuration: Configuration,
project_name: &str,
initial_code: Option<&str>,
) -> Result<Project, InvokeError> {
configuration
.create_new_project_directory(project_name, initial_code)
.await
.map_err(InvokeError::from_anyhow)
}
/// List all the projects in the project directory.
#[tauri::command]
async fn list_projects(configuration: Configuration) -> Result<Vec<Project>, InvokeError> {
configuration.list_projects().await.map_err(InvokeError::from_anyhow)
}
/// Get information about a project.
#[tauri::command]
async fn get_project_info(configuration: Configuration, project_path: &str) -> Result<Project, InvokeError> {
configuration
.get_project_info(project_path)
.await
.map_err(InvokeError::from_anyhow)
}
/// Parse the project route.
#[tauri::command]
async fn parse_project_route(configuration: Configuration, route: &str) -> Result<ProjectRoute, InvokeError> {
ProjectRoute::from_route(&configuration, route).map_err(InvokeError::from_anyhow)
}
#[tauri::command]
async fn read_dir_recursive(path: &str) -> Result<FileEntry, InvokeError> {
kcl_lib::settings::utils::walk_dir(&Path::new(path).to_path_buf())
.await
.map_err(InvokeError::from_anyhow)
}
/// This command instantiates a new window with auth.
/// The string returned from this method is the access token.
#[tauri::command]
async fn login(app: tauri::AppHandle, host: &str) -> Result<String, InvokeError> {
println!("Logging in...");
log::debug!("Logging in...");
// Do an OAuth 2.0 Device Authorization Grant dance to get a token.
let device_auth_url = oauth2::DeviceAuthorizationUrl::new(format!("{host}/oauth2/device/auth"))
.map_err(|e| InvokeError::from_anyhow(e.into()))?;
@ -103,8 +237,7 @@ async fn login(app: tauri::AppHandle, host: &str) -> Result<String, InvokeError>
let auth_client = oauth2::basic::BasicClient::new(
oauth2::ClientId::new(client_id),
None,
oauth2::AuthUrl::new(format!("{host}/authorize"))
.map_err(|e| InvokeError::from_anyhow(e.into()))?,
oauth2::AuthUrl::new(format!("{host}/authorize")).map_err(|e| InvokeError::from_anyhow(e.into()))?,
Some(
oauth2::TokenUrl::new(format!("{host}/oauth2/device/token"))
.map_err(|e| InvokeError::from_anyhow(e.into()))?,
@ -132,12 +265,10 @@ async fn login(app: tauri::AppHandle, host: &str) -> Result<String, InvokeError>
// and bypass the shell::open call as it fails on GitHub Actions.
let e2e_tauri_enabled = env::var("E2E_TAURI_ENABLED").is_ok();
if e2e_tauri_enabled {
println!(
"E2E_TAURI_ENABLED is set, won't open {} externally",
auth_uri.secret()
);
fs::write("/tmp/kittycad_user_code", details.user_code().secret())
.expect("Unable to write /tmp/kittycad_user_code file");
log::warn!("E2E_TAURI_ENABLED is set, won't open {} externally", auth_uri.secret());
tokio::fs::write("/tmp/kittycad_user_code", details.user_code().secret())
.await
.map_err(|e| InvokeError::from_anyhow(e.into()))?;
} else {
app.shell()
.open(auth_uri.secret(), None)
@ -160,10 +291,7 @@ async fn login(app: tauri::AppHandle, host: &str) -> Result<String, InvokeError>
///This command returns the KittyCAD user info given a token.
/// The string returned from this method is the user info as a json string.
#[tauri::command]
async fn get_user(
token: Option<String>,
hostname: &str,
) -> Result<kittycad::types::User, InvokeError> {
async fn get_user(token: &str, hostname: &str) -> Result<kittycad::types::User, InvokeError> {
// Use the host passed in if it's set.
// Otherwise, use the default host.
let host = if hostname.is_empty() {
@ -180,10 +308,10 @@ async fn get_user(
baseurl = format!("http://{host}")
}
}
println!("Getting user info...");
log::debug!("Getting user info...");
// use kittycad library to fetch the user info from /user/me
let mut client = kittycad::Client::new(token.unwrap());
let mut client = kittycad::Client::new(token);
if baseurl != DEFAULT_HOST {
client.set_base_url(&baseurl);
@ -202,50 +330,186 @@ async fn get_user(
/// From this GitHub comment: https://github.com/tauri-apps/tauri/issues/4062#issuecomment-1338048169
/// But with the Linux support removed since we don't need it for now.
#[tauri::command]
fn show_in_folder(path: String) {
#[cfg(target_os = "windows")]
fn show_in_folder(path: &str) -> Result<(), InvokeError> {
#[cfg(not(unix))]
{
Command::new("explorer")
.args(["/select,", &path]) // The comma after select is not a typo
.args(["/select,", path]) // The comma after select is not a typo
.spawn()
.unwrap();
.map_err(|e| InvokeError::from_anyhow(e.into()))?;
}
#[cfg(target_os = "macos")]
#[cfg(unix)]
{
Command::new("open").args(["-R", &path]).spawn().unwrap();
Command::new("open")
.args(["-R", path])
.spawn()
.map_err(|e| InvokeError::from_anyhow(e.into()))?;
}
Ok(())
}
#[allow(dead_code)]
fn open_url_sync(app: &tauri::AppHandle, url: &url::Url) {
log::debug!("Opening URL: {:?}", url);
let cloned_url = url.clone();
let runner: tauri::async_runtime::JoinHandle<Result<ProjectState>> = tauri::async_runtime::spawn(async move {
let url_str = cloned_url.path().to_string();
log::debug!("Opening URL path : {}", url_str);
let path = Path::new(url_str.as_str());
ProjectState::new_from_path(path.to_path_buf()).await
});
// Block on the handle.
match tauri::async_runtime::block_on(runner) {
Ok(Ok(store)) => {
// Create a state object to hold the project.
app.manage(state::Store::new(store));
}
Err(e) => {
log::warn!("Error opening URL:{} {:?}", url, e);
}
Ok(Err(e)) => {
log::warn!("Error opening URL:{} {:?}", url, e);
}
}
}
fn main() {
fn main() -> Result<()> {
tauri::Builder::default()
.setup(|_app| {
#[cfg(debug_assertions)]
{
use tauri::Manager;
_app.get_webview("main").unwrap().open_devtools();
}
#[cfg(not(debug_assertions))]
{
_app.handle()
.plugin(tauri_plugin_updater::Builder::new().build())?;
}
Ok(())
})
.invoke_handler(tauri::generate_handler![
get_state,
set_state,
get_initial_default_dir,
initialize_project_directory,
create_new_project_directory,
list_projects,
get_project_info,
parse_project_route,
get_user,
login,
read_toml,
read_txt_file,
read_dir_recursive,
show_in_folder,
read_app_settings_file,
write_app_settings_file,
read_project_settings_file,
write_project_settings_file,
])
.plugin(tauri_plugin_cli::init())
.plugin(tauri_plugin_deep_link::init())
.plugin(tauri_plugin_dialog::init())
.plugin(tauri_plugin_fs::init())
.plugin(tauri_plugin_http::init())
.plugin(
tauri_plugin_log::Builder::new()
.targets([
tauri_plugin_log::Target::new(tauri_plugin_log::TargetKind::Stdout),
tauri_plugin_log::Target::new(tauri_plugin_log::TargetKind::LogDir { file_name: None }),
])
.level(log::LevelFilter::Debug)
.build(),
)
.plugin(tauri_plugin_os::init())
.plugin(tauri_plugin_process::init())
.plugin(tauri_plugin_shell::init())
.run(tauri::generate_context!())
.expect("error while running tauri application");
.setup(|app| {
// Do update things.
#[cfg(debug_assertions)]
{
app.get_webview("main").unwrap().open_devtools();
}
#[cfg(not(debug_assertions))]
#[cfg(feature = "updater")]
{
app.handle().plugin(tauri_plugin_updater::Builder::new().build())?;
}
let mut verbose = false;
let mut source_path: Option<PathBuf> = None;
match app.cli().matches() {
// `matches` here is a Struct with { args, subcommand }.
// `args` is `HashMap<String, ArgData>` where `ArgData` is a struct with { value, occurrences }.
// `subcommand` is `Option<Box<SubcommandMatches>>` where `SubcommandMatches` is a struct with { name, matches }.
Ok(matches) => {
if let Some(verbose_flag) = matches.args.get("verbose") {
let Some(value) = verbose_flag.value.as_bool() else {
return Err(
anyhow::anyhow!("Error parsing CLI arguments: verbose flag is not a boolean").into(),
);
};
verbose = value;
}
// Get the path we are trying to open.
if let Some(source_arg) = matches.args.get("source") {
// We don't do an else here because this can be null.
if let Some(value) = source_arg.value.as_str() {
log::info!("Got path in cli argument: {}", value);
source_path = Some(Path::new(value).to_path_buf());
}
}
}
Err(err) => {
return Err(anyhow::anyhow!("Error parsing CLI arguments: {:?}", err).into());
}
}
if verbose {
log::debug!("Verbose mode enabled.");
}
// If we have a source path to open, make sure it exists.
let Some(source_path) = source_path else {
// The user didn't provide a source path to open.
// Run the app as normal.
app.manage(state::Store::default());
return Ok(());
};
if !source_path.exists() {
return Err(anyhow::anyhow!(
"Error: the path `{}` you are trying to open does not exist",
source_path.display()
)
.into());
}
let runner: tauri::async_runtime::JoinHandle<Result<ProjectState>> =
tauri::async_runtime::spawn(async move { ProjectState::new_from_path(source_path).await });
// Block on the handle.
let store = tauri::async_runtime::block_on(runner)??;
// Create a state object to hold the project.
app.manage(state::Store::new(store));
// Listen on the deep links.
app.listen("deep-link://new-url", |event| {
log::info!("got deep-link url: {:?}", event);
// TODO: open_url_sync(app.handle(), event.url);
});
Ok(())
})
.build(tauri::generate_context!())?
.run(
#[allow(unused_variables)]
|app, event| {
#[cfg(any(target_os = "macos", target_os = "ios"))]
if let tauri::RunEvent::Opened { urls } = event {
log::info!("Opened URLs: {:?}", urls);
// Handle the first URL.
// TODO: do we want to handle more than one URL?
// Under what conditions would we even have more than one?
if let Some(url) = urls.first() {
open_url_sync(app, url);
}
}
},
);
Ok(())
}

21
src-tauri/src/state.rs Normal file
View File

@ -0,0 +1,21 @@
//! State management for the application.
use kcl_lib::settings::types::file::ProjectState;
use tokio::sync::Mutex;
#[derive(Debug, Default)]
pub struct Store(Mutex<Option<ProjectState>>);
impl Store {
pub fn new(p: ProjectState) -> Self {
Self(Mutex::new(Some(p)))
}
pub async fn get(&self) -> Option<ProjectState> {
self.0.lock().await.clone()
}
pub async fn set(&self, p: Option<ProjectState>) {
*self.0.lock().await = p;
}
}

View File

@ -0,0 +1,8 @@
{
"$schema": "../node_modules/@tauri-apps/cli/schema.json",
"bundle": {
"macOS": {
"entitlements": "entitlements/app-store.entitlements"
}
}
}

View File

@ -37,23 +37,42 @@
}
},
"longDescription": "",
"macOS": {
"entitlements": null,
"exceptionDomain": "",
"frameworks": [],
"providerShortName": null,
"signingIdentity": null
},
"macOS": {},
"resources": [],
"shortDescription": "",
"targets": "all"
},
"identifier": "dev.zoo.modeling-app",
"plugins": {
"cli": {
"description": "Zoo Modeling App CLI",
"args": [
{
"short": "v",
"name": "verbose",
"description": "Verbosity level"
},
{
"name": "source",
"description": "The file or directory to open",
"required": false,
"index": 1,
"takesValue": true
}
],
"subcommands": {}
},
"deep-link": {
"domains": [
{
"host": "app.zoo.dev"
}
]
},
"shell": {
"open": true
}
},
"productName": "Zoo Modeling App",
"version": "0.17.3"
"version": "0.21.0"
}

View File

@ -5,7 +5,45 @@
"certificateThumbprint": "F4C9A52FF7BC26EE5E054946F6B11DEEA94C748D",
"digestAlgorithm": "sha256",
"timestampUrl": "http://timestamp.digicert.com"
}
},
"fileAssociations": [
{
"ext": ["kcl"],
"mimeType": "text/vnd.zoo.kcl"
},
{
"ext": ["obj"],
"mimeType": "model/obj"
},
{
"ext": ["gltf"],
"mimeType": "model/gltf+json"
},
{
"ext": ["glb"],
"mimeType": "model/gltf+binary"
},
{
"ext": ["fbx", "fbxb"],
"mimeType": "model/fbx"
},
{
"ext": ["stl"],
"mimeType": "model/stl"
},
{
"ext": ["ply"],
"mimeType": "model/ply"
},
{
"ext": ["step", "stp"],
"mimeType": "model/step"
},
{
"ext": ["sldprt"],
"mimeType": "model/sldprt"
}
]
},
"plugins": {
"updater": {

View File

@ -30,6 +30,7 @@ import SettingsAuthProvider from 'components/SettingsAuthProvider'
import LspProvider from 'components/LspProvider'
import { KclContextProvider } from 'lang/KclProvider'
import { BROWSER_PROJECT_NAME } from 'lib/constants'
import { getState, setState } from 'lib/tauri'
const router = createBrowserRouter([
{
@ -52,10 +53,29 @@ const router = createBrowserRouter([
children: [
{
path: paths.INDEX,
loader: () =>
isTauri()
loader: async () => {
const inTauri = isTauri()
if (inTauri) {
const appState = await getState()
if (appState) {
// Reset the state.
// We do this so that we load the initial state from the cli but everything
// else we can ignore.
await setState(undefined)
// Redirect to the file if we have a file path.
if (appState.current_file) {
return redirect(
paths.FILE + '/' + encodeURIComponent(appState.current_file)
)
}
}
}
return inTauri
? redirect(paths.HOME)
: redirect(paths.FILE + '/%2F' + BROWSER_PROJECT_NAME),
: redirect(paths.FILE + '/%2F' + BROWSER_PROJECT_NAME)
},
},
{
loader: fileLoader,

View File

@ -4,7 +4,6 @@ import { engineCommandManager, kclManager } from 'lib/singletons'
import { useModelingContext } from 'hooks/useModelingContext'
import { useCommandsContext } from 'hooks/useCommandsContext'
import { ActionButton } from 'components/ActionButton'
import usePlatform from 'hooks/usePlatform'
import { isSingleCursorInPipe } from 'lang/queryAst'
import { useKclContext } from 'lang/KclProvider'
import {
@ -14,7 +13,6 @@ import {
import { useStore } from 'useStore'
export const Toolbar = () => {
const platform = usePlatform()
const { commandBarSend } = useCommandsContext()
const { state, send, context } = useModelingContext()
const toolbarButtonsRef = useRef<HTMLUListElement>(null)
@ -274,17 +272,8 @@ export const Toolbar = () => {
}
return (
<div className="max-w-full flex items-stretch rounded-l-sm rounded-r-full bg-chalkboard-10/80 dark:bg-chalkboard-110/70 relative">
<menu className="flex-1 pl-1 pr-2 py-0 overflow-hidden rounded-l-sm whitespace-nowrap border-solid border border-primary/30 dark:border-chalkboard-90 border-r-0">
<ToolbarButtons />
</menu>
<ActionButton
Element="button"
onClick={() => commandBarSend({ type: 'Open' })}
className="rounded-r-full pr-4 self-stretch border-primary/30 hover:border-primary dark:border-chalkboard-80 dark:bg-chalkboard-80 text-primary"
>
{platform === 'macos' ? '⌘K' : 'Ctrl+/'}
</ActionButton>
</div>
<menu className="max-w-full overflow-hidden whitespace-nowrap rounded px-1.5 py-0.5 backdrop-blur-sm bg-chalkboard-10/80 dark:bg-chalkboard-110/70 relative">
<ToolbarButtons />
</menu>
)
}

View File

@ -246,13 +246,31 @@ export class CameraControls {
camSettings.center.y,
camSettings.center.z
)
this.camera.up.set(camSettings.up.x, camSettings.up.y, camSettings.up.z)
if (this.camera instanceof PerspectiveCamera && camSettings.ortho) {
this.useOrthographicCamera()
}
if (this.camera instanceof OrthographicCamera && !camSettings.ortho) {
this.usePerspectiveCamera()
}
if (this.camera instanceof PerspectiveCamera && camSettings.fov_y) {
this.camera.fov = camSettings.fov_y
} else if (
this.camera instanceof OrthographicCamera &&
camSettings.ortho_scale
) {
this.camera.zoom = camSettings.ortho_scale
const distanceToTarget = new Vector3(
camSettings.pos.x,
camSettings.pos.y,
camSettings.pos.z
).distanceTo(
new Vector3(
camSettings.center.x,
camSettings.center.y,
camSettings.center.z
)
)
this.camera.zoom = (camSettings.ortho_scale * 40) / distanceToTarget
}
this.onCameraChange()
}
@ -965,10 +983,10 @@ export class CameraControls {
// Pure function helpers
function calculateNearFarFromFOV(fov: number) {
const nearFarRatio = (fov - 3) / (45 - 3)
// const nearFarRatio = (fov - 3) / (45 - 3)
// const z_near = 0.1 + nearFarRatio * (5 - 0.1)
const z_far = 1000 + nearFarRatio * (100000 - 1000)
return { z_near: 0.1, z_far }
// const z_far = 1000 + nearFarRatio * (100000 - 1000)
return { z_near: 0.1, z_far: 1000 }
}
function convertThreeCamValuesToEngineCam({
@ -1043,3 +1061,62 @@ function _getInteractionType(
if (enableZoom && interactionGuards.zoom.dragCallback(event)) return 'zoom'
return state
}
/**
* Tells the engine to fire it's animation waits for it to finish and then requests camera settings
* to ensure the client-side camera is synchronized with the engine's camera state.
*
* @param engineCommandManager Our websocket singleton
* @param entityId - The ID of face or sketchPlane.
*/
export async function letEngineAnimateAndSyncCamAfter(
engineCommandManager: EngineCommandManager,
entityId: string
) {
await engineCommandManager.sendSceneCommand({
type: 'modeling_cmd_req',
cmd_id: uuidv4(),
cmd: {
type: 'enable_sketch_mode',
adjust_camera: true,
animated: !isReducedMotion(),
ortho: false,
entity_id: entityId,
},
})
// wait 600ms (animation takes 500, + 100 for safety)
await new Promise((resolve) =>
setTimeout(resolve, isReducedMotion() ? 100 : 600)
)
await engineCommandManager.sendSceneCommand({
// CameraControls subscribes to default_camera_get_settings response events
// firing this at connection ensure the camera's are synced initially
type: 'modeling_cmd_req',
cmd_id: uuidv4(),
cmd: {
type: 'default_camera_get_settings',
},
})
await engineCommandManager.sendSceneCommand({
type: 'modeling_cmd_req',
cmd_id: uuidv4(),
cmd: {
type: 'enable_sketch_mode',
adjust_camera: true,
animated: false,
ortho: true,
entity_id: entityId,
},
})
await new Promise((resolve) => setTimeout(resolve, 50))
await engineCommandManager.sendSceneCommand({
// CameraControls subscribes to default_camera_get_settings response events
// firing this at connection ensure the camera's are synced initially
type: 'modeling_cmd_req',
cmd_id: uuidv4(),
cmd: {
type: 'default_camera_get_settings',
},
})
}

View File

@ -3,7 +3,6 @@ import { useModelingContext } from 'hooks/useModelingContext'
import { cameraMouseDragGuards } from 'lib/cameraControls'
import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext'
import { useStore } from 'useStore'
import { ARROWHEAD, DEBUG_SHOW_BOTH_SCENES } from './sceneInfra'
import { ReactCameraProperties } from './CameraControls'
import { throttle } from 'lib/utils'
@ -47,10 +46,6 @@ export const ClientSideScene = ({
const canvasRef = useRef<HTMLDivElement>(null)
const { state, send, context } = 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.
@ -69,7 +64,6 @@ export const ClientSideScene = ({
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)
@ -96,7 +90,8 @@ export const ClientSideScene = ({
cursor = 'grabbing'
} else if (
state.matches('Sketch.Line tool') ||
state.matches('Sketch.Tangential arc to')
state.matches('Sketch.Tangential arc to') ||
state.matches('Sketch.Rectangle tool')
) {
cursor = 'crosshair'
} else {
@ -110,9 +105,9 @@ export const ClientSideScene = ({
style={{ cursor: cursor }}
className={`absolute inset-0 h-full w-full transition-all duration-300 ${
hideClient ? 'opacity-0' : 'opacity-100'
} ${hideServer ? 'bg-black' : ''} ${
} ${hideServer ? 'bg-chalkboard-10 dark:bg-chalkboard-100' : ''} ${
!hideClient && !hideServer && state.matches('Sketch')
? 'bg-black/80'
? 'bg-chalkboard-10/80 dark:bg-chalkboard-100/80'
: ''
}`}
></div>

View File

@ -57,6 +57,7 @@ import {
kclManager,
sceneInfra,
codeManager,
editorManager,
} from 'lib/singletons'
import { getNodeFromPath, getNodePathFromSourceRange } from 'lang/queryAst'
import { executeAst, useStore } from 'useStore'
@ -96,6 +97,7 @@ import {
getRectangleCallExpressions,
updateRectangleSketch,
} from 'lib/rectangleTool'
import { getThemeColorForThreeJs } from 'lib/theme'
type DraftSegment = 'line' | 'tangentialArcTo'
@ -214,8 +216,9 @@ export class SceneEntities {
const orthoFactor = orthoScale(sceneInfra.camControls.camera)
const baseXColor = 0x000055
const baseYColor = 0x550000
const xAxisGeometry = new BoxGeometry(100000, 0.3, 0.01)
const yAxisGeometry = new BoxGeometry(0.3, 100000, 0.01)
const axisPixelWidth = 1.6
const xAxisGeometry = new BoxGeometry(100000, axisPixelWidth, 0.01)
const yAxisGeometry = new BoxGeometry(axisPixelWidth, 100000, 0.01)
const xAxisMaterial = new MeshBasicMaterial({
color: baseXColor,
depthTest: false,
@ -354,6 +357,7 @@ export class SceneEntities {
id: sketchGroup.start.__geoMeta.id,
pathToNode: segPathToNode,
scale: factor,
theme: sceneInfra._theme,
})
_profileStart.layers.set(SKETCH_LAYER)
_profileStart.traverse((child) => {
@ -404,6 +408,7 @@ export class SceneEntities {
isDraftSegment,
scale: factor,
texture: sceneInfra.extraSegmentTexture,
theme: sceneInfra._theme,
})
} else {
seg = straightSegment({
@ -415,6 +420,7 @@ export class SceneEntities {
scale: factor,
callExpName,
texture: sceneInfra.extraSegmentTexture,
theme: sceneInfra._theme,
})
}
seg.layers.set(SKETCH_LAYER)
@ -962,7 +968,7 @@ export class SceneEntities {
if (!draftInfo)
// don't want to mod the user's code yet as they have't committed to the change yet
// plus this would be the truncated ast being recast, it would be wrong
codeManager.updateCodeStateEditor(code)
codeManager.updateCodeEditor(code)
const { programMemory } = await executeAst({
ast: truncatedAst,
useFakeExecutor: true,
@ -1323,30 +1329,31 @@ export class SceneEntities {
selected.material.color = defaultPlaneColor(type)
},
onClick: async (args) => {
const checkExtrudeFaceClick = async (): Promise<boolean> => {
const checkExtrudeFaceClick = async (): Promise<
['face' | 'plane' | 'other', string]
> => {
const { streamDimensions } = useStore.getState()
const { entity_id } = await sendSelectEventToEngine(
args?.mouseEvent,
document.getElementById('video-stream') as HTMLVideoElement,
streamDimensions
)
if (!entity_id) return false
if (!entity_id) return ['other', '']
if (
engineCommandManager.defaultPlanes?.xy === entity_id ||
engineCommandManager.defaultPlanes?.xz === entity_id ||
engineCommandManager.defaultPlanes?.yz === entity_id
) {
return ['plane', entity_id]
}
const artifact = this.engineCommandManager.artifactMap[entity_id]
if (artifact?.commandType !== 'solid3d_get_extrusion_face_info')
return false
const faceInfo: Models['FaceIsPlanar_type'] = (
await this.engineCommandManager.sendSceneCommand({
type: 'modeling_cmd_req',
cmd_id: uuidv4(),
cmd: {
type: 'face_is_planar',
object_id: entity_id,
},
})
)?.data?.data
return ['other', entity_id]
const faceInfo = await getFaceDetails(entity_id)
if (!faceInfo?.origin || !faceInfo?.z_axis || !faceInfo?.y_axis)
return false
const { z_axis, origin, y_axis } = faceInfo
return ['other', entity_id]
const { z_axis, y_axis, origin } = faceInfo
const pathToNode = getNodePathFromSourceRange(
kclManager.ast,
artifact.range
@ -1366,12 +1373,15 @@ export class SceneEntities {
artifact?.additionalData?.type === 'cap'
? artifact.additionalData.info
: 'none',
faceId: entity_id,
},
})
return true
return ['face', entity_id]
}
if (await checkExtrudeFaceClick()) return
const faceResult = await checkExtrudeFaceClick()
console.log('faceResult', faceResult)
if (faceResult[0] === 'face') return
if (!args || !args.intersects?.[0]) return
if (args.mouseEvent.which !== 1) return
@ -1397,6 +1407,7 @@ export class SceneEntities {
plane: planeString,
zAxis,
yAxis,
planeId: faceResult[1],
},
})
},
@ -1423,7 +1434,7 @@ export class SceneEntities {
parent.userData.pathToNode,
'CallExpression'
).node
sceneInfra.highlightCallback([node.start, node.end])
editorManager.setHighlightRange([node.start, node.end])
const yellow = 0xffff00
colorSegment(selected, yellow)
const extraSegmentGroup = parent.getObjectByName(EXTRA_SEGMENT_HANDLE)
@ -1459,10 +1470,10 @@ export class SceneEntities {
}
return
}
sceneInfra.highlightCallback([0, 0])
editorManager.setHighlightRange([0, 0])
},
onMouseLeave: ({ selected, ...rest }: OnMouseEnterLeaveArgs) => {
sceneInfra.highlightCallback([0, 0])
editorManager.setHighlightRange([0, 0])
const parent = getParentGroup(selected, [
STRAIGHT_SEGMENT,
TANGENTIAL_ARC_TO_SEGMENT,
@ -1496,7 +1507,10 @@ export class SceneEntities {
const isSelected = parent?.userData?.isSelected
colorSegment(
selected,
isSelected ? 0x0000ff : parent?.userData?.baseColor || 0xffffff
isSelected
? 0x0000ff
: parent?.userData?.baseColor ||
getThemeColorForThreeJs(sceneInfra._theme)
)
const extraSegmentGroup = parent?.getObjectByName(EXTRA_SEGMENT_HANDLE)
if (extraSegmentGroup) {
@ -1680,7 +1694,7 @@ export async function getSketchOrientationDetails(
sketchPathToNode: PathToNode
): Promise<{
quat: Quaternion
sketchDetails: SketchDetails
sketchDetails: SketchDetails & { faceId?: string }
}> {
const sketchGroup = sketchGroupFromPathToNode({
pathToNode: sketchPathToNode,
@ -1696,20 +1710,13 @@ export async function getSketchOrientationDetails(
zAxis: [zAxis.x, zAxis.y, zAxis.z],
yAxis: [sketchGroup.yAxis.x, sketchGroup.yAxis.y, sketchGroup.yAxis.z],
origin: [0, 0, 0],
faceId: sketchGroup.on.id,
},
}
}
if (sketchGroup.on.type === 'face') {
const faceInfo: Models['FaceIsPlanar_type'] = (
await engineCommandManager.sendSceneCommand({
type: 'modeling_cmd_req',
cmd_id: uuidv4(),
cmd: {
type: 'face_is_planar',
object_id: sketchGroup.on.faceId,
},
})
)?.data?.data
const faceInfo = await getFaceDetails(sketchGroup.on.faceId)
if (!faceInfo?.origin || !faceInfo?.z_axis || !faceInfo?.y_axis)
throw new Error('faceInfo')
const { z_axis, y_axis, origin } = faceInfo
@ -1724,6 +1731,7 @@ export async function getSketchOrientationDetails(
zAxis: [z_axis.x, z_axis.y, z_axis.z],
yAxis: [y_axis.x, y_axis.y, y_axis.z],
origin: [origin.x, origin.y, origin.z],
faceId: sketchGroup.on.faceId,
},
}
}
@ -1732,6 +1740,46 @@ export async function getSketchOrientationDetails(
)
}
/**
* Retrieves orientation details for a given entity representing a face (brep face or default plane).
* This function asynchronously fetches and returns the origin, x-axis, y-axis, and z-axis details
* for a specified entity ID. It is primarily used to obtain the orientation of a face in the scene,
* which is essential for calculating the correct positioning and alignment of the client side sketch.
*
* @param entityId - The ID of the entity for which orientation details are being fetched.
* @returns A promise that resolves with the orientation details of the face.
*/
async function getFaceDetails(
entityId: string
): Promise<Models['FaceIsPlanar_type']> {
// TODO mode engine connection to allow batching returns and batch the following
await engineCommandManager.sendSceneCommand({
type: 'modeling_cmd_req',
cmd_id: uuidv4(),
cmd: {
type: 'enable_sketch_mode',
adjust_camera: false,
animated: false,
ortho: false,
entity_id: entityId,
},
})
// TODO change typing to get_sketch_mode_plane once lib is updated
const faceInfo: Models['FaceIsPlanar_type'] = (
await engineCommandManager.sendSceneCommand({
type: 'modeling_cmd_req',
cmd_id: uuidv4(),
cmd: { type: 'get_sketch_mode_plane' },
})
)?.data?.data
await engineCommandManager.sendSceneCommand({
type: 'modeling_cmd_req',
cmd_id: uuidv4(),
cmd: { type: 'sketch_mode_disable' },
})
return faceInfo
}
export function getQuaternionFromZAxis(zAxis: Vector3): Quaternion {
const dummyCam = new PerspectiveCamera()
dummyCam.up.set(0, 0, 1)

View File

@ -24,13 +24,13 @@ import {
import { compareVec2Epsilon2 } from 'lang/std/sketch'
import { useModelingContext } from 'hooks/useModelingContext'
import * as TWEEN from '@tweenjs/tween.js'
import { SourceRange } from 'lang/wasm'
import { Axis } from 'lib/selections'
import { type BaseUnit } from 'lib/settings/settingsTypes'
import { CameraControls } from './CameraControls'
import { EngineCommandManager } from 'lang/std/engineConnection'
import { settings } from 'lib/settings/initialSettings'
import { MouseState } from 'machines/modelingMachine'
import { Themes } from 'lib/theme'
type SendType = ReturnType<typeof useModelingContext>['send']
@ -102,6 +102,7 @@ export class SceneInfra {
isFovAnimationInProgress = false
_baseUnit: BaseUnit = 'mm'
_baseUnitMultiplier = 1
_theme: Themes = Themes.System
extraSegmentTexture: Texture
lastMouseState: MouseState = { type: 'idle' }
onDragStartCallback: (arg: OnDragCallbackArgs) => void = () => {}
@ -138,6 +139,9 @@ export class SceneInfra {
this._baseUnitMultiplier
)
}
set theme(theme: Themes) {
this._theme = theme
}
resetMouseListeners = () => {
this.setCallbacks({
onDragStart: () => {},
@ -149,10 +153,6 @@ export class SceneInfra {
onMouseLeave: () => {},
})
}
highlightCallback: (a: SourceRange) => void = () => {}
setHighlightCallback(cb: (a: SourceRange) => void) {
this.highlightCallback = cb
}
modelingSend: SendType = (() => {}) as any
setSend(send: SendType) {

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