Compare commits

...

47 Commits

Author SHA1 Message Date
440eb2636a Cut release v0.21.6 (#2450)
* Cut release v0.21.6

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

* trigger ci

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-05-22 12:05:44 +10:00
344e72d7ec Cam fix (#2448)
* Revert "sketch dies on exit XY sketch (#2397)"

This reverts commit 75c6ae6e66.

* cam fix

* fmt
2024-05-22 01:19:13 +00:00
ec7b733a0d fix project list showing projects of double clicked files (#2441)
* make sure there is at least one kcl file in the dir to show in list

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

* open the correct file not assuming main.kcl

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

* add file path tests

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

* update settings paths

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

* new images

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-21 18:14:49 -07:00
63159c1cb8 fix reset settings in browser (#2434)
Signed-off-by: Jess Frazelle <github@jessfraz.com>
2024-05-21 13:33:52 -07:00
df62a995b5 Reset data channel seq every connect (closes #336) (#2431)
Reset inSequence back to 1 every time we connect to the Engine,
otherwise we'll continue to think the current sequence is a high number
(while the engine has no memory of the last session -- and may even be a
new engine instance!) and ignore messages until we pass that counter
again.

Signed-off-by: Paul Tagliamonte <paul@zoo.dev>
2024-05-21 14:52:14 -04:00
fa762c1c4d throw error on both ranges (#2428)
* highlight both ranges

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

* add playwright test

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

---------

Signed-off-by: Jess Frazelle <github@jessfraz.com>
2024-05-21 00:49:57 -07:00
82b03a9d47 Add stdlib functions for getting sketch profile start and its components (#2373)
* Add stdlib functions for getting sketch profile start and its components

* Fix it up and actually generate snapshots

* cargo fmt

* Use `.to` instead of `.from`

* Update docs with EXPECTORATE=overwrite

* Add README

* fmt

* Update flow test to account for more autocompletion options when typing "start"

---------

Co-authored-by: Jess Frazelle <jessfraz@users.noreply.github.com>
2024-05-21 00:44:02 -07:00
793b7407f6 add TS ast walker (#2425) 2024-05-21 16:44:08 +10:00
040bcc2c09 bump zip (#2423)
* bump zip

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

* rerender images

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

* fix markdown

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

---------

Signed-off-by: Jess Frazelle <github@jessfraz.com>
2024-05-21 05:07:56 +00:00
ae2e219394 fix empty tag on sketch on face (#2424)
add test

Signed-off-by: Jess Frazelle <github@jessfraz.com>
2024-05-20 21:59:56 -07:00
a83f549257 Bump @tauri-apps/plugin-fs from 2.0.0-beta.2 to 2.0.0-beta.3 (#2403)
Bumps @tauri-apps/plugin-fs from 2.0.0-beta.2 to 2.0.0-beta.3.

---
updated-dependencies:
- dependency-name: "@tauri-apps/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-05-20 21:37:27 -07:00
3871d2858f Bump @tauri-apps/plugin-updater from 2.0.0-beta.2 to 2.0.0-beta.3 (#2404)
Bumps @tauri-apps/plugin-updater from 2.0.0-beta.2 to 2.0.0-beta.3.

---
updated-dependencies:
- dependency-name: "@tauri-apps/plugin-updater"
  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-20 21:37:13 -07:00
3effb87f8e Bump react-router-dom from 6.22.3 to 6.23.1 (#2405)
Bumps [react-router-dom](https://github.com/remix-run/react-router/tree/HEAD/packages/react-router-dom) from 6.22.3 to 6.23.1.
- [Release notes](https://github.com/remix-run/react-router/releases)
- [Changelog](https://github.com/remix-run/react-router/blob/main/packages/react-router-dom/CHANGELOG.md)
- [Commits](https://github.com/remix-run/react-router/commits/react-router-dom@6.23.1/packages/react-router-dom)

---
updated-dependencies:
- dependency-name: react-router-dom
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-20 21:36:57 -07:00
3f2f035a9b Bump three from 0.163.0 to 0.164.1 (#2406)
Bumps [three](https://github.com/mrdoob/three.js) from 0.163.0 to 0.164.1.
- [Release notes](https://github.com/mrdoob/three.js/releases)
- [Commits](https://github.com/mrdoob/three.js/commits)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-20 21:36:47 -07:00
4735eaef8c Bump vitest from 1.5.0 to 1.6.0 (#2402)
Bumps [vitest](https://github.com/vitest-dev/vitest/tree/HEAD/packages/vitest) from 1.5.0 to 1.6.0.
- [Release notes](https://github.com/vitest-dev/vitest/releases)
- [Commits](https://github.com/vitest-dev/vitest/commits/v1.6.0/packages/vitest)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-20 21:36:35 -07:00
69f8da058a add tests for min and max (#2420)
* add tests

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

* Update src/wasm-lib/kcl/src/std/math.rs

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

* fmt

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

* add another

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

---------

Signed-off-by: Jess Frazelle <github@jessfraz.com>
Co-authored-by: Kurt Hutten <k.hutten@protonmail.ch>
2024-05-20 21:14:00 -07:00
93ebf13621 Wrapper for keybindings (codemirror and app) (#2421)
* start

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

* add hotkey wrapper

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-05-20 20:52:33 -07:00
20c4d44b8b Cut release v0.21.5 (#2418) 2024-05-21 10:35:10 +10:00
8ea8f80e32 Sort keys alphabetically in the Variables/Memory panel (#2417) 2024-05-20 17:28:51 -05:00
d73339fd8d Edit mode prep (#2370)
* remove edit mode

* small rename

* update with compat with old edit mode

* exit edit mode still needed

* add log to export
2024-05-21 05:55:34 +10:00
031b230690 Disable home logo link in the browser app (#2371)
* Disable logo link in browser, clean up menu components' organization

* Include the app name in Tauri as screenreader-only text

* Fix tsc issue in component test

* Add a playwright test to ensure logo is never a link in the browser

* Update broken component tests
2024-05-20 14:59:59 -04:00
1125d74f12 Update serde (#2415) 2024-05-20 18:01:48 +00:00
5c7a2822d0 Add some JSDocs comments to types, convert some existing comments. (#2363)
* Add some JSDocs comments to types, convert some existing comments.

* Add JSDoc comments to EngineCommandManager

* Add deprecation message to `lastArtifactMap`

* fmt

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

* 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>
2024-05-20 13:38:51 -04:00
d44b1f8e54 Fix code editor user input in older version of Safari (#2350)
Fix where code editor wasn't allowing code insertion in older versions of Safari.

Co-authored-by: Jess Frazelle <jessfraz@users.noreply.github.com>
2024-05-20 11:28:34 -04:00
c4ca69496b update deps (#2412)
* update deps

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-20 06:36:44 +00:00
f06de7f586 Bump proc-macro2 from 1.0.82 to 1.0.83 in /src/wasm-lib (#2409)
Bumps [proc-macro2](https://github.com/dtolnay/proc-macro2) from 1.0.82 to 1.0.83.
- [Release notes](https://github.com/dtolnay/proc-macro2/releases)
- [Commits](https://github.com/dtolnay/proc-macro2/compare/1.0.82...1.0.83)

---
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>
Co-authored-by: Jess Frazelle <jessfraz@users.noreply.github.com>
2024-05-19 22:08:21 -07:00
75c6ae6e66 sketch dies on exit XY sketch (#2397)
* sketch dies on exit XY sketch

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

* trigger ci

* fix test

* fix test

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-05-20 14:38:19 +10:00
48639d70db Bump anyhow from 1.0.83 to 1.0.86 in /src/wasm-lib (#2407)
Bumps [anyhow](https://github.com/dtolnay/anyhow) from 1.0.83 to 1.0.86.
- [Release notes](https://github.com/dtolnay/anyhow/releases)
- [Commits](https://github.com/dtolnay/anyhow/compare/1.0.83...1.0.86)

---
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-19 21:12:51 -07:00
c565d9670d Bump syn from 2.0.64 to 2.0.65 in /src/wasm-lib (#2410)
Bumps [syn](https://github.com/dtolnay/syn) from 2.0.64 to 2.0.65.
- [Release notes](https://github.com/dtolnay/syn/releases)
- [Commits](https://github.com/dtolnay/syn/compare/2.0.64...2.0.65)

---
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-19 21:12:15 -07:00
7bf5953299 Bump thiserror from 1.0.60 to 1.0.61 in /src/wasm-lib (#2411)
Bumps [thiserror](https://github.com/dtolnay/thiserror) from 1.0.60 to 1.0.61.
- [Release notes](https://github.com/dtolnay/thiserror/releases)
- [Commits](https://github.com/dtolnay/thiserror/compare/1.0.60...1.0.61)

---
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-19 21:05:36 -07:00
a9ab35e55f double-clicking a model file (obj, stl, etc) will auto generate a file with an import statement and open it (#2400)
* open model files and generate the content

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-20 00:45:09 +00:00
15418e98b0 missed a file (#2399)
updates

Signed-off-by: Jess Frazelle <github@jessfraz.com>
2024-05-20 00:16:26 +00:00
20838bf618 actually make import samples run (#2398)
* actually make import samples run

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-05-20 00:02:25 +00:00
acd52ab350 fix function variable panel issue (#2392)
* fix function variable panel issue

* fix test
2024-05-19 20:56:19 +10:00
75b9d2913f fix max std lib (#2391) 2024-05-19 16:42:22 +10:00
d92e6f6453 Bump toml from 0.8.12 to 0.8.13 in /src/wasm-lib (#2367)
Bumps [toml](https://github.com/toml-rs/toml) from 0.8.12 to 0.8.13.
- [Commits](https://github.com/toml-rs/toml/compare/toml-v0.8.12...toml-v0.8.13)

---
updated-dependencies:
- dependency-name: toml
  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-17 17:36:50 -07:00
c1a879837e Bump deps (#2385) 2024-05-17 21:06:16 +00:00
daacca500c Tell ts-rs to no worry about serde tags (#2387)
These build warnings are persistent and they're load-bearing to other
code.

Signed-off-by: Paul Tagliamonte <paul@zoo.dev>
2024-05-17 16:46:42 -04:00
c1e8bb5288 Add "copy to clipboard" button to Variables pane (#2388) 2024-05-17 20:40:20 +00:00
8ca4166b08 Bump serde from 1.0.201 to 1.0.202 in /src/wasm-lib (#2368)
Bumps [serde](https://github.com/serde-rs/serde) from 1.0.201 to 1.0.202.
- [Release notes](https://github.com/serde-rs/serde/releases)
- [Commits](https://github.com/serde-rs/serde/compare/v1.0.201...v1.0.202)

---
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-17 10:53:33 -07:00
4624f1c0ba Update tauri's Cargo.lock (#2381) 2024-05-17 17:19:50 +00:00
7ac6a3a4f2 Bump itertools from 0.12.1 to 0.13.0 in /src/wasm-lib (#2377)
Bumps [itertools](https://github.com/rust-itertools/itertools) from 0.12.1 to 0.13.0.
- [Changelog](https://github.com/rust-itertools/itertools/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rust-itertools/itertools/compare/v0.12.1...v0.13.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-17 09:09:23 -07:00
3cbf2b194a Bump syn from 2.0.63 to 2.0.64 in /src/wasm-lib (#2378)
Bumps [syn](https://github.com/dtolnay/syn) from 2.0.63 to 2.0.64.
- [Release notes](https://github.com/dtolnay/syn/releases)
- [Commits](https://github.com/dtolnay/syn/compare/2.0.63...2.0.64)

---
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-17 09:09:00 -07:00
44f06aa199 Cut release v0.21.4 (#2380) 2024-05-17 15:40:56 +00:00
1b878865b8 Set default extrude distance expression to something more sensible, like 5 (#2351)
* Set default extrude distance expression to something more sensible, like 5

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

* Rerun CI

* run CI

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

* CI

* Use old 5 + 7 for highlight test it's touchy

* Same with sketch on face it seems to work better with 5 + 7 and that's fine

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-05-17 10:29:04 -04:00
3b840e9a80 Disable all pane resize handles except right (#2372)
* Disable all pane resize handles except right

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

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-05-16 22:30:47 -04:00
4a0811eec8 Fix CSS bug causing ProjectCard links to flicker (#2375)
on Microsoft desktop apps running old versions of Edge
2024-05-16 21:05:20 -04:00
125 changed files with 5950 additions and 1019 deletions

View File

@ -59,7 +59,9 @@ 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
```
@ -100,6 +102,7 @@ yarn test
Which will run our suite of [Vitest unit](https://vitest.dev/) and [React Testing Library E2E](https://testing-library.com/docs/react-testing-library/intro/) tests, in interactive mode by default.
For running the rust (not tauri rust though) only, you can
```bash
cd src/wasm-lib
cargo test
@ -162,6 +165,7 @@ console.log(
- `)
)
```
grab the md list and delete any that are older than the last bump
2. Merge the PR
@ -191,23 +195,26 @@ $ cargo +nightly fuzz run parser
For more information on fuzzing you can check out
[this guide](https://rust-fuzz.github.io/book/cargo-fuzz.html).
### Playwright
First time running plawright locally, you'll need to add the secrets file
```bash
touch ./e2e/playwright/playwright-secrets.env
printf 'token="your-token"\nsnapshottoken="your-snapshot-token"' > ./e2e/playwright/playwright-secrets.env
```
then replace "your-token" with a dev token from dev.zoo.dev/account/api-tokens
then:
run playwright
```
yarn playwright test
```
run a specific test suite
```
yarn playwright test src/e2e-tests/example.spec.ts
```
@ -216,14 +223,17 @@ run a specific test change the test from `test('...` to `test.only('...`
(note if you commit this, the tests will instantly fail without running any of the tests)
run headed
```
yarn playwright test --headed
```
run with step through debugger
```
PWDEBUG=1 yarn playwright test
```
However, if you want a debugger I recommend using VSCode and the `playwright` extension, as the above command is a cruder debugger that steps into every function call which is annoying.
With the extension you can set a breakpoint after `waitForDefaultPlanesVisibilityChange` in order to skip app loading, then the vscode debugger's "step over" is much better for being able to stay at the right level of abstraction as you debug the code.
@ -268,12 +278,11 @@ Where `./store` should look like this
</details>
However because much of our tests involve clicking in the stream at specific locations, it's code-gen looks `await page.locator('video').click();` when really we need to use a pixel coord, so I think it's of limited use.
#### Some notes on CI
The tests are broken into snapshot tests and non-snapshot tests, and they run in that order, they automatically commit new snap shots, so if you see an image commit check it was an intended change. If we have non-determinism in the snapshots such that they are always committing new images, hopefully this annoyance makes us fix them asap, if you notice this happening let Kurt know. But for the odd occasion `git reset --hard HEAD~ && git push -f` is your friend.
The tests are broken into snapshot tests and non-snapshot tests, and they run in that order, they automatically commit new snap shots, so if you see an image commit check it was an intended change. If we have non-determinism in the snapshots such that they are always committing new images, hopefully this annoyance makes us fix them asap, if you notice this happening let Kurt know. But for the odd occasion `git reset --hard HEAD~ && git push -f` is your friend.
How to interpret failing playwright tests?
If your tests fail, click through to the action and see that the tests failed on a line that includes `await page.getByTestId('loading').waitFor({ state: 'detached' })`, this means the test fail because the stream never started. It's you choice if you want to re-run the test, or ignore the failure.
@ -299,3 +308,7 @@ PS: for the debug panel, the following JSON is useful for snapping the camera
```
</details>
## KCL
For how to contribute to KCL, [see our KCL README](https://github.com/KittyCAD/modeling-app/tree/main/src/wasm-lib/kcl).

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

@ -56,6 +56,9 @@ layout: manual
* [`patternLinear3d`](kcl/patternLinear3d)
* [`pi`](kcl/pi)
* [`pow`](kcl/pow)
* [`profileStart`](kcl/profileStart)
* [`profileStartX`](kcl/profileStartX)
* [`profileStartY`](kcl/profileStartY)
* [`revolve`](kcl/revolve)
* [`segAng`](kcl/segAng)
* [`segEndX`](kcl/segEndX)

File diff suppressed because one or more lines are too long

206
docs/kcl/profileStart.md Normal file

File diff suppressed because one or more lines are too long

201
docs/kcl/profileStartX.md Normal file

File diff suppressed because one or more lines are too long

200
docs/kcl/profileStartY.md Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

View File

@ -13,6 +13,7 @@ import {
} from './storageStates'
import * as TOML from '@iarna/toml'
import { Coords2d } from 'lang/std/sketch'
import { KCL_DEFAULT_LENGTH } from 'lib/constants'
/*
debug helper: unfortunately we do rely on exact coord mouse clicks in a few places
@ -347,6 +348,15 @@ test('if you use the format keyboard binding it formats your code', async ({
|> close(%)`)
})
test('ensure the Zoo logo is not a link in browser app', async ({ page }) => {
await page.setViewportSize({ width: 1000, height: 500 })
await page.goto('/')
const zooLogo = page.locator('[data-testid="app-logo"]')
// Make sure it's not a link
await expect(zooLogo).not.toHaveAttribute('href')
})
test('if you write invalid kcl you get inlined errors', async ({ page }) => {
const u = getUtils(page)
await page.setViewportSize({ width: 1000, height: 500 })
@ -414,6 +424,80 @@ test('if you write invalid kcl you get inlined errors', async ({ page }) => {
await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible()
})
test('error with 2 source ranges gets 2 diagnostics', async ({ page }) => {
const u = getUtils(page)
await page.addInitScript(async () => {
localStorage.setItem(
'persistCode',
`const length = .750
const width = 0.500
const height = 0.500
const dia = 4
fn squareHole = (l, w) => {
const squareHoleSketch = startSketchOn('XY')
|> startProfileAt([-width / 2, -length / 2], %)
|> lineTo([width / 2, -length / 2], %)
|> lineTo([width / 2, length / 2], %)
|> lineTo([-width / 2, length / 2], %)
|> close(%)
return squareHoleSketch
}
`
)
})
await page.setViewportSize({ width: 1000, height: 500 })
await page.goto('/')
const lspStartPromise = page.waitForEvent('console', async (message) => {
// it would be better to wait for a message that the kcl lsp has started by looking for the message message.text().includes('[lsp] [window/logMessage]')
// but that doesn't seem to make it to the console for macos/safari :(
if (message.text().includes('start kcl lsp')) {
await new Promise((resolve) => setTimeout(resolve, 200))
return true
}
return false
})
await page.goto('/')
await u.waitForAuthSkipAppStart()
await lspStartPromise
await u.openDebugPanel()
await u.expectCmdLog('[data-message-type="execution-done"]')
await u.closeDebugPanel()
// check no error to begin with
await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible()
// Click on the bottom of the code editor to add a new line
await page.click('.cm-content')
await page.keyboard.press('ArrowDown')
await page.keyboard.press('ArrowDown')
await page.keyboard.press('ArrowDown')
await page.keyboard.press('ArrowDown')
await page.keyboard.press('ArrowDown')
await page.keyboard.press('ArrowDown')
await page.keyboard.press('ArrowDown')
await page.keyboard.press('ArrowDown')
await page.keyboard.press('ArrowDown')
await page.keyboard.press('ArrowDown')
await page.keyboard.press('ArrowDown')
await page.keyboard.press('ArrowDown')
await page.keyboard.press('ArrowDown')
await page.keyboard.press('Enter')
await page.keyboard.type(`const extrusion = startSketchOn('XY')
|> circle([0, 0], dia/2, %)
|> hole(squareHole(length, width, height), %)
|> extrude(height, %)`)
// error in gutter
await expect(page.locator('.cm-lint-marker-error').first()).toBeVisible()
await page.hover('.cm-lint-marker-error:first-child')
await expect(page.getByText('Expected 2 arguments, got 3')).toBeVisible()
// Make sure there are two diagnostics
await expect(page.locator('.cm-lint-marker-error')).toHaveCount(2)
})
test('if your kcl gets an error from the engine it is inlined', async ({
page,
}) => {
@ -636,8 +720,8 @@ test('Auto complete works', async ({ page }) => {
await page.click('.cm-content')
await page.keyboard.type('const part001 = start')
// expect there to be three auto complete options
await expect(page.locator('.cm-completionLabel')).toHaveCount(3)
// expect there to be six auto complete options
await expect(page.locator('.cm-completionLabel')).toHaveCount(6)
await page.getByText('startSketchOn').click()
await page.keyboard.type("'XZ'")
await page.keyboard.press('Tab')
@ -765,6 +849,130 @@ test('Project settings can be set and override user settings', async ({
await expect(page.locator('select[name="app-theme"]')).toHaveValue('light')
})
test('Project settings can be opened with keybinding from the editor', async ({
page,
}) => {
await page.setViewportSize({ width: 1200, height: 500 })
await page.goto('/', { waitUntil: 'domcontentloaded' })
await page
.getByRole('button', { name: 'Start Sketch' })
.waitFor({ state: 'visible' })
// Put the cursor in the editor
await page.click('.cm-content')
// Open the settings modal with the browser keyboard shortcut
await page.keyboard.press('Meta+Shift+,')
await expect(
page.getByRole('heading', { name: 'Settings', exact: true })
).toBeVisible()
await page
.locator('select[name="app-theme"]')
.selectOption({ value: 'light' })
// Verify the toast appeared
await expect(
page.getByText(`Set theme to "light" for this project`)
).toBeVisible()
// Check that the theme changed
await expect(page.locator('body')).not.toHaveClass(`body-bg dark`)
// Check that the user setting was not changed
await page.getByRole('radio', { name: 'User' }).click()
await expect(page.locator('select[name="app-theme"]')).toHaveValue('dark')
// Roll back to default "system" theme
await page
.getByText(
'themeRoll back themeRoll back to match defaultThe overall appearance of the appl'
)
.hover()
await page
.getByRole('button', {
name: 'Roll back theme ; Has tooltip: Roll back to match default',
})
.click()
await expect(page.locator('select[name="app-theme"]')).toHaveValue('system')
// Check that the project setting did not change
await page.getByRole('radio', { name: 'Project' }).click()
await expect(page.locator('select[name="app-theme"]')).toHaveValue('light')
})
test('Project and user settings can be reset', async ({ page }) => {
await page.setViewportSize({ width: 1200, height: 500 })
await page.goto('/', { waitUntil: 'domcontentloaded' })
await page
.getByRole('button', { name: 'Start Sketch' })
.waitFor({ state: 'visible' })
// Put the cursor in the editor
await page.click('.cm-content')
// Open the settings modal with the browser keyboard shortcut
await page.keyboard.press('Meta+Shift+,')
await expect(
page.getByRole('heading', { name: 'Settings', exact: true })
).toBeVisible()
// Click the reset settings button.
await page.getByRole('button', { name: 'Restore default settings' }).click()
await page
.locator('select[name="app-theme"]')
.selectOption({ value: 'light' })
// Verify the toast appeared
await expect(
page.getByText(`Set theme to "light" for this project`)
).toBeVisible()
// Check that the theme changed
await expect(page.locator('body')).not.toHaveClass(`body-bg dark`)
await expect(page.locator('select[name="app-theme"]')).toHaveValue('light')
// Check that the user setting was not changed
await page.getByRole('radio', { name: 'User' }).click()
await expect(page.locator('select[name="app-theme"]')).toHaveValue('system')
// Click the reset settings button.
await page.getByRole('button', { name: 'Restore default settings' }).click()
// Verify it is now set to the default value
await expect(page.locator('select[name="app-theme"]')).toHaveValue('system')
// Set the user theme to light.
await page
.locator('select[name="app-theme"]')
.selectOption({ value: 'light' })
// Verify the toast appeared
await expect(
page.getByText(`Set theme to "light" as a user default`)
).toBeVisible()
// Check that the theme changed
await expect(page.locator('body')).not.toHaveClass(`body-bg dark`)
await expect(page.locator('select[name="app-theme"]')).toHaveValue('light')
await page.getByRole('radio', { name: 'Project' }).click()
await expect(page.locator('select[name="app-theme"]')).toHaveValue('light')
// Click the reset settings button.
await page.getByRole('button', { name: 'Restore default settings' }).click()
// Verify it is now set to the default value
await expect(page.locator('select[name="app-theme"]')).toHaveValue('system')
await page.getByRole('radio', { name: 'User' }).click()
await expect(page.locator('select[name="app-theme"]')).toHaveValue('system')
// Click the reset settings button.
await page.getByRole('button', { name: 'Restore default settings' }).click()
// Verify it is now set to the default value
await expect(page.locator('select[name="app-theme"]')).toHaveValue('system')
})
test('Click through each onboarding step', async ({ page }) => {
const u = getUtils(page)
@ -1082,6 +1290,51 @@ test.describe('Command bar tests', () => {
await expect(page.locator('body')).not.toHaveClass(`body-bg dark`)
})
test('Command bar keybinding works from code editor and can change a setting', async ({
page,
}) => {
// Brief boilerplate
await page.setViewportSize({ width: 1200, height: 500 })
await page.goto('/', { waitUntil: 'domcontentloaded' })
let cmdSearchBar = page.getByPlaceholder('Search commands')
// Put the cursor in the code editor
await page.click('.cm-content')
// Now try the same, but with the keyboard shortcut, check focus
await page.keyboard.press('Meta+K')
await expect(cmdSearchBar).toBeVisible()
await expect(cmdSearchBar).toBeFocused()
// Try typing in the command bar
await page.keyboard.type('theme')
const themeOption = page.getByRole('option', {
name: 'Settings · app · theme',
})
await expect(themeOption).toBeVisible()
await themeOption.click()
const themeInput = page.getByPlaceholder('Select an option')
await expect(themeInput).toBeVisible()
await expect(themeInput).toBeFocused()
// Select dark theme
await page.keyboard.press('ArrowDown')
await page.keyboard.press('ArrowDown')
await page.keyboard.press('ArrowDown')
await expect(page.getByRole('option', { name: 'system' })).toHaveAttribute(
'data-headlessui-state',
'active'
)
await page.keyboard.press('Enter')
// Check the toast appeared
await expect(
page.getByText(`Set theme to "system" for this project`)
).toBeVisible()
// Check that the theme changed
await expect(page.locator('body')).not.toHaveClass(`body-bg dark`)
})
test('Can extrude from the command bar', async ({ page }) => {
await page.addInitScript(async () => {
localStorage.setItem(
@ -1145,7 +1398,7 @@ test.describe('Command bar tests', () => {
// Assert we're back on the distance step
await expect(
page.getByRole('button', { name: 'Distance 12', exact: false })
page.getByRole('button', { name: 'Distance 5', exact: false })
).toBeDisabled()
await continueButton.click()
@ -1156,7 +1409,7 @@ test.describe('Command bar tests', () => {
// Unfortunately this indentation seems to matter for the test
await expect(page.locator('.cm-content')).toHaveText(
`const distance = sqrt(20)
const distance001 = 5 + 7
const distance001 = ${KCL_DEFAULT_LENGTH}
const part001 = startSketchOn('-XZ')
|> startProfileAt([-6.95, 10.98], %)
|> line([25.1, 0.41], %)
@ -1353,7 +1606,7 @@ test('ProgramMemory can be serialised', async ({ page }) => {
test('Hovering over 3d features highlights code', async ({ page }) => {
const u = getUtils(page)
await page.addInitScript(async () => {
await page.addInitScript(async (KCL_DEFAULT_LENGTH) => {
localStorage.setItem(
'persistCode',
`const part001 = startSketchOn('-XZ')
@ -1378,7 +1631,7 @@ test('Hovering over 3d features highlights code', async ({ page }) => {
|> extrude(5 + 7, %)
`
)
})
}, KCL_DEFAULT_LENGTH)
await page.setViewportSize({ width: 1000, height: 500 })
await page.goto('/')
await u.waitForAuthSkipAppStart()
@ -1957,6 +2210,6 @@ test('Extrude from command bar selects extrude line after', async ({
await page.keyboard.press('Enter')
await page.waitForTimeout(100)
await expect(page.locator('.cm-activeLine')).toHaveText(
` |> extrude(5 + 7, %)`
` |> extrude(${KCL_DEFAULT_LENGTH}, %)`
)
})

View File

@ -4,7 +4,7 @@ import { getUtils } from './test-utils'
import { Models } from '@kittycad/lib'
import fsp from 'fs/promises'
import { spawn } from 'child_process'
import { APP_NAME } from 'lib/constants'
import { APP_NAME, KCL_DEFAULT_LENGTH } from 'lib/constants'
import JSZip from 'jszip'
import path from 'path'
import { TEST_SETTINGS, TEST_SETTINGS_KEY } from './storageStates'
@ -273,6 +273,8 @@ const part001 = startSketchOn('-XZ')
for (let { modelPath, imagePath, outputType } of exportLocations) {
// May change depending on the file being dealt with
let cliCommand = `export ZOO_TOKEN=${secrets.snapshottoken} && zoo file snapshot --output-format=png --src-format=${outputType} ${modelPath} ${imagePath}`
const fileSize = (await fsp.stat(modelPath)).size
console.log(`Size of the file at ${modelPath}: ${fileSize} bytes`)
const parentPath = path.dirname(modelPath)
@ -718,7 +720,7 @@ test.describe('Client side scene scale should match engine scale', () => {
test('Sketch on face with none z-up', async ({ page, context }) => {
const u = getUtils(page)
await context.addInitScript(async () => {
await context.addInitScript(async (KCL_DEFAULT_LENGTH) => {
localStorage.setItem(
'persistCode',
`const part001 = startSketchOn('-XZ')
@ -726,16 +728,16 @@ test('Sketch on face with none z-up', async ({ page, context }) => {
|> line([9.31, 10.55], %, 'seg01')
|> line([11.91, -10.42], %)
|> close(%)
|> extrude(5 + 7, %)
|> extrude(${KCL_DEFAULT_LENGTH}, %)
const part002 = startSketchOn(part001, 'seg01')
|> startProfileAt([8, 8], %)
|> line([4.68, 3.05], %)
|> line([0, -7.79], %, 'seg02')
|> close(%)
|> extrude(5 + 7, %)
|> extrude(${KCL_DEFAULT_LENGTH}, %)
`
)
})
}, KCL_DEFAULT_LENGTH)
await page.setViewportSize({ width: 1200, height: 500 })
await page.goto('/')

Binary file not shown.

Before

Width:  |  Height:  |  Size: 41 KiB

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 48 KiB

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 45 KiB

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 36 KiB

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 74 KiB

After

Width:  |  Height:  |  Size: 71 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 47 KiB

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 51 KiB

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 46 KiB

After

Width:  |  Height:  |  Size: 41 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: 51 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 45 KiB

After

Width:  |  Height:  |  Size: 47 KiB

View File

@ -1,6 +1,6 @@
{
"name": "untitled-app",
"version": "0.21.3",
"version": "0.21.6",
"private": true,
"dependencies": {
"@codemirror/autocomplete": "^6.16.0",
@ -17,12 +17,12 @@
"@replit/codemirror-interact": "^6.3.1",
"@tauri-apps/api": "2.0.0-beta.8",
"@tauri-apps/plugin-dialog": "^2.0.0-beta.2",
"@tauri-apps/plugin-fs": "^2.0.0-beta.2",
"@tauri-apps/plugin-fs": "^2.0.0-beta.3",
"@tauri-apps/plugin-http": "^2.0.0-beta.2",
"@tauri-apps/plugin-os": "^2.0.0-beta.2",
"@tauri-apps/plugin-process": "^2.0.0-beta.2",
"@tauri-apps/plugin-shell": "^2.0.0-beta.2",
"@tauri-apps/plugin-updater": "^2.0.0-beta.2",
"@tauri-apps/plugin-updater": "^2.0.0-beta.3",
"@testing-library/jest-dom": "^5.14.1",
"@testing-library/react": "^15.0.2",
"@testing-library/user-event": "^14.5.2",
@ -52,15 +52,15 @@
"react-json-view": "^1.21.3",
"react-modal": "^3.16.1",
"react-modal-promise": "^1.0.2",
"react-router-dom": "^6.22.3",
"react-router-dom": "^6.23.1",
"sketch-helpers": "^0.0.4",
"swr": "^2.2.5",
"three": "^0.163.0",
"three": "^0.164.1",
"ts-node": "^10.9.2",
"typescript": "^5.4.5",
"ua-parser-js": "^1.0.37",
"uuid": "^9.0.1",
"vitest": "^1.5.0",
"vitest": "^1.6.0",
"vscode-jsonrpc": "^8.1.0",
"vscode-languageserver-protocol": "^3.17.5",
"wasm-pack": "^0.12.1",

207
src-tauri/Cargo.lock generated
View File

@ -169,9 +169,9 @@ dependencies = [
[[package]]
name = "anyhow"
version = "1.0.83"
version = "1.0.86"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "25bdb32cbbdce2b519a9cd7df3a678443100e265d5e25ca763b7572a5104f5f3"
checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da"
dependencies = [
"backtrace",
]
@ -199,7 +199,7 @@ dependencies = [
"tauri-plugin-shell",
"tauri-plugin-updater",
"tokio",
"toml 0.8.12",
"toml 0.8.13",
"url",
]
@ -212,6 +212,15 @@ dependencies = [
"num-traits",
]
[[package]]
name = "arbitrary"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7d5a26814d8dcb93b0e5a0ff3c6d80a8843bafb21b39e8e18a6f05471870e110"
dependencies = [
"derive_arbitrary",
]
[[package]]
name = "arrayvec"
version = "0.7.4"
@ -334,7 +343,7 @@ checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.63",
"syn 2.0.65",
]
[[package]]
@ -369,7 +378,7 @@ checksum = "c6fa2087f2753a7da8cc1c0dbfcf89579dd57458e36769de5ac750b4671737ca"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.63",
"syn 2.0.65",
]
[[package]]
@ -415,7 +424,7 @@ checksum = "3c87f3f15e7794432337fc718554eaa4dc8f04c9677a950ffe366f20a162ae42"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.63",
"syn 2.0.65",
]
[[package]]
@ -564,7 +573,7 @@ dependencies = [
"proc-macro-crate 3.1.0",
"proc-macro2",
"quote",
"syn 2.0.63",
"syn 2.0.65",
"syn_derive",
]
@ -756,7 +765,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a969e13a7589e9e3e4207e153bae624ade2b5622fb4684a4923b23ec3d57719"
dependencies = [
"serde",
"toml 0.8.12",
"toml 0.8.13",
]
[[package]]
@ -873,7 +882,7 @@ dependencies = [
"heck 0.5.0",
"proc-macro2",
"quote",
"syn 2.0.63",
"syn 2.0.65",
]
[[package]]
@ -1075,7 +1084,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13b588ba4ac1a99f7f2964d24b3d896ddc6bf847ee3855dbd4366f058cfcd331"
dependencies = [
"quote",
"syn 2.0.63",
"syn 2.0.65",
]
[[package]]
@ -1085,7 +1094,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "edb49164822f3ee45b17acd4a208cfc1251410cf0cad9a833234c9890774dd9f"
dependencies = [
"quote",
"syn 2.0.63",
"syn 2.0.65",
]
[[package]]
@ -1109,7 +1118,7 @@ dependencies = [
"proc-macro2",
"quote",
"strsim 0.10.0",
"syn 2.0.63",
"syn 2.0.65",
]
[[package]]
@ -1120,7 +1129,7 @@ checksum = "a668eda54683121533a393014d8692171709ff57a7d61f187b6e782719f8933f"
dependencies = [
"darling_core",
"quote",
"syn 2.0.63",
"syn 2.0.65",
]
[[package]]
@ -1167,7 +1176,7 @@ checksum = "377af281d8f23663862a7c84623bc5dcf7f8c44b13c7496a590bdc157f941a43"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.63",
"syn 2.0.65",
"synstructure",
]
@ -1204,7 +1213,18 @@ dependencies = [
"regex",
"serde",
"serde_tokenstream",
"syn 2.0.63",
"syn 2.0.65",
]
[[package]]
name = "derive_arbitrary"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "67e77553c4162a157adbf834ebae5b415acbecbeafc7a74b0e886657506a7611"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.65",
]
[[package]]
@ -1258,6 +1278,17 @@ version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bd0c93bb4b0c6d9b77f4435b0ae98c24d17f1c45b2ff844c6151a07256ca923b"
[[package]]
name = "displaydoc"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.65",
]
[[package]]
name = "dlib"
version = "0.5.2"
@ -1287,7 +1318,7 @@ checksum = "f2b99bf03862d7f545ebc28ddd33a665b50865f4dfd84031a393823879bd4c54"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.63",
"syn 2.0.65",
]
[[package]]
@ -1341,7 +1372,7 @@ dependencies = [
"cc",
"memchr",
"rustc_version",
"toml 0.8.12",
"toml 0.8.13",
"vswhom",
"winreg 0.52.0",
]
@ -1385,7 +1416,7 @@ checksum = "5c785274071b1b420972453b306eeca06acf4633829db4223b58a2a8c5953bc4"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.63",
"syn 2.0.65",
]
[[package]]
@ -1536,7 +1567,7 @@ checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.63",
"syn 2.0.65",
]
[[package]]
@ -1652,7 +1683,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.63",
"syn 2.0.65",
]
[[package]]
@ -1928,7 +1959,7 @@ dependencies = [
"proc-macro-error",
"proc-macro2",
"quote",
"syn 2.0.63",
"syn 2.0.65",
]
[[package]]
@ -1956,7 +1987,7 @@ dependencies = [
"inflections",
"proc-macro2",
"quote",
"syn 2.0.63",
"syn 2.0.65",
]
[[package]]
@ -2031,7 +2062,7 @@ dependencies = [
"proc-macro-error",
"proc-macro2",
"quote",
"syn 2.0.63",
"syn 2.0.65",
]
[[package]]
@ -2569,7 +2600,7 @@ dependencies = [
"thiserror",
"tokio",
"tokio-tungstenite",
"toml 0.8.12",
"toml 0.8.13",
"tower-lsp",
"ts-rs",
"url",
@ -2579,7 +2610,7 @@ dependencies = [
"wasm-bindgen-futures",
"web-sys",
"winnow 0.5.40",
"zip",
"zip 1.3.0",
]
[[package]]
@ -2640,7 +2671,7 @@ checksum = "0611fc9b9786175da21d895ffa0f65039e19c9111e94a41b7af999e3b95f045f"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.63",
"syn 2.0.65",
]
[[package]]
@ -3362,7 +3393,7 @@ dependencies = [
"regex",
"regex-syntax 0.7.5",
"structmeta 0.2.0",
"syn 2.0.63",
"syn 2.0.65",
]
[[package]]
@ -3376,7 +3407,7 @@ dependencies = [
"regex",
"regex-syntax 0.8.3",
"structmeta 0.3.0",
"syn 2.0.63",
"syn 2.0.65",
]
[[package]]
@ -3518,7 +3549,7 @@ dependencies = [
"phf_shared 0.11.2",
"proc-macro2",
"quote",
"syn 2.0.63",
"syn 2.0.65",
]
[[package]]
@ -3586,7 +3617,7 @@ checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.63",
"syn 2.0.65",
]
[[package]]
@ -4424,7 +4455,7 @@ dependencies = [
"proc-macro2",
"quote",
"serde_derive_internals",
"syn 2.0.63",
"syn 2.0.65",
]
[[package]]
@ -4509,9 +4540,9 @@ dependencies = [
[[package]]
name = "serde"
version = "1.0.201"
version = "1.0.202"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "780f1cebed1629e4753a1a38a3c72d30b97ec044f0aef68cb26650a3c5cf363c"
checksum = "226b61a0d411b2ba5ff6d7f73a476ac4f8bb900373459cd00fab8512828ba395"
dependencies = [
"serde_derive",
]
@ -4527,13 +4558,13 @@ dependencies = [
[[package]]
name = "serde_derive"
version = "1.0.201"
version = "1.0.202"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c5e405930b9796f1c00bee880d03fc7e0bb4b9a11afc776885ffe84320da2865"
checksum = "6048858004bcff69094cd972ed40a32500f153bd3be9f716b2eed2e8217c4838"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.63",
"syn 2.0.65",
]
[[package]]
@ -4544,7 +4575,7 @@ checksum = "330f01ce65a3a5fe59a60c82f3c9a024b573b8a6e875bd233fe5f934e71d54e3"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.63",
"syn 2.0.65",
]
[[package]]
@ -4577,14 +4608,14 @@ checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.63",
"syn 2.0.65",
]
[[package]]
name = "serde_spanned"
version = "0.6.5"
version = "0.6.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eb3622f419d1296904700073ea6cc23ad690adbd66f13ea683df73298736f0c1"
checksum = "79e674e01f999af37c49f70a6ede167a8a60b2503e56c5599532a65baa5969a0"
dependencies = [
"serde",
]
@ -4598,7 +4629,7 @@ dependencies = [
"proc-macro2",
"quote",
"serde",
"syn 2.0.63",
"syn 2.0.65",
]
[[package]]
@ -4640,7 +4671,7 @@ dependencies = [
"darling",
"proc-macro2",
"quote",
"syn 2.0.63",
"syn 2.0.65",
]
[[package]]
@ -4908,7 +4939,7 @@ dependencies = [
"proc-macro2",
"quote",
"structmeta-derive 0.2.0",
"syn 2.0.63",
"syn 2.0.65",
]
[[package]]
@ -4920,7 +4951,7 @@ dependencies = [
"proc-macro2",
"quote",
"structmeta-derive 0.3.0",
"syn 2.0.63",
"syn 2.0.65",
]
[[package]]
@ -4931,7 +4962,7 @@ checksum = "a60bcaff7397072dca0017d1db428e30d5002e00b6847703e2e42005c95fbe00"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.63",
"syn 2.0.65",
]
[[package]]
@ -4942,7 +4973,7 @@ checksum = "152a0b65a590ff6c3da95cabe2353ee04e6167c896b28e3b14478c2636c922fc"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.63",
"syn 2.0.65",
]
[[package]]
@ -4964,7 +4995,7 @@ dependencies = [
"proc-macro2",
"quote",
"rustversion",
"syn 2.0.63",
"syn 2.0.65",
]
[[package]]
@ -4997,9 +5028,9 @@ dependencies = [
[[package]]
name = "syn"
version = "2.0.63"
version = "2.0.65"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bf5be731623ca1a1fb7d8be6f261a3be6d3e2337b8a1f97be944d020c8fcb704"
checksum = "d2863d96a84c6439701d7a38f9de935ec562c8832cc55d1dde0f513b52fad106"
dependencies = [
"proc-macro2",
"quote",
@ -5015,7 +5046,7 @@ dependencies = [
"proc-macro-error",
"proc-macro2",
"quote",
"syn 2.0.63",
"syn 2.0.65",
]
[[package]]
@ -5032,7 +5063,7 @@ checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.63",
"syn 2.0.65",
]
[[package]]
@ -5074,7 +5105,7 @@ dependencies = [
"cfg-expr",
"heck 0.5.0",
"pkg-config",
"toml 0.8.12",
"toml 0.8.13",
"version-compare",
]
@ -5227,7 +5258,7 @@ dependencies = [
"serde_json",
"tauri-utils",
"tauri-winres",
"toml 0.8.12",
"toml 0.8.13",
"walkdir",
]
@ -5249,7 +5280,7 @@ dependencies = [
"serde",
"serde_json",
"sha2",
"syn 2.0.63",
"syn 2.0.65",
"tauri-utils",
"thiserror",
"time",
@ -5267,7 +5298,7 @@ dependencies = [
"heck 0.4.1",
"proc-macro2",
"quote",
"syn 2.0.63",
"syn 2.0.65",
"tauri-codegen",
"tauri-utils",
]
@ -5285,7 +5316,7 @@ dependencies = [
"serde",
"serde_json",
"tauri-utils",
"toml 0.8.12",
"toml 0.8.13",
"walkdir",
]
@ -5471,7 +5502,7 @@ dependencies = [
"tokio",
"url",
"windows-sys 0.52.0",
"zip",
"zip 0.6.6",
]
[[package]]
@ -5546,7 +5577,7 @@ dependencies = [
"serde_with",
"swift-rs",
"thiserror",
"toml 0.8.12",
"toml 0.8.13",
"url",
"urlpattern",
"walkdir",
@ -5602,22 +5633,22 @@ checksum = "8eaa81235c7058867fa8c0e7314f33dcce9c215f535d1913822a2b3f5e289f3c"
[[package]]
name = "thiserror"
version = "1.0.60"
version = "1.0.61"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "579e9083ca58dd9dcf91a9923bb9054071b9ebbd800b342194c9feb0ee89fc18"
checksum = "c546c80d6be4bc6a00c0f01730c08df82eaa7a7a61f11d656526506112cc1709"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.60"
version = "1.0.61"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2470041c06ec3ac1ab38d0356a6119054dedaea53e12fbefc0de730a1c08524"
checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.63",
"syn 2.0.65",
]
[[package]]
@ -5706,7 +5737,7 @@ checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.63",
"syn 2.0.65",
]
[[package]]
@ -5774,21 +5805,21 @@ dependencies = [
[[package]]
name = "toml"
version = "0.8.12"
version = "0.8.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e9dd1545e8208b4a5af1aa9bbd0b4cf7e9ea08fabc5d0a5c67fcaafa17433aa3"
checksum = "a4e43f8cc456c9704c851ae29c67e17ef65d2c30017c17a9765b89c382dc8bba"
dependencies = [
"serde",
"serde_spanned",
"toml_datetime",
"toml_edit 0.22.12",
"toml_edit 0.22.13",
]
[[package]]
name = "toml_datetime"
version = "0.6.5"
version = "0.6.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1"
checksum = "4badfd56924ae69bcc9039335b2e017639ce3f9b001c393c1b2d1ef846ce2cbf"
dependencies = [
"serde",
]
@ -5830,9 +5861,9 @@ dependencies = [
[[package]]
name = "toml_edit"
version = "0.22.12"
version = "0.22.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d3328d4f68a705b2a4498da1d580585d39a6510f98318a2cec3018a7ec61ddef"
checksum = "c127785850e8c20836d49732ae6abfa47616e60bf9d9f57c43c250361a9db96c"
dependencies = [
"indexmap 2.2.6",
"serde",
@ -5895,7 +5926,7 @@ checksum = "84fd902d4e0b9a4b27f2f440108dc034e1758628a9b702f8ec61ad66355422fa"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.63",
"syn 2.0.65",
]
[[package]]
@ -5924,7 +5955,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.63",
"syn 2.0.65",
]
[[package]]
@ -6048,7 +6079,7 @@ dependencies = [
"Inflector",
"proc-macro2",
"quote",
"syn 2.0.63",
"syn 2.0.65",
"termcolor",
]
@ -6261,7 +6292,7 @@ dependencies = [
"proc-macro-error",
"proc-macro2",
"quote",
"syn 2.0.63",
"syn 2.0.65",
]
[[package]]
@ -6360,7 +6391,7 @@ dependencies = [
"once_cell",
"proc-macro2",
"quote",
"syn 2.0.63",
"syn 2.0.65",
"wasm-bindgen-shared",
]
@ -6394,7 +6425,7 @@ checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.63",
"syn 2.0.65",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]
@ -6535,7 +6566,7 @@ checksum = "ac1345798ecd8122468840bcdf1b95e5dc6d2206c5e4b0eafa078d061f59c9bc"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.63",
"syn 2.0.65",
]
[[package]]
@ -6641,7 +6672,7 @@ checksum = "f6fc35f58ecd95a9b71c4f2329b911016e6bec66b3f2e6a4aad86bd2e99e2f9b"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.63",
"syn 2.0.65",
]
[[package]]
@ -6652,7 +6683,7 @@ checksum = "08990546bf4edef8f431fa6326e032865f27138718c587dc21bc0265bbcb57cc"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.63",
"syn 2.0.65",
]
[[package]]
@ -7094,7 +7125,7 @@ checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.63",
"syn 2.0.65",
]
[[package]]
@ -7123,6 +7154,20 @@ dependencies = [
"zstd",
]
[[package]]
name = "zip"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1f4a27345eb6f7aa7bd015ba7eb4175fa4e1b462a29874b779e0bbcf96c6ac7"
dependencies = [
"arbitrary",
"crc32fast",
"crossbeam-utils",
"displaydoc",
"indexmap 2.2.6",
"thiserror",
]
[[package]]
name = "zstd"
version = "0.11.2+zstd.1.5.2"

View File

@ -121,11 +121,8 @@ async fn write_app_settings_file(app: tauri::AppHandle, configuration: Configura
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);
async fn get_project_settings_file_path(project_path: &str) -> Result<PathBuf, InvokeError> {
let project_dir = std::path::Path::new(project_path);
if !project_dir.exists() {
tokio::fs::create_dir_all(&project_dir)
@ -137,11 +134,8 @@ async fn get_project_settings_file_path(
}
#[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?;
async fn read_project_settings_file(project_path: &str) -> Result<ProjectConfiguration, InvokeError> {
let settings_path = get_project_settings_file_path(project_path).await?;
// Check if this file exists.
if !settings_path.exists() {
@ -159,11 +153,10 @@ async fn read_project_settings_file(
#[tauri::command]
async fn write_project_settings_file(
app_settings: Configuration,
project_name: &str,
project_path: &str,
configuration: ProjectConfiguration,
) -> Result<(), InvokeError> {
let settings_path = get_project_settings_file_path(app_settings, project_name).await?;
let settings_path = get_project_settings_file_path(project_path).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

View File

@ -74,5 +74,5 @@
}
},
"productName": "Zoo Modeling App",
"version": "0.21.3"
"version": "0.21.6"
}

View File

@ -23,6 +23,7 @@ import { useRefreshSettings } from 'hooks/useRefreshSettings'
import { ModelingSidebar } from 'components/ModelingSidebar/ModelingSidebar'
import { LowerRightControls } from 'components/LowerRightControls'
import ModalContainer from 'react-modal-promise'
import useHotkeyWrapper from 'lib/hotkeyWrapper'
export function App() {
useRefreshSettings(paths.FILE + 'SETTINGS')
@ -63,8 +64,8 @@ export function App() {
useHotkeys('backspace', (e) => {
e.preventDefault()
})
useHotkeys(
isTauri() ? 'mod + ,' : 'shift + mod + ,',
useHotkeyWrapper(
[isTauri() ? 'mod + ,' : 'shift + mod + ,'],
() => navigate(filePath + paths.SETTINGS),
{
splitKey: '|',

View File

@ -34,7 +34,7 @@ export const AppHeader = ({
}
>
<ProjectSidebarMenu
renderAsLink={!enableMenu}
enableMenu={enableMenu}
project={project?.project}
file={project?.file}
/>

View File

@ -1,11 +1,11 @@
import { Dialog, Popover, Transition } from '@headlessui/react'
import { Fragment, useEffect } from 'react'
import { useHotkeys } from 'react-hotkeys-hook'
import { useCommandsContext } from 'hooks/useCommandsContext'
import CommandBarArgument from './CommandBarArgument'
import CommandComboBox from '../CommandComboBox'
import CommandBarReview from './CommandBarReview'
import { useLocation } from 'react-router-dom'
import useHotkeyWrapper from 'lib/hotkeyWrapper'
export const CommandBar = () => {
const { pathname } = useLocation()
@ -22,7 +22,7 @@ export const CommandBar = () => {
}, [pathname])
// Hook up keyboard shortcuts
useHotkeys(['mod+k', 'mod+/'], () => {
useHotkeyWrapper(['mod+k', 'mod+/'], () => {
if (commandBarState.context.commands.length === 0) return
if (commandBarState.matches('Closed')) {
commandBarSend({ type: 'Open' })

View File

@ -56,7 +56,7 @@ function CommandBarSelectionInput({
// In future the engine's edit mode will go away and this will be handled differently.
useEffect(() => {
kclManager.exitEditMode()
return () => kclManager.enterEditMode()
return () => kclManager.defaultSelectionFilter()
}, [])
// Fast-forward through this arg if it's marked as skippable

View File

@ -8,7 +8,6 @@ import { Dialog, Disclosure } from '@headlessui/react'
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { faChevronRight, faTrashAlt } from '@fortawesome/free-solid-svg-icons'
import { useFileContext } from 'hooks/useFileContext'
import { useHotkeys } from 'react-hotkeys-hook'
import styles from './FileTree.module.css'
import { sortProject } from 'lib/tauriFS'
import { FILE_EXT } from 'lib/constants'
@ -16,6 +15,7 @@ import { CustomIcon } from './CustomIcon'
import { codeManager, kclManager } from 'lib/singletons'
import { useDocumentHasFocus } from 'hooks/useDocumentHasFocus'
import { useLspContext } from './LspProvider'
import useHotkeyWrapper from 'lib/hotkeyWrapper'
function getIndentationCSS(level: number) {
return `calc(1rem * ${level + 1})`
@ -333,8 +333,8 @@ export const FileTreeMenu = () => {
send({ type: 'Create file', data: { name: '', makeDir: true } })
}
useHotkeys('meta + n', createFile)
useHotkeys('meta + shift + n', createFolder)
useHotkeyWrapper(['meta + n'], createFile)
useHotkeyWrapper(['meta + shift + n'], createFolder)
return (
<>

View File

@ -22,8 +22,7 @@ import {
LspWorker,
} from 'editor/plugins/lsp/types'
import { wasmUrl } from 'lang/wasm'
const DEFAULT_FILE_NAME: string = 'main.kcl'
import { PROJECT_ENTRYPOINT } from 'lib/constants'
function getWorkspaceFolders(): LSP.WorkspaceFolder[] {
return []
@ -137,7 +136,7 @@ export const LspProvider = ({ children }: { children: React.ReactNode }) => {
if (isKclLspServerReady && !TEST && kclLspClient) {
// Set up the lsp plugin.
const lsp = kclLanguage({
documentUri: `file:///${DEFAULT_FILE_NAME}`,
documentUri: `file:///${PROJECT_ENTRYPOINT}`,
workspaceFolders: getWorkspaceFolders(),
client: kclLspClient,
})
@ -211,7 +210,7 @@ export const LspProvider = ({ children }: { children: React.ReactNode }) => {
if (isCopilotLspServerReady && !TEST && copilotLspClient) {
// Set up the lsp plugin.
const lsp = copilotPlugin({
documentUri: `file:///${DEFAULT_FILE_NAME}`,
documentUri: `file:///${PROJECT_ENTRYPOINT}`,
workspaceFolders: getWorkspaceFolders(),
client: copilotLspClient,
allowHTMLContent: true,
@ -236,7 +235,7 @@ export const LspProvider = ({ children }: { children: React.ReactNode }) => {
redirect: boolean
) => {
const currentFilePath = projectBasename(
file?.path || DEFAULT_FILE_NAME,
file?.path || PROJECT_ENTRYPOINT,
projectPath || ''
)
lspClients.forEach((lspClient) => {
@ -267,7 +266,7 @@ export const LspProvider = ({ children }: { children: React.ReactNode }) => {
if (file) {
// Send that the file was opened.
const filename = projectBasename(
file?.path || DEFAULT_FILE_NAME,
file?.path || PROJECT_ENTRYPOINT,
project?.path || ''
)
lspClients.forEach((lspClient) => {
@ -285,7 +284,7 @@ export const LspProvider = ({ children }: { children: React.ReactNode }) => {
const onFileOpen = (filePath: string | null, projectPath: string | null) => {
const currentFilePath = projectBasename(
filePath || DEFAULT_FILE_NAME,
filePath || PROJECT_ENTRYPOINT,
projectPath || ''
)
lspClients.forEach((lspClient) => {
@ -302,7 +301,7 @@ export const LspProvider = ({ children }: { children: React.ReactNode }) => {
const onFileClose = (filePath: string | null, projectPath: string | null) => {
const currentFilePath = projectBasename(
filePath || DEFAULT_FILE_NAME,
filePath || PROJECT_ENTRYPOINT,
projectPath || ''
)
lspClients.forEach((lspClient) => {

View File

@ -55,9 +55,9 @@ import { Models } from '@kittycad/lib/dist/types/src'
import toast from 'react-hot-toast'
import { EditorSelection } from '@uiw/react-codemirror'
import { CoreDumpManager } from 'lib/coredump'
import { useHotkeys } from 'react-hotkeys-hook'
import { useSearchParams } from 'react-router-dom'
import { letEngineAnimateAndSyncCamAfter } from 'clientSideScene/CameraControls'
import useHotkeyWrapper from 'lib/hotkeyWrapper'
type MachineContext<T extends AnyStateMachine> = {
state: StateFrom<T>
@ -103,7 +103,7 @@ export const ModelingMachineProvider = ({
htmlRef,
token
)
useHotkeys('meta + shift + .', () => coreDump(coreDumpManager, true))
useHotkeyWrapper(['meta + shift + .'], () => coreDump(coreDumpManager, true))
// Settings machine setup
// const retrievedSettings = useRef(

View File

@ -36,10 +36,6 @@ import {
import interact from '@replit/codemirror-interact'
import { kclManager, editorManager, codeManager } from 'lib/singletons'
import { useHotkeys } from 'react-hotkeys-hook'
import { isTauri } from 'lib/isTauri'
import { useNavigate } from 'react-router-dom'
import { paths } from 'lib/paths'
import makeUrlPathRelative from 'lib/makeUrlPathRelative'
import { useLspContext } from 'components/LspProvider'
import { Prec, EditorState, Extension } from '@codemirror/state'
import {
@ -67,7 +63,6 @@ export const KclEditorPane = () => {
? getSystemTheme()
: context.app.theme.current
const { copilotLSP, kclLSP } = useLspContext()
const navigate = useNavigate()
useEffect(() => {
if (typeof window === 'undefined') return
@ -76,6 +71,8 @@ export const KclEditorPane = () => {
return () => window.removeEventListener('online', onlineCallback)
}, [])
// Since these already exist in the editor, we don't need to define them
// with the wrapper.
useHotkeys('mod+z', (e) => {
e.preventDefault()
editorManager.undo()
@ -87,6 +84,7 @@ export const KclEditorPane = () => {
const textWrapping = context.textEditor.textWrapping
const cursorBlinking = context.textEditor.blinkingCursor
const codeMirrorHotkeys = codeManager.getCodemirrorHotkeys()
const editorExtensions = useMemo(() => {
const extensions = [
@ -106,20 +104,7 @@ export const KclEditorPane = () => {
...completionKeymap,
...lintKeymap,
indentWithTab,
{
key: 'Meta-k',
run: () => {
editorManager.commandBarSend({ type: 'Open' })
return false
},
},
{
key: isTauri() ? 'Meta-,' : 'Meta-Shift-,',
run: () => {
navigate(makeUrlPathRelative(paths.SETTINGS))
return false
},
},
...codeMirrorHotkeys,
{
key: editorShortcutMeta.convertToVariable.codeMirror,
run: () => {
@ -188,7 +173,13 @@ export const KclEditorPane = () => {
}
return extensions
}, [kclLSP, copilotLSP, textWrapping.current, cursorBlinking.current])
}, [
kclLSP,
copilotLSP,
textWrapping.current,
cursorBlinking.current,
codeMirrorHotkeys,
])
const initialCode = useRef(codeManager.code)

View File

@ -38,7 +38,7 @@ describe('processMemory', () => {
expect(output.otherVar).toEqual(3)
expect(output).toEqual({
myVar: 5,
myFn: undefined,
myFn: '__function(a)__',
otherVar: 3,
theExtrude: [
{

View File

@ -3,6 +3,43 @@ import { useMemo } from 'react'
import { ProgramMemory, Path, ExtrudeSurface } from 'lang/wasm'
import { useKclContext } from 'lang/KclProvider'
import { useResolvedTheme } from 'hooks/useResolvedTheme'
import { ActionButton } from 'components/ActionButton'
import toast from 'react-hot-toast'
import Tooltip from 'components/Tooltip'
export const MemoryPaneMenu = () => {
const { programMemory } = useKclContext()
function copyProgramMemoryToClipboard() {
if (globalThis && 'navigator' in globalThis) {
try {
navigator.clipboard.writeText(JSON.stringify(programMemory))
toast.success('Program memory copied to clipboard')
} catch (e) {
toast.error('Failed to copy program memory to clipboard')
}
}
}
return (
<>
<ActionButton
Element="button"
iconStart={{
icon: 'clipboardPlus',
iconClassName: '!text-current',
bgClassName: 'bg-transparent',
}}
className="!p-0 !bg-transparent hover:text-primary border-transparent hover:border-primary !outline-none"
onClick={copyProgramMemoryToClipboard}
>
<Tooltip position="bottom-right" delay={750}>
Copy to clipboard
</Tooltip>
</ActionButton>
</>
)
}
export const MemoryPane = () => {
const theme = useResolvedTheme()
@ -24,6 +61,7 @@ export const MemoryPane = () => {
displayObjectSize={true}
indentWidth={2}
quotesOnKeys={false}
sortKeys={true}
name={false}
theme={theme === 'light' ? 'rjv-default' : 'monokai'}
/>
@ -46,6 +84,10 @@ export const processMemory = (programMemory: ProgramMemory) => {
processedMemory[key] = val.value.map(({ ...rest }: ExtrudeSurface) => {
return rest
})
} else if ((val.type as any) === 'Function') {
processedMemory[key] = `__function(${(val as any)?.expression?.params
?.map?.(({ identifier }: any) => identifier?.name || '')
.join(', ')})__`
} else {
processedMemory[key] = val.value
}

View File

@ -10,7 +10,7 @@ import { KclEditorMenu } from 'components/ModelingSidebar/ModelingPanes/KclEdito
import { CustomIconName } from 'components/CustomIcon'
import { KclEditorPane } from 'components/ModelingSidebar/ModelingPanes/KclEditorPane'
import { ReactNode } from 'react'
import { MemoryPane } from './MemoryPane'
import { MemoryPane, MemoryPaneMenu } from './MemoryPane'
import { KclErrorsPane, LogsPane } from './LoggingPanes'
import { DebugPane } from './DebugPane'
import { FileTreeInner, FileTreeMenu } from 'components/FileTree'
@ -61,6 +61,7 @@ export const bottomPanes: SidebarPane[] = [
title: 'Variables',
icon: faSquareRootVariable,
Content: MemoryPane,
Menu: MemoryPaneMenu,
keybinding: 'shift + v',
},
{

View File

@ -47,6 +47,13 @@ export function ModelingSidebar({ paneOpacity }: ModelingSidebarProps) {
right:
(openPanes.length === 0 ? 'hidden ' : 'block ') +
'translate-x-1/2 hover:bg-chalkboard-10 hover:dark:bg-chalkboard-110 bg-transparent transition-colors duration-75 transition-ease-out delay-100 ',
left: 'hidden',
top: 'hidden',
topLeft: 'hidden',
topRight: 'hidden',
bottom: 'hidden',
bottomLeft: 'hidden',
bottomRight: 'hidden',
}}
>
<div className={styles.grid + ' flex-1'}>

View File

@ -70,9 +70,9 @@ function ProjectCard({
className="group relative min-h-[5em] p-1 rounded-sm border border-chalkboard-20 dark:border-chalkboard-80 hover:!border-primary"
>
{isEditing ? (
<form onSubmit={handleSave} className="flex gap-2 items-center">
<form onSubmit={handleSave} className="flex items-center gap-2">
<input
className="dark:bg-chalkboard-80 dark:border-chalkboard-40 min-w-0 p-1 focus:outline-none"
className="min-w-0 p-1 dark:bg-chalkboard-80 dark:border-chalkboard-40 focus:outline-none"
type="text"
id="newProjectName"
name="newProjectName"
@ -81,7 +81,7 @@ function ProjectCard({
defaultValue={project.name}
ref={inputRef}
/>
<div className="flex gap-1 items-center">
<div className="flex items-center gap-1">
<ActionButton
Element="button"
type="submit"
@ -117,78 +117,76 @@ function ProjectCard({
</form>
) : (
<>
<div className="p-1 flex flex-col h-full gap-2">
<Link
className="flex-1 !no-underline !text-chalkboard-110 dark:!text-chalkboard-10 after:content-[''] after:absolute after:inset-0"
to={`${paths.FILE}/${encodeURIComponent(project.path)}`}
data-testid="project-link"
>
{project.name?.replace(FILE_EXT, '')}
</Link>
<span className="text-chalkboard-60 text-xs">
<Link
className="relative z-0 flex flex-col h-full gap-2 p-1 !no-underline !text-chalkboard-110 dark:!text-chalkboard-10"
to={`${paths.FILE}/${encodeURIComponent(project.default_file)}`}
data-testid="project-link"
>
<div className="flex-1">{project.name?.replace(FILE_EXT, '')}</div>
<span className="text-xs text-chalkboard-60">
{numberOfFiles} file{numberOfFiles === 1 ? '' : 's'}{' '}
{numberOfFolders > 0 &&
`/ ${numberOfFolders} folder${
numberOfFolders === 1 ? '' : 's'
}`}
</span>
<span className="text-chalkboard-60 text-xs">
<span className="text-xs text-chalkboard-60">
Edited{' '}
{project.metadata && project.metadata?.modified
? getDisplayedTime(project.metadata.modified)
: 'never'}
</span>
<div className="absolute z-10 bottom-2 right-2 flex gap-1 items-center opacity-0 group-hover:opacity-100 group-focus-within:opacity-100">
<ActionButton
Element="button"
iconStart={{
icon: faPenAlt,
className: 'p-1',
iconClassName: 'dark:!text-chalkboard-20',
bgClassName: '!bg-transparent',
size: 'xs',
}}
onClick={(e) => {
e.stopPropagation()
e.nativeEvent.stopPropagation()
setIsEditing(true)
}}
className="!p-0"
>
<Tooltip position="left" delay={1000}>
Rename project
</Tooltip>
</ActionButton>
<ActionButton
Element="button"
iconStart={{
icon: faTrashAlt,
className: 'p-1',
size: 'xs',
bgClassName: '!bg-transparent',
iconClassName: '!text-destroy-70',
}}
className="!p-0 hover:border-destroy-40 dark:hover:border-destroy-40"
onClick={(e) => {
e.stopPropagation()
e.nativeEvent.stopPropagation()
setIsConfirmingDelete(true)
}}
>
<Tooltip position="left" delay={1000}>
Delete project
</Tooltip>
</ActionButton>
</div>
</Link>
<div className="absolute z-10 flex items-center gap-1 opacity-0 bottom-2 right-2 group-hover:opacity-100 group-focus-within:opacity-100">
<ActionButton
Element="button"
iconStart={{
icon: faPenAlt,
className: 'p-1',
iconClassName: 'dark:!text-chalkboard-20',
bgClassName: '!bg-transparent',
size: 'xs',
}}
onClick={(e) => {
e.stopPropagation()
e.nativeEvent.stopPropagation()
setIsEditing(true)
}}
className="!p-0"
>
<Tooltip position="left" delay={1000}>
Rename project
</Tooltip>
</ActionButton>
<ActionButton
Element="button"
iconStart={{
icon: faTrashAlt,
className: 'p-1',
size: 'xs',
bgClassName: '!bg-transparent',
iconClassName: '!text-destroy-70',
}}
className="!p-0 hover:border-destroy-40 dark:hover:border-destroy-40"
onClick={(e) => {
e.stopPropagation()
e.nativeEvent.stopPropagation()
setIsConfirmingDelete(true)
}}
>
<Tooltip position="left" delay={1000}>
Delete project
</Tooltip>
</ActionButton>
</div>
<Dialog
open={isConfirmingDelete}
onClose={() => setIsConfirmingDelete(false)}
className="relative z-50"
>
<div className="fixed inset-0 bg-chalkboard-110/80 grid place-content-center">
<Dialog.Panel className="rounded p-4 bg-chalkboard-10 dark:bg-chalkboard-100 border border-destroy-80 max-w-2xl">
<Dialog.Title as="h2" className="text-2xl font-bold mb-4">
<div className="fixed inset-0 grid bg-chalkboard-110/80 place-content-center">
<Dialog.Panel className="max-w-2xl p-4 border rounded bg-chalkboard-10 dark:bg-chalkboard-100 border-destroy-80">
<Dialog.Title as="h2" className="mb-4 text-2xl font-bold">
Delete File
</Dialog.Title>
<Dialog.Description>

View File

@ -24,6 +24,7 @@ const projectWellFormed = {
},
kcl_file_count: 1,
directory_count: 0,
default_file: '/some/path/Simple Box/main.kcl',
} satisfies Project
describe('ProjectSidebarMenu tests', () => {
@ -32,7 +33,7 @@ describe('ProjectSidebarMenu tests', () => {
<BrowserRouter>
<CommandBarProvider>
<SettingsAuthProviderJest>
<ProjectSidebarMenu project={projectWellFormed} />
<ProjectSidebarMenu project={projectWellFormed} enableMenu={true} />
</SettingsAuthProviderJest>
</CommandBarProvider>
</BrowserRouter>
@ -53,7 +54,7 @@ describe('ProjectSidebarMenu tests', () => {
<BrowserRouter>
<CommandBarProvider>
<SettingsAuthProviderJest>
<ProjectSidebarMenu />
<ProjectSidebarMenu enableMenu={true} />
</SettingsAuthProviderJest>
</CommandBarProvider>
</BrowserRouter>
@ -64,22 +65,18 @@ describe('ProjectSidebarMenu tests', () => {
expect(screen.getByTestId('projectName')).toHaveTextContent(APP_NAME)
})
test('Renders as a link if set to do so', () => {
test('Disables popover menu by default', () => {
render(
<BrowserRouter>
<CommandBarProvider>
<SettingsAuthProviderJest>
<ProjectSidebarMenu
project={projectWellFormed}
renderAsLink={true}
/>
<ProjectSidebarMenu project={projectWellFormed} />
</SettingsAuthProviderJest>
</CommandBarProvider>
</BrowserRouter>
)
expect(screen.getByTestId('project-sidebar-link')).toBeInTheDocument()
expect(screen.getByTestId('project-sidebar-link-name')).toHaveTextContent(
expect(screen.getByTestId('project-name')).toHaveTextContent(
projectWellFormed.name
)
})

View File

@ -17,53 +17,63 @@ import { engineCommandManager } from 'lib/singletons'
const ProjectSidebarMenu = ({
project,
file,
renderAsLink = false,
enableMenu = false,
}: {
renderAsLink?: boolean
enableMenu?: boolean
project?: IndexLoaderData['project']
file?: IndexLoaderData['file']
}) => {
const { onProjectClose } = useLspContext()
return (
<div className="!no-underline h-full mr-auto max-h-min min-h-12 min-w-max flex items-center gap-2">
<Link
onClick={() => {
onProjectClose(file || null, project?.path || null, false)
// Clear the scene and end the session.
engineCommandManager.endSession()
}}
to={paths.HOME}
className="relative h-full grid place-content-center group p-1.5 before:block before:content-[''] before:absolute before:inset-0 before:bottom-2.5 before:z-[-1] before:bg-primary hover:before:brightness-110 before:rounded-b-sm"
>
<Logo className="w-auto h-4 text-chalkboard-10" />
</Link>
{renderAsLink ? (
<>
<Link
onClick={() => {
onProjectClose(file || null, project?.path || null, false)
// Clear the scene and end the session.
engineCommandManager.endSession()
}}
to={paths.HOME}
className="!no-underline"
data-testid="project-sidebar-link"
>
<span
className="hidden text-sm text-chalkboard-110 dark:text-chalkboard-20 whitespace-nowrap lg:block"
data-testid="project-sidebar-link-name"
>
{project?.name ? project.name : APP_NAME}
</span>
</Link>
</>
) : (
<AppLogoLink project={project} file={file} />
{enableMenu ? (
<ProjectMenuPopover project={project} file={file} />
) : (
<span
className="hidden select-none cursor-default text-sm text-chalkboard-110 dark:text-chalkboard-20 whitespace-nowrap lg:block"
data-testid="project-name"
>
{project?.name ? project.name : APP_NAME}
</span>
)}
</div>
)
}
function AppLogoLink({
project,
file,
}: {
project?: IndexLoaderData['project']
file?: IndexLoaderData['file']
}) {
const { onProjectClose } = useLspContext()
const wrapperClassName =
"relative h-full grid place-content-center group p-1.5 before:block before:content-[''] before:absolute before:inset-0 before:bottom-2.5 before:z-[-1] before:bg-primary before:rounded-b-sm"
const logoClassName = 'w-auto h-4 text-chalkboard-10'
return isTauri() ? (
<Link
data-testid="app-logo"
onClick={() => {
onProjectClose(file || null, project?.path || null, false)
// Clear the scene and end the session.
engineCommandManager.endSession()
}}
to={paths.HOME}
className={wrapperClassName + ' hover:before:brightness-110'}
>
<Logo className={logoClassName} />
<span className="sr-only">{APP_NAME}</span>
</Link>
) : (
<div className={wrapperClassName} data-testid="app-logo">
<Logo className={logoClassName} />
<span className="sr-only">{APP_NAME}</span>
</div>
)
}
function ProjectMenuPopover({
project,
file,

View File

@ -172,7 +172,7 @@ export const SettingsAuthProviderBase = ({
},
'Execute AST': () => kclManager.executeCode(true),
persistSettings: (context) =>
saveSettings(context, loadedProject?.project?.name),
saveSettings(context, loadedProject?.project?.path),
},
}
)

View File

@ -134,6 +134,7 @@ code {
#code-mirror-override > div,
#code-mirror-override .cm-editor {
@apply bg-transparent h-full;
@apply select-auto;
}
#code-mirror-override .cm-scroller {

View File

@ -188,7 +188,7 @@ export class KclManager {
engineCommandManager: this.engineCommandManager,
})
sceneInfra.modelingSend({ type: 'code edit during sketch' })
enterEditMode(programMemory, this.engineCommandManager)
defaultSelectionFilter(programMemory, this.engineCommandManager)
this.isExecuting = false
// Check the cancellation token for this execution before applying side effects
if (this._cancelTokens.get(currentExecutionId)) {
@ -348,9 +348,6 @@ export class KclManager {
void this.engineCommandManager.setPlaneHidden(this.defaultPlanes.yz, true)
void this.engineCommandManager.setPlaneHidden(this.defaultPlanes.xz, true)
}
enterEditMode() {
enterEditMode(this.programMemory, this.engineCommandManager)
}
exitEditMode() {
this.engineCommandManager.sendSceneCommand({
type: 'modeling_cmd_req',
@ -358,9 +355,12 @@ export class KclManager {
cmd: { type: 'edit_mode_exit' },
})
}
defaultSelectionFilter() {
defaultSelectionFilter(this.programMemory, this.engineCommandManager)
}
}
function enterEditMode(
function defaultSelectionFilter(
programMemory: ProgramMemory,
engineCommandManager: EngineCommandManager
) {

View File

@ -6,6 +6,7 @@ import { isTauri } from 'lib/isTauri'
import { writeTextFile } from '@tauri-apps/plugin-fs'
import toast from 'react-hot-toast'
import { editorManager } from 'lib/singletons'
import { KeyBinding } from '@uiw/react-codemirror'
const PERSIST_CODE_TOKEN = 'persistCode'
@ -13,6 +14,7 @@ export default class CodeManager {
private _code: string = bracket
#updateState: (arg: string) => void = () => {}
private _currentFilePath: string | null = null
private _hotkeys: { [key: string]: () => void } = {}
constructor() {
if (isTauri()) {
@ -48,6 +50,20 @@ export default class CodeManager {
this.#updateState = setCode
}
registerHotkey(hotkey: string, callback: () => void) {
this._hotkeys[hotkey] = callback
}
getCodemirrorHotkeys(): KeyBinding[] {
return Object.keys(this._hotkeys).map((key) => ({
key,
run: () => {
this._hotkeys[key]()
return false
},
}))
}
updateCurrentFilePath(path: string) {
this._currentFilePath = path
}

View File

@ -1,20 +1,21 @@
import { ToolTip } from '../useStore'
import { Selection, Selections } from 'lib/selections'
import {
ArrayExpression,
BinaryExpression,
Program,
SyntaxType,
Value,
CallExpression,
ExpressionStatement,
VariableDeclaration,
ReturnStatement,
ArrayExpression,
PathToNode,
PipeExpression,
Program,
ProgramMemory,
ReturnStatement,
SketchGroup,
SourceRange,
PipeExpression,
SyntaxType,
Value,
VariableDeclaration,
VariableDeclarator,
} from './wasm'
import { createIdentifier, splitPathAtLastIndex } from './modifyAst'
import { getSketchSegmentFromSourceRange } from './std/sketchConstraints'
@ -295,6 +296,58 @@ export function getNodePathFromSourceRange(
return path
}
type KCLNode =
| Value
| ExpressionStatement
| VariableDeclaration
| VariableDeclarator
| ReturnStatement
export function traverse(
node: KCLNode,
option: {
enter?: (node: KCLNode) => void
leave?: (node: KCLNode) => void
}
) {
option?.enter?.(node)
const _traverse = (node: KCLNode) => traverse(node, option)
if (node.type === 'VariableDeclaration') {
node.declarations.forEach(_traverse)
} else if (node.type === 'VariableDeclarator') {
_traverse(node.init)
} else if (node.type === 'PipeExpression') {
node.body.forEach(_traverse)
} else if (node.type === 'CallExpression') {
_traverse(node.callee)
node.arguments.forEach(_traverse)
} else if (node.type === 'BinaryExpression') {
_traverse(node.left)
_traverse(node.right)
} else if (node.type === 'Identifier') {
// do nothing
} else if (node.type === 'Literal') {
// do nothing
} else if (node.type === 'ArrayExpression') {
node.elements.forEach(_traverse)
} else if (node.type === 'ObjectExpression') {
node.properties.forEach(({ key, value }) => {
_traverse(key)
_traverse(value)
})
} else if (node.type === 'UnaryExpression') {
_traverse(node.argument)
} else if (node.type === 'MemberExpression') {
// hmm this smell
_traverse(node.object)
_traverse(node.property)
} else if ('body' in node && Array.isArray(node.body)) {
node.body.forEach(_traverse)
}
option?.leave?.(node)
}
export interface PrevVariable<T> {
key: string
value: T

View File

@ -54,8 +54,16 @@ interface PendingCommand extends CommandInfo {
resolve: (val: ResolveCommand) => void
}
/**
* The ArtifactMap is a client-side representation of the artifacts that
* have been sent to the server-side engine. It is used to keep track of
* the state of each command, and to resolve the promise that was returned.
* It is also used to keep track of what entities are in the engine scene,
* so that we can associate IDs returned from the engine with the
* lines of KCL code that generated them.
*/
export interface ArtifactMap {
[key: string]: ResultCommand | PendingCommand | FailedCommand
[commandId: string]: ResultCommand | PendingCommand | FailedCommand
}
interface NewTrackArgs {
@ -63,10 +71,11 @@ interface NewTrackArgs {
mediaStream: MediaStream
}
// This looks funny, I know. This is needed because node and the browser
// disagree as to the type. In a browser it's a number, but in node it's a
// "Timeout".
type Timeout = ReturnType<typeof setTimeout>
/** This looks funny, I know. This is needed because node and the browser
* disagree as to the type. In a browser it's a number, but in node it's a
* "Timeout".
*/
type IsomorphicTimeout = ReturnType<typeof setTimeout>
type ClientMetrics = Models['ClientMetrics_type']
@ -188,9 +197,11 @@ export type EngineConnectionState =
| State<EngineConnectionStateType.Disconnecting, DisconnectingValue>
| State<EngineConnectionStateType.Disconnected, void>
// EngineConnection encapsulates the connection(s) to the Engine
// for the EngineCommandManager; namely, the underlying WebSocket
// and WebRTC connections.
/**
* EngineConnection encapsulates the connection(s) to the Engine
* for the EngineCommandManager; namely, the underlying WebSocket
* and WebRTC connections.
*/
class EngineConnection {
websocket?: WebSocket
pc?: RTCPeerConnection
@ -227,23 +238,40 @@ class EngineConnection {
this.onConnectionStateChange(this._state)
}
private failedConnTimeout: Timeout | null
private failedConnTimeout: IsomorphicTimeout | null
readonly url: string
private readonly token?: string
// For now, this is only used by the NetworkHealthIndicator.
// We can eventually use it for more, but one step at a time.
/**For now, this is only used by the NetworkHealthIndicator.
* We can eventually use it for more, but one step at a time.
*/
private onConnectionStateChange: (state: EngineConnectionState) => void
// These are used for the EngineCommandManager and were created
// before onConnectionStateChange existed.
/**
* Used for the EngineCommandManager, created before
* onConnectionStateChange existed.
*/
private onEngineConnectionOpen: (engineConnection: EngineConnection) => void
/**
* Used for the EngineCommandManager, created before
* onConnectionStateChange existed.
*/
private onConnectionStarted: (engineConnection: EngineConnection) => void
/**
* Used for the EngineCommandManager, created before
* onConnectionStateChange existed.
*/
private onClose: (engineConnection: EngineConnection) => void
/**
* Used for the EngineCommandManager, created before
* onConnectionStateChange existed.
*/
private onNewTrack: (track: NewTrackArgs) => void
// TODO: actual type is ClientMetrics
/**
* @todo actual type is `ClientMetrics`
*/
public webrtcStatsCollector?: () => Promise<WebRTCClientMetrics>
private engineCommandManager: EngineCommandManager
@ -322,11 +350,13 @@ class EngineConnection {
}
}
// connect will attempt to connect to the Engine over a WebSocket, and
// establish the WebRTC connections.
//
// This will attempt the full handshake, and retry if the connection
// did not establish.
/**
* Attempts to connect to the Engine over a WebSocket, and
* establish the WebRTC connections.
*
* This will attempt the full handshake, and retry if the connection
* did not establish.
*/
connect() {
if (this.isConnecting() || this.isReady()) {
return
@ -521,6 +551,7 @@ class EngineConnection {
// Everything is now connected.
this.state = { type: EngineConnectionStateType.ConnectionEstablished }
this.engineCommandManager.inSequence = 1
this.onEngineConnectionOpen(this)
})
@ -908,21 +939,66 @@ export type CommandLog =
data: null
}
/**
* The EngineCommandManager is the main interface to the Engine for Modeling App.
*
* It is responsible for sending commands to the Engine, and managing the state
* of those commands. It also sets up and tears down the connection to the Engine
* through the {@link EngineConnection} class.
*
* It also maintains an {@link artifactMap} that keeps track of the state of each
* command, and the artifacts that have been generated by those commands.
*/
export class EngineCommandManager {
/**
* The artifactMap is a client-side representation of the commands that have been sent
* to the server-side geometry engine, and the state of their resulting artifacts.
*
* It is used to keep track of the state of each command, which can fail, succeed, or be
* pending.
*
* It is also used to keep track of our client's understanding of what is in the engine scene
* so that we can map to and from KCL code. Each artifact maintains a source range to the part
* of the KCL code that generated it.
*/
artifactMap: ArtifactMap = {}
/**
* The {@link ArtifactMap} from the previous engine connection. This is used as a fallback
* when the engine connection is reset without a full client-side refresh.
*
* @deprecated This was used during a short time when we were choosing to not execute the engine in certain cases.
*/
lastArtifactMap: ArtifactMap = {}
/**
* The client-side representation of the scene command artifacts that have been sent to the server;
* that is, the *non-modeling* commands and corresponding artifacts.
*
* For modeling commands, see {@link artifactMap}.
*/
sceneCommandArtifacts: ArtifactMap = {}
/**
* A counter that is incremented with each command sent over the *unreliable* channel to the engine.
* This is compared to the latest received {@link inSequence} number to determine if we should ignore
* any out-of-order late responses in the unreliable channel.
*/
outSequence = 1
/**
* The latest sequence number received from the engine over the *unreliable* channel.
* This is compared to the {@link outSequence} number to determine if we should ignore
* any out-of-order late responses in the unreliable channel.
*/
inSequence = 1
pool?: string
engineConnection?: EngineConnection
defaultPlanes: DefaultPlanes | null = null
commandLogs: CommandLog[] = []
_commandLogCallBack: (command: CommandLog[]) => void = () => {}
// Folks should realize that wait for ready does not get called _everytime_
// the connection resets and restarts, it only gets called the first time.
// Be careful what you put here.
private resolveReady = () => {}
/** Folks should realize that wait for ready does not get called _everytime_
* the connection resets and restarts, it only gets called the first time.
*
* Be careful what you put here.
*/
waitForReady: Promise<void> = new Promise((resolve) => {
this.resolveReady = resolve
})

View File

@ -1,5 +1,6 @@
import { Models } from '@kittycad/lib'
import { CommandSetConfig, KclCommandValue } from 'lib/commandTypes'
import { KCL_DEFAULT_LENGTH } from 'lib/constants'
import { Selections } from 'lib/selections'
import { modelingMachine } from 'machines/modelingMachine'
@ -135,7 +136,7 @@ export const modelingMachineConfig: CommandSetConfig<
// },
distance: {
inputType: 'kcl',
defaultValue: '5 + 7',
defaultValue: KCL_DEFAULT_LENGTH,
required: true,
},
},

View File

@ -42,3 +42,5 @@ export const RELEVANT_FILE_TYPES = [
] as const
/** The default name for a tutorial project */
export const ONBOARDING_PROJECT_NAME = 'Tutorial Project $nn'
/** The default KCL length expression */
export const KCL_DEFAULT_LENGTH = `5`

37
src/lib/hotkeyWrapper.ts Normal file
View File

@ -0,0 +1,37 @@
import { Options, useHotkeys } from 'react-hotkeys-hook'
import { useEffect } from 'react'
import { codeManager } from './singletons'
// Hotkey wrapper wraps hotkeys for the app (outside of the editor)
// With hotkeys inside the editor.
// This way we can have hotkeys defined in one place and not have to worry about
// conflicting hotkeys, or them only being implemented for the app but not
// inside the editor.
// TODO: would be nice if this didn't have to be a react hook. It's not needed
// for the code mirror stuff but but it is needed for the useHotkeys hook.
export default function useHotkeyWrapper(
hotkey: string[],
callback: () => void,
additionalOptions?: Options
) {
useHotkeys(hotkey, callback, additionalOptions)
useEffect(() => {
for (const key of hotkey) {
const keybinding = mapHotkeyToCodeMirrorHotkey(key)
codeManager.registerHotkey(keybinding, callback)
}
})
}
// Convert hotkey to code mirror hotkey
// See: https://codemirror.net/docs/ref/#view.KeyBinding
function mapHotkeyToCodeMirrorHotkey(hotkey: string): string {
return hotkey
.replaceAll('+', '-')
.replaceAll(' ', '')
.replaceAll('mod', 'Meta')
.replaceAll('meta', 'Meta')
.replaceAll('ctrl', 'Ctrl')
.replaceAll('shift', 'Shift')
.replaceAll('alt', 'Alt')
}

View File

@ -38,8 +38,8 @@ export const settingsLoader: LoaderFunction = async ({
configuration
)
if (projectPathData) {
const { project_name } = projectPathData
const { settings: s } = await loadAndValidateSettings(project_name)
const { project_path } = projectPathData
const { settings: s } = await loadAndValidateSettings(project_path)
settings = s
}
}
@ -118,6 +118,7 @@ export const fileLoader: LoaderFunction = async ({
children: [],
kcl_file_count: 0,
directory_count: 0,
default_file: project_path,
},
file: {
name: current_file_name,

View File

@ -77,7 +77,29 @@ export async function getEventForSelectWithPoint(
},
}
}
const _artifact = engineCommandManager.artifactMap[data.entity_id]
let _artifact = engineCommandManager.artifactMap[data.entity_id]
if (!_artifact) {
// This logic for getting the parent id is for solid2ds as in edit mode it return the face id
// but we don't recognise that in the artifact map because we store the path id when the path is
// created, the solid2d is implicitly created with the close stdlib function
// there's plans to get the faceId back from the solid2d creation
// https://github.com/KittyCAD/engine/issues/2094
// at which point we can add it to the artifact map and remove this logic
const parentId = (
await engineCommandManager.sendSceneCommand({
type: 'modeling_cmd_req',
cmd: {
type: 'entity_get_parent_id',
entity_id: data.entity_id,
},
cmd_id: uuidv4(),
})
)?.data?.data?.entity_id
const parentArtifact = engineCommandManager.artifactMap[parentId]
if (parentArtifact) {
_artifact = parentArtifact
}
}
const sourceRange = _artifact?.range
if (_artifact) {
if (_artifact.commandType === 'solid3d_get_extrusion_face_info') {

View File

@ -147,7 +147,7 @@ export interface AppSettings {
}
export async function loadAndValidateSettings(
projectName?: string
projectPath?: string
): Promise<AppSettings> {
const settings = createSettings()
const inTauri = isTauri()
@ -166,9 +166,9 @@ export async function loadAndValidateSettings(
setSettingsAtLevel(settings, 'user', appSettingsPayload)
// Load the project settings if they exist
if (projectName) {
if (projectPath) {
const projectSettings = inTauri
? await readProjectSettingsFile(appSettings, projectName)
? await readProjectSettingsFile(projectPath)
: readLocalStorageProjectSettingsFile()
const projectSettingsPayload =
@ -182,7 +182,7 @@ export async function loadAndValidateSettings(
export async function saveSettings(
allSettings: typeof settings,
projectName?: string
projectPath?: string
) {
// Make sure we have wasm initialized.
await initPromise
@ -204,7 +204,7 @@ export async function saveSettings(
)
}
if (!projectName) {
if (!projectPath) {
// If we're not saving project settings, we're done.
return
}
@ -217,7 +217,7 @@ export async function saveSettings(
// Write the project settings.
if (inTauri) {
await writeProjectSettingsFile(appSettings, projectName, projectSettings)
await writeProjectSettingsFile(projectPath, projectSettings)
} else {
localStorage.setItem(
localStorageProjectSettingsPath(),

View File

@ -8,23 +8,36 @@ import { Project } from 'wasm-lib/kcl/bindings/Project'
import { FileEntry } from 'wasm-lib/kcl/bindings/FileEntry'
import { ProjectState } from 'wasm-lib/kcl/bindings/ProjectState'
import { ProjectRoute } from 'wasm-lib/kcl/bindings/ProjectRoute'
import { isTauri } from './isTauri'
// Get the app state from tauri.
export async function getState(): Promise<ProjectState | undefined> {
if (!isTauri()) {
return undefined
}
return await invoke<ProjectState | undefined>('get_state')
}
// Set the app state in tauri.
export async function setState(state: ProjectState | undefined): Promise<void> {
if (!isTauri()) {
return
}
return await invoke('set_state', { state })
}
// Get the initial default dir for holding all projects.
export async function getInitialDefaultDir(): Promise<string> {
if (!isTauri()) {
return ''
}
return invoke<string>('get_initial_default_dir')
}
export async function showInFolder(path: string | undefined): Promise<void> {
if (!isTauri()) {
return
}
if (!path) {
console.error('path is undefined cannot call tauri showInFolder')
return
@ -34,7 +47,10 @@ export async function showInFolder(path: string | undefined): Promise<void> {
export async function initializeProjectDirectory(
settings: Configuration
): Promise<string> {
): Promise<string | undefined> {
if (!isTauri()) {
return undefined
}
return await invoke<string>('initialize_project_directory', {
configuration: settings,
})
@ -127,24 +143,20 @@ export async function writeAppSettingsFile(
// Read project settings file.
export async function readProjectSettingsFile(
appSettings: Configuration,
projectName: string
projectPath: string
): Promise<ProjectConfiguration> {
return await invoke<ProjectConfiguration>('read_project_settings_file', {
appSettings,
projectName,
projectPath,
})
}
// Write project settings file.
export async function writeProjectSettingsFile(
appSettings: Configuration,
projectName: string,
projectPath: string,
settings: ProjectConfiguration
): Promise<void> {
return await invoke('write_project_settings_file', {
appSettings,
projectName,
projectPath,
configuration: settings,
})
}

View File

@ -906,12 +906,20 @@ export const modelingMachine = createMachine(
type: 'default_camera_set_perspective',
},
})
const center = { x: 0, y: 0, z: 0 }
const camPos = sceneInfra.camControls.camera.position
if (camPos.x === 0 && camPos.y === 0) {
// looking straight up or down is going to cause issues with the engine
// tweaking the center to be a little off center
// TODO come up with a proper fix
center.y = 0.05
}
await engineCommandManager.sendSceneCommand({
type: 'modeling_cmd_req',
cmd_id: uuidv4(),
cmd: {
type: 'default_camera_look_at',
center: { x: 0, y: 0, z: 0 },
center,
vantage: sceneInfra.camControls.camera.position,
up: { x: 0, y: 0, z: 1 },
},
@ -1045,7 +1053,8 @@ export const modelingMachine = createMachine(
filter: ['face', 'plane'],
},
}),
'set selection filter to defaults': () => kclManager.enterEditMode(),
'set selection filter to defaults': () =>
kclManager.defaultSelectionFilter(),
},
// end actions
}

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

@ -155,9 +155,9 @@ dependencies = [
[[package]]
name = "anyhow"
version = "1.0.83"
version = "1.0.86"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "25bdb32cbbdce2b519a9cd7df3a678443100e265d5e25ca763b7572a5104f5f3"
checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da"
dependencies = [
"backtrace",
]
@ -177,6 +177,15 @@ dependencies = [
"num-traits 0.2.18",
]
[[package]]
name = "arbitrary"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7d5a26814d8dcb93b0e5a0ff3c6d80a8843bafb21b39e8e18a6f05471870e110"
dependencies = [
"derive_arbitrary",
]
[[package]]
name = "arc-swap"
version = "1.7.0"
@ -246,7 +255,7 @@ checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.63",
"syn 2.0.65",
]
[[package]]
@ -257,7 +266,7 @@ checksum = "c6fa2087f2753a7da8cc1c0dbfcf89579dd57458e36769de5ac750b4671737ca"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.63",
"syn 2.0.65",
]
[[package]]
@ -280,7 +289,7 @@ checksum = "3c87f3f15e7794432337fc718554eaa4dc8f04c9677a950ffe366f20a162ae42"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.63",
"syn 2.0.65",
]
[[package]]
@ -596,7 +605,7 @@ dependencies = [
"heck 0.5.0",
"proc-macro2",
"quote",
"syn 2.0.63",
"syn 2.0.65",
]
[[package]]
@ -846,7 +855,7 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.63",
"syn 2.0.65",
]
[[package]]
@ -870,7 +879,7 @@ dependencies = [
"proc-macro2",
"quote",
"strsim 0.10.0",
"syn 2.0.63",
"syn 2.0.65",
]
[[package]]
@ -881,7 +890,7 @@ checksum = "a668eda54683121533a393014d8692171709ff57a7d61f187b6e782719f8933f"
dependencies = [
"darling_core",
"quote",
"syn 2.0.63",
"syn 2.0.65",
]
[[package]]
@ -922,7 +931,7 @@ checksum = "377af281d8f23663862a7c84623bc5dcf7f8c44b13c7496a590bdc157f941a43"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.63",
"syn 2.0.65",
"synstructure 0.13.1",
]
@ -976,7 +985,18 @@ dependencies = [
"rustfmt-wrapper",
"serde",
"serde_tokenstream",
"syn 2.0.63",
"syn 2.0.65",
]
[[package]]
name = "derive_arbitrary"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "67e77553c4162a157adbf834ebae5b415acbecbeafc7a74b0e886657506a7611"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.65",
]
[[package]]
@ -988,7 +1008,7 @@ dependencies = [
"diesel_table_macro_syntax",
"proc-macro2",
"quote",
"syn 2.0.63",
"syn 2.0.65",
]
[[package]]
@ -997,7 +1017,7 @@ version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc5557efc453706fed5e4fa85006fe9817c224c3f480a34c7e5959fd700921c5"
dependencies = [
"syn 2.0.63",
"syn 2.0.65",
]
[[package]]
@ -1026,7 +1046,7 @@ checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.63",
"syn 2.0.65",
]
[[package]]
@ -1051,9 +1071,9 @@ dependencies = [
[[package]]
name = "either"
version = "1.10.0"
version = "1.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "11157ac094ffbdde99aa67b23417ebdd801842852b500e395a45a9c0aac03e4a"
checksum = "3dca9240753cf90908d7e4aac30f630662b02aebaa1b58a3cadabdb23385b58b"
[[package]]
name = "elliptic-curve"
@ -1108,7 +1128,7 @@ checksum = "03cdc46ec28bd728e67540c528013c6a10eb69a02eb31078a1bda695438cbfb8"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.63",
"syn 2.0.65",
]
[[package]]
@ -1325,7 +1345,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.63",
"syn 2.0.65",
]
[[package]]
@ -1436,7 +1456,7 @@ dependencies = [
"inflections",
"proc-macro2",
"quote",
"syn 2.0.63",
"syn 2.0.65",
]
[[package]]
@ -1652,7 +1672,7 @@ dependencies = [
"futures-util",
"http 0.2.12",
"hyper",
"rustls 0.21.11",
"rustls 0.21.12",
"tokio",
"tokio-rustls 0.24.1",
]
@ -1853,18 +1873,18 @@ dependencies = [
[[package]]
name = "itertools"
version = "0.11.0"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57"
checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569"
dependencies = [
"either",
]
[[package]]
name = "itertools"
version = "0.12.1"
version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569"
checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186"
dependencies = [
"either",
]
@ -1918,7 +1938,7 @@ dependencies = [
"iai",
"image",
"insta",
"itertools 0.12.1",
"itertools 0.13.0",
"js-sys",
"kittycad",
"kittycad-execution-plan-macros",
@ -1959,18 +1979,18 @@ dependencies = [
"pretty_assertions",
"proc-macro2",
"quote",
"syn 2.0.63",
"syn 2.0.65",
]
[[package]]
name = "kittycad"
version = "0.3.1"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2c6e12eb45fd9a28c8e99dbdef54556246b39acee14e4aa6f0fc43636caa62d9"
checksum = "81d3481e1b63f063ccf7d211f643da0563d2a42dd8b95b6006610742b729996b"
dependencies = [
"anyhow",
"async-trait",
"base64 0.21.7",
"base64 0.22.1",
"bigdecimal",
"bytes",
"chrono",
@ -1979,10 +1999,10 @@ dependencies = [
"format_serde_error",
"futures",
"http 0.2.12",
"itertools 0.10.5",
"itertools 0.12.1",
"log",
"mime_guess",
"parse-display 0.8.2",
"parse-display 0.9.0",
"phonenumber",
"rand 0.8.5",
"reqwest",
@ -2032,7 +2052,7 @@ checksum = "0611fc9b9786175da21d895ffa0f65039e19c9111e94a41b7af999e3b95f045f"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.63",
"syn 2.0.65",
]
[[package]]
@ -2083,7 +2103,7 @@ checksum = "385775cc9d5bf25579f3029824ca1a6e7ab1b7c338e972ec8e8fcefff801f353"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.63",
"syn 2.0.65",
]
[[package]]
@ -2457,7 +2477,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.63",
"syn 2.0.65",
]
[[package]]
@ -2614,7 +2634,7 @@ dependencies = [
"regex",
"regex-syntax 0.7.5",
"structmeta 0.2.0",
"syn 2.0.63",
"syn 2.0.65",
]
[[package]]
@ -2628,7 +2648,7 @@ dependencies = [
"regex",
"regex-syntax 0.8.2",
"structmeta 0.3.0",
"syn 2.0.63",
"syn 2.0.65",
]
[[package]]
@ -2658,14 +2678,14 @@ checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
[[package]]
name = "phonenumber"
version = "0.3.3+8.13.9"
version = "0.3.5+8.13.36"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "635f3e6288e4f01c049d89332a031bd74f25d64b6fb94703ca966e819488cd06"
checksum = "f174c8db59b620032bd52b655fc97000458850fec0db35fcd4e802b668517ec0"
dependencies = [
"bincode",
"either",
"fnv",
"itertools 0.11.0",
"itertools 0.12.1",
"lazy_static",
"nom",
"quick-xml",
@ -2694,7 +2714,7 @@ checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.63",
"syn 2.0.65",
]
[[package]]
@ -2841,9 +2861,9 @@ dependencies = [
[[package]]
name = "proc-macro2"
version = "1.0.82"
version = "1.0.83"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ad3d49ab951a01fbaafe34f2ec74122942fe18a3f9814c3268f1bb72042131b"
checksum = "0b33eb56c327dec362a9e55b3ad14f9d2f0904fb5a5b03b513ab5465399e9f43"
dependencies = [
"unicode-ident",
]
@ -3078,7 +3098,7 @@ dependencies = [
"once_cell",
"percent-encoding",
"pin-project-lite",
"rustls 0.21.11",
"rustls 0.21.12",
"rustls-pemfile 1.0.4",
"serde",
"serde_json",
@ -3304,9 +3324,9 @@ dependencies = [
[[package]]
name = "rustls"
version = "0.21.11"
version = "0.21.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7fecbfb7b1444f477b345853b1fce097a2c6fb637b2bfb87e6bc5db0f043fae4"
checksum = "3f56a14d1f48b391359b22f731fd4bd7e43c97f3c50eee276f3aa09c94784d3e"
dependencies = [
"log",
"ring 0.17.8",
@ -3316,9 +3336,9 @@ dependencies = [
[[package]]
name = "rustls"
version = "0.22.2"
version = "0.22.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e87c9956bd9807afa1f77e0f7594af32566e830e088a5576d27c5b6f30f49d41"
checksum = "bf4ef73721ac7bcd79b2b315da7779d8fc09718c6b3d2d1b2d94850eb8c18432"
dependencies = [
"log",
"ring 0.17.8",
@ -3419,9 +3439,9 @@ dependencies = [
[[package]]
name = "schemars"
version = "0.8.17"
version = "0.8.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f55c82c700538496bdc329bb4918a81f87cc8888811bd123cf325a0f2f8d309"
checksum = "b0218ceea14babe24a4a5836f86ade86c1effbc198164e619194cb5069187e29"
dependencies = [
"bigdecimal",
"bytes",
@ -3436,14 +3456,14 @@ dependencies = [
[[package]]
name = "schemars_derive"
version = "0.8.17"
version = "0.8.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "83263746fe5e32097f06356968a077f96089739c927a61450efa069905eec108"
checksum = "3ed5a1ccce8ff962e31a165d41f6e2a2dd1245099dc4d594f5574a86cd90f4d3"
dependencies = [
"proc-macro2",
"quote",
"serde_derive_internals",
"syn 2.0.63",
"syn 2.0.65",
]
[[package]]
@ -3519,9 +3539,9 @@ checksum = "92d43fe69e652f3df9bdc2b85b2854a0825b86e4fb76bc44d945137d053639ca"
[[package]]
name = "serde"
version = "1.0.201"
version = "1.0.202"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "780f1cebed1629e4753a1a38a3c72d30b97ec044f0aef68cb26650a3c5cf363c"
checksum = "226b61a0d411b2ba5ff6d7f73a476ac4f8bb900373459cd00fab8512828ba395"
dependencies = [
"serde_derive",
]
@ -3537,13 +3557,13 @@ dependencies = [
[[package]]
name = "serde_derive"
version = "1.0.201"
version = "1.0.202"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c5e405930b9796f1c00bee880d03fc7e0bb4b9a11afc776885ffe84320da2865"
checksum = "6048858004bcff69094cd972ed40a32500f153bd3be9f716b2eed2e8217c4838"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.63",
"syn 2.0.65",
]
[[package]]
@ -3554,7 +3574,7 @@ checksum = "330f01ce65a3a5fe59a60c82f3c9a024b573b8a6e875bd233fe5f934e71d54e3"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.63",
"syn 2.0.65",
]
[[package]]
@ -3577,14 +3597,14 @@ checksum = "0b2e6b945e9d3df726b65d6ee24060aff8e3533d431f677a9695db04eff9dfdb"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.63",
"syn 2.0.65",
]
[[package]]
name = "serde_spanned"
version = "0.6.5"
version = "0.6.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eb3622f419d1296904700073ea6cc23ad690adbd66f13ea683df73298736f0c1"
checksum = "79e674e01f999af37c49f70a6ede167a8a60b2503e56c5599532a65baa5969a0"
dependencies = [
"serde",
]
@ -3598,7 +3618,7 @@ dependencies = [
"proc-macro2",
"quote",
"serde",
"syn 2.0.63",
"syn 2.0.65",
]
[[package]]
@ -3773,7 +3793,7 @@ dependencies = [
"proc-macro2",
"quote",
"structmeta-derive 0.2.0",
"syn 2.0.63",
"syn 2.0.65",
]
[[package]]
@ -3785,7 +3805,7 @@ dependencies = [
"proc-macro2",
"quote",
"structmeta-derive 0.3.0",
"syn 2.0.63",
"syn 2.0.65",
]
[[package]]
@ -3796,7 +3816,7 @@ checksum = "a60bcaff7397072dca0017d1db428e30d5002e00b6847703e2e42005c95fbe00"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.63",
"syn 2.0.65",
]
[[package]]
@ -3807,7 +3827,7 @@ checksum = "152a0b65a590ff6c3da95cabe2353ee04e6167c896b28e3b14478c2636c922fc"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.63",
"syn 2.0.65",
]
[[package]]
@ -3879,9 +3899,9 @@ dependencies = [
[[package]]
name = "syn"
version = "2.0.63"
version = "2.0.65"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bf5be731623ca1a1fb7d8be6f261a3be6d3e2337b8a1f97be944d020c8fcb704"
checksum = "d2863d96a84c6439701d7a38f9de935ec562c8832cc55d1dde0f513b52fad106"
dependencies = [
"proc-macro2",
"quote",
@ -3914,7 +3934,7 @@ checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.63",
"syn 2.0.65",
]
[[package]]
@ -4000,22 +4020,22 @@ dependencies = [
[[package]]
name = "thiserror"
version = "1.0.60"
version = "1.0.61"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "579e9083ca58dd9dcf91a9923bb9054071b9ebbd800b342194c9feb0ee89fc18"
checksum = "c546c80d6be4bc6a00c0f01730c08df82eaa7a7a61f11d656526506112cc1709"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.60"
version = "1.0.61"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2470041c06ec3ac1ab38d0356a6119054dedaea53e12fbefc0de730a1c08524"
checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.63",
"syn 2.0.65",
]
[[package]]
@ -4122,7 +4142,7 @@ checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.63",
"syn 2.0.65",
]
[[package]]
@ -4141,7 +4161,7 @@ version = "0.24.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081"
dependencies = [
"rustls 0.21.11",
"rustls 0.21.12",
"tokio",
]
@ -4151,7 +4171,7 @@ version = "0.25.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "775e0c0f0adb3a2f22a00c4745d728b479985fc15ee7ca6a2608388c5569860f"
dependencies = [
"rustls 0.22.2",
"rustls 0.22.4",
"rustls-pki-types",
"tokio",
]
@ -4164,7 +4184,7 @@ checksum = "c83b561d025642014097b66e6c1bb422783339e0909e4429cde4749d1990bc38"
dependencies = [
"futures-util",
"log",
"rustls 0.22.2",
"rustls 0.22.4",
"rustls-native-certs",
"rustls-pki-types",
"tokio",
@ -4188,9 +4208,9 @@ dependencies = [
[[package]]
name = "toml"
version = "0.8.12"
version = "0.8.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e9dd1545e8208b4a5af1aa9bbd0b4cf7e9ea08fabc5d0a5c67fcaafa17433aa3"
checksum = "a4e43f8cc456c9704c851ae29c67e17ef65d2c30017c17a9765b89c382dc8bba"
dependencies = [
"serde",
"serde_spanned",
@ -4200,18 +4220,18 @@ dependencies = [
[[package]]
name = "toml_datetime"
version = "0.6.5"
version = "0.6.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1"
checksum = "4badfd56924ae69bcc9039335b2e017639ce3f9b001c393c1b2d1ef846ce2cbf"
dependencies = [
"serde",
]
[[package]]
name = "toml_edit"
version = "0.22.9"
version = "0.22.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e40bb779c5187258fd7aad0eb68cb8706a0a81fa712fbea808ab43c4b8374c4"
checksum = "c127785850e8c20836d49732ae6abfa47616e60bf9d9f57c43c250361a9db96c"
dependencies = [
"indexmap 2.2.5",
"serde",
@ -4285,7 +4305,7 @@ checksum = "84fd902d4e0b9a4b27f2f440108dc034e1758628a9b702f8ec61ad66355422fa"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.63",
"syn 2.0.65",
]
[[package]]
@ -4313,7 +4333,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.63",
"syn 2.0.65",
]
[[package]]
@ -4390,7 +4410,7 @@ dependencies = [
"Inflector",
"proc-macro2",
"quote",
"syn 2.0.63",
"syn 2.0.65",
"termcolor",
]
@ -4407,7 +4427,7 @@ dependencies = [
"httparse",
"log",
"rand 0.8.5",
"rustls 0.22.2",
"rustls 0.22.4",
"rustls-pki-types",
"sha1",
"thiserror",
@ -4586,7 +4606,7 @@ dependencies = [
"proc-macro-error",
"proc-macro2",
"quote",
"syn 2.0.63",
"syn 2.0.65",
]
[[package]]
@ -4662,7 +4682,7 @@ dependencies = [
"once_cell",
"proc-macro2",
"quote",
"syn 2.0.63",
"syn 2.0.65",
"wasm-bindgen-shared",
]
@ -4697,7 +4717,7 @@ checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.63",
"syn 2.0.65",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]
@ -4800,7 +4820,7 @@ dependencies = [
"ring 0.17.8",
"rtcp",
"rtp",
"rustls 0.21.11",
"rustls 0.21.12",
"sdp",
"serde",
"serde_json",
@ -4860,7 +4880,7 @@ dependencies = [
"rand_core 0.6.4",
"rcgen",
"ring 0.17.8",
"rustls 0.21.11",
"rustls 0.21.12",
"sec1",
"serde",
"sha1",
@ -5269,7 +5289,7 @@ checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.63",
"syn 2.0.65",
]
[[package]]
@ -5289,18 +5309,21 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.63",
"syn 2.0.65",
]
[[package]]
name = "zip"
version = "0.6.6"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "760394e246e4c28189f19d488c058bf16f564016aefac5d32bb1f3b51d5e9261"
checksum = "f1f4a27345eb6f7aa7bd015ba7eb4175fa4e1b462a29874b779e0bbcf96c6ac7"
dependencies = [
"byteorder",
"arbitrary",
"crc32fast",
"crossbeam-utils",
"displaydoc",
"indexmap 2.2.5",
"thiserror",
]
[[package]]

View File

@ -17,7 +17,7 @@ kcl-lib = { path = "kcl" }
kittycad = { workspace = true }
serde_json = "1.0.116"
tokio = { version = "1.37.0", features = ["sync"] }
toml = "0.8.12"
toml = "0.8.13"
uuid = { version = "1.8.0", features = ["v4", "js", "serde"] }
wasm-bindgen = "0.2.91"
wasm-bindgen-futures = "0.4.42"

View File

@ -18,12 +18,12 @@ once_cell = "1.19.0"
proc-macro2 = "1"
quote = "1"
regex = "1.10"
serde = { version = "1.0.201", features = ["derive"] }
serde = { version = "1.0.202", features = ["derive"] }
serde_tokenstream = "0.2"
syn = { version = "2.0.63", features = ["full"] }
syn = { version = "2.0.65", features = ["full"] }
[dev-dependencies]
anyhow = "1.0.83"
anyhow = "1.0.86"
expectorate = "1.1.0"
pretty_assertions = "1.4.0"
rustfmt-wrapper = "0.2.1"

View File

@ -201,7 +201,7 @@ fn do_stdlib_inner(
.code_blocks
.iter()
.enumerate()
.map(|(index, code_block)| generate_code_block_test(&fn_name_str, code_block, index, &metadata.tags))
.map(|(index, code_block)| generate_code_block_test(&fn_name_str, code_block, index))
.collect::<Vec<_>>();
let tags = metadata
@ -731,27 +731,13 @@ fn parse_array_type(type_name: &str) -> Option<(&str, usize)> {
// For each kcl code block, we want to generate a test that checks that the
// code block is valid kcl code and compiles and executes.
fn generate_code_block_test(
fn_name: &str,
code_block: &str,
index: usize,
tags: &[String],
) -> proc_macro2::TokenStream {
fn generate_code_block_test(fn_name: &str, code_block: &str, index: usize) -> proc_macro2::TokenStream {
let test_name = format_ident!("serial_test_example_{}{}", fn_name, index);
let test_name_mock = format_ident!("test_mock_example_{}{}", fn_name, index);
let test_name_str = format!("serial_test_example_{}{}", fn_name, index);
// TODO: We ignore import for now, because the files don't exist and we just want
// to show easy imports.
let ignored = if tags.contains(&"norun".to_string()) {
quote! { #[ignore] }
} else {
quote! {}
};
quote! {
#[tokio::test(flavor = "multi_thread")]
#ignored
async fn #test_name_mock() {
let tokens = crate::token::lexer(#code_block).unwrap();
let parser = crate::parser::Parser::new(tokens);
@ -768,7 +754,6 @@ fn generate_code_block_test(
}
#[tokio::test(flavor = "multi_thread", worker_threads = 5)]
#ignored
async fn #test_name() {
let user_agent = concat!(env!("CARGO_PKG_NAME"), ".rs/", env!("CARGO_PKG_VERSION"),);
let http_client = reqwest::Client::builder()

View File

@ -340,43 +340,6 @@ fn test_stdlib_doc_comment_with_code() {
expectorate::assert_contents("tests/doc_comment_with_code.gen", &get_text_fmt(&item).unwrap());
}
#[test]
fn test_stdlib_doc_comment_with_code_on_ignored_function() {
let (item, errors) = do_stdlib(
quote! {
name = "import",
tags = ["norun"]
},
quote! {
/// This is some function.
/// It does shit.
///
/// This is code.
/// It does other shit.
/// import
///
/// ```
/// This is another code block.
/// yes sirrr.
/// import
/// ```
fn inner_import(
/// The args to do shit to.
args: Option<kittycad::types::InputFormat>
) -> Result<Vec<Box<SketchGroup>>> {
args
}
},
)
.unwrap();
assert!(errors.is_empty());
expectorate::assert_contents(
"tests/doc_comment_with_code_on_ignored_function.gen",
&get_text_fmt(&item).unwrap(),
);
}
#[test]
fn test_stdlib_fail_non_camel_case() {
let (_, errors) = do_stdlib(

View File

@ -1,303 +0,0 @@
#[cfg(test)]
mod test_examples_import {
#[tokio::test(flavor = "multi_thread")]
#[ignore]
async fn test_mock_example_import0() {
let tokens =
crate::token::lexer("This is another code block.\nyes sirrr.\nimport").unwrap();
let parser = crate::parser::Parser::new(tokens);
let program = parser.ast().unwrap();
let ctx = crate::executor::ExecutorContext {
engine: std::sync::Arc::new(Box::new(
crate::engine::conn_mock::EngineConnection::new()
.await
.unwrap(),
)),
fs: std::sync::Arc::new(crate::fs::FileManager::new()),
stdlib: std::sync::Arc::new(crate::std::StdLib::new()),
settings: Default::default(),
is_mock: true,
};
ctx.run(program, None).await.unwrap();
}
#[tokio::test(flavor = "multi_thread", worker_threads = 5)]
#[ignore]
async fn serial_test_example_import0() {
let user_agent = concat!(env!("CARGO_PKG_NAME"), ".rs/", env!("CARGO_PKG_VERSION"),);
let http_client = reqwest::Client::builder()
.user_agent(user_agent)
.timeout(std::time::Duration::from_secs(600))
.connect_timeout(std::time::Duration::from_secs(60));
let ws_client = reqwest::Client::builder()
.user_agent(user_agent)
.timeout(std::time::Duration::from_secs(600))
.connect_timeout(std::time::Duration::from_secs(60))
.connection_verbose(true)
.tcp_keepalive(std::time::Duration::from_secs(600))
.http1_only();
let token = std::env::var("KITTYCAD_API_TOKEN").expect("KITTYCAD_API_TOKEN not set");
let mut client = kittycad::Client::new_from_reqwest(token, http_client, ws_client);
if let Ok(addr) = std::env::var("LOCAL_ENGINE_ADDR") {
client.set_base_url(addr);
}
let tokens =
crate::token::lexer("This is another code block.\nyes sirrr.\nimport").unwrap();
let parser = crate::parser::Parser::new(tokens);
let program = parser.ast().unwrap();
let ctx = crate::executor::ExecutorContext::new(&client, Default::default())
.await
.unwrap();
ctx.run(program, None).await.unwrap();
ctx.engine
.send_modeling_cmd(
uuid::Uuid::new_v4(),
crate::executor::SourceRange::default(),
kittycad::types::ModelingCmd::ZoomToFit {
object_ids: Default::default(),
padding: 0.1,
},
)
.await
.unwrap();
let resp = ctx
.engine
.send_modeling_cmd(
uuid::Uuid::new_v4(),
crate::executor::SourceRange::default(),
kittycad::types::ModelingCmd::TakeSnapshot {
format: kittycad::types::ImageFormat::Png,
},
)
.await
.unwrap();
let output_file =
std::env::temp_dir().join(format!("kcl_output_{}.png", uuid::Uuid::new_v4()));
if let kittycad::types::OkWebSocketResponseData::Modeling {
modeling_response: kittycad::types::OkModelingCmdResponse::TakeSnapshot { data },
} = &resp
{
std::fs::write(&output_file, &data.contents.0).unwrap();
} else {
panic!("Unexpected response from engine: {:?}", resp);
}
let actual = image::io::Reader::open(output_file)
.unwrap()
.decode()
.unwrap();
twenty_twenty::assert_image(
&format!("tests/outputs/{}.png", "serial_test_example_import0"),
&actual,
1.0,
);
}
#[tokio::test(flavor = "multi_thread")]
#[ignore]
async fn test_mock_example_import1() {
let tokens = crate::token::lexer("This is code.\nIt does other shit.\nimport").unwrap();
let parser = crate::parser::Parser::new(tokens);
let program = parser.ast().unwrap();
let ctx = crate::executor::ExecutorContext {
engine: std::sync::Arc::new(Box::new(
crate::engine::conn_mock::EngineConnection::new()
.await
.unwrap(),
)),
fs: std::sync::Arc::new(crate::fs::FileManager::new()),
stdlib: std::sync::Arc::new(crate::std::StdLib::new()),
settings: Default::default(),
is_mock: true,
};
ctx.run(program, None).await.unwrap();
}
#[tokio::test(flavor = "multi_thread", worker_threads = 5)]
#[ignore]
async fn serial_test_example_import1() {
let user_agent = concat!(env!("CARGO_PKG_NAME"), ".rs/", env!("CARGO_PKG_VERSION"),);
let http_client = reqwest::Client::builder()
.user_agent(user_agent)
.timeout(std::time::Duration::from_secs(600))
.connect_timeout(std::time::Duration::from_secs(60));
let ws_client = reqwest::Client::builder()
.user_agent(user_agent)
.timeout(std::time::Duration::from_secs(600))
.connect_timeout(std::time::Duration::from_secs(60))
.connection_verbose(true)
.tcp_keepalive(std::time::Duration::from_secs(600))
.http1_only();
let token = std::env::var("KITTYCAD_API_TOKEN").expect("KITTYCAD_API_TOKEN not set");
let mut client = kittycad::Client::new_from_reqwest(token, http_client, ws_client);
if let Ok(addr) = std::env::var("LOCAL_ENGINE_ADDR") {
client.set_base_url(addr);
}
let tokens = crate::token::lexer("This is code.\nIt does other shit.\nimport").unwrap();
let parser = crate::parser::Parser::new(tokens);
let program = parser.ast().unwrap();
let ctx = crate::executor::ExecutorContext::new(&client, Default::default())
.await
.unwrap();
ctx.run(program, None).await.unwrap();
ctx.engine
.send_modeling_cmd(
uuid::Uuid::new_v4(),
crate::executor::SourceRange::default(),
kittycad::types::ModelingCmd::ZoomToFit {
object_ids: Default::default(),
padding: 0.1,
},
)
.await
.unwrap();
let resp = ctx
.engine
.send_modeling_cmd(
uuid::Uuid::new_v4(),
crate::executor::SourceRange::default(),
kittycad::types::ModelingCmd::TakeSnapshot {
format: kittycad::types::ImageFormat::Png,
},
)
.await
.unwrap();
let output_file =
std::env::temp_dir().join(format!("kcl_output_{}.png", uuid::Uuid::new_v4()));
if let kittycad::types::OkWebSocketResponseData::Modeling {
modeling_response: kittycad::types::OkModelingCmdResponse::TakeSnapshot { data },
} = &resp
{
std::fs::write(&output_file, &data.contents.0).unwrap();
} else {
panic!("Unexpected response from engine: {:?}", resp);
}
let actual = image::io::Reader::open(output_file)
.unwrap()
.decode()
.unwrap();
twenty_twenty::assert_image(
&format!("tests/outputs/{}.png", "serial_test_example_import1"),
&actual,
1.0,
);
}
}
#[allow(non_camel_case_types, missing_docs)]
#[doc = "Std lib function: import\nThis is some function.\nIt does shit."]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, schemars :: JsonSchema, ts_rs :: TS)]
#[ts(export)]
pub(crate) struct Import {}
#[allow(non_upper_case_globals, missing_docs)]
#[doc = "Std lib function: import\nThis is some function.\nIt does shit."]
pub(crate) const Import: Import = Import {};
fn boxed_import(
args: crate::std::Args,
) -> std::pin::Pin<
Box<
dyn std::future::Future<
Output = anyhow::Result<crate::executor::MemoryItem, crate::errors::KclError>,
> + Send,
>,
> {
Box::pin(import(args))
}
impl crate::docs::StdLibFn for Import {
fn name(&self) -> String {
"import".to_string()
}
fn summary(&self) -> String {
"This is some function.".to_string()
}
fn description(&self) -> String {
"It does shit.".to_string()
}
fn tags(&self) -> Vec<String> {
vec!["norun".to_string()]
}
fn args(&self) -> Vec<crate::docs::StdLibFnArg> {
let mut settings = schemars::gen::SchemaSettings::openapi3();
settings.inline_subschemas = true;
let mut generator = schemars::gen::SchemaGenerator::new(settings);
vec![crate::docs::StdLibFnArg {
name: "args".to_string(),
type_: "kittycad::types::InputFormat".to_string(),
schema: <Option<kittycad::types::InputFormat>>::json_schema(&mut generator),
required: false,
}]
}
fn return_value(&self) -> Option<crate::docs::StdLibFnArg> {
let mut settings = schemars::gen::SchemaSettings::openapi3();
settings.inline_subschemas = true;
let mut generator = schemars::gen::SchemaGenerator::new(settings);
Some(crate::docs::StdLibFnArg {
name: "".to_string(),
type_: "[SketchGroup]".to_string(),
schema: <Vec<SketchGroup>>::json_schema(&mut generator),
required: true,
})
}
fn unpublished(&self) -> bool {
false
}
fn deprecated(&self) -> bool {
false
}
fn examples(&self) -> Vec<String> {
let code_blocks = vec![
"This is another code block.\nyes sirrr.\nimport",
"This is code.\nIt does other shit.\nimport",
];
code_blocks
.iter()
.map(|cb| {
let tokens = crate::token::lexer(cb).unwrap();
let parser = crate::parser::Parser::new(tokens);
let program = parser.ast().unwrap();
let mut options: crate::ast::types::FormatOptions = Default::default();
options.insert_final_newline = false;
program.recast(&options, 0)
})
.collect::<Vec<String>>()
}
fn std_lib_fn(&self) -> crate::std::StdFn {
boxed_import
}
fn clone_box(&self) -> Box<dyn crate::docs::StdLibFn> {
Box::new(self.clone())
}
}
#[doc = r" This is some function."]
#[doc = r" It does shit."]
#[doc = r""]
#[doc = r" This is code."]
#[doc = r" It does other shit."]
#[doc = r" import"]
#[doc = r""]
#[doc = r" ```"]
#[doc = r" This is another code block."]
#[doc = r" yes sirrr."]
#[doc = r" import"]
#[doc = r" ```"]
fn inner_import(
#[doc = r" The args to do shit to."] args: Option<kittycad::types::InputFormat>,
) -> Result<Vec<Box<SketchGroup>>> {
args
}

View File

@ -14,7 +14,7 @@ kittycad-execution-plan-traits = { workspace = true }
kittycad-execution-plan-macros = { workspace = true }
kittycad-modeling-cmds = { workspace = true }
kittycad-modeling-session = { workspace = true }
thiserror = "1.0.60"
thiserror = "1.0.61"
tokio = { version = "1.37.0", features = ["macros", "rt"] }
twenty-twenty = "0.7.0"
uuid = "1.8"

Binary file not shown.

Before

Width:  |  Height:  |  Size: 80 KiB

After

Width:  |  Height:  |  Size: 80 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 82 KiB

After

Width:  |  Height:  |  Size: 82 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 79 KiB

After

Width:  |  Height:  |  Size: 79 KiB

View File

@ -15,7 +15,7 @@ databake = "0.1.7"
kcl-lib = { path = "../kcl" }
proc-macro2 = "1"
quote = "1"
syn = { version = "2.0.63", features = ["full"] }
syn = { version = "2.0.65", features = ["full"] }
[dev-dependencies]
pretty_assertions = "1.4.0"

View File

@ -11,7 +11,7 @@ keywords = ["kcl", "KittyCAD", "CAD"]
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
anyhow = { version = "1.0.83", features = ["backtrace"] }
anyhow = { version = "1.0.86", features = ["backtrace"] }
async-recursion = "1.1.1"
async-trait = "0.1.80"
base64 = "0.22.1"
@ -33,17 +33,17 @@ parse-display = "0.9.0"
reqwest = { version = "0.11.26", default-features = false, features = ["stream", "rustls-tls"] }
ropey = "1.6.1"
schemars = { version = "0.8.17", features = ["impl_json_schema", "url", "uuid1"] }
serde = { version = "1.0.201", features = ["derive"] }
serde = { version = "1.0.202", features = ["derive"] }
serde_json = "1.0.116"
sha2 = "0.10.8"
thiserror = "1.0.60"
toml = "0.8.12"
ts-rs = { version = "7.1.1", features = ["uuid-impl", "url-impl", "chrono-impl"] }
thiserror = "1.0.61"
toml = "0.8.13"
ts-rs = { version = "7.1.1", features = ["uuid-impl", "url-impl", "chrono-impl", "no-serde-warnings"] }
url = { version = "2.5.0", features = ["serde"] }
uuid = { version = "1.8.0", features = ["v4", "js", "serde"] }
validator = { version = "0.18.1", features = ["derive"] }
winnow = "0.5.40"
zip = { version = "0.6.6", default-features = false }
zip = { version = "1.3.0", default-features = false }
[target.'cfg(target_arch = "wasm32")'.dependencies]
js-sys = { version = "0.3.69" }
@ -80,7 +80,7 @@ expectorate = "1.1.0"
iai = "0.1"
image = "0.24.9"
insta = { version = "1.38.0", features = ["json"] }
itertools = "0.12.1"
itertools = "0.13.0"
pretty_assertions = "1.4.0"
tokio = { version = "1.37.0", features = ["rt-multi-thread", "macros", "time"] }
twenty-twenty = "0.7.0"

View File

@ -0,0 +1,20 @@
# KCL
Our language for defining geometry and working with our Geometry Engine efficiently. Short for KittyCAD Language, named after our Design API.
## Contributing a standard library function
We've built a lot of tooling to make contributing to KCL easier. If you are interested in contributing a new standard library function to KCL, here is the rough process:
1. Open just the wasm-lib folder in your editor of choice. VS Code, for example, struggles to run rust-analyzer on the entire modeling-app directory because it's such a turducken of TS and Rust code.
2. Find the definition for similar standard library functions in `./kcl/src/std` and place your new one near it or in the same category file.
3. Add your new code. A new standard library function consists of:
4. A `pub async` of the actual standard library function in Rust
5. A doc comment block containing at least one example using your new standard library function (the Rust compiler will error if you don't provide an example our teammates are dope)
6. A `stdlib` macro providing the name that will need to be written by KCL users to use the function (this is usually a camelCase version of your Rust implementation, which is named with snake_case)
7. An inner function that is published only to the crate
8. Add your new standard library function to [the long list of CORE_FNS in mod.rs](https://github.com/KittyCAD/modeling-app/blob/main/src/wasm-lib/kcl/src/std/mod.rs#L42)
9. Get a production Zoo dev token and run `export KITTYCAD_API_TOKEN=your-token-here` in a terminal
10. Run `TWENTY_TWENTY=overwrite cargo nextest run --workspace --no-fail-fast` to take snapshot tests of your example code running in the engine
11. Run `EXPECTORATE=overwrite cargo test --all generate_stdlib -- --nocapture` to generate new Markdown documentation for your function that will be used [to generate docs on our website](https://zoo.dev/docs/kcl).
12. Create a PR in GitHub.

View File

@ -64,10 +64,59 @@ dependencies = [
]
[[package]]
name = "anyhow"
version = "1.0.81"
name = "anstream"
version = "0.6.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0952808a6c2afd1aa8947271f3a60f1a6763c7b912d210184c5149b5cf147247"
checksum = "418c75fa768af9c03be99d17643f93f79bbba589895012a80e3452a19ddda15b"
dependencies = [
"anstyle",
"anstyle-parse",
"anstyle-query",
"anstyle-wincon",
"colorchoice",
"is_terminal_polyfill",
"utf8parse",
]
[[package]]
name = "anstyle"
version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "038dfcf04a5feb68e9c60b21c9625a54c2c0616e79b72b0fd87075a056ae1d1b"
[[package]]
name = "anstyle-parse"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c03a11a9034d92058ceb6ee011ce58af4a9bf61491aa7e1e59ecd24bd40d22d4"
dependencies = [
"utf8parse",
]
[[package]]
name = "anstyle-query"
version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a64c907d4e79225ac72e2a354c9ce84d50ebb4586dee56c82b3ee73004f537f5"
dependencies = [
"windows-sys 0.52.0",
]
[[package]]
name = "anstyle-wincon"
version = "3.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "61a38449feb7068f52bb06c12759005cf459ee52bb4adc1d5a7c4322d716fb19"
dependencies = [
"anstyle",
"windows-sys 0.52.0",
]
[[package]]
name = "anyhow"
version = "1.0.83"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "25bdb32cbbdce2b519a9cd7df3a678443100e265d5e25ca763b7572a5104f5f3"
dependencies = [
"backtrace",
]
@ -105,24 +154,24 @@ dependencies = [
[[package]]
name = "async-recursion"
version = "1.1.0"
version = "1.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "30c5ef0ede93efbf733c1a727f3b6b5a1060bbedd5600183e66f6e4be4af0ec5"
checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.58",
"syn 2.0.64",
]
[[package]]
name = "async-trait"
version = "0.1.79"
version = "0.1.80"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a507401cad91ec6a857ed5513a2073c82a9b9048762b885bb98655b306964681"
checksum = "c6fa2087f2753a7da8cc1c0dbfcf89579dd57458e36769de5ac750b4671737ca"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.58",
"syn 2.0.64",
]
[[package]]
@ -176,6 +225,12 @@ version = "0.21.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "414dcefbc63d77c526a76b3afcf6fbb9b5e2791c19c3aa2297733208750c6e53"
[[package]]
name = "base64"
version = "0.22.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
[[package]]
name = "bigdecimal"
version = "0.4.1"
@ -287,9 +342,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "chrono"
version = "0.4.37"
version = "0.4.38"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a0d04d43504c61aa6c7531f1871dd0d418d91130162063b789da00fd7057a5e"
checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401"
dependencies = [
"android-tzdata",
"iana-time-zone",
@ -300,6 +355,54 @@ dependencies = [
"windows-targets 0.52.0",
]
[[package]]
name = "clap"
version = "4.5.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "90bc066a67923782aa8515dbaea16946c5bcc5addbd668bb80af688e53e548a0"
dependencies = [
"clap_builder",
"clap_derive",
]
[[package]]
name = "clap_builder"
version = "4.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae129e2e766ae0ec03484e609954119f123cc1fe650337e155d03b022f24f7b4"
dependencies = [
"anstream",
"anstyle",
"clap_lex",
"strsim",
"unicase",
"unicode-width",
]
[[package]]
name = "clap_derive"
version = "4.5.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "528131438037fd55894f62d6e9f068b8f45ac57ffa77517819645d10aed04f64"
dependencies = [
"heck 0.5.0",
"proc-macro2",
"quote",
"syn 2.0.64",
]
[[package]]
name = "clap_lex"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce"
[[package]]
name = "colorchoice"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b6a852b24ab71dffc585bcb46eaf7959d175cb865a7152e35b348d1b2960422"
[[package]]
name = "colored"
version = "2.1.0"
@ -369,6 +472,41 @@ dependencies = [
"typenum",
]
[[package]]
name = "darling"
version = "0.20.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "83b2eb4d90d12bdda5ed17de686c2acb4c57914f8f921b8da7e112b5a36f3fe1"
dependencies = [
"darling_core",
"darling_macro",
]
[[package]]
name = "darling_core"
version = "0.20.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "622687fe0bac72a04e5599029151f5796111b90f1baaa9b544d807a5e31cd120"
dependencies = [
"fnv",
"ident_case",
"proc-macro2",
"quote",
"strsim",
"syn 2.0.64",
]
[[package]]
name = "darling_macro"
version = "0.20.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "733cabb43482b1a1b53eee8583c2b9e8684d592215ea83efd305dd31bc2f0178"
dependencies = [
"darling_core",
"quote",
"syn 2.0.64",
]
[[package]]
name = "dashmap"
version = "5.5.3"
@ -407,7 +545,7 @@ checksum = "377af281d8f23663862a7c84623bc5dcf7f8c44b13c7496a590bdc157f941a43"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.58",
"syn 2.0.64",
"synstructure",
]
@ -419,9 +557,7 @@ checksum = "f2696e8a945f658fd14dc3b87242e6b80cd0f36ff04ea560fa39082368847946"
[[package]]
name = "derive-docs"
version = "0.1.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "138b94245509a9dd516008788b585c34847829cf37b40a758b4aa581cf94f147"
version = "0.1.18"
dependencies = [
"Inflector",
"convert_case",
@ -431,7 +567,7 @@ dependencies = [
"regex",
"serde",
"serde_tokenstream",
"syn 2.0.58",
"syn 2.0.64",
]
[[package]]
@ -479,9 +615,9 @@ checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
[[package]]
name = "form_urlencoded"
version = "1.2.0"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a62bc1cf6f830c2ec14a513a9fb124d0a213a629668a4186f329db21fe045652"
checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456"
dependencies = [
"percent-encoding",
]
@ -561,7 +697,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.58",
"syn 2.0.64",
]
[[package]]
@ -624,22 +760,28 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6fb8d784f27acf97159b40fc4db5ecd8aa23b9ad5ef69cdd136d3bc80665f0c0"
[[package]]
name = "gltf-derive"
version = "1.4.0"
name = "git_rev"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "438ffe1a5540d75403feaf23636b164e816e93f6f03131674722b3886ce32a57"
checksum = "60884563ea313b5037683cd5d44f1e14e9cb07b08543756242a65887f9cff48e"
[[package]]
name = "gltf-derive"
version = "1.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "14070e711538afba5d6c807edb74bcb84e5dbb9211a3bf5dea0dfab5b24f4c51"
dependencies = [
"inflections",
"proc-macro2",
"quote",
"syn 2.0.58",
"syn 2.0.64",
]
[[package]]
name = "gltf-json"
version = "1.4.0"
version = "1.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "655951ba557f2bc69ea4b0799446bae281fa78efae6319968bdd2c3e9a06d8e1"
checksum = "e6176f9d60a7eab0a877e8e96548605dedbde9190a7ae1e80bbcc1c9af03ab14"
dependencies = [
"gltf-derive",
"serde",
@ -684,6 +826,12 @@ version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
[[package]]
name = "heck"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
[[package]]
name = "hermit-abi"
version = "0.3.2"
@ -774,7 +922,7 @@ dependencies = [
"futures-util",
"http 0.2.9",
"hyper",
"rustls 0.21.7",
"rustls 0.21.12",
"tokio",
"tokio-rustls 0.24.1",
]
@ -803,10 +951,16 @@ dependencies = [
]
[[package]]
name = "idna"
version = "0.4.0"
name = "ident_case"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c"
checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
[[package]]
name = "idna"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6"
dependencies = [
"unicode-bidi",
"unicode-normalization",
@ -844,6 +998,12 @@ version = "2.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "28b29a3cd74f0f4598934efe3aeba42bae0eb4680554128851ebbecb02af14e6"
[[package]]
name = "is_terminal_polyfill"
version = "1.70.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8478577c03552c21db0e2724ffb8986a5ce7af88107e6be5d2ee6e158c12800"
[[package]]
name = "itertools"
version = "0.10.5"
@ -879,18 +1039,22 @@ dependencies = [
[[package]]
name = "kcl-lib"
version = "0.1.47"
version = "0.1.55"
dependencies = [
"anyhow",
"approx",
"async-recursion",
"async-trait",
"base64 0.22.1",
"bson",
"chrono",
"clap",
"dashmap",
"databake",
"derive-docs",
"form_urlencoded",
"futures",
"git_rev",
"gltf-json",
"js-sys",
"kittycad",
@ -908,13 +1072,16 @@ dependencies = [
"thiserror",
"tokio",
"tokio-tungstenite",
"toml",
"tower-lsp",
"ts-rs",
"url",
"uuid",
"validator",
"wasm-bindgen",
"wasm-bindgen-futures",
"web-sys",
"winnow",
"winnow 0.5.40",
"zip",
]
@ -928,9 +1095,9 @@ dependencies = [
[[package]]
name = "kittycad"
version = "0.2.63"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "93a332250e08fd715ad3d5826e04d36da1c5bb42d0c1b1ff1f0598278b9ebf3c"
checksum = "2c6e12eb45fd9a28c8e99dbdef54556246b39acee14e4aa6f0fc43636caa62d9"
dependencies = [
"anyhow",
"async-trait",
@ -938,6 +1105,7 @@ dependencies = [
"bigdecimal",
"bytes",
"chrono",
"clap",
"data-encoding",
"format_serde_error",
"futures",
@ -968,7 +1136,7 @@ checksum = "0611fc9b9786175da21d895ffa0f65039e19c9111e94a41b7af999e3b95f045f"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.58",
"syn 2.0.64",
]
[[package]]
@ -1240,7 +1408,7 @@ dependencies = [
"regex",
"regex-syntax 0.7.5",
"structmeta 0.2.0",
"syn 2.0.58",
"syn 2.0.64",
]
[[package]]
@ -1254,14 +1422,14 @@ dependencies = [
"regex",
"regex-syntax 0.8.2",
"structmeta 0.3.0",
"syn 2.0.58",
"syn 2.0.64",
]
[[package]]
name = "percent-encoding"
version = "2.3.0"
version = "2.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94"
checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
[[package]]
name = "phonenumber"
@ -1301,7 +1469,7 @@ checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.58",
"syn 2.0.64",
]
[[package]]
@ -1348,9 +1516,9 @@ dependencies = [
[[package]]
name = "proc-macro2"
version = "1.0.78"
version = "1.0.82"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae"
checksum = "8ad3d49ab951a01fbaafe34f2ec74122942fe18a3f9814c3268f1bb72042131b"
dependencies = [
"unicode-ident",
]
@ -1495,7 +1663,7 @@ dependencies = [
"once_cell",
"percent-encoding",
"pin-project-lite",
"rustls 0.21.7",
"rustls 0.21.12",
"rustls-pemfile 1.0.3",
"serde",
"serde_json",
@ -1563,21 +1731,21 @@ checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76"
[[package]]
name = "rustls"
version = "0.21.7"
version = "0.21.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cd8d6c9f025a446bc4d18ad9632e69aec8f287aa84499ee335599fabd20c3fd8"
checksum = "3f56a14d1f48b391359b22f731fd4bd7e43c97f3c50eee276f3aa09c94784d3e"
dependencies = [
"log",
"ring 0.16.20",
"rustls-webpki 0.101.4",
"ring 0.17.8",
"rustls-webpki 0.101.7",
"sct",
]
[[package]]
name = "rustls"
version = "0.22.2"
version = "0.22.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e87c9956bd9807afa1f77e0f7594af32566e830e088a5576d27c5b6f30f49d41"
checksum = "bf4ef73721ac7bcd79b2b315da7779d8fc09718c6b3d2d1b2d94850eb8c18432"
dependencies = [
"log",
"ring 0.17.8",
@ -1627,12 +1795,12 @@ checksum = "048a63e5b3ac996d78d402940b5fa47973d2d080c6c6fffa1d0f19c4445310b7"
[[package]]
name = "rustls-webpki"
version = "0.101.4"
version = "0.101.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7d93931baf2d282fff8d3a532bbfd7653f734643161b87e3e01e59a04439bf0d"
checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765"
dependencies = [
"ring 0.16.20",
"untrusted 0.7.1",
"ring 0.17.8",
"untrusted 0.9.0",
]
[[package]]
@ -1669,9 +1837,9 @@ dependencies = [
[[package]]
name = "schemars"
version = "0.8.16"
version = "0.8.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "45a28f4c49489add4ce10783f7911893516f15afe45d015608d41faca6bc4d29"
checksum = "fc6e7ed6919cb46507fb01ff1654309219f62b4d603822501b0b80d42f6f21ef"
dependencies = [
"bigdecimal",
"bytes",
@ -1686,14 +1854,14 @@ dependencies = [
[[package]]
name = "schemars_derive"
version = "0.8.16"
version = "0.8.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c767fd6fa65d9ccf9cf026122c1b555f2ef9a4f0cea69da4d7dbc3e258d30967"
checksum = "185f2b7aa7e02d418e453790dde16890256bbd2bcd04b7dc5348811052b53f49"
dependencies = [
"proc-macro2",
"quote",
"serde_derive_internals",
"syn 1.0.109",
"syn 2.0.64",
]
[[package]]
@ -1737,9 +1905,9 @@ dependencies = [
[[package]]
name = "serde"
version = "1.0.197"
version = "1.0.202"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3fb1c873e1b9b056a4dc4c0c198b24c3ffa059243875552b2bd0933b1aee4ce2"
checksum = "226b61a0d411b2ba5ff6d7f73a476ac4f8bb900373459cd00fab8512828ba395"
dependencies = [
"serde_derive",
]
@ -1755,31 +1923,31 @@ dependencies = [
[[package]]
name = "serde_derive"
version = "1.0.197"
version = "1.0.202"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b"
checksum = "6048858004bcff69094cd972ed40a32500f153bd3be9f716b2eed2e8217c4838"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.58",
"syn 2.0.64",
]
[[package]]
name = "serde_derive_internals"
version = "0.26.0"
version = "0.29.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "85bf8229e7920a9f636479437026331ce11aa132b4dde37d121944a44d6e5f3c"
checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711"
dependencies = [
"proc-macro2",
"quote",
"syn 1.0.109",
"syn 2.0.64",
]
[[package]]
name = "serde_json"
version = "1.0.115"
version = "1.0.117"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "12dc5c46daa8e9fdf4f5e71b6cf9a53f2487da0e86e55808e2d35539666497dd"
checksum = "455182ea6142b14f93f4bc5320a2b31c1f266b66a4a5c858b013302a5d8cbfc3"
dependencies = [
"indexmap 2.2.3",
"itoa",
@ -1795,7 +1963,16 @@ checksum = "8725e1dfadb3a50f7e5ce0b1a540466f6ed3fe7a0fca2ac2b8b831d31316bd00"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.58",
"syn 2.0.64",
]
[[package]]
name = "serde_spanned"
version = "0.6.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "79e674e01f999af37c49f70a6ede167a8a60b2503e56c5599532a65baa5969a0"
dependencies = [
"serde",
]
[[package]]
@ -1807,7 +1984,7 @@ dependencies = [
"proc-macro2",
"quote",
"serde",
"syn 2.0.58",
"syn 2.0.64",
]
[[package]]
@ -1918,6 +2095,12 @@ version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e9557cb6521e8d009c51a8666f09356f4b817ba9ba0981a305bd86aee47bd35c"
[[package]]
name = "strsim"
version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
[[package]]
name = "structmeta"
version = "0.2.0"
@ -1927,7 +2110,7 @@ dependencies = [
"proc-macro2",
"quote",
"structmeta-derive 0.2.0",
"syn 2.0.58",
"syn 2.0.64",
]
[[package]]
@ -1939,7 +2122,7 @@ dependencies = [
"proc-macro2",
"quote",
"structmeta-derive 0.3.0",
"syn 2.0.58",
"syn 2.0.64",
]
[[package]]
@ -1950,7 +2133,7 @@ checksum = "a60bcaff7397072dca0017d1db428e30d5002e00b6847703e2e42005c95fbe00"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.58",
"syn 2.0.64",
]
[[package]]
@ -1961,7 +2144,7 @@ checksum = "152a0b65a590ff6c3da95cabe2353ee04e6167c896b28e3b14478c2636c922fc"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.58",
"syn 2.0.64",
]
[[package]]
@ -1979,7 +2162,7 @@ version = "0.24.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e385be0d24f186b4ce2f9982191e7101bb737312ad61c1f2f984f34bcf85d59"
dependencies = [
"heck",
"heck 0.4.1",
"proc-macro2",
"quote",
"rustversion",
@ -2005,9 +2188,9 @@ dependencies = [
[[package]]
name = "syn"
version = "2.0.58"
version = "2.0.64"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "44cfb93f38070beee36b3fef7d4f5a16f27751d94b187b666a5cc5e9b0d30687"
checksum = "7ad3dee41f36859875573074334c200d1add8e4a87bb37113ebd31d926b7b11f"
dependencies = [
"proc-macro2",
"quote",
@ -2028,7 +2211,7 @@ checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.58",
"syn 2.0.64",
]
[[package]]
@ -2069,22 +2252,22 @@ dependencies = [
[[package]]
name = "thiserror"
version = "1.0.58"
version = "1.0.61"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "03468839009160513471e86a034bb2c5c0e4baae3b43f79ffc55c4a5427b3297"
checksum = "c546c80d6be4bc6a00c0f01730c08df82eaa7a7a61f11d656526506112cc1709"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.58"
version = "1.0.61"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c61f3ba182994efc43764a46c018c347bc492c79f024e705f46567b418f6d4f7"
checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.58",
"syn 2.0.64",
]
[[package]]
@ -2157,7 +2340,7 @@ checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.58",
"syn 2.0.64",
]
[[package]]
@ -2166,7 +2349,7 @@ version = "0.24.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081"
dependencies = [
"rustls 0.21.7",
"rustls 0.21.12",
"tokio",
]
@ -2176,7 +2359,7 @@ version = "0.25.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "775e0c0f0adb3a2f22a00c4745d728b479985fc15ee7ca6a2608388c5569860f"
dependencies = [
"rustls 0.22.2",
"rustls 0.22.4",
"rustls-pki-types",
"tokio",
]
@ -2189,7 +2372,7 @@ checksum = "c83b561d025642014097b66e6c1bb422783339e0909e4429cde4749d1990bc38"
dependencies = [
"futures-util",
"log",
"rustls 0.22.2",
"rustls 0.22.4",
"rustls-native-certs",
"rustls-pki-types",
"tokio",
@ -2211,6 +2394,40 @@ dependencies = [
"tracing",
]
[[package]]
name = "toml"
version = "0.8.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a4e43f8cc456c9704c851ae29c67e17ef65d2c30017c17a9765b89c382dc8bba"
dependencies = [
"serde",
"serde_spanned",
"toml_datetime",
"toml_edit",
]
[[package]]
name = "toml_datetime"
version = "0.6.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4badfd56924ae69bcc9039335b2e017639ce3f9b001c393c1b2d1ef846ce2cbf"
dependencies = [
"serde",
]
[[package]]
name = "toml_edit"
version = "0.22.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c127785850e8c20836d49732ae6abfa47616e60bf9d9f57c43c250361a9db96c"
dependencies = [
"indexmap 2.2.3",
"serde",
"serde_spanned",
"toml_datetime",
"winnow 0.6.8",
]
[[package]]
name = "tower"
version = "0.4.13"
@ -2263,7 +2480,7 @@ checksum = "84fd902d4e0b9a4b27f2f440108dc034e1758628a9b702f8ec61ad66355422fa"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.58",
"syn 2.0.64",
]
[[package]]
@ -2292,7 +2509,7 @@ checksum = "5f4f31f56159e98206da9efd823404b79b6ef3143b4a7ab76e67b1751b25a4ab"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.58",
"syn 2.0.64",
]
[[package]]
@ -2316,8 +2533,10 @@ version = "7.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc2cae1fc5d05d47aa24b64f9a4f7cba24cdc9187a2084dd97ac57bef5eccae6"
dependencies = [
"chrono",
"thiserror",
"ts-rs-macros",
"url",
"uuid",
]
@ -2330,7 +2549,7 @@ dependencies = [
"Inflector",
"proc-macro2",
"quote",
"syn 2.0.58",
"syn 2.0.64",
"termcolor",
]
@ -2347,7 +2566,7 @@ dependencies = [
"httparse",
"log",
"rand",
"rustls 0.22.2",
"rustls 0.22.4",
"rustls-pki-types",
"sha1",
"thiserror",
@ -2397,6 +2616,12 @@ version = "1.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36"
[[package]]
name = "unicode-width"
version = "0.1.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68f5e5f3158ecfd4b8ff6fe086db7c8467a2dfdac97fe420f2b7c4aa97af66d6"
[[package]]
name = "untrusted"
version = "0.7.1"
@ -2411,9 +2636,9 @@ checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1"
[[package]]
name = "url"
version = "2.4.1"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "143b538f18257fac9cad154828a57c6bf5157e1aa604d4816b5995bf6de87ae5"
checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633"
dependencies = [
"form_urlencoded",
"idna",
@ -2427,6 +2652,12 @@ version = "0.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9"
[[package]]
name = "utf8parse"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
[[package]]
name = "uuid"
version = "1.8.0"
@ -2439,6 +2670,36 @@ dependencies = [
"wasm-bindgen",
]
[[package]]
name = "validator"
version = "0.18.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "db79c75af171630a3148bd3e6d7c4f42b6a9a014c2945bc5ed0020cbb8d9478e"
dependencies = [
"idna",
"once_cell",
"regex",
"serde",
"serde_derive",
"serde_json",
"url",
"validator_derive",
]
[[package]]
name = "validator_derive"
version = "0.18.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "55591299b7007f551ed1eb79a684af7672c19c3193fb9e0a31936987bb2438ec"
dependencies = [
"darling",
"once_cell",
"proc-macro-error",
"proc-macro2",
"quote",
"syn 2.0.64",
]
[[package]]
name = "version_check"
version = "0.9.4"
@ -2481,7 +2742,7 @@ dependencies = [
"once_cell",
"proc-macro2",
"quote",
"syn 2.0.58",
"syn 2.0.64",
"wasm-bindgen-shared",
]
@ -2515,7 +2776,7 @@ checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.58",
"syn 2.0.64",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]
@ -2736,6 +2997,15 @@ dependencies = [
"memchr",
]
[[package]]
name = "winnow"
version = "0.6.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3c52e9c97a68071b23e836c9380edae937f17b9c4667bd021973efc689f618d"
dependencies = [
"memchr",
]
[[package]]
name = "winreg"
version = "0.50.0"

View File

@ -1214,7 +1214,11 @@ impl CallExpression {
let func = memory.get(&fn_name, self.into())?;
let result = func
.call_fn(fn_args, memory.clone(), ctx.clone())
.await?
.await
.map_err(|e| {
// Add the call expression to the source ranges.
e.add_source_ranges(vec![self.into()])
})?
.ok_or_else(|| {
KclError::UndefinedValue(KclErrorDetails {
message: format!("Result of user-defined function {} is undefined", fn_name),

View File

@ -142,6 +142,25 @@ impl KclError {
new
}
pub fn add_source_ranges(&self, source_ranges: Vec<SourceRange>) -> Self {
let mut new = self.clone();
match &mut new {
KclError::Lexical(e) => e.source_ranges.extend(source_ranges),
KclError::Syntax(e) => e.source_ranges.extend(source_ranges),
KclError::Semantic(e) => e.source_ranges.extend(source_ranges),
KclError::Type(e) => e.source_ranges.extend(source_ranges),
KclError::Unimplemented(e) => e.source_ranges.extend(source_ranges),
KclError::Unexpected(e) => e.source_ranges.extend(source_ranges),
KclError::ValueAlreadyDefined(e) => e.source_ranges.extend(source_ranges),
KclError::UndefinedValue(e) => e.source_ranges.extend(source_ranges),
KclError::InvalidExpression(e) => e.source_ranges.extend(source_ranges),
KclError::Engine(e) => e.source_ranges.extend(source_ranges),
KclError::Internal(e) => e.source_ranges.extend(source_ranges),
}
new
}
}
/// This is different than to_string() in that it will serialize the Error

View File

@ -498,14 +498,13 @@ impl Backend {
for (entry, value) in self.code_map.inner().await.iter() {
let file_name = entry.replace("file://", "").to_string();
let options = zip::write::FileOptions::default().compression_method(zip::CompressionMethod::Stored);
let options = zip::write::SimpleFileOptions::default().compression_method(zip::CompressionMethod::Stored);
zip.start_file(file_name, options)?;
zip.write_all(value)?;
}
// Apply the changes you've made.
// Dropping the `ZipWriter` will have the same effect, but may silently fail
zip.finish()?;
drop(zip);
Ok(buf)
}

View File

@ -1,5 +1,6 @@
//! Types for interacting with files in projects.
#[cfg(not(target_arch = "wasm32"))]
use std::path::{Path, PathBuf};
use anyhow::Result;
@ -7,7 +8,7 @@ use parse_display::{Display, FromStr};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use crate::settings::types::{Configuration, DEFAULT_PROJECT_KCL_FILE};
use crate::settings::types::Configuration;
/// State management for the application.
#[derive(Debug, Default, Clone, Deserialize, Serialize, JsonSchema, ts_rs::TS, PartialEq)]
@ -48,7 +49,7 @@ impl ProjectState {
.map_err(|e| anyhow::anyhow!("Error loading project from path {}: {:?}", source_path.display(), e))?;
// Check if we have a main.kcl file in the project.
let project_file = source_path.join(DEFAULT_PROJECT_KCL_FILE);
let project_file = source_path.join(crate::settings::types::DEFAULT_PROJECT_KCL_FILE);
if !project_file.exists() {
// Create the default file in the project.
@ -93,6 +94,44 @@ impl ProjectState {
.await
.map_err(|e| anyhow::anyhow!("Error loading project from path {}: {:?}", source_path.display(), e))?;
// If we got a import model file, we need to check if we have a file in the project for
// this import model.
if crate::settings::utils::IMPORT_FILE_EXTENSIONS.contains(&ext) {
let import_file_name = source_path
.file_name()
.ok_or_else(|| anyhow::anyhow!("Error getting the file name of the file: {}", source_path.display()))?
.to_string_lossy()
.to_string();
// Check if we have a file in the project for this import model.
let kcl_wrapper_filename = format!("{}.kcl", import_file_name);
let kcl_wrapper_file_path = parent.join(&kcl_wrapper_filename);
if !kcl_wrapper_file_path.exists() {
// Create the file in the project.
// With the default import content.
tokio::fs::write(
&kcl_wrapper_file_path,
format!(
r#"// This file was automatically generated by the application when you
// double-clicked on the model file.
// You can edit this file to add your own content.
// But we recommend you keep the import statement as it is.
// For more information on the import statement, see the documentation at:
// https://zoo.dev/docs/kcl/import
const model = import("{}")"#,
import_file_name
)
.as_bytes(),
)
.await?;
}
return Ok(ProjectState {
project,
current_file: Some(kcl_wrapper_file_path.display().to_string()),
});
}
Ok(ProjectState {
project,
current_file: Some(source_path.display().to_string()),
@ -209,6 +248,8 @@ pub struct Project {
#[serde(default)]
#[ts(type = "number")]
pub directory_count: u64,
/// The default file to open on load.
pub default_file: String,
}
impl Project {
@ -228,12 +269,13 @@ impl Project {
}
let file = crate::settings::utils::walk_dir(&path).await?;
let metadata = std::fs::metadata(path).ok().map(|m| m.into());
let metadata = std::fs::metadata(&path).ok().map(|m| m.into());
let mut project = Self {
file,
file: file.clone(),
metadata,
kcl_file_count: 0,
directory_count: 0,
default_file: get_default_kcl_file_for_dir(path, file).await?,
};
project.populate_kcl_file_count()?;
project.populate_directory_count()?;
@ -271,6 +313,40 @@ impl Project {
}
}
/// Get the default KCL file for a directory.
/// This determines what the default file to open is.
#[cfg(not(target_arch = "wasm32"))]
#[async_recursion::async_recursion]
pub async fn get_default_kcl_file_for_dir<P>(dir: P, file: FileEntry) -> Result<String>
where
P: AsRef<Path> + Send,
{
// Make sure the dir is a directory.
if !dir.as_ref().is_dir() {
return Err(anyhow::anyhow!("Path `{}` is not a directory", dir.as_ref().display()));
}
let default_file = dir.as_ref().join(crate::settings::types::DEFAULT_PROJECT_KCL_FILE);
if !default_file.exists() {
// Find a kcl file in the directory.
if let Some(children) = file.children {
for entry in children.iter() {
if entry.name.ends_with(".kcl") {
return Ok(dir.as_ref().join(&entry.name).display().to_string());
} else if entry.children.is_some() {
// Recursively find a kcl file in the directory.
return get_default_kcl_file_for_dir(entry.path.clone(), entry.clone()).await;
}
}
}
// If we didn't find a kcl file, create one.
tokio::fs::write(&default_file, vec![]).await?;
}
Ok(default_file.display().to_string())
}
/// Information about a file or directory.
#[derive(Debug, Default, Clone, Deserialize, Serialize, JsonSchema, ts_rs::TS, PartialEq)]
#[ts(export)]
@ -550,4 +626,100 @@ mod tests {
}
);
}
#[test]
fn test_project_route_from_route_non_main_file() {
let mut configuration = crate::settings::types::Configuration::default();
configuration.settings.project.directory =
std::path::PathBuf::from("/Users/macinatormax/Documents/kittycad-modeling-projects");
let route = "/Users/macinatormax/Documents/kittycad-modeling-projects/assembly/thing.kcl";
let state = super::ProjectRoute::from_route(&configuration, route).unwrap();
assert_eq!(
state,
super::ProjectRoute {
project_name: Some("assembly".to_string()),
project_path: "/Users/macinatormax/Documents/kittycad-modeling-projects/assembly".to_string(),
current_file_name: Some("thing.kcl".to_string()),
current_file_path: Some(
"/Users/macinatormax/Documents/kittycad-modeling-projects/assembly/thing.kcl".to_string()
),
}
);
}
#[tokio::test]
async fn test_default_kcl_file_for_dir_non_exist() {
let name = format!("kittycad-modeling-projects-{}", uuid::Uuid::new_v4());
let dir = std::env::temp_dir().join(&name);
std::fs::create_dir_all(&dir).unwrap();
let file = crate::settings::utils::walk_dir(&dir).await.unwrap();
let default_file = super::get_default_kcl_file_for_dir(&dir, file).await.unwrap();
assert_eq!(default_file, dir.join("main.kcl").display().to_string());
std::fs::remove_dir_all(dir).unwrap();
}
#[tokio::test]
async fn test_default_kcl_file_for_dir_main_kcl() {
let name = format!("kittycad-modeling-projects-{}", uuid::Uuid::new_v4());
let dir = std::env::temp_dir().join(&name);
std::fs::create_dir_all(&dir).unwrap();
std::fs::write(dir.join("main.kcl"), vec![]).unwrap();
let file = crate::settings::utils::walk_dir(&dir).await.unwrap();
let default_file = super::get_default_kcl_file_for_dir(&dir, file).await.unwrap();
assert_eq!(default_file, dir.join("main.kcl").display().to_string());
std::fs::remove_dir_all(dir).unwrap();
}
#[tokio::test]
async fn test_default_kcl_file_for_dir_thing_kcl() {
let name = format!("kittycad-modeling-projects-{}", uuid::Uuid::new_v4());
let dir = std::env::temp_dir().join(&name);
std::fs::create_dir_all(&dir).unwrap();
std::fs::write(dir.join("thing.kcl"), vec![]).unwrap();
let file = crate::settings::utils::walk_dir(&dir).await.unwrap();
let default_file = super::get_default_kcl_file_for_dir(&dir, file).await.unwrap();
assert_eq!(default_file, dir.join("thing.kcl").display().to_string());
std::fs::remove_dir_all(dir).unwrap();
}
#[tokio::test]
async fn test_default_kcl_file_for_dir_nested_main_kcl() {
let name = format!("kittycad-modeling-projects-{}", uuid::Uuid::new_v4());
let dir = std::env::temp_dir().join(&name);
std::fs::create_dir_all(&dir).unwrap();
std::fs::create_dir_all(dir.join("assembly")).unwrap();
std::fs::write(dir.join("assembly").join("main.kcl"), vec![]).unwrap();
let file = crate::settings::utils::walk_dir(&dir).await.unwrap();
let default_file = super::get_default_kcl_file_for_dir(&dir, file).await.unwrap();
assert_eq!(
default_file,
dir.join("assembly").join("main.kcl").display().to_string()
);
std::fs::remove_dir_all(dir).unwrap();
}
#[tokio::test]
async fn test_default_kcl_file_for_dir_nested_thing_kcl() {
let name = format!("kittycad-modeling-projects-{}", uuid::Uuid::new_v4());
let dir = std::env::temp_dir().join(&name);
std::fs::create_dir_all(&dir).unwrap();
std::fs::create_dir_all(dir.join("assembly")).unwrap();
std::fs::write(dir.join("assembly").join("thing.kcl"), vec![]).unwrap();
let file = crate::settings::utils::walk_dir(&dir).await.unwrap();
let default_file = super::get_default_kcl_file_for_dir(&dir, file).await.unwrap();
assert_eq!(
default_file,
dir.join("assembly").join("thing.kcl").display().to_string()
);
std::fs::remove_dir_all(dir).unwrap();
}
}

View File

@ -109,6 +109,7 @@ impl Configuration {
// Because we just created it and it's empty.
children: None,
},
default_file: project_file.to_string_lossy().to_string(),
metadata: Some(tokio::fs::metadata(&project_dir).await?.into()),
kcl_file_count: 1,
directory_count: 0,
@ -130,7 +131,13 @@ impl Configuration {
continue;
}
projects.push(self.get_project_info(&e.path().display().to_string()).await?);
// Make sure the project has at least one kcl file in it.
let project = self.get_project_info(&e.path().display().to_string()).await?;
if project.kcl_file_count == 0 {
continue;
}
projects.push(project);
}
Ok(projects)
@ -150,11 +157,14 @@ impl Configuration {
return Err(anyhow::anyhow!("Project path is not a directory: {}", project_path));
}
let walked = crate::settings::utils::walk_dir(project_dir).await?;
let mut project = crate::settings::types::file::Project {
file: crate::settings::utils::walk_dir(project_dir).await?,
file: walked.clone(),
metadata: Some(tokio::fs::metadata(&project_dir).await?.into()),
kcl_file_count: 0,
directory_count: 0,
default_file: crate::settings::types::file::get_default_kcl_file_for_dir(project_dir, walked).await?,
};
// Populate the number of KCL files in the project.

View File

@ -8,14 +8,21 @@ use clap::ValueEnum;
use crate::settings::types::file::FileEntry;
lazy_static::lazy_static! {
pub static ref RELEVANT_EXTENSIONS: Vec<String> = {
let mut relevant_extensions = vec!["kcl".to_string(), "stp".to_string(), "glb".to_string(), "fbxb".to_string()];
pub static ref IMPORT_FILE_EXTENSIONS: Vec<String> = {
let mut import_file_extensions = vec!["stp".to_string(), "glb".to_string(), "fbxb".to_string()];
let named_extensions = kittycad::types::FileImportFormat::value_variants()
.iter()
.map(|x| format!("{}", x))
.collect::<Vec<String>>();
// Add all the default import formats.
relevant_extensions.extend_from_slice(&named_extensions);
import_file_extensions.extend_from_slice(&named_extensions);
import_file_extensions
};
pub static ref RELEVANT_EXTENSIONS: Vec<String> = {
let mut relevant_extensions = IMPORT_FILE_EXTENSIONS.clone();
relevant_extensions.push("kcl".to_string());
relevant_extensions
};
}
@ -26,6 +33,16 @@ pub async fn walk_dir<P>(dir: P) -> Result<FileEntry>
where
P: AsRef<Path> + Send,
{
// Make sure the path is a directory.
if !dir.as_ref().is_dir() {
return Err(anyhow::anyhow!("Path `{}` is not a directory", dir.as_ref().display()));
}
// Make sure the directory exists.
if !dir.as_ref().exists() {
return Err(anyhow::anyhow!("Directory `{}` does not exist", dir.as_ref().display()));
}
let mut entry = FileEntry {
name: dir
.as_ref()

View File

@ -129,27 +129,27 @@ pub async fn import(args: Args) -> Result<MemoryItem, KclError> {
/// not in browser.
///
/// ```no_run
/// const model = import("thing.obj")
/// const model = import("tests/inputs/cube.obj")
/// ```
///
/// ```no_run
/// const model = import("cube.obj", {type: "obj", units: "m"})
/// const model = import("tests/inputs/cube.obj", {type: "obj", units: "m"})
/// ```
///
/// ```no_run
/// const model = import("my_model.gltf")
/// const model = import("tests/inputs/cube.gltf")
/// ```
///
/// ```no_run
/// const model = import("my_model.sldprt")
/// const model = import("tests/inputs/cube.sldprt")
/// ```
///
/// ```no_run
/// const model = import("my_model.step")
/// const model = import("tests/inputs/cube.step")
/// ```
#[stdlib {
name = "import",
tags = ["norun"],
tags = [],
}]
async fn inner_import(
file_path: String,
@ -163,6 +163,9 @@ async fn inner_import(
}));
}
//print the current working directory
println!("Current working directory: {:?}", std::env::current_dir().unwrap());
// Make sure the file exists.
if !args.ctx.fs.exists(&file_path, args.source_range).await? {
return Err(KclError::Semantic(KclErrorDetails {

View File

@ -239,7 +239,7 @@ pub async fn max(args: Args) -> Result<MemoryItem, KclError> {
tags = ["math"],
}]
fn inner_max(args: Vec<f64>) -> f64 {
let mut max = std::f64::MAX;
let mut max = std::f64::MIN;
for arg in args.iter() {
if *arg > max {
max = *arg;
@ -533,3 +533,38 @@ pub async fn to_degrees(args: Args) -> Result<MemoryItem, KclError> {
fn inner_to_degrees(num: f64) -> Result<f64, KclError> {
Ok(num.to_degrees())
}
#[cfg(test)]
mod tests {
use pretty_assertions::assert_eq;
use super::*;
#[test]
fn test_inner_max() {
let nums = vec![4.0, 5.0, 6.0];
let result = inner_max(nums);
assert_eq!(result, 6.0);
}
#[test]
fn test_inner_max_with_neg() {
let nums = vec![4.0, -5.0];
let result = inner_max(nums);
assert_eq!(result, 4.0);
}
#[test]
fn test_inner_min() {
let nums = vec![4.0, 5.0, 6.0];
let result = inner_min(nums);
assert_eq!(result, 4.0);
}
#[test]
fn test_inner_min_with_neg() {
let nums = vec![4.0, -5.0];
let result = inner_min(nums);
assert_eq!(result, -5.0);
}
}

View File

@ -68,6 +68,9 @@ lazy_static! {
Box::new(crate::std::sketch::StartSketchAt),
Box::new(crate::std::sketch::StartSketchOn),
Box::new(crate::std::sketch::StartProfileAt),
Box::new(crate::std::sketch::ProfileStartX),
Box::new(crate::std::sketch::ProfileStartY),
Box::new(crate::std::sketch::ProfileStart),
Box::new(crate::std::sketch::Close),
Box::new(crate::std::sketch::Arc),
Box::new(crate::std::sketch::TangentialArc),
@ -1058,9 +1061,7 @@ layout: manual
fn_docs.push_str("\n```\n\n");
// If the function has tags, we should add them to the docs.
let mut tags = internal_fn.tags().clone();
// Remove norun tag from the list of tags.
tags.retain(|tag| tag != "norun");
let tags = internal_fn.tags().clone();
if !tags.is_empty() {
fn_docs.push_str("### Tags\n\n");
for tag in tags {
@ -1079,9 +1080,7 @@ layout: manual
// If this is not a "utilities" function,
// we should add the image to the docs.
if !internal_fn.tags().contains(&"utilities".to_string())
&& !internal_fn.tags().contains(&"norun".to_string())
{
if !internal_fn.tags().contains(&"utilities".to_string()) {
// Get the path to this specific rust file.
let dir = env!("CARGO_MANIFEST_DIR");

View File

@ -12,7 +12,7 @@ use crate::{
errors::{KclError, KclErrorDetails},
executor::{
BasePath, ExtrudeGroup, ExtrudeSurface, Face, GeoMeta, MemoryItem, Path, Plane, PlaneType, Point2d, Point3d,
Position, Rotation, SketchGroup, SketchGroupSet, SketchSurface, SourceRange,
Position, Rotation, SketchGroup, SketchGroupSet, SketchSurface, SourceRange, UserVal,
},
std::{
utils::{
@ -995,27 +995,35 @@ async fn start_sketch_on_face(
args: Args,
) -> Result<Box<Face>, KclError> {
let extrude_plane_id = match tag {
SketchOnFaceTag::String(ref s) => extrude_group
.value
.iter()
.find_map(|extrude_surface| match extrude_surface {
ExtrudeSurface::ExtrudePlane(extrude_plane) if extrude_plane.name == *s => {
Some(Ok(extrude_plane.face_id))
}
ExtrudeSurface::ExtrudeArc(extrude_arc) if extrude_arc.name == *s => {
Some(Err(KclError::Type(KclErrorDetails {
message: format!("Cannot sketch on a non-planar surface: `{}`", tag),
source_ranges: vec![args.source_range],
})))
}
ExtrudeSurface::ExtrudePlane(_) | ExtrudeSurface::ExtrudeArc(_) => None,
})
.ok_or_else(|| {
KclError::Type(KclErrorDetails {
message: format!("Expected a face with the tag `{}`", tag),
SketchOnFaceTag::String(ref s) => {
if s.is_empty() {
return Err(KclError::Type(KclErrorDetails {
message: "Expected a non-empty tag for the face to sketch on".to_string(),
source_ranges: vec![args.source_range],
}));
}
extrude_group
.value
.iter()
.find_map(|extrude_surface| match extrude_surface {
ExtrudeSurface::ExtrudePlane(extrude_plane) if extrude_plane.name == *s => {
Some(Ok(extrude_plane.face_id))
}
ExtrudeSurface::ExtrudeArc(extrude_arc) if extrude_arc.name == *s => {
Some(Err(KclError::Type(KclErrorDetails {
message: format!("Cannot sketch on a non-planar surface: `{}`", tag),
source_ranges: vec![args.source_range],
})))
}
ExtrudeSurface::ExtrudePlane(_) | ExtrudeSurface::ExtrudeArc(_) => None,
})
})??,
.ok_or_else(|| {
KclError::Type(KclErrorDetails {
message: format!("Expected a face with the tag `{}`", tag),
source_ranges: vec![args.source_range],
})
})??
}
SketchOnFaceTag::StartOrEnd(StartOrEnd::Start) => extrude_group.start_cap_id.ok_or_else(|| {
KclError::Type(KclErrorDetails {
message: "Expected a start face to sketch on".to_string(),
@ -1206,6 +1214,78 @@ pub(crate) async fn inner_start_profile_at(
Ok(Box::new(sketch_group))
}
/// Returns the X component of the sketch profile start point.
pub async fn profile_start_x(args: Args) -> Result<MemoryItem, KclError> {
let sketch_group: Box<SketchGroup> = args.get_sketch_group()?;
let x = inner_profile_start_x(sketch_group)?;
args.make_user_val_from_f64(x)
}
/// ```no_run
/// const sketch001 = startSketchOn('XY')
/// |> startProfileAt([5, 2], %)
/// |> angledLine([-26.6, 50], %)
/// |> angledLine([90, 50], %)
/// |> angledLineToX({ angle: 30, to: profileStartX(%) }, %)
/// ```
#[stdlib {
name = "profileStartX"
}]
pub(crate) fn inner_profile_start_x(sketch_group: Box<SketchGroup>) -> Result<f64, KclError> {
Ok(sketch_group.start.to[0])
}
/// Returns the Y component of the sketch profile start point.
pub async fn profile_start_y(args: Args) -> Result<MemoryItem, KclError> {
let sketch_group: Box<SketchGroup> = args.get_sketch_group()?;
let x = inner_profile_start_y(sketch_group)?;
args.make_user_val_from_f64(x)
}
/// ```no_run
/// const sketch001 = startSketchOn('XY')
/// |> startProfileAt([5, 2], %)
/// |> angledLine({ angle: -60, length: 14 }, %)
/// |> angledLineToY({ angle: 30, to: profileStartY(%) }, %)
/// ```
#[stdlib {
name = "profileStartY"
}]
pub(crate) fn inner_profile_start_y(sketch_group: Box<SketchGroup>) -> Result<f64, KclError> {
Ok(sketch_group.start.to[1])
}
/// Returns the sketch profile start point.
pub async fn profile_start(args: Args) -> Result<MemoryItem, KclError> {
let sketch_group: Box<SketchGroup> = args.get_sketch_group()?;
let point = inner_profile_start(sketch_group)?;
Ok(MemoryItem::UserVal(UserVal {
value: serde_json::to_value(point).map_err(|e| {
KclError::Type(KclErrorDetails {
message: format!("Failed to convert point to json: {}", e),
source_ranges: vec![args.source_range],
})
})?,
meta: Default::default(),
}))
}
/// ```no_run
/// const sketch001 = startSketchOn('XY')
/// |> startProfileAt([5, 2], %)
/// |> angledLine({ angle: 120, length: 50 }, %, 'seg01')
/// |> angledLine({ angle: segAng('seg01', %) + 120, length: 50 }, %)
/// |> lineTo(profileStart(%), %)
/// |> close(%)
/// |> extrude(20, %)
/// ```
#[stdlib {
name = "profileStart"
}]
pub(crate) fn inner_profile_start(sketch_group: Box<SketchGroup>) -> Result<[f64; 2], KclError> {
Ok(sketch_group.start.to)
}
/// Close the current sketch.
pub async fn close(args: Args) -> Result<MemoryItem, KclError> {
let (sketch_group, tag): (Box<SketchGroup>, Option<String>) = args.get_sketch_group_and_optional_tag()?;

View File

@ -0,0 +1,108 @@
{
"asset": {
"generator": "Khronos glTF Blender I/O v3.4.50",
"version": "2.0"
},
"scene": 0,
"scenes": [
{
"name": "Scene",
"nodes": [0]
}
],
"nodes": [
{
"mesh": 0,
"name": "Cube"
}
],
"materials": [
{
"doubleSided": true,
"name": "Material",
"pbrMetallicRoughness": {
"baseColorFactor": [
0.800000011920929, 0.800000011920929, 0.800000011920929, 1
],
"metallicFactor": 0,
"roughnessFactor": 0.5
}
}
],
"meshes": [
{
"name": "Cube",
"primitives": [
{
"attributes": {
"POSITION": 0,
"TEXCOORD_0": 1,
"NORMAL": 2
},
"indices": 3,
"material": 0
}
]
}
],
"accessors": [
{
"bufferView": 0,
"componentType": 5126,
"count": 24,
"max": [1, 1, 1],
"min": [-1, -1, -1],
"type": "VEC3"
},
{
"bufferView": 1,
"componentType": 5126,
"count": 24,
"type": "VEC2"
},
{
"bufferView": 2,
"componentType": 5126,
"count": 24,
"type": "VEC3"
},
{
"bufferView": 3,
"componentType": 5123,
"count": 36,
"type": "SCALAR"
}
],
"bufferViews": [
{
"buffer": 0,
"byteLength": 288,
"byteOffset": 0,
"target": 34962
},
{
"buffer": 0,
"byteLength": 192,
"byteOffset": 288,
"target": 34962
},
{
"buffer": 0,
"byteLength": 288,
"byteOffset": 480,
"target": 34962
},
{
"buffer": 0,
"byteLength": 72,
"byteOffset": 768,
"target": 34963
}
],
"buffers": [
{
"byteLength": 840,
"uri": "data:application/octet-stream;base64,AACAPwAAgD8AAIC/AACAPwAAgD8AAIC/AACAPwAAgD8AAIC/AACAPwAAgL8AAIC/AACAPwAAgL8AAIC/AACAPwAAgL8AAIC/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgL8AAIA/AACAPwAAgL8AAIA/AACAPwAAgL8AAIA/AACAvwAAgD8AAIC/AACAvwAAgD8AAIC/AACAvwAAgD8AAIC/AACAvwAAgL8AAIC/AACAvwAAgL8AAIC/AACAvwAAgL8AAIC/AACAvwAAgD8AAIA/AACAvwAAgD8AAIA/AACAvwAAgD8AAIA/AACAvwAAgL8AAIA/AACAvwAAgL8AAIA/AACAvwAAgL8AAIA/AAAgPwAAAD8AACA/AAAAPwAAID8AAAA/AADAPgAAAD8AAMA+AAAAPwAAwD4AAAA/AAAgPwAAgD4AACA/AACAPgAAID8AAIA+AADAPgAAgD4AAMA+AACAPgAAwD4AAIA+AAAgPwAAQD8AACA/AABAPwAAYD8AAAA/AAAAPgAAAD8AAMA+AABAPwAAwD4AAEA/AAAgPwAAAAAAACA/AACAPwAAYD8AAIA+AAAAPgAAgD4AAMA+AAAAAAAAwD4AAIA/AAAAAAAAAAAAAIC/AAAAAAAAgD8AAACAAACAPwAAAAAAAACAAAAAAAAAgL8AAACAAAAAAAAAAAAAAIC/AACAPwAAAAAAAACAAAAAAAAAAAAAAIA/AAAAAAAAgD8AAACAAACAPwAAAAAAAACAAAAAAAAAgL8AAACAAAAAAAAAAAAAAIA/AACAPwAAAAAAAACAAACAvwAAAAAAAACAAAAAAAAAAAAAAIC/AAAAAAAAgD8AAACAAAAAAAAAgL8AAACAAACAvwAAAAAAAACAAAAAAAAAAAAAAIC/AAAAAAAAAAAAAIA/AACAvwAAAAAAAACAAAAAAAAAgD8AAACAAAAAAAAAgL8AAACAAAAAAAAAAAAAAIA/AACAvwAAAAAAAACAAQAOABQAAQAUAAcACgAGABIACgASABYAFwATAAwAFwAMABAADwADAAkADwAJABUABQACAAgABQAIAAsAEQANAAAAEQAAAAQA"
}
]
}

View File

@ -0,0 +1,12 @@
# Blender MTL File: 'None'
# Material Count: 1
newmtl Material
Ns 359.999993
Ka 1.000000 1.000000 1.000000
Kd 0.800000 0.800000 0.800000
Ks 0.500000 0.500000 0.500000
Ke 0.000000 0.000000 0.000000
Ni 1.450000
d 1.000000
illum 2

View File

@ -0,0 +1,40 @@
# Blender v3.2.2 OBJ File: ''
# www.blender.org
mtllib cube.mtl
o Cube
v -1.000000 -1.000000 1.000000
v -1.000000 -1.000000 -1.000000
v -1.000000 1.000000 1.000000
v -1.000000 1.000000 -1.000000
v 1.000000 -1.000000 1.000000
v 1.000000 -1.000000 -1.000000
v 1.000000 1.000000 1.000000
v 1.000000 1.000000 -1.000000
vt 0.625000 0.500000
vt 0.875000 0.500000
vt 0.875000 0.750000
vt 0.625000 0.750000
vt 0.375000 0.750000
vt 0.625000 1.000000
vt 0.375000 1.000000
vt 0.375000 0.000000
vt 0.625000 0.000000
vt 0.625000 0.250000
vt 0.375000 0.250000
vt 0.125000 0.500000
vt 0.375000 0.500000
vt 0.125000 0.750000
vn 0.0000 0.0000 1.0000
vn 0.0000 1.0000 0.0000
vn 1.0000 0.0000 0.0000
vn 0.0000 0.0000 -1.0000
vn -1.0000 0.0000 0.0000
vn 0.0000 -1.0000 0.0000
usemtl Material
s off
f 1/1/1 5/2/1 7/3/1 3/4/1
f 4/5/2 3/4/2 7/6/2 8/7/2
f 8/8/3 7/9/3 5/10/3 6/11/3
f 6/12/4 2/13/4 4/5/4 8/14/4
f 2/13/5 1/1/5 3/4/5 4/5/5
f 6/11/6 5/10/6 1/1/6 2/13/6

Binary file not shown.

View File

@ -0,0 +1,254 @@
ISO-10303-21;
HEADER;
FILE_DESCRIPTION (( 'STEP AP203' ),
'1' );
FILE_NAME ('Cube_1m.STEP',
'2023-07-31T15:10:19',
( '' ),
( '' ),
'SwSTEP 2.0',
'SolidWorks 2023',
'' );
FILE_SCHEMA (( 'CONFIG_CONTROL_DESIGN' ));
ENDSEC;
DATA;
#1 = AXIS2_PLACEMENT_3D ( 'NONE', #133, #116, #72 ) ;
#2 =( LENGTH_UNIT ( ) NAMED_UNIT ( * ) SI_UNIT ( .MILLI., .METRE. ) );
#3 = FACE_OUTER_BOUND ( 'NONE', #99, .T. ) ;
#4 = CARTESIAN_POINT ( 'NONE', ( 500.0000000000000000, 1000.000000000000000, -500.0000000000000000 ) ) ;
#5 = VECTOR ( 'NONE', #233, 1000.000000000000000 ) ;
#6 = ORIENTED_EDGE ( 'NONE', *, *, #15, .T. ) ;
#7 = DIRECTION ( 'NONE', ( -0.000000000000000000, -1.000000000000000000, -0.000000000000000000 ) ) ;
#8 = ORIENTED_EDGE ( 'NONE', *, *, #75, .T. ) ;
#9 = VECTOR ( 'NONE', #205, 1000.000000000000000 ) ;
#10 = LOCAL_TIME ( 11, 10, 19.00000000000000000, #88 ) ;
#11 = PLANE ( 'NONE', #220 ) ;
#12 = CARTESIAN_POINT ( 'NONE', ( -500.0000000000000000, 1000.000000000000000, 500.0000000000000000 ) ) ;
#13 = DIRECTION ( 'NONE', ( -0.000000000000000000, -1.000000000000000000, -0.000000000000000000 ) ) ;
#14 = CARTESIAN_POINT ( 'NONE', ( 500.0000000000000000, 1000.000000000000000, -500.0000000000000000 ) ) ;
#15 = EDGE_CURVE ( 'NONE', #40, #58, #139, .T. ) ;
#16 = CALENDAR_DATE ( 2023, 31, 7 ) ;
#17 = AXIS2_PLACEMENT_3D ( 'NONE', #175, #73, #126 ) ;
#18 = PERSON_AND_ORGANIZATION ( #134, #228 ) ;
#19 = PLANE ( 'NONE', #145 ) ;
#20 = PERSON_AND_ORGANIZATION ( #134, #228 ) ;
#21 =( GEOMETRIC_REPRESENTATION_CONTEXT ( 3 ) GLOBAL_UNCERTAINTY_ASSIGNED_CONTEXT ( ( #152 ) ) GLOBAL_UNIT_ASSIGNED_CONTEXT ( ( #2, #168, #174 ) ) REPRESENTATION_CONTEXT ( 'NONE', 'WORKASPACE' ) );
#22 = FACE_OUTER_BOUND ( 'NONE', #137, .T. ) ;
#23 = PRODUCT_DEFINITION ( 'UNKNOWN', '', #51, #86 ) ;
#24 = DIRECTION ( 'NONE', ( 0.000000000000000000, 0.000000000000000000, -1.000000000000000000 ) ) ;
#25 = APPLICATION_PROTOCOL_DEFINITION ( 'international standard', 'config_control_design', 1994, #59 ) ;
#26 = PLANE ( 'NONE', #17 ) ;
#27 = EDGE_LOOP ( 'NONE', ( #141, #8, #121, #123 ) ) ;
#28 = CARTESIAN_POINT ( 'NONE', ( 500.0000000000000000, 1000.000000000000000, -500.0000000000000000 ) ) ;
#29 = CARTESIAN_POINT ( 'NONE', ( -500.0000000000000000, 0.000000000000000000, -500.0000000000000000 ) ) ;
#30 = FACE_OUTER_BOUND ( 'NONE', #78, .T. ) ;
#31 = LINE ( 'NONE', #183, #179 ) ;
#32 = PLANE ( 'NONE', #173 ) ;
#33 = VECTOR ( 'NONE', #44, 1000.000000000000000 ) ;
#34 = DATE_AND_TIME ( #16, #224 ) ;
#35 = ADVANCED_FACE ( 'NONE', ( #198 ), #26, .T. ) ;
#36 = CALENDAR_DATE ( 2023, 31, 7 ) ;
#37 = VECTOR ( 'NONE', #61, 1000.000000000000000 ) ;
#38 = CC_DESIGN_APPROVAL ( #210, ( #51 ) ) ;
#39 = CARTESIAN_POINT ( 'NONE', ( -500.0000000000000000, 1000.000000000000000, 500.0000000000000000 ) ) ;
#40 = VERTEX_POINT ( 'NONE', #98 ) ;
#41 = ORIENTED_EDGE ( 'NONE', *, *, #119, .F. ) ;
#42 = ORIENTED_EDGE ( 'NONE', *, *, #69, .T. ) ;
#43 = CC_DESIGN_PERSON_AND_ORGANIZATION_ASSIGNMENT ( #167, #188, ( #180 ) ) ;
#44 = DIRECTION ( 'NONE', ( 0.000000000000000000, 0.000000000000000000, 1.000000000000000000 ) ) ;
#45 = CARTESIAN_POINT ( 'NONE', ( -500.0000000000000000, 0.000000000000000000, 500.0000000000000000 ) ) ;
#46 = APPLICATION_CONTEXT ( 'configuration controlled 3d designs of mechanical parts and assemblies' ) ;
#47 = LINE ( 'NONE', #203, #101 ) ;
#48 = ORIENTED_EDGE ( 'NONE', *, *, #144, .F. ) ;
#49 = CARTESIAN_POINT ( 'NONE', ( 500.0000000000000000, 0.000000000000000000, 500.0000000000000000 ) ) ;
#50 = CARTESIAN_POINT ( 'NONE', ( 500.0000000000000000, 1000.000000000000000, 500.0000000000000000 ) ) ;
#51 = PRODUCT_DEFINITION_FORMATION_WITH_SPECIFIED_SOURCE ( 'ANY', '', #68, .NOT_KNOWN. ) ;
#52 = DATE_AND_TIME ( #110, #186 ) ;
#53 = CC_DESIGN_PERSON_AND_ORGANIZATION_ASSIGNMENT ( #192, #92, ( #68 ) ) ;
#54 = DATE_AND_TIME ( #117, #90 ) ;
#55 = VECTOR ( 'NONE', #209, 1000.000000000000000 ) ;
#56 = PLANE ( 'NONE', #129 ) ;
#57 = VERTEX_POINT ( 'NONE', #127 ) ;
#58 = VERTEX_POINT ( 'NONE', #49 ) ;
#59 = APPLICATION_CONTEXT ( 'configuration controlled 3d designs of mechanical parts and assemblies' ) ;
#60 = EDGE_CURVE ( 'NONE', #57, #80, #185, .T. ) ;
#61 = DIRECTION ( 'NONE', ( -0.000000000000000000, -1.000000000000000000, -0.000000000000000000 ) ) ;
#62 = ORIENTED_EDGE ( 'NONE', *, *, #211, .F. ) ;
#63 = LINE ( 'NONE', #232, #236 ) ;
#64 = ORIENTED_EDGE ( 'NONE', *, *, #96, .F. ) ;
#65 = CARTESIAN_POINT ( 'NONE', ( 500.0000000000000000, 0.000000000000000000, -500.0000000000000000 ) ) ;
#66 = PLANE ( 'NONE', #70 ) ;
#67 = CLOSED_SHELL ( 'NONE', ( #226, #164, #169, #234, #35, #219 ) ) ;
#68 = PRODUCT ( 'Cube_1m', 'Cube_1m', '', ( #155 ) ) ;
#69 = EDGE_CURVE ( 'NONE', #80, #120, #31, .T. ) ;
#70 = AXIS2_PLACEMENT_3D ( 'NONE', #138, #105, #217 ) ;
#71 = DATE_AND_TIME ( #172, #194 ) ;
#72 = DIRECTION ( 'NONE', ( 1.000000000000000000, 0.000000000000000000, 0.000000000000000000 ) ) ;
#73 = DIRECTION ( 'NONE', ( 0.000000000000000000, 1.000000000000000000, 0.000000000000000000 ) ) ;
#74 = VERTEX_POINT ( 'NONE', #124 ) ;
#75 = EDGE_CURVE ( 'NONE', #80, #135, #81, .T. ) ;
#76 = LINE ( 'NONE', #50, #170 ) ;
#77 = CC_DESIGN_APPROVAL ( #166, ( #23 ) ) ;
#78 = EDGE_LOOP ( 'NONE', ( #97, #199, #230, #42 ) ) ;
#79 = CARTESIAN_POINT ( 'NONE', ( 0.000000000000000000, 0.000000000000000000, 0.000000000000000000 ) ) ;
#80 = VERTEX_POINT ( 'NONE', #28 ) ;
#81 = LINE ( 'NONE', #103, #85 ) ;
#82 = DIRECTION ( 'NONE', ( -1.000000000000000000, -0.000000000000000000, -0.000000000000000000 ) ) ;
#83 = CC_DESIGN_PERSON_AND_ORGANIZATION_ASSIGNMENT ( #93, #115, ( #23 ) ) ;
#84 = APPROVAL_DATE_TIME ( #112, #210 ) ;
#85 = VECTOR ( 'NONE', #82, 1000.000000000000000 ) ;
#86 = DESIGN_CONTEXT ( 'detailed design', #59, 'design' ) ;
#87 = APPROVAL_STATUS ( 'not_yet_approved' ) ;
#88 = COORDINATED_UNIVERSAL_TIME_OFFSET ( 5, 0, .BEHIND. ) ;
#89 = COORDINATED_UNIVERSAL_TIME_OFFSET ( 5, 0, .BEHIND. ) ;
#90 = LOCAL_TIME ( 11, 10, 19.00000000000000000, #154 ) ;
#91 = PERSON_AND_ORGANIZATION ( #134, #228 ) ;
#92 = PERSON_AND_ORGANIZATION_ROLE ( 'design_owner' ) ;
#93 = PERSON_AND_ORGANIZATION ( #134, #228 ) ;
#94 = APPLICATION_PROTOCOL_DEFINITION ( 'international standard', 'config_control_design', 1994, #46 ) ;
#95 = APPROVAL ( #87, 'UNSPECIFIED' ) ;
#96 = EDGE_CURVE ( 'NONE', #74, #57, #162, .T. ) ;
#97 = ORIENTED_EDGE ( 'NONE', *, *, #144, .T. ) ;
#98 = CARTESIAN_POINT ( 'NONE', ( -500.0000000000000000, 0.000000000000000000, 500.0000000000000000 ) ) ;
#99 = EDGE_LOOP ( 'NONE', ( #6, #125, #64, #118 ) ) ;
#100 = DIRECTION ( 'NONE', ( 1.000000000000000000, 0.000000000000000000, -0.000000000000000000 ) ) ;
#101 = VECTOR ( 'NONE', #204, 1000.000000000000000 ) ;
#102 = EDGE_LOOP ( 'NONE', ( #178, #202, #41, #48 ) ) ;
#103 = CARTESIAN_POINT ( 'NONE', ( -500.0000000000000000, 1000.000000000000000, -500.0000000000000000 ) ) ;
#104 = CARTESIAN_POINT ( 'NONE', ( -500.0000000000000000, 1000.000000000000000, -500.0000000000000000 ) ) ;
#105 = DIRECTION ( 'NONE', ( 1.000000000000000000, 0.000000000000000000, 0.000000000000000000 ) ) ;
#106 = PRODUCT_RELATED_PRODUCT_CATEGORY ( 'detail', '', ( #68 ) ) ;
#107 = EDGE_CURVE ( 'NONE', #135, #215, #140, .T. ) ;
#108 = CC_DESIGN_APPROVAL ( #95, ( #180 ) ) ;
#109 = SHAPE_DEFINITION_REPRESENTATION ( #146, #222 ) ;
#110 = CALENDAR_DATE ( 2023, 31, 7 ) ;
#111 = PERSON_AND_ORGANIZATION ( #134, #228 ) ;
#112 = DATE_AND_TIME ( #36, #10 ) ;
#113 = APPROVAL_PERSON_ORGANIZATION ( #18, #95, #130 ) ;
#114 = APPROVAL_PERSON_ORGANIZATION ( #111, #210, #132 ) ;
#115 = PERSON_AND_ORGANIZATION_ROLE ( 'creator' ) ;
#116 = DIRECTION ( 'NONE', ( 0.000000000000000000, 0.000000000000000000, 1.000000000000000000 ) ) ;
#117 = CALENDAR_DATE ( 2023, 31, 7 ) ;
#118 = ORIENTED_EDGE ( 'NONE', *, *, #128, .T. ) ;
#119 = EDGE_CURVE ( 'NONE', #215, #40, #63, .T. ) ;
#120 = VERTEX_POINT ( 'NONE', #159 ) ;
#121 = ORIENTED_EDGE ( 'NONE', *, *, #211, .T. ) ;
#122 = DIRECTION ( 'NONE', ( 0.000000000000000000, 0.000000000000000000, 1.000000000000000000 ) ) ;
#123 = ORIENTED_EDGE ( 'NONE', *, *, #96, .T. ) ;
#124 = CARTESIAN_POINT ( 'NONE', ( -500.0000000000000000, 1000.000000000000000, 500.0000000000000000 ) ) ;
#125 = ORIENTED_EDGE ( 'NONE', *, *, #189, .F. ) ;
#126 = DIRECTION ( 'NONE', ( 0.000000000000000000, -0.000000000000000000, 1.000000000000000000 ) ) ;
#127 = CARTESIAN_POINT ( 'NONE', ( 500.0000000000000000, 1000.000000000000000, 500.0000000000000000 ) ) ;
#128 = EDGE_CURVE ( 'NONE', #74, #40, #142, .T. ) ;
#129 = AXIS2_PLACEMENT_3D ( 'NONE', #4, #195, #171 ) ;
#130 = APPROVAL_ROLE ( '' ) ;
#131 = APPROVAL_DATE_TIME ( #71, #166 ) ;
#132 = APPROVAL_ROLE ( '' ) ;
#133 = CARTESIAN_POINT ( 'NONE', ( 0.000000000000000000, 0.000000000000000000, 0.000000000000000000 ) ) ;
#134 = PERSON ( 'UNSPECIFIED', 'UNSPECIFIED', 'UNSPECIFIED', ('UNSPECIFIED'), ('UNSPECIFIED'), ('UNSPECIFIED') ) ;
#135 = VERTEX_POINT ( 'NONE', #214 ) ;
#136 = APPROVAL_PERSON_ORGANIZATION ( #91, #166, #151 ) ;
#137 = EDGE_LOOP ( 'NONE', ( #158, #176, #157, #212 ) ) ;
#138 = CARTESIAN_POINT ( 'NONE', ( -500.0000000000000000, 1000.000000000000000, -500.0000000000000000 ) ) ;
#139 = LINE ( 'NONE', #45, #55 ) ;
#140 = LINE ( 'NONE', #177, #37 ) ;
#141 = ORIENTED_EDGE ( 'NONE', *, *, #60, .T. ) ;
#142 = LINE ( 'NONE', #231, #193 ) ;
#143 = FACE_OUTER_BOUND ( 'NONE', #227, .T. ) ;
#144 = EDGE_CURVE ( 'NONE', #120, #215, #47, .T. ) ;
#145 = AXIS2_PLACEMENT_3D ( 'NONE', #39, #24, #207 ) ;
#146 = PRODUCT_DEFINITION_SHAPE ( 'NONE', 'NONE', #23 ) ;
#147 = PERSON_AND_ORGANIZATION_ROLE ( 'creator' ) ;
#148 = CC_DESIGN_PERSON_AND_ORGANIZATION_ASSIGNMENT ( #165, #147, ( #51 ) ) ;
#149 = DATE_TIME_ROLE ( 'classification_date' ) ;
#150 = EDGE_CURVE ( 'NONE', #58, #120, #163, .T. ) ;
#151 = APPROVAL_ROLE ( '' ) ;
#152 = UNCERTAINTY_MEASURE_WITH_UNIT (LENGTH_MEASURE( 1.000000000000000082E-05 ), #2, 'distance_accuracy_value', 'NONE');
#153 = CC_DESIGN_SECURITY_CLASSIFICATION ( #180, ( #51 ) ) ;
#154 = COORDINATED_UNIVERSAL_TIME_OFFSET ( 5, 0, .BEHIND. ) ;
#155 = MECHANICAL_CONTEXT ( 'NONE', #46, 'mechanical' ) ;
#156 = VECTOR ( 'NONE', #229, 1000.000000000000000 ) ;
#157 = ORIENTED_EDGE ( 'NONE', *, *, #60, .F. ) ;
#158 = ORIENTED_EDGE ( 'NONE', *, *, #150, .T. ) ;
#159 = CARTESIAN_POINT ( 'NONE', ( 500.0000000000000000, 0.000000000000000000, -500.0000000000000000 ) ) ;
#160 = ORIENTED_EDGE ( 'NONE', *, *, #119, .T. ) ;
#161 = DIRECTION ( 'NONE', ( 0.000000000000000000, 1.000000000000000000, 0.000000000000000000 ) ) ;
#162 = LINE ( 'NONE', #12, #9 ) ;
#163 = LINE ( 'NONE', #65, #5 ) ;
#164 = ADVANCED_FACE ( 'NONE', ( #30 ), #32, .F. ) ;
#165 = PERSON_AND_ORGANIZATION ( #134, #228 ) ;
#166 = APPROVAL ( #196, 'UNSPECIFIED' ) ;
#167 = PERSON_AND_ORGANIZATION ( #134, #228 ) ;
#168 =( NAMED_UNIT ( * ) PLANE_ANGLE_UNIT ( ) SI_UNIT ( $, .RADIAN. ) );
#169 = ADVANCED_FACE ( 'NONE', ( #143 ), #66, .F. ) ;
#170 = VECTOR ( 'NONE', #7, 1000.000000000000000 ) ;
#171 = DIRECTION ( 'NONE', ( 0.000000000000000000, 0.000000000000000000, 1.000000000000000000 ) ) ;
#172 = CALENDAR_DATE ( 2023, 31, 7 ) ;
#173 = AXIS2_PLACEMENT_3D ( 'NONE', #104, #184, #100 ) ;
#174 =( NAMED_UNIT ( * ) SI_UNIT ( $, .STERADIAN. ) SOLID_ANGLE_UNIT ( ) );
#175 = CARTESIAN_POINT ( 'NONE', ( 0.000000000000000000, 1000.000000000000000, 0.000000000000000000 ) ) ;
#176 = ORIENTED_EDGE ( 'NONE', *, *, #69, .F. ) ;
#177 = CARTESIAN_POINT ( 'NONE', ( -500.0000000000000000, 1000.000000000000000, -500.0000000000000000 ) ) ;
#178 = ORIENTED_EDGE ( 'NONE', *, *, #150, .F. ) ;
#179 = VECTOR ( 'NONE', #13, 1000.000000000000000 ) ;
#180 = SECURITY_CLASSIFICATION ( '', '', #223 ) ;
#181 = FACE_OUTER_BOUND ( 'NONE', #102, .T. ) ;
#182 = ORIENTED_EDGE ( 'NONE', *, *, #107, .T. ) ;
#183 = CARTESIAN_POINT ( 'NONE', ( 500.0000000000000000, 1000.000000000000000, -500.0000000000000000 ) ) ;
#184 = DIRECTION ( 'NONE', ( 0.000000000000000000, 0.000000000000000000, 1.000000000000000000 ) ) ;
#185 = LINE ( 'NONE', #14, #156 ) ;
#186 = LOCAL_TIME ( 11, 10, 19.00000000000000000, #221 ) ;
#187 = CC_DESIGN_DATE_AND_TIME_ASSIGNMENT ( #54, #197, ( #23 ) ) ;
#188 = PERSON_AND_ORGANIZATION_ROLE ( 'classification_officer' ) ;
#189 = EDGE_CURVE ( 'NONE', #57, #58, #76, .T. ) ;
#190 = APPROVAL_STATUS ( 'not_yet_approved' ) ;
#191 = PERSON_AND_ORGANIZATION_ROLE ( 'design_supplier' ) ;
#192 = PERSON_AND_ORGANIZATION ( #134, #228 ) ;
#193 = VECTOR ( 'NONE', #216, 1000.000000000000000 ) ;
#194 = LOCAL_TIME ( 11, 10, 19.00000000000000000, #208 ) ;
#195 = DIRECTION ( 'NONE', ( -1.000000000000000000, 0.000000000000000000, 0.000000000000000000 ) ) ;
#196 = APPROVAL_STATUS ( 'not_yet_approved' ) ;
#197 = DATE_TIME_ROLE ( 'creation_date' ) ;
#198 = FACE_OUTER_BOUND ( 'NONE', #27, .T. ) ;
#199 = ORIENTED_EDGE ( 'NONE', *, *, #107, .F. ) ;
#200 = ORIENTED_EDGE ( 'NONE', *, *, #128, .F. ) ;
#201 = CARTESIAN_POINT ( 'NONE', ( -500.0000000000000000, 1000.000000000000000, -500.0000000000000000 ) ) ;
#202 = ORIENTED_EDGE ( 'NONE', *, *, #15, .F. ) ;
#203 = CARTESIAN_POINT ( 'NONE', ( -500.0000000000000000, 0.000000000000000000, -500.0000000000000000 ) ) ;
#204 = DIRECTION ( 'NONE', ( -1.000000000000000000, -0.000000000000000000, -0.000000000000000000 ) ) ;
#205 = DIRECTION ( 'NONE', ( 1.000000000000000000, 0.000000000000000000, 0.000000000000000000 ) ) ;
#206 = DIRECTION ( 'NONE', ( 0.000000000000000000, -0.000000000000000000, 1.000000000000000000 ) ) ;
#207 = DIRECTION ( 'NONE', ( -1.000000000000000000, 0.000000000000000000, -0.000000000000000000 ) ) ;
#208 = COORDINATED_UNIVERSAL_TIME_OFFSET ( 5, 0, .BEHIND. ) ;
#209 = DIRECTION ( 'NONE', ( 1.000000000000000000, 0.000000000000000000, 0.000000000000000000 ) ) ;
#210 = APPROVAL ( #190, 'UNSPECIFIED' ) ;
#211 = EDGE_CURVE ( 'NONE', #135, #74, #213, .T. ) ;
#212 = ORIENTED_EDGE ( 'NONE', *, *, #189, .T. ) ;
#213 = LINE ( 'NONE', #201, #33 ) ;
#214 = CARTESIAN_POINT ( 'NONE', ( -500.0000000000000000, 1000.000000000000000, -500.0000000000000000 ) ) ;
#215 = VERTEX_POINT ( 'NONE', #29 ) ;
#216 = DIRECTION ( 'NONE', ( -0.000000000000000000, -1.000000000000000000, -0.000000000000000000 ) ) ;
#217 = DIRECTION ( 'NONE', ( 0.000000000000000000, 0.000000000000000000, -1.000000000000000000 ) ) ;
#218 = CC_DESIGN_DATE_AND_TIME_ASSIGNMENT ( #52, #149, ( #180 ) ) ;
#219 = ADVANCED_FACE ( 'NONE', ( #181 ), #11, .F. ) ;
#220 = AXIS2_PLACEMENT_3D ( 'NONE', #79, #161, #206 ) ;
#221 = COORDINATED_UNIVERSAL_TIME_OFFSET ( 5, 0, .BEHIND. ) ;
#222 = ADVANCED_BREP_SHAPE_REPRESENTATION ( 'Cube_1m', ( #235, #1 ), #21 ) ;
#223 = SECURITY_CLASSIFICATION_LEVEL ( 'unclassified' ) ;
#224 = LOCAL_TIME ( 11, 10, 19.00000000000000000, #89 ) ;
#225 = CC_DESIGN_PERSON_AND_ORGANIZATION_ASSIGNMENT ( #20, #191, ( #51 ) ) ;
#226 = ADVANCED_FACE ( 'NONE', ( #22 ), #56, .F. ) ;
#227 = EDGE_LOOP ( 'NONE', ( #160, #200, #62, #182 ) ) ;
#228 = ORGANIZATION ( 'UNSPECIFIED', 'UNSPECIFIED', '' ) ;
#229 = DIRECTION ( 'NONE', ( -0.000000000000000000, -0.000000000000000000, -1.000000000000000000 ) ) ;
#230 = ORIENTED_EDGE ( 'NONE', *, *, #75, .F. ) ;
#231 = CARTESIAN_POINT ( 'NONE', ( -500.0000000000000000, 1000.000000000000000, 500.0000000000000000 ) ) ;
#232 = CARTESIAN_POINT ( 'NONE', ( -500.0000000000000000, 0.000000000000000000, -500.0000000000000000 ) ) ;
#233 = DIRECTION ( 'NONE', ( -0.000000000000000000, -0.000000000000000000, -1.000000000000000000 ) ) ;
#234 = ADVANCED_FACE ( 'NONE', ( #3 ), #19, .F. ) ;
#235 = MANIFOLD_SOLID_BREP ( 'Boss-Extrude1', #67 ) ;
#236 = VECTOR ( 'NONE', #122, 1000.000000000000000 ) ;
#237 = APPROVAL_DATE_TIME ( #34, #95 ) ;
ENDSEC;
END-ISO-10303-21;

Binary file not shown.

Before

Width:  |  Height:  |  Size: 83 KiB

After

Width:  |  Height:  |  Size: 82 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 214 KiB

After

Width:  |  Height:  |  Size: 214 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 125 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 131 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 131 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 119 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 120 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 120 KiB

After

Width:  |  Height:  |  Size: 120 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 106 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 109 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 95 KiB

View File

@ -2057,3 +2057,58 @@ const bracket = startSketchOn('XY')
r#"engine: KclErrorDetails { source_ranges: [SourceRange([1443, 1443])], message: "Modeling command failed: Some([ApiError { error_code: BadRequest, message: \"Fillet failed\" }])" }"#
);
}
#[tokio::test(flavor = "multi_thread")]
async fn serial_test_error_empty_start_sketch_on_string() {
let code = r#"const part001 = startSketchOn('-XZ')
|> startProfileAt([75.75, 184.25], %)
|> line([190.03, -118.13], %)
|> line([-33.38, -202.86], %)
|> line([-315.86, -64.2], %)
|> tangentialArcTo([-147.66, 121.34], %)
|> close(%)
|> extrude(100, %)
const secondSketch = startSketchOn(part001, '')
|> circle([-20, 50], 40, %)
|> extrude(20, %)
"#;
let result = execute_and_snapshot(code, kcl_lib::settings::types::UnitLength::Mm).await;
assert!(result.is_err());
assert_eq!(
result.err().unwrap().to_string(),
r#"type: KclErrorDetails { source_ranges: [SourceRange([272, 298])], message: "Expected a non-empty tag for the face to sketch on" }"#
);
}
#[tokio::test(flavor = "multi_thread")]
async fn serial_test_error_user_function_wrong_args() {
let code = r#"const length = .750
const width = 0.500
const height = 0.500
const dia = 4
fn squareHole = (l, w) => {
const squareHoleSketch = startSketchOn('XY')
|> startProfileAt([-width / 2, -length / 2], %)
|> lineTo([width / 2, -length / 2], %)
|> lineTo([width / 2, length / 2], %)
|> lineTo([-width / 2, length / 2], %)
|> close(%)
return squareHoleSketch
}
const extrusion = startSketchOn('XY')
|> circle([0, 0], dia/2, %)
|> hole(squareHole(length, width, height), %)
|> extrude(height, %)
"#;
let result = execute_and_snapshot(code, kcl_lib::settings::types::UnitLength::Mm).await;
assert!(result.is_err());
assert_eq!(
result.err().unwrap().to_string(),
r#"semantic: KclErrorDetails { source_ranges: [SourceRange([92, 364]), SourceRange([444, 477])], message: "Expected 2 arguments, got 3" }"#
);
}

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