Compare commits

...

19 Commits

Author SHA1 Message Date
099c48cd63 better docs on solids and sketches (#5428)
* updates

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

* fix docs;

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

* updates

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

* updates

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

* updates

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

* updates

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

* updates

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

* updates

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

* updates

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

* updates

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

* parens

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

* parens

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

---------

Signed-off-by: Jess Frazelle <github@jessfraz.com>
2025-02-20 03:48:27 +00:00
f35cd3ef26 Generalise and simplify offsetPlane (#5429)
* Generalise and simplify offsetPlane

Signed-off-by: Nick Cameron <nrc@ncameron.org>

* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)

* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)

---------

Signed-off-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2025-02-20 15:55:29 +13:00
695c432d1e CM KCL: highlight property names, labeled args and annotations (#5386)
* CM KCL: highlight property names in objects

* CM KCL: highlight arg labels in fn calls

* CM KCL: highlight annotations

* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)

---------

Co-authored-by: Matt Mundell <matt@mundell.me>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2025-02-20 01:17:18 +00:00
2835665a6a KCL: Script for releasing (#5422) 2025-02-19 16:56:51 -06:00
45707d2974 More numeric types baby steps (#5388)
* Add units to Paths

Signed-off-by: Nick Cameron <nrc@ncameron.org>

* Add some NumericType combination functions

Signed-off-by: Nick Cameron <nrc@ncameron.org>

* Fix docs/json/snippets generation from schemas

Signed-off-by: Nick Cameron <nrc@ncameron.org>

---------

Signed-off-by: Nick Cameron <nrc@ncameron.org>
2025-02-20 10:12:37 +13:00
b2e1d21d45 Add Delete to right-click context menu of feature tree operations (#5302)
* Revert "Revert multi-profile (#4812)"

This reverts commit efe8089b08.

* fix poor 1000ms wait UX

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

* trigger CI

* Add Rust side artifacts for startSketchOn face or plane (#4834)

* Add Rust side artifacts for startSketchOn face or plane

* move ast digging

---------

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

* lint

* lint

* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-macos-8-cores)

* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)

* A snapshot a day keeps the bugs away! 📷🐛 (OS: windows-16-cores)

* trigger CI

* chore: disabled file watcher which prevents faster file write (#4835)

* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)

* partial fixes

* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)

* Trigger CI

* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)

* Trigger CI

* Fix up all the tests

* Fix partial execution

* wip

* WIP

* wip

* rust changes to make three point confrom to same as others since we're not ready with name params yet

* most of the fix for 3 point circle

* get overlays working for circle three point

* fmt

* fix types

* cargo fmt

* add face codef ref for walls and caps

* fix sketch on face after updates to rust side artifact graph

* some things needed for multi-profile tests

* bad attempts at fixing rust

* more

* more

* fix rust

* more rust fixes

* overlay fix

* remove duplicate test

* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)

* lint and typing

* maybe fix a unit test

* small thing

* WIP: Add Delete right click menu item to Feature Tree
Copying code around
Fixes #5090

* I don't know why it works

* WIP

* fix circ dep

* fix unit test

* fix some tests

* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)

* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)

* Working deletion machine loo

* Working helix deletion

* Extend deletion to more things

* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)

* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)

* fix sweep point-and-click test

* fix more tests and add a fix me

* fix more tests

* fix electron specific test

* tsc

* more test tweaks

* update docs

* commint snaps?

* is clippy happy now?

* clippy again

* test works now without me changing anything big-fixed-itself

* small bug

* make three point have cross hair to make it consistent with othe rtools

* fix up state diagram

* fmt

* add draft point for first click of three point circ

* 1 test for three point circle

* 2 test for three point circle

* clean up

* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)

* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)

* remove bad doc comment

* remove test skip

* remove onboarding test changes

* Update src/lang/modifyAst.ts

Co-authored-by: Jonathan Tran <jonnytran@gmail.com>

* Update output from simulation tests

* Fix to use correct source ranges

This also reduces cloning.

* Change back to skipping face cap none and both

* Update output after changing back to skipping none and both

* Fix clippy warning

* fix profile start snap bug

* WIP: migrate to actor

* add path ids to cap

* fix going into edit sketch

* make other startSketchOn's work

* fix snapshot test

* explain function name

* Update src/lib/rectangleTool.ts

Co-authored-by: Frank Noirot <frank@zoo.dev>

* rename error

* remove file tree from diff

* Update src/clientSideScene/segments.ts

Co-authored-by: Frank Noirot <frank@zoo.dev>

* nit

* Continue actor migration

* Prevent double write to KCL code on revolve

* Clean up

* Update output after adding cap-to-path graph edge

* Clean up

* Update machine diag

* Update context menu hotkey class

* Fix edit/select sketch-on-cap via feature tree

* clean up for face codeRef

* fix changing tools part way through circle/rect tools

* fix delete of circle profile

* fix close profiles

* fix closing profile bug (tangentArcTo being ignored)

* remove stale comment

* Delete paths associated with sketch when the sketch plane is deleted

* Add support for deleting sketches on caps (not walls)

* get delet working for walls

* make delet of extrusions work for multi profile

* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)

* Delete the sketch statement too on the cap and wall cases

* Don't write to file in `split-sketch-pipe-if-needed` unless necessary

* Don't wait for file write to complete within `updateEditorWithAstAndWriteToFile`
It is already debounced internally. If we await it, we will have to wait for a debounced timeout

* Fix bad conflict resolution

* Fix a few things post merge

* Add guard back, fixing tests

* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)

* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)

* Add e2e test

* Working tests on ubuntu

* Another one

* Update src/machines/featureTreeMachine.ts

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

* Fix sketch test
@Irev-Dev's suggestion

---------

Co-authored-by: Kurt Hutten Irev-Dev <k.hutten@protonmail.ch>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Jonathan Tran <jonnytran@gmail.com>
Co-authored-by: Kevin Nadro <nadr0@users.noreply.github.com>
Co-authored-by: 49lf <ircsurfer33@gmail.com>
Co-authored-by: Frank Noirot <frank@zoo.dev>
Co-authored-by: Frank Noirot <frankjohnson1993@gmail.com>
2025-02-19 18:43:27 +00:00
b98f5605b6 [bugfix] Fix user settings opening (#5414)
* Create .gitattributes

* Fix projectPath URI decoding

* Try to enforce UTF-8 encoding on all files except Windows-specific
scripts

* edit file encoding

* edit gitattributes

* Update kcl-samples

* fmt
2025-02-19 13:31:07 -05:00
c050e03df1 Fix sketch test (#5425)
@Irev-Dev's suggestion
2025-02-19 13:30:53 -05:00
b6fa4325f0 Bump modeling-cmds from 0.2.93 to 0.2.97 (#5327)
* Bump modeling-cmds from 0.2.93 to 0.2.96

Among other things, this switches the default units for imported file
formats to millimeters from meters.

* Fix wasm build error

* Upgrade to the latest modeling-cmds 0.2.97
2025-02-19 10:30:26 -08:00
0804aecc63 Fix typo in error msg (#5411)
Part of https://github.com/KittyCAD/modeling-app/issues/5355
2025-02-19 17:40:17 +00:00
3dfc2c86e1 Revert "Fix isomorphic-copy to work on Windows (#5417)" (#5424)
This reverts commit 18d87b99bd.
2025-02-19 11:31:14 -05:00
71647ede29 Release KCL 38 (#5421) 2025-02-19 10:26:02 -05:00
cd679f4be3 Fix units not getting set properly with whole module imports (#5418)
* Add test for whole modules with non-default units

* Update output files since adding test

* Fix to not drop batched commands due to isolated mode

* Update output after fix

* Update other sim test outputs
2025-02-19 16:11:11 +11:00
18d87b99bd Fix isomorphic-copy to work on Windows (#5417)
The current command uses the proper `copy` command but does not use
`\`'s, so it fails on my Windows machine.
2025-02-18 20:25:12 -05:00
70a2202877 change everything to rwlocks for thread safety (#5416)
* make everything in engine a rwlock and cleanup repetitive code

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

* updates

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

* updates

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

* updates

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

* updates

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

* updates

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

* docs

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

---------

Signed-off-by: Jess Frazelle <github@jessfraz.com>
2025-02-18 21:50:13 +00:00
f5c9f84ae9 test equipping tools mid tool use removes have baked expression (#5403)
test equiping tools mid tool use removes have baked expression
2025-02-19 07:45:44 +11:00
71f701dec7 Don't auto-hide menu bar on Windows and Linux (#5415)
This auto-hide behavior causes the bar to toggle visible and hidden when the user hits the <kbd>Alt</kbd> key, which can be quite often especially with Trackpad Friendly controls enabled.
2025-02-19 07:45:29 +11:00
bffbed1d42 Release KCL 37 (#5409)
* Use kcl-samples 'next' branch

* Release KCL 37
2025-02-18 12:29:34 -06:00
9f60ed8e75 Fix yarn lock after yarn install (#5408) 2025-02-18 12:38:28 -05:00
370 changed files with 43770 additions and 19824 deletions

13
.gitattributes vendored Normal file
View File

@ -0,0 +1,13 @@
# Set default behavior to automatically normalize line endings.
* text=auto
# Force batch scripts to always use CRLF line endings so that if a repo is accessed
# in Windows via a file share from Linux, the scripts will work.
*.{cmd,[cC][mM][dD]} text working-tree-encoding=UTF-16LE eol=CRLF
*.{bat,[bB][aA][tT]} text working-tree-encoding=UTF-16LE eol=CRLF
*.{ics,[iI][cC][sS]} text working-tree-encoding=UTF-16LE eol=CRLF
*.{ps1,[iP][sS][1]} text working-tree-encoding=UTF-16LE eol=CRLF
# Force bash scripts to always use LF line endings so that if a repo is accessed
# in Unix via a file share from Windows, the scripts will work.
*.sh text eol=lfol=lf

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -18,6 +18,7 @@ A base path.
|----------|------|-------------|----------|
| `from` |`[number, number]`| The from point. | No |
| `to` |`[number, number]`| The to point. | No |
| `units` |[`UnitLen`](/docs/kcl/types/UnitLen)| A base path. | No |
| `tag` |[`TagDeclarator`](/docs/kcl/types#tag-declaration)| The tag of the path. | No |
| `__geoMeta` |[`GeoMeta`](/docs/kcl/types/GeoMeta)| Metadata. | No |

View File

@ -33,6 +33,7 @@ layout: manual
----
A unit of length.
**Type:** `object`
@ -140,6 +141,7 @@ layout: manual
----
A unit of angle.
**Type:** `object`

View File

@ -27,6 +27,7 @@ A path that goes to a point.
| `type` |enum: `ToPoint`| | No |
| `from` |`[number, number]`| The from point. | No |
| `to` |`[number, number]`| The to point. | No |
| `units` |[`UnitLen`](/docs/kcl/types/UnitLen)| A path. | No |
| `tag` |[`TagDeclarator`](/docs/kcl/types#tag-declaration)| The tag of the path. | No |
| `__geoMeta` |[`GeoMeta`](/docs/kcl/types/GeoMeta)| Metadata. | No |
@ -49,6 +50,7 @@ A arc that is tangential to the last path segment that goes to a point
| `ccw` |`boolean`| arc's direction | No |
| `from` |`[number, number]`| The from point. | No |
| `to` |`[number, number]`| The to point. | No |
| `units` |[`UnitLen`](/docs/kcl/types/UnitLen)| A path. | No |
| `tag` |[`TagDeclarator`](/docs/kcl/types#tag-declaration)| The tag of the path. | No |
| `__geoMeta` |[`GeoMeta`](/docs/kcl/types/GeoMeta)| Metadata. | No |
@ -71,6 +73,7 @@ A arc that is tangential to the last path segment
| `ccw` |`boolean`| arc's direction | No |
| `from` |`[number, number]`| The from point. | No |
| `to` |`[number, number]`| The to point. | No |
| `units` |[`UnitLen`](/docs/kcl/types/UnitLen)| A path. | No |
| `tag` |[`TagDeclarator`](/docs/kcl/types#tag-declaration)| The tag of the path. | No |
| `__geoMeta` |[`GeoMeta`](/docs/kcl/types/GeoMeta)| Metadata. | No |
@ -94,6 +97,7 @@ a complete arc
| `ccw` |`boolean`| arc's direction This is used to compute the tangential angle. | No |
| `from` |`[number, number]`| The from point. | No |
| `to` |`[number, number]`| The to point. | No |
| `units` |[`UnitLen`](/docs/kcl/types/UnitLen)| A path. | No |
| `tag` |[`TagDeclarator`](/docs/kcl/types#tag-declaration)| The tag of the path. | No |
| `__geoMeta` |[`GeoMeta`](/docs/kcl/types/GeoMeta)| Metadata. | No |
@ -117,6 +121,7 @@ A base path.
| `p3` |`[number, number]`| Point 3 of the circle | No |
| `from` |`[number, number]`| The from point. | No |
| `to` |`[number, number]`| The to point. | No |
| `units` |[`UnitLen`](/docs/kcl/types/UnitLen)| A path. | No |
| `tag` |[`TagDeclarator`](/docs/kcl/types#tag-declaration)| The tag of the path. | No |
| `__geoMeta` |[`GeoMeta`](/docs/kcl/types/GeoMeta)| Metadata. | No |
@ -138,6 +143,7 @@ A path that is horizontal.
| `x` |`number`| The x coordinate. | No |
| `from` |`[number, number]`| The from point. | No |
| `to` |`[number, number]`| The to point. | No |
| `units` |[`UnitLen`](/docs/kcl/types/UnitLen)| A path. | No |
| `tag` |[`TagDeclarator`](/docs/kcl/types#tag-declaration)| The tag of the path. | No |
| `__geoMeta` |[`GeoMeta`](/docs/kcl/types/GeoMeta)| Metadata. | No |
@ -160,6 +166,7 @@ An angled line to.
| `y` |`number`| The y coordinate. | No |
| `from` |`[number, number]`| The from point. | No |
| `to` |`[number, number]`| The to point. | No |
| `units` |[`UnitLen`](/docs/kcl/types/UnitLen)| A path. | No |
| `tag` |[`TagDeclarator`](/docs/kcl/types#tag-declaration)| The tag of the path. | No |
| `__geoMeta` |[`GeoMeta`](/docs/kcl/types/GeoMeta)| Metadata. | No |
@ -180,6 +187,7 @@ A base path.
| `type` |enum: `Base`| | No |
| `from` |`[number, number]`| The from point. | No |
| `to` |`[number, number]`| The to point. | No |
| `units` |[`UnitLen`](/docs/kcl/types/UnitLen)| A path. | No |
| `tag` |[`TagDeclarator`](/docs/kcl/types#tag-declaration)| The tag of the path. | No |
| `__geoMeta` |[`GeoMeta`](/docs/kcl/types/GeoMeta)| Metadata. | No |
@ -203,6 +211,7 @@ A circular arc, not necessarily tangential to the current point.
| `ccw` |`boolean`| True if the arc is counterclockwise. | No |
| `from` |`[number, number]`| The from point. | No |
| `to` |`[number, number]`| The to point. | No |
| `units` |[`UnitLen`](/docs/kcl/types/UnitLen)| A path. | No |
| `tag` |[`TagDeclarator`](/docs/kcl/types#tag-declaration)| The tag of the path. | No |
| `__geoMeta` |[`GeoMeta`](/docs/kcl/types/GeoMeta)| Metadata. | No |

View File

@ -6,6 +6,40 @@ layout: manual
A sketch is a collection of paths.
When you define a sketch to a variable like:
```kcl
mySketch = startSketchOn('XY')
|> startProfileAt([-12, 12], %)
|> line(end = [24, 0])
|> line(end = [0, -24])
|> line(end = [-24, 0])
|> close()
```
The `mySketch` variable will be an executed [`Sketch`](/docs/kcl/types/Sketch) object. Executed being past tense, because the engine has already executed the commands to create the sketch.
The previous sketch commands will never be executed again, in this case.
If you would like to encapsulate the commands to create the sketch any time you call it, you can use a function.
```kcl
fn createSketch() {
return startSketchOn('XY')
|> startProfileAt([-12, 12], %)
|> line(end = [24, 0])
|> line(end = [0, -24])
|> line(end = [-24, 0])
|> close()
}
```
Now, every time you call `createSketch()`, the commands will be executed and a new sketch will be created.
When you assign the result of `createSketch()` to a variable (`mySketch = createSketch()`), you are assigning the executed sketch to that variable. Meaning that the sketch `mySketch` will not be executed again.
You can still execute _new_ commands on the sketch like `extrude`, `revolve`, `loft`, etc. and the sketch will be updated.
**Type:** `object`

View File

@ -14,6 +14,40 @@ A sketch or a group of sketches.
A sketch is a collection of paths.
When you define a sketch to a variable like:
```kcl
mySketch = startSketchOn('XY')
|> startProfileAt([-12, 12], %)
|> line(end = [24, 0])
|> line(end = [0, -24])
|> line(end = [-24, 0])
|> close()
```
The `mySketch` variable will be an executed [`Sketch`](/docs/kcl/types/Sketch) object. Executed being past tense, because the engine has already executed the commands to create the sketch.
The previous sketch commands will never be executed again, in this case.
If you would like to encapsulate the commands to create the sketch any time you call it, you can use a function.
```kcl
fn createSketch() {
return startSketchOn('XY')
|> startProfileAt([-12, 12], %)
|> line(end = [24, 0])
|> line(end = [0, -24])
|> line(end = [-24, 0])
|> close()
}
```
Now, every time you call `createSketch()`, the commands will be executed and a new sketch will be created.
When you assign the result of `createSketch()` to a variable (`mySketch = createSketch()`), you are assigning the executed sketch to that variable. Meaning that the sketch `mySketch` will not be executed again.
You can still execute _new_ commands on the sketch like `extrude`, `revolve`, `loft`, etc. and the sketch will be updated.
**Type:** `object`

View File

@ -1,10 +1,46 @@
---
title: "Solid"
excerpt: "An solid is a collection of extrude surfaces."
excerpt: "A solid is a collection of extrude surfaces."
layout: manual
---
An solid is a collection of extrude surfaces.
A solid is a collection of extrude surfaces.
When you define a solid to a variable like:
```kcl
myPart = startSketchOn('XY')
|> startProfileAt([-12, 12], %)
|> line(end = [24, 0])
|> line(end = [0, -24])
|> line(end = [-24, 0])
|> close()
|> extrude(length = 6)
```
The `myPart` variable will be an executed [`Solid`](/docs/kcl/types/Solid) object. Executed being past tense, because the engine has already executed the commands to create the solid.
The previous solid commands will never be executed again, in this case.
If you would like to encapsulate the commands to create the solid any time you call it, you can use a function.
```kcl
fn createPart() {
return startSketchOn('XY')
|> startProfileAt([-12, 12], %)
|> line(end = [24, 0])
|> line(end = [0, -24])
|> line(end = [-24, 0])
|> close()
|> extrude(length = 6)
}
```
Now, every time you call `createPart()`, the commands will be executed and a new solid will be created.
When you assign the result of `createPart()` to a variable (`myPart = createPart()`), you are assigning the executed solid to that variable. Meaning that the solid `myPart` will not be executed again.
You can still execute _new_ commands on the solid like `shell`, `fillet`, `chamfer`, etc. and the solid will be updated.
**Type:** `object`
@ -24,7 +60,7 @@ An solid is a collection of extrude surfaces.
| `startCapId` |`string`| The id of the extrusion start cap | No |
| `endCapId` |`string`| The id of the extrusion end cap | No |
| `edgeCuts` |`[` [`EdgeCut`](/docs/kcl/types/EdgeCut) `]`| Chamfers or fillets on this solid. | No |
| `units` |[`UnitLen`](/docs/kcl/types/UnitLen)| An solid is a collection of extrude surfaces. | No |
| `units` |[`UnitLen`](/docs/kcl/types/UnitLen)| A solid is a collection of extrude surfaces. | No |
| `__meta` |`[` [`Metadata`](/docs/kcl/types/Metadata) `]`| Metadata. | No |

View File

@ -12,7 +12,43 @@ A solid or a group of solids.
**This schema accepts exactly one of the following:**
An solid is a collection of extrude surfaces.
A solid is a collection of extrude surfaces.
When you define a solid to a variable like:
```kcl
myPart = startSketchOn('XY')
|> startProfileAt([-12, 12], %)
|> line(end = [24, 0])
|> line(end = [0, -24])
|> line(end = [-24, 0])
|> close()
|> extrude(length = 6)
```
The `myPart` variable will be an executed [`Solid`](/docs/kcl/types/Solid) object. Executed being past tense, because the engine has already executed the commands to create the solid.
The previous solid commands will never be executed again, in this case.
If you would like to encapsulate the commands to create the solid any time you call it, you can use a function.
```kcl
fn createPart() {
return startSketchOn('XY')
|> startProfileAt([-12, 12], %)
|> line(end = [24, 0])
|> line(end = [0, -24])
|> line(end = [-24, 0])
|> close()
|> extrude(length = 6)
}
```
Now, every time you call `createPart()`, the commands will be executed and a new solid will be created.
When you assign the result of `createPart()` to a variable (`myPart = createPart()`), you are assigning the executed solid to that variable. Meaning that the solid `myPart` will not be executed again.
You can still execute _new_ commands on the solid like `shell`, `fillet`, `chamfer`, etc. and the solid will be updated.
**Type:** `object`

View File

@ -1,9 +1,10 @@
---
title: "UnitAngle"
excerpt: ""
excerpt: "A unit of angle."
layout: manual
---
A unit of angle.

View File

@ -1,9 +1,10 @@
---
title: "UnitLen"
excerpt: ""
excerpt: "A unit of length."
layout: manual
---
A unit of length.

File diff suppressed because one or more lines are too long

View File

@ -5,6 +5,7 @@ import { ToolbarFixture } from './fixtures/toolbarFixture'
import fs from 'node:fs/promises'
import path from 'node:path'
import { getUtils } from './test-utils'
import { Locator } from '@playwright/test'
// test file is for testing point an click code gen functionality that's not sketch mode related
@ -2506,6 +2507,94 @@ extrude002 = extrude(sketch002, length = 50)
})
})
const shellPointAndClickDeletionCases = [
{ shouldUseKeyboard: true },
{ shouldUseKeyboard: false },
]
shellPointAndClickDeletionCases.forEach(({ shouldUseKeyboard }) => {
test(`Shell point-and-click deletion (shouldUseKeyboard: ${shouldUseKeyboard})`, async ({
context,
page,
homePage,
scene,
editor,
toolbar,
cmdBar,
}) => {
const sketchCode = `sketch001 = startSketchOn('XY')
profile001 = startProfileAt([-20, 20], sketch001)
|> xLine(40, %)
|> yLine(-60, %)
|> xLine(-40, %)
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|> close()
`
const extrudeCode = `extrude001 = extrude(profile001, length = 40)
`
const shellCode = `shell001 = shell(extrude001, faces = ['end'], thickness = 5)
`
const initialCode = sketchCode + extrudeCode + shellCode
await context.addInitScript((initialCode) => {
localStorage.setItem('persistCode', initialCode)
}, initialCode)
await page.setBodyDimensions({ width: 1000, height: 500 })
await homePage.goToModelingScene()
await scene.waitForExecutionDone()
await toolbar.openPane('feature-tree')
// One dumb hardcoded screen pixel value
const testPoint = { x: 590, y: 400 }
const extrudeColor: [number, number, number] = [100, 100, 100]
const sketchColor: [number, number, number] = [140, 140, 140]
const defaultPlaneColor: [number, number, number] = [50, 50, 100]
const deleteOperation = async (operationButton: Locator) => {
if (shouldUseKeyboard) {
await operationButton.click({ button: 'left' })
await page.keyboard.press('Backspace')
} else {
await operationButton.click({ button: 'right' })
const editButton = page.getByTestId('context-menu-delete')
await editButton.click()
}
}
await test.step(`Look for the grey of the extrude shape`, async () => {
await scene.expectPixelColor(extrudeColor, testPoint, 20)
})
await test.step('Delete shell and confirm deletion', async () => {
const operationButton = await toolbar.getFeatureTreeOperation(
'Shell',
0
)
await deleteOperation(operationButton)
await scene.expectPixelColor(extrudeColor, testPoint, 20)
await editor.expectEditor.not.toContain(shellCode)
})
await test.step('Delete extrude and confirm deletion', async () => {
const operationButton = await toolbar.getFeatureTreeOperation(
'Extrude',
0
)
await deleteOperation(operationButton)
await editor.expectEditor.not.toContain(extrudeCode)
await scene.expectPixelColor(sketchColor, testPoint, 20)
})
await test.step('Delete sketch and confirm empty scene', async () => {
const operationButton = await toolbar.getFeatureTreeOperation(
'Sketch',
0
)
await deleteOperation(operationButton)
await editor.expectEditor.toContain('')
await scene.expectPixelColor(defaultPlaneColor, testPoint, 20)
})
})
})
test(`Shell dry-run validation rejects sweeps`, async ({
context,
page,

View File

@ -1353,6 +1353,99 @@ test.describe(`Sketching with offset planes`, () => {
})
test.describe('multi-profile sketching', () => {
test(
`test it removes half-finished expressions when changing tools in sketch mode`,
{ tag: ['@skipWin'] },
async ({ context, page, scene, toolbar, editor, homePage }) => {
// We seed the scene with a single offset plane
await context.addInitScript(() => {
localStorage.setItem(
'persistCode',
`yo = 5
sketch001 = startSketchOn('XZ')
profile001 = startProfileAt([121.52, 168.25], sketch001)
|> line(end = [115.04, 113.61])
|> line(end = [130.87, -97.79])
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|> close()
profile002 = startProfileAt([117.2, 56.08], sketch001)
|> line(end = [166.82, 25.89])
|> yLine(-107.86, %)
`
)
})
await homePage.goToModelingScene()
await expect(
page.getByRole('button', { name: 'Start Sketch' })
).not.toBeDisabled()
await (await toolbar.getFeatureTreeOperation('Sketch', 0)).dblclick()
await page.waitForTimeout(600)
const [circlePoint1] = scene.makeMouseHelpers(700, 200)
await test.step('equip circle tool and click first point', async () => {
await toolbar.circleBtn.click()
await page.waitForTimeout(100)
await circlePoint1()
await editor.expectEditor.toContain('profile003 = circle({ center = [')
})
await test.step('equip line tool and verify circle code is removed', async () => {
await toolbar.lineBtn.click()
await editor.expectEditor.not.toContain('profile003 = circle({')
})
const [circle3Point1] = scene.makeMouseHelpers(650, 200)
const [circle3Point2] = scene.makeMouseHelpers(750, 200)
await test.step('equip three point circle tool and click first two points', async () => {
await toolbar.selectCircleThreePoint()
await page.waitForTimeout(100)
await circle3Point1()
await page.waitForTimeout(100)
await circle3Point2()
await editor.expectEditor.toContain('profile003 = circleThreePoint(')
})
await test.step('equip line tool and verify three point circle code is removed', async () => {
await toolbar.lineBtn.click()
await editor.expectEditor.not.toContain(
'profile003 = circleThreePoint('
)
})
const [cornerRectPoint1] = scene.makeMouseHelpers(600, 300)
await test.step('equip corner rectangle tool and click first point', async () => {
await toolbar.rectangleBtn.click()
await page.waitForTimeout(100)
await cornerRectPoint1()
await editor.expectEditor.toContain('profile003 = startProfileAt(')
})
await test.step('equip line tool and verify corner rectangle code is removed', async () => {
await toolbar.lineBtn.click()
await editor.expectEditor.not.toContain('profile003 = startProfileAt(')
})
const [centerRectPoint1] = scene.makeMouseHelpers(700, 300)
await test.step('equip center rectangle tool and click first point', async () => {
await toolbar.selectCenterRectangle()
await page.waitForTimeout(100)
await centerRectPoint1()
await editor.expectEditor.toContain('profile003 = startProfileAt(')
})
await test.step('equip line tool and verify center rectangle code is removed', async () => {
await toolbar.lineBtn.click()
await editor.expectEditor.not.toContain('profile003 = startProfileAt(')
})
}
)
test(
`snapToProfile start only works for current profile`,
{ tag: ['@skipWin'] },
@ -1885,7 +1978,7 @@ profile004 = circleThreePoint(sketch001, p1 = [13.44, -6.8], p2 = [13.39, -2.07]
)
test(
'Can delete a profile in the editor while is sketch mode, and sketch mode does not break, can ctrl+z to undo after constraint with variable was added',
{ tag: ['@skipWin'] },
{ tag: ['@skipWin', '@skipLinux'] },
async ({ scene, toolbar, editor, cmdBar, page, homePage }) => {
await page.addInitScript(async () => {
localStorage.setItem(
@ -1936,9 +2029,6 @@ profile003 = circle({ center = [6.92, -4.2], radius = 3.16 }, sketch001)
await moveToClearToolBarPopover()
await pointOnSegment({ shouldDbClick: true })
await page.waitForTimeout(600)
await toolbar.lineBtn.click()
await page.waitForTimeout(100)
})
await test.step('select and delete code for a profile', async () => {})
@ -1969,7 +2059,7 @@ profile003 = circle({ center = [6.92, -4.2], radius = 3.16 }, sketch001)
activeLines: ['|>line(end = [-0.41,6.99])'],
highlightedCode: 'line(end = [-0.41,6.99])',
})
}).toPass({ timeout: 10_000, intervals: [1000] })
}).toPass({ timeout: 30_000, intervals: [1500] })
await toolbar.lengthConstraintBtn.click()
await cmdBar.progressCmdBar()

Binary file not shown.

Before

Width:  |  Height:  |  Size: 76 KiB

After

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 143 KiB

After

Width:  |  Height:  |  Size: 143 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 127 KiB

After

Width:  |  Height:  |  Size: 127 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 49 KiB

After

Width:  |  Height:  |  Size: 49 KiB

View File

@ -85,7 +85,7 @@
"fmt": "prettier --write ./src *.ts *.json *.js ./e2e ./packages",
"fmt-check": "prettier --check ./src *.ts *.json *.js ./e2e ./packages",
"fetch:wasm": "./get-latest-wasm-bundle.sh",
"fetch:samples": "echo \"Fetching latest KCL samples...\" && curl -o public/kcl-samples-manifest-fallback.json https://raw.githubusercontent.com/KittyCAD/kcl-samples/achalmers/offset-plane-kwargs/manifest.json",
"fetch:samples": "echo \"Fetching latest KCL samples...\" && curl -o public/kcl-samples-manifest-fallback.json https://raw.githubusercontent.com/KittyCAD/kcl-samples/next/manifest.json",
"isomorphic-copy-wasm": "(copy src/wasm-lib/pkg/wasm_lib_bg.wasm public || cp src/wasm-lib/pkg/wasm_lib_bg.wasm public)",
"build:wasm-dev": "yarn wasm-prep && (cd src/wasm-lib && wasm-pack build --dev --target web --out-dir pkg && cargo test -p kcl-lib export_bindings) && yarn isomorphic-copy-wasm && yarn fmt",
"build:wasm": "yarn wasm-prep && cd src/wasm-lib && wasm-pack build --release --target web --out-dir pkg && cargo test -p kcl-lib export_bindings && cd ../.. && yarn isomorphic-copy-wasm && yarn fmt",

View File

@ -20,10 +20,14 @@ export const kclHighlight = styleTags({
LineComment: t.lineComment,
BlockComment: t.blockComment,
Shebang: t.meta,
AnnotationName: t.annotation,
PipeSubstitution: t.atom,
VariableDefinition: t.definition(t.variableName),
VariableName: t.variableName,
PropertyName: t.propertyName,
'AnnotationProperty/PropertyName': t.definition(t.propertyName),
'ObjectProperty/PropertyName': t.definition(t.propertyName),
'LabeledArgument/ArgumentLabel': t.definition(t.propertyName),
TagDeclarator: t.tagName,
'( )': t.paren,
'{ }': t.brace,

View File

@ -34,6 +34,13 @@
"title": "Car Wheel Assembly",
"description": "A car wheel assembly with a rotor, tire, and lug nuts."
},
{
"file": "main.kcl",
"pathFromProjectDirectoryToFirstFile": "color-cube/main.kcl",
"multipleFiles": false,
"title": "Color Cube",
"description": "This is a color cube centered about the origin. It is used to help determine orientation in the scene."
},
{
"file": "main.kcl",
"pathFromProjectDirectoryToFirstFile": "cycloidal-gear/main.kcl",

View File

@ -1,7 +1,6 @@
import toast from 'react-hot-toast'
import { ActionIcon, ActionIconProps } from './ActionIcon'
import {
MouseEvent,
RefObject,
useCallback,
useEffect,
@ -148,24 +147,22 @@ interface ContextMenuItemProps {
onClick?: () => void
hotkey?: string
'data-testid'?: string
disabled?: boolean
}
export function ContextMenuItem(props: ContextMenuItemProps) {
const { children, icon, onClick, hotkey } = props
const { children, icon, onClick, hotkey, disabled } = props
return (
<button
disabled={disabled}
data-testid={props['data-testid']}
className="flex items-center gap-2 py-1 px-2 cursor-pointer hover:bg-chalkboard-20 dark:hover:bg-chalkboard-80 border-none text-left"
onClick={onClick}
onClick={disabled ? undefined : onClick}
>
{icon && <ActionIcon icon={icon} bgClassName="!bg-transparent" />}
<div className="flex-1">{children}</div>
{hotkey && (
<kbd className="px-1.5 py-0.5 rounded bg-primary/10 text-primary dark:bg-chalkboard-80 dark:text-chalkboard-40">
{hotkey}
</kbd>
)}
{hotkey && <kbd className="hotkey">{hotkey}</kbd>}
</button>
)
}

View File

@ -1574,7 +1574,7 @@ export const ModelingMachineProvider = ({
: updatedSketchNodePaths[0]
}
if (doesNeedSplitting) {
if (doesNeedSplitting || indexToDelete >= 0) {
await kclManager.executeAstMock(moddedAst)
await codeManager.updateEditorWithAstAndWriteToFile(moddedAst)
}

View File

@ -324,6 +324,20 @@ const OperationItem = (props: {
}
}
function deleteOperation() {
if (
props.item.type === 'StdLibCall' ||
props.item.type === 'UserDefinedFunctionCall'
) {
props.send({
type: 'deleteOperation',
data: {
targetSourceRange: sourceRangeFromRust(props.item.sourceRange),
},
})
}
}
const menuItems = useMemo(
() => [
<ContextMenuItem
@ -364,14 +378,24 @@ const OperationItem = (props: {
</ContextMenuItem>,
]
: []),
...(props.item.type === 'StdLibCall' &&
stdLibMap[props.item.name]?.prepareToEdit
...(props.item.type === 'StdLibCall'
? [
<ContextMenuItem onClick={enterEditFlow}>
Edit {name}
<ContextMenuItem
disabled={!stdLibMap[props.item.name]?.prepareToEdit}
onClick={enterEditFlow}
hotkey="Double click"
>
Edit
</ContextMenuItem>,
]
: []),
<ContextMenuItem
onClick={deleteOperation}
hotkey="Delete"
data-testid="context-menu-delete"
>
Delete
</ContextMenuItem>,
],
[props.item, props.send]
)

View File

@ -54,9 +54,27 @@ describe('processMemory', () => {
},
],
theSketch: [
{ type: 'ToPoint', to: [-3.35, 0.17], from: [0, 0], tag: null },
{ type: 'ToPoint', to: [0.98, 5.16], from: [-3.35, 0.17], tag: null },
{ type: 'ToPoint', to: [2.15, 4.32], from: [0.98, 5.16], tag: null },
{
type: 'ToPoint',
to: [-3.35, 0.17],
from: [0, 0],
units: { type: 'Mm' },
tag: null,
},
{
type: 'ToPoint',
to: [0.98, 5.16],
from: [-3.35, 0.17],
units: { type: 'Mm' },
tag: null,
},
{
type: 'ToPoint',
to: [2.15, 4.32],
from: [0.98, 5.16],
units: { type: 'Mm' },
tag: null,
},
],
})
})

View File

@ -20,7 +20,7 @@ import {
getSettingsFolderPaths,
} from 'lib/desktopFS'
import { useDotDotSlash } from 'hooks/useDotDotSlash'
import { ForwardedRef, forwardRef, useEffect } from 'react'
import { ForwardedRef, forwardRef, useEffect, useMemo } from 'react'
import { useLspContext } from 'components/LspProvider'
import { toSync } from 'lib/utils'
import { reportRejection } from 'lib/trap'
@ -44,20 +44,22 @@ export const AllSettingsFields = forwardRef(
settings: { send, context, state },
} = useSettingsAuthContext()
const projectPath =
isFileSettings && isDesktop()
? decodeURI(
location.pathname
.replace(PATHS.FILE + window.electron.sep, '')
.replace(PATHS.SETTINGS, '')
.slice(
0,
decodeURI(location.pathname).lastIndexOf(
window.electron.path.sep
)
)
)
: undefined
const projectPath = useMemo(() => {
const filteredPathname = location.pathname
.replace(PATHS.FILE, '')
.replace(PATHS.SETTINGS, '')
const lastSlashIndex = filteredPathname.lastIndexOf(
// This is slicing off any remaining browser path segments,
// so we don't use window.electron.sep here
'/'
)
const projectPath =
isFileSettings && isDesktop()
? decodeURIComponent(filteredPathname.slice(lastSlashIndex + 1))
: undefined
return projectPath
}, [location.pathname])
function restartOnboarding() {
send({
@ -197,9 +199,7 @@ export const AllSettingsFields = forwardRef(
<ActionButton
Element="button"
onClick={toSync(async () => {
const paths = await getSettingsFolderPaths(
projectPath ? decodeURIComponent(projectPath) : undefined
)
const paths = await getSettingsFolderPaths(projectPath)
const finalPath = paths[searchParamTab]
if (!finalPath) {
return new Error('finalPath undefined')

View File

@ -24,6 +24,7 @@ const mySketch001 = startSketchOn('XY')
start: {
to: [0, 0],
from: [0, 0],
units: { type: 'Mm' },
tag: null,
__geoMeta: {
id: expect.any(String),
@ -35,6 +36,7 @@ const mySketch001 = startSketchOn('XY')
type: 'ToPoint',
tag: null,
to: [-1.59, -1.54],
units: { type: 'Mm' },
from: [0, 0],
__geoMeta: {
sourceRange: [expect.any(Number), expect.any(Number), 0],
@ -45,6 +47,7 @@ const mySketch001 = startSketchOn('XY')
type: 'ToPoint',
to: [0.46, -5.82],
from: [-1.59, -1.54],
units: { type: 'Mm' },
tag: null,
__geoMeta: {
sourceRange: [expect.any(Number), expect.any(Number), 0],
@ -111,6 +114,7 @@ const mySketch001 = startSketchOn('XY')
type: 'ToPoint',
from: [0, 0],
to: [-1.59, -1.54],
units: { type: 'Mm' },
tag: null,
__geoMeta: {
id: expect.any(String),
@ -121,6 +125,7 @@ const mySketch001 = startSketchOn('XY')
type: 'ToPoint',
from: [-1.59, -1.54],
to: [0.46, -5.82],
units: { type: 'Mm' },
tag: null,
__geoMeta: {
id: expect.any(String),
@ -229,6 +234,7 @@ const sk2 = startSketchOn('XY')
type: 'ToPoint',
from: [0, 0],
to: [-2.5, 0],
units: { type: 'Mm' },
tag: null,
__geoMeta: {
id: expect.any(String),
@ -239,6 +245,7 @@ const sk2 = startSketchOn('XY')
type: 'ToPoint',
from: [-2.5, 0],
to: [0, 10],
units: { type: 'Mm' },
tag: {
end: expect.any(Number),
start: expect.any(Number),
@ -254,6 +261,7 @@ const sk2 = startSketchOn('XY')
type: 'ToPoint',
from: [0, 10],
to: [2.5, 0],
units: { type: 'Mm' },
tag: null,
__geoMeta: {
id: expect.any(String),
@ -335,6 +343,7 @@ const sk2 = startSketchOn('XY')
type: 'ToPoint',
from: [0, 0],
to: [-2.5, 0],
units: { type: 'Mm' },
tag: null,
__geoMeta: {
id: expect.any(String),
@ -345,6 +354,7 @@ const sk2 = startSketchOn('XY')
type: 'ToPoint',
from: [-2.5, 0],
to: [0, 3],
units: { type: 'Mm' },
tag: {
end: expect.any(Number),
start: expect.any(Number),
@ -360,6 +370,7 @@ const sk2 = startSketchOn('XY')
type: 'ToPoint',
from: [0, 3],
to: [2.5, 0],
units: { type: 'Mm' },
tag: null,
__geoMeta: {
id: expect.any(String),

View File

@ -72,6 +72,7 @@ const newVar = myVar + 1`
type: 'ToPoint',
to: [0, 2],
from: [0, 0],
units: { type: 'Mm' },
__geoMeta: {
sourceRange: [expect.any(Number), expect.any(Number), 0],
id: expect.any(String),
@ -87,6 +88,7 @@ const newVar = myVar + 1`
type: 'ToPoint',
to: [2, 3],
from: [0, 2],
units: { type: 'Mm' },
tag: null,
__geoMeta: {
sourceRange: [expect.any(Number), expect.any(Number), 0],
@ -97,6 +99,7 @@ const newVar = myVar + 1`
type: 'ToPoint',
to: [5, -1],
from: [2, 3],
units: { type: 'Mm' },
__geoMeta: {
sourceRange: [expect.any(Number), expect.any(Number), 0],
id: expect.any(String),
@ -165,6 +168,7 @@ const newVar = myVar + 1`
start: {
to: [0, 0],
from: [0, 0],
units: { type: 'Mm' },
tag: null,
__geoMeta: {
id: expect.any(String),
@ -188,6 +192,7 @@ const newVar = myVar + 1`
type: 'ToPoint',
to: [1, 1],
from: [0, 0],
units: { type: 'Mm' },
tag: null,
__geoMeta: {
sourceRange: [expect.any(Number), expect.any(Number), 0],
@ -198,6 +203,7 @@ const newVar = myVar + 1`
type: 'ToPoint',
to: [0, 1],
from: [1, 1],
units: { type: 'Mm' },
__geoMeta: {
sourceRange: [expect.any(Number), expect.any(Number), 0],
id: expect.any(String),
@ -213,6 +219,7 @@ const newVar = myVar + 1`
type: 'ToPoint',
to: [1, 1],
from: [0, 1],
units: { type: 'Mm' },
tag: null,
__geoMeta: {
sourceRange: [expect.any(Number), expect.any(Number), 0],

View File

@ -32,7 +32,7 @@ child_process.spawnSync('git', [
'clone',
'--single-branch',
'--branch',
'achalmers/offset-plane-kwargs',
'next',
URL_GIT_KCL_SAMPLES,
DIR_KCL_SAMPLES,
])

View File

@ -0,0 +1,38 @@
import { Selection } from 'lib/selections'
import { getFaceDetails } from 'clientSideScene/sceneEntities'
import { deleteFromSelection } from 'lang/modifyAst'
import { codeManager, engineCommandManager, kclManager } from 'lib/singletons'
import { err } from 'lib/trap'
import { executeAst } from 'lang/langHelpers'
export const deletionErrorMessage =
'Unable to delete selection. Please edit manually in code pane.'
export async function deleteSelectionPromise(
selection: Selection
): Promise<Error | void> {
let ast = kclManager.ast
const modifiedAst = await deleteFromSelection(
ast,
selection,
kclManager.variables,
engineCommandManager.artifactGraph,
getFaceDetails
)
if (err(modifiedAst)) {
return new Error(deletionErrorMessage)
}
const testExecute = await executeAst({
ast: modifiedAst,
engineCommandManager,
isMock: true,
})
if (testExecute.errors.length) {
return new Error(deletionErrorMessage)
}
await kclManager.updateAst(modifiedAst, true)
await codeManager.updateEditorWithAstAndWriteToFile(modifiedAst)
}

View File

@ -386,6 +386,7 @@ part001 = startSketchOn('XY')
type: 'ToPoint',
to: [5.62, 1.79],
from: [3.48, 0.44],
units: { type: 'Mm' },
tag: null,
})
})
@ -401,6 +402,7 @@ part001 = startSketchOn('XY')
expect(segment).toEqual({
to: [0, 0.04],
from: [0, 0.04],
units: { type: 'Mm' },
tag: null,
type: 'Base',
})

View File

@ -106,6 +106,7 @@ export type ModelingCommandSchema = {
prompt: string
selection: Selections
}
'Delete selection': {}
}
export const modelingMachineCommandConfig: StateMachineCommandSetConfig<

View File

@ -375,14 +375,7 @@ export const toolbarConfig: Record<ToolbarModeName, ToolbarMode> = {
},
icon: 'line',
status: 'available',
disabled: (state) =>
state.matches('Sketch no face') ||
state.matches({
Sketch: { 'Rectangle tool': 'Awaiting second corner' },
}) ||
state.matches({
Sketch: { 'Circle tool': 'Awaiting Radius' },
}),
disabled: (state) => state.matches('Sketch no face'),
title: 'Line',
hotkey: (state) =>
state.matches({ Sketch: 'Line tool' }) ? ['Esc', 'L'] : 'L',

File diff suppressed because one or more lines are too long

View File

@ -44,7 +44,6 @@ import {
addHelix,
addOffsetPlane,
addSweep,
deleteFromSelection,
extrudeSketch,
loftSketches,
} from 'lang/modifyAst'
@ -70,12 +69,10 @@ import {
} from 'components/Toolbar/SetAbsDistance'
import { ModelingCommandSchema } from 'lib/commandBarConfigs/modelingCommandConfig'
import { err, reportRejection, trap } from 'lib/trap'
import { getFaceDetails } from 'clientSideScene/sceneEntities'
import { DefaultPlaneStr } from 'lib/planes'
import { uuidv4 } from 'lib/utils'
import { Coords2d } from 'lang/std/sketch'
import { deleteSegment } from 'clientSideScene/ClientSideSceneComp'
import { executeAst } from 'lang/langHelpers'
import toast from 'react-hot-toast'
import { ToolbarModeName } from 'lib/toolbar'
import { quaternionFromUpNForward } from 'clientSideScene/helpers'
@ -83,6 +80,11 @@ import { Mesh, Vector3 } from 'three'
import { MachineManager } from 'components/MachineManagerProvider'
import { addShell } from 'lang/modifyAst/addShell'
import { KclCommandValue } from 'lib/commandTypes'
import { ModelingMachineContext } from 'components/ModelingMachineProvider'
import {
deleteSelectionPromise,
deletionErrorMessage,
} from 'lang/modifyAst/deleteSelection'
import { getPathsFromPlaneArtifact } from 'lang/std/artifactGraph'
import { createProfileStartHandle } from 'clientSideScene/segments'
import { DRAFT_POINT } from 'clientSideScene/sceneInfra'
@ -308,6 +310,10 @@ export type ModelingMachineEvent =
| { type: 'Helix'; data: ModelingCommandSchema['Helix'] }
| { type: 'Text-to-CAD'; data: ModelingCommandSchema['Text-to-CAD'] }
| { type: 'Prompt-to-edit'; data: ModelingCommandSchema['Prompt-to-edit'] }
| {
type: 'Delete selection'
data: ModelingCommandSchema['Delete selection']
}
| {
type: 'Add rectangle origin'
data: [x: number, y: number]
@ -731,38 +737,6 @@ export const modelingMachine = setup({
}
})().catch(reportRejection)
},
'AST delete selection': ({ context: { selectionRanges } }) => {
;(async () => {
const errorMessage =
'Unable to delete selection. Please edit manually in code pane.'
let ast = kclManager.ast
const modifiedAst = await deleteFromSelection(
ast,
selectionRanges.graphSelections[0],
kclManager.variables,
engineCommandManager.artifactGraph,
getFaceDetails
)
if (err(modifiedAst)) {
toast.error(errorMessage)
return
}
const testExecute = await executeAst({
ast: modifiedAst,
engineCommandManager,
isMock: true,
})
if (testExecute.errors.length) {
toast.error(errorMessage)
return
}
await kclManager.updateAst(modifiedAst, true)
await codeManager.updateEditorWithAstAndWriteToFile(modifiedAst)
})().catch(reportRejection)
},
'set selection filter to curves only': () => {
;(async () => {
await engineCommandManager.sendSceneCommand({
@ -2170,6 +2144,34 @@ export const modelingMachine = setup({
input: ModelingCommandSchema['Prompt-to-edit']
}) => {}
),
deleteSelectionAstMod: fromPromise(
({
input: { selectionRanges },
}: {
input: { selectionRanges: Selections }
}) => {
return new Promise((resolve, reject) => {
if (!selectionRanges) {
reject(new Error(deletionErrorMessage))
}
const selection = selectionRanges.graphSelections[0]
if (!selectionRanges) {
reject(new Error(deletionErrorMessage))
}
deleteSelectionPromise(selection)
.then((result) => {
if (err(result)) {
reject(result)
return
}
resolve(result)
})
.catch(reject)
})
}
),
},
// end actors
}).createMachine({
@ -2243,10 +2245,9 @@ export const modelingMachine = setup({
},
'Delete selection': {
target: 'idle',
target: 'Applying Delete selection',
guard: 'has valid selection for deletion',
actions: ['AST delete selection'],
reenter: false,
reenter: true,
},
'Text-to-CAD': {
@ -3366,6 +3367,28 @@ export const modelingMachine = setup({
onError: 'idle',
},
},
'Applying Delete selection': {
invoke: {
src: 'deleteSelectionAstMod',
id: 'deleteSelectionAstMod',
input: ({ event, context }) => {
return { selectionRanges: context.selectionRanges }
},
onDone: 'idle',
onError: {
target: 'idle',
reenter: true,
actions: ({ event }) => {
if ('error' in event && err(event.error)) {
toast.error(event.error.message)
}
},
},
},
},
},
initial: 'idle',

View File

@ -82,7 +82,7 @@ const createWindow = (pathToOpen?: string, reuse?: boolean): BrowserWindow => {
}
if (!newWindow) {
newWindow = new BrowserWindow({
autoHideMenuBar: true,
autoHideMenuBar: false,
show: false,
width: 1800,
height: 1200,

View File

@ -34,7 +34,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011"
dependencies = [
"cfg-if",
"getrandom",
"getrandom 0.2.15",
"once_cell",
"version_check",
"zerocopy",
@ -715,9 +715,9 @@ dependencies = [
[[package]]
name = "data-encoding"
version = "2.7.0"
version = "2.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0e60eed09d8c01d3cee5b7d30acb059b76614c918fa0f992e0dd6eeb10daad6f"
checksum = "575f75dfd25738df5b91b8e43e14d44bda14637a58fae779fd2b064f8bf3e010"
[[package]]
name = "deranged"
@ -730,7 +730,7 @@ dependencies = [
[[package]]
name = "derive-docs"
version = "0.1.36"
version = "0.1.38"
dependencies = [
"Inflector",
"anyhow",
@ -1080,10 +1080,22 @@ dependencies = [
"cfg-if",
"js-sys",
"libc",
"wasi",
"wasi 0.11.0+wasi-snapshot-preview1",
"wasm-bindgen",
]
[[package]]
name = "getrandom"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "43a49c392881ce6d5c3b8cb70f98717b7c07aabbdff06687b9030dbfbe2725f8"
dependencies = [
"cfg-if",
"libc",
"wasi 0.13.3+wasi-0.2.2",
"windows-targets",
]
[[package]]
name = "gimli"
version = "0.31.1"
@ -1712,7 +1724,7 @@ dependencies = [
[[package]]
name = "kcl-lib"
version = "0.2.35"
version = "0.2.38"
dependencies = [
"anyhow",
"approx 0.5.1",
@ -1779,7 +1791,7 @@ dependencies = [
[[package]]
name = "kcl-test-server"
version = "0.1.21"
version = "0.1.38"
dependencies = [
"anyhow",
"hyper 0.14.32",
@ -1846,9 +1858,9 @@ dependencies = [
[[package]]
name = "kittycad-modeling-cmds"
version = "0.2.93"
version = "0.2.97"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "67a993046541732e3c3ddd8a0364b55b7b138a9258beff353b6e7a043a41dce3"
checksum = "6c37ad10b8a2afdcd1852d027f123cf4e38864ea93e0fda5c7ee1e8a49af49fb"
dependencies = [
"anyhow",
"chrono",
@ -2094,7 +2106,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd"
dependencies = [
"libc",
"wasi",
"wasi 0.11.0+wasi-snapshot-preview1",
"windows-sys 0.52.0",
]
@ -2688,7 +2700,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2fe5ef3495d7d2e377ff17b1a8ce2ee2ec2a18cde8b6ad6619d65d0701c135d"
dependencies = [
"bytes",
"getrandom",
"getrandom 0.2.15",
"rand 0.8.5",
"ring",
"rustc-hash 2.1.0",
@ -2785,7 +2797,7 @@ version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
dependencies = [
"getrandom",
"getrandom 0.2.15",
]
[[package]]
@ -2965,7 +2977,7 @@ dependencies = [
"anyhow",
"async-trait",
"futures",
"getrandom",
"getrandom 0.2.15",
"http 1.2.0",
"hyper 1.5.2",
"parking_lot 0.11.2",
@ -2986,7 +2998,7 @@ checksum = "73e6153390585f6961341b50e5a1931d6be6dee4292283635903c26ef9d980d2"
dependencies = [
"anyhow",
"async-trait",
"getrandom",
"getrandom 0.2.15",
"http 1.2.0",
"matchit",
"opentelemetry",
@ -3019,7 +3031,7 @@ checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d"
dependencies = [
"cc",
"cfg-if",
"getrandom",
"getrandom 0.2.15",
"libc",
"spin",
"untrusted",
@ -3605,7 +3617,7 @@ checksum = "9a8a559c81686f576e8cd0290cd2a24a2a9ad80c98b3478856500fcbd7acd704"
dependencies = [
"cfg-if",
"fastrand",
"getrandom",
"getrandom 0.2.15",
"once_cell",
"rustix",
"windows-sys 0.59.0",
@ -4196,11 +4208,12 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
[[package]]
name = "uuid"
version = "1.12.1"
version = "1.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b3758f5e68192bb96cc8f9b7e2c2cfdabb435499a28499a42f8f984092adad4b"
checksum = "ced87ca4be083373936a67f8de945faa23b6b42384bd5b64434850802c6dccd0"
dependencies = [
"getrandom",
"getrandom 0.3.1",
"js-sys",
"serde",
"wasm-bindgen",
]
@ -4272,6 +4285,15 @@ version = "0.11.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
name = "wasi"
version = "0.13.3+wasi-0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "26816d2e1a4a36a2940b96c5296ce403917633dff8f3440e9b236ed6f6bacad2"
dependencies = [
"wit-bindgen-rt",
]
[[package]]
name = "wasm-bindgen"
version = "0.2.100"
@ -4353,6 +4375,7 @@ dependencies = [
"console_error_panic_hook",
"data-encoding",
"futures",
"getrandom 0.2.15",
"gloo-utils",
"image",
"js-sys",
@ -4592,6 +4615,15 @@ dependencies = [
"memchr",
]
[[package]]
name = "wit-bindgen-rt"
version = "0.33.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3268f3d866458b787f390cf61f4bbb563b922d091359f9608842999eaee3943c"
dependencies = [
"bitflags 2.8.0",
]
[[package]]
name = "write16"
version = "1.0.0"

View File

@ -39,6 +39,8 @@ dhat-heap = ["kcl-lib/dhat-heap"]
[target.'cfg(target_arch = "wasm32")'.dependencies]
console_error_panic_hook = "0.1.7"
futures = "0.3.31"
# Enable the feature in a transitive dependency.
getrandom = { version = "0.2", features = ["js"] }
js-sys = "0.3.72"
tower-lsp = { version = "0.20.0", default-features = false, features = ["runtime-agnostic"] }
wasm-bindgen-futures = { version = "0.4.44", features = ["futures-core-03-stream"] }
@ -78,7 +80,7 @@ members = [
[workspace.dependencies]
http = "1"
kittycad = { version = "0.3.28", default-features = false, features = ["js", "requests"] }
kittycad-modeling-cmds = { version = "0.2.93", features = [
kittycad-modeling-cmds = { version = "0.2.97", features = [
"ts-rs",
"websocket",
] }

View File

@ -1,7 +1,7 @@
[package]
name = "derive-docs"
description = "A tool for generating documentation from Rust derive macros"
version = "0.1.36"
version = "0.1.38"
edition = "2021"
license = "MIT"
repository = "https://github.com/KittyCAD/modeling-app"

View File

@ -328,7 +328,7 @@ fn do_stdlib_inner(
let label_required = !(i == 0 && metadata.unlabeled_first);
if ty_string != "ExecState" && ty_string != "Args" {
let schema = quote! {
generator.root_schema_for::<#ty_ident>()
#docs_crate::cleanup_number_tuples_root(generator.root_schema_for::<#ty_ident>())
};
arg_types.push(quote! {
#docs_crate::StdLibFnArg {
@ -393,7 +393,7 @@ fn do_stdlib_inner(
let return_type = if !ret_ty_string.is_empty() || ret_ty_string != "()" {
let ret_ty_string = rust_type_to_openapi_type(&ret_ty_string);
quote! {
let schema = generator.root_schema_for::<#return_type_inner>();
let schema = #docs_crate::cleanup_number_tuples_root(generator.root_schema_for::<#return_type_inner>());
Some(#docs_crate::StdLibFnArg {
name: "".to_string(),
type_: #ret_ty_string.to_string(),

View File

@ -111,7 +111,7 @@ impl crate::docs::StdLibFn for SomeFn {
vec![crate::docs::StdLibFnArg {
name: "data".to_string(),
type_: "Foo".to_string(),
schema: generator.root_schema_for::<Foo>(),
schema: crate::docs::cleanup_number_tuples_root(generator.root_schema_for::<Foo>()),
required: true,
label_required: true,
description: String::new().to_string(),
@ -123,7 +123,7 @@ impl crate::docs::StdLibFn for SomeFn {
let mut settings = schemars::gen::SchemaSettings::openapi3();
settings.inline_subschemas = inline_subschemas;
let mut generator = schemars::gen::SchemaGenerator::new(settings);
let schema = generator.root_schema_for::<i32>();
let schema = crate::docs::cleanup_number_tuples_root(generator.root_schema_for::<i32>());
Some(crate::docs::StdLibFnArg {
name: "".to_string(),
type_: "i32".to_string(),

View File

@ -111,7 +111,7 @@ impl crate::docs::StdLibFn for SomeFn {
vec![crate::docs::StdLibFnArg {
name: "data".to_string(),
type_: "string".to_string(),
schema: generator.root_schema_for::<str>(),
schema: crate::docs::cleanup_number_tuples_root(generator.root_schema_for::<str>()),
required: true,
label_required: true,
description: String::new().to_string(),
@ -123,7 +123,7 @@ impl crate::docs::StdLibFn for SomeFn {
let mut settings = schemars::gen::SchemaSettings::openapi3();
settings.inline_subschemas = inline_subschemas;
let mut generator = schemars::gen::SchemaGenerator::new(settings);
let schema = generator.root_schema_for::<i32>();
let schema = crate::docs::cleanup_number_tuples_root(generator.root_schema_for::<i32>());
Some(crate::docs::StdLibFnArg {
name: "".to_string(),
type_: "i32".to_string(),

View File

@ -172,7 +172,9 @@ impl crate::docs::StdLibFn for Show {
vec![crate::docs::StdLibFnArg {
name: "args".to_string(),
type_: "[number]".to_string(),
schema: generator.root_schema_for::<[f64; 2usize]>(),
schema: crate::docs::cleanup_number_tuples_root(
generator.root_schema_for::<[f64; 2usize]>(),
),
required: true,
label_required: true,
description: String::new().to_string(),
@ -184,7 +186,7 @@ impl crate::docs::StdLibFn for Show {
let mut settings = schemars::gen::SchemaSettings::openapi3();
settings.inline_subschemas = inline_subschemas;
let mut generator = schemars::gen::SchemaGenerator::new(settings);
let schema = generator.root_schema_for::<f64>();
let schema = crate::docs::cleanup_number_tuples_root(generator.root_schema_for::<f64>());
Some(crate::docs::StdLibFnArg {
name: "".to_string(),
type_: "number".to_string(),

View File

@ -112,7 +112,7 @@ impl crate::docs::StdLibFn for Show {
vec![crate::docs::StdLibFnArg {
name: "args".to_string(),
type_: "number".to_string(),
schema: generator.root_schema_for::<f64>(),
schema: crate::docs::cleanup_number_tuples_root(generator.root_schema_for::<f64>()),
required: true,
label_required: true,
description: String::new().to_string(),
@ -124,7 +124,7 @@ impl crate::docs::StdLibFn for Show {
let mut settings = schemars::gen::SchemaSettings::openapi3();
settings.inline_subschemas = inline_subschemas;
let mut generator = schemars::gen::SchemaGenerator::new(settings);
let schema = generator.root_schema_for::<f64>();
let schema = crate::docs::cleanup_number_tuples_root(generator.root_schema_for::<f64>());
Some(crate::docs::StdLibFnArg {
name: "".to_string(),
type_: "number".to_string(),

View File

@ -173,7 +173,9 @@ impl crate::docs::StdLibFn for MyFunc {
vec![crate::docs::StdLibFnArg {
name: "args".to_string(),
type_: "kittycad::types::InputFormat".to_string(),
schema: generator.root_schema_for::<Option<kittycad::types::InputFormat>>(),
schema: crate::docs::cleanup_number_tuples_root(
generator.root_schema_for::<Option<kittycad::types::InputFormat>>(),
),
required: false,
label_required: true,
description: String::new().to_string(),
@ -185,7 +187,8 @@ impl crate::docs::StdLibFn for MyFunc {
let mut settings = schemars::gen::SchemaSettings::openapi3();
settings.inline_subschemas = inline_subschemas;
let mut generator = schemars::gen::SchemaGenerator::new(settings);
let schema = generator.root_schema_for::<Vec<Sketch>>();
let schema =
crate::docs::cleanup_number_tuples_root(generator.root_schema_for::<Vec<Sketch>>());
Some(crate::docs::StdLibFnArg {
name: "".to_string(),
type_: "[Sketch]".to_string(),

View File

@ -174,7 +174,9 @@ impl crate::docs::StdLibFn for LineTo {
crate::docs::StdLibFnArg {
name: "data".to_string(),
type_: "LineToData".to_string(),
schema: generator.root_schema_for::<LineToData>(),
schema: crate::docs::cleanup_number_tuples_root(
generator.root_schema_for::<LineToData>(),
),
required: true,
label_required: true,
description: String::new().to_string(),
@ -183,7 +185,9 @@ impl crate::docs::StdLibFn for LineTo {
crate::docs::StdLibFnArg {
name: "sketch".to_string(),
type_: "Sketch".to_string(),
schema: generator.root_schema_for::<Sketch>(),
schema: crate::docs::cleanup_number_tuples_root(
generator.root_schema_for::<Sketch>(),
),
required: true,
label_required: true,
description: "the sketch you're adding the line to".to_string(),
@ -196,7 +200,7 @@ impl crate::docs::StdLibFn for LineTo {
let mut settings = schemars::gen::SchemaSettings::openapi3();
settings.inline_subschemas = inline_subschemas;
let mut generator = schemars::gen::SchemaGenerator::new(settings);
let schema = generator.root_schema_for::<Sketch>();
let schema = crate::docs::cleanup_number_tuples_root(generator.root_schema_for::<Sketch>());
Some(crate::docs::StdLibFnArg {
name: "".to_string(),
type_: "Sketch".to_string(),

View File

@ -172,7 +172,9 @@ impl crate::docs::StdLibFn for Min {
vec![crate::docs::StdLibFnArg {
name: "args".to_string(),
type_: "[number]".to_string(),
schema: generator.root_schema_for::<Vec<f64>>(),
schema: crate::docs::cleanup_number_tuples_root(
generator.root_schema_for::<Vec<f64>>(),
),
required: true,
label_required: true,
description: String::new().to_string(),
@ -184,7 +186,7 @@ impl crate::docs::StdLibFn for Min {
let mut settings = schemars::gen::SchemaSettings::openapi3();
settings.inline_subschemas = inline_subschemas;
let mut generator = schemars::gen::SchemaGenerator::new(settings);
let schema = generator.root_schema_for::<f64>();
let schema = crate::docs::cleanup_number_tuples_root(generator.root_schema_for::<f64>());
Some(crate::docs::StdLibFnArg {
name: "".to_string(),
type_: "number".to_string(),

View File

@ -112,7 +112,9 @@ impl crate::docs::StdLibFn for Show {
vec![crate::docs::StdLibFnArg {
name: "args".to_string(),
type_: "number".to_string(),
schema: generator.root_schema_for::<Option<f64>>(),
schema: crate::docs::cleanup_number_tuples_root(
generator.root_schema_for::<Option<f64>>(),
),
required: false,
label_required: true,
description: String::new().to_string(),
@ -124,7 +126,7 @@ impl crate::docs::StdLibFn for Show {
let mut settings = schemars::gen::SchemaSettings::openapi3();
settings.inline_subschemas = inline_subschemas;
let mut generator = schemars::gen::SchemaGenerator::new(settings);
let schema = generator.root_schema_for::<f64>();
let schema = crate::docs::cleanup_number_tuples_root(generator.root_schema_for::<f64>());
Some(crate::docs::StdLibFnArg {
name: "".to_string(),
type_: "number".to_string(),

View File

@ -112,7 +112,9 @@ impl crate::docs::StdLibFn for Import {
vec![crate::docs::StdLibFnArg {
name: "args".to_string(),
type_: "kittycad::types::InputFormat".to_string(),
schema: generator.root_schema_for::<Option<kittycad::types::InputFormat>>(),
schema: crate::docs::cleanup_number_tuples_root(
generator.root_schema_for::<Option<kittycad::types::InputFormat>>(),
),
required: false,
label_required: true,
description: String::new().to_string(),
@ -124,7 +126,7 @@ impl crate::docs::StdLibFn for Import {
let mut settings = schemars::gen::SchemaSettings::openapi3();
settings.inline_subschemas = inline_subschemas;
let mut generator = schemars::gen::SchemaGenerator::new(settings);
let schema = generator.root_schema_for::<f64>();
let schema = crate::docs::cleanup_number_tuples_root(generator.root_schema_for::<f64>());
Some(crate::docs::StdLibFnArg {
name: "".to_string(),
type_: "number".to_string(),

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