Compare commits

...

30 Commits

Author SHA1 Message Date
07eaf93e78 Cut release v0.21.1 (#2347) 2024-05-13 10:05:29 -04:00
6a5ca3088a remove getExtrudeWallTransform (#2342)
* remove getExtrudeWallTransform

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>
2024-05-10 17:28:14 -07:00
6501072d80 turn on formatting test now working (#2341)
turn on test now working

Signed-off-by: Jess Frazelle <github@jessfraz.com>
2024-05-10 23:51:54 +00:00
726fd02bad Add a component for toolbar buttons with a dropdown, consolidate Constrain actions under one (#2327) 2024-05-10 19:02:11 -04:00
d0f9ae475f format button wasnt working, will add playwright test so we don't regress again (#2340)
* add test

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

* add another test

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

* add another test

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

* updates

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

* off by one error

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

* add two tests

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

* fix typos

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>

* fix semantic tokens for commants

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

* fix tests

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

---------

Signed-off-by: Jess Frazelle <github@jessfraz.com>
2024-05-10 22:30:40 +00:00
da323e22d4 Cut release v0.21.0 (#2334) 2024-05-10 16:23:04 -04:00
8dc3628e9b Bump kittycad-modeling-cmds from 0.2.23 to 0.2.24 in /src/wasm-lib (#2330)
Bumps [kittycad-modeling-cmds](https://github.com/KittyCAD/modeling-api) from 0.2.23 to 0.2.24.
- [Commits](https://github.com/KittyCAD/modeling-api/compare/kittycad-modeling-cmds-0.2.23...kittycad-modeling-cmds-0.2.24)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-09 11:38:43 -07:00
253744867b Franknoirot/sketch light mode (#2328) 2024-05-09 08:38:42 -04:00
c45eb1e3e3 clean up old imports (#2331) 2024-05-09 15:38:04 +10:00
758aac9328 fix unreliable channel (#2329)
* fix unreliable channel

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

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-08 10:56:37 -07:00
b3d4ab91fc Bump serde from 1.0.200 to 1.0.201 in /src/wasm-lib (#2320)
Bumps [serde](https://github.com/serde-rs/serde) from 1.0.200 to 1.0.201.
- [Release notes](https://github.com/serde-rs/serde/releases)
- [Commits](https://github.com/serde-rs/serde/compare/v1.0.200...v1.0.201)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-08 10:56:21 -07:00
5e73fa45f0 Remove useBackdropHighlight (#2323) 2024-05-08 12:27:41 -04:00
17d23a17db Move the command bar out to the right in the AppHeader (#2317)
* Move the command bar out to the right in the AppHeader

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

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

* trigger ci

* trigger ci

---------

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

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

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-07 09:40:21 -07:00
9c9ffa0d03 Bump anyhow from 1.0.82 to 1.0.83 in /src/wasm-lib (#2309)
Bumps [anyhow](https://github.com/dtolnay/anyhow) from 1.0.82 to 1.0.83.
- [Release notes](https://github.com/dtolnay/anyhow/releases)
- [Commits](https://github.com/dtolnay/anyhow/compare/1.0.82...1.0.83)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-07 09:40:13 -07:00
c62b9f1f04 Bump thiserror from 1.0.59 to 1.0.60 in /src/wasm-lib (#2307)
Bumps [thiserror](https://github.com/dtolnay/thiserror) from 1.0.59 to 1.0.60.
- [Release notes](https://github.com/dtolnay/thiserror/releases)
- [Commits](https://github.com/dtolnay/thiserror/compare/1.0.59...1.0.60)

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

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

This reverts commit e7ab645267.

* rejig out side of xstate

* test tweak

* more test tweak

* refactor some codeManager stuff

* tsc

* try and fix tests

* revert small uneeded change

* fix

* snapshot tweak

* more test tweak

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

* small tweak

* disable bad test

* fmt

---------

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

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

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

* updates

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

* fix ts

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

* fix

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

* small changes

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

* updates

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

---------

Signed-off-by: Jess Frazelle <github@jessfraz.com>
2024-05-03 14:45:39 -07:00
44f0d7c25c turn on tauri app logging (#2296)
* turn on tauri app logging

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

* fixups

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

* switch everything to logs;

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

* remove other shit logs

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

* fix macos open

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

---------

Signed-off-by: Jess Frazelle <github@jessfraz.com>
2024-05-03 13:49:26 -07:00
3ccb04c4e7 Fix Create Release commit sha (#2293) 2024-05-03 05:37:27 -04:00
258 changed files with 1519 additions and 1628 deletions

View File

@ -239,8 +239,8 @@ jobs:
includeDebug: true
args: "${{ env.TAURI_ARGS_MACOS }} ${{ env.TAURI_ARGS_UBUNTU }}"
- name: Mac App Store
if: ${{ env.BUILD_RELEASE == 'true' && matrix.os == 'macos-14' }}
- name: Build for Mac TestFlight (nightly)
if: ${{ github.event_name == 'schedule' && matrix.os == 'macos-14' }}
shell: bash
run: |
unset APPLE_SIGNING_IDENTITY
@ -302,9 +302,9 @@ jobs:
APPLE_STORE_P12_PASSWORD: ${{ secrets.APPLE_STORE_P12_PASSWORD }}
- name: 'Upload app to TestFlight'
- name: 'Upload to Mac TestFlight (nightly)'
uses: apple-actions/upload-testflight-build@v1
if: ${{ env.BUILD_RELEASE == 'true' && matrix.os == 'macos-14' }}
if: ${{ github.event_name == 'schedule' && matrix.os == 'macos-14' }}
with:
app-path: 'src-tauri/target/universal-apple-darwin/release/bundle/macos/Zoo Modeling App.pkg'
issuer-id: ${{ secrets.APPLE_STORE_ISSUER_ID }}
@ -313,8 +313,8 @@ jobs:
app-type: osx
- name: Clean up after Mac App Store
if: ${{ env.BUILD_RELEASE == 'true' && matrix.os == 'macos-14' }}
- name: Clean up after Mac TestFlight (nightly)
if: ${{ github.event_name == 'schedule' && matrix.os == 'macos-14' }}
shell: bash
run: |
git status
@ -354,7 +354,7 @@ jobs:
path: "${{ env.PREFIX }}/${{ env.MODE }}/bundle/*/*"
- name: Run e2e tests (linux only)
if: matrix.os == 'ubuntu-latest'
if: ${{ matrix.os == 'ubuntu-latest' && github.event_name != 'release' && github.event_name != 'schedule' }}
run: |
cargo install tauri-driver --force
source .env.${{ env.BUILD_RELEASE == 'true' && 'production' || 'development' }}

View File

@ -17,11 +17,11 @@ jobs:
name: Read Cut release PR info and create release
with:
script: |
const { owner, repo, sha } = context.repo
const { owner, repo } = context.repo
const pulls = await github.rest.repos.listPullRequestsAssociatedWithCommit({
owner,
repo,
commit_sha: sha,
commit_sha: context.sha,
})
const { title, body } = pulls.data[0]
const version = title.split('Cut release ')[1]

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

View File

@ -31,7 +31,6 @@ layout: manual
* [`fillet`](kcl/fillet)
* [`floor`](kcl/floor)
* [`getEdge`](kcl/getEdge)
* [`getExtrudeWallTransform`](kcl/getExtrudeWallTransform)
* [`getNextAdjacentEdge`](kcl/getNextAdjacentEdge)
* [`getOppositeEdge`](kcl/getOppositeEdge)
* [`getPreviousAdjacentEdge`](kcl/getPreviousAdjacentEdge)

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

View File

@ -28338,839 +28338,6 @@
"const box = startSketchOn('XY')\n |> startProfileAt([0, 0], %)\n |> line([0, 10], %)\n |> line([10, 0], %)\n |> line([0, -10], %, 'revolveAxis')\n |> close(%)\n |> extrude(10, %)\n\nconst sketch001 = startSketchOn('XY')\n |> startProfileAt([0, -10], %)\n |> line([0, -10], %)\n |> line([2, 0], %)\n |> line([0, 10], %)\n |> close(%)\n |> revolve({\n axis: getEdge('revolveAxis', box),\n angle: 90\n }, %)"
]
},
{
"name": "getExtrudeWallTransform",
"summary": "Returns the extrude wall transform.",
"description": "",
"tags": [],
"args": [
{
"name": "surface_name",
"type": "string",
"schema": {
"type": "string"
},
"required": true
},
{
"name": "extrude_group",
"type": "ExtrudeGroup",
"schema": {
"description": "An extrude group is a collection of extrude surfaces.",
"type": "object",
"required": [
"__meta",
"height",
"id",
"position",
"rotation",
"sketchGroupValues",
"value",
"xAxis",
"yAxis",
"zAxis"
],
"properties": {
"__meta": {
"description": "Metadata.",
"type": "array",
"items": {
"description": "Metadata.",
"type": "object",
"required": [
"sourceRange"
],
"properties": {
"sourceRange": {
"description": "The source range.",
"type": "array",
"items": {
"type": "integer",
"format": "uint",
"minimum": 0.0
},
"maxItems": 2,
"minItems": 2
}
}
}
},
"endCapId": {
"description": "The id of the extrusion end cap",
"type": "string",
"format": "uuid",
"nullable": true
},
"height": {
"description": "The height of the extrude group.",
"type": "number",
"format": "double"
},
"id": {
"description": "The id of the extrude group.",
"type": "string",
"format": "uuid"
},
"position": {
"description": "The position of the extrude group.",
"type": "array",
"items": {
"type": "number",
"format": "double"
},
"maxItems": 3,
"minItems": 3
},
"rotation": {
"description": "The rotation of the extrude group.",
"type": "array",
"items": {
"type": "number",
"format": "double"
},
"maxItems": 4,
"minItems": 4
},
"sketchGroupValues": {
"description": "The sketch group paths.",
"type": "array",
"items": {
"description": "A path.",
"oneOf": [
{
"description": "A path that goes to a point.",
"type": "object",
"required": [
"__geoMeta",
"from",
"name",
"to",
"type"
],
"properties": {
"__geoMeta": {
"description": "Metadata.",
"type": "object",
"required": [
"id",
"sourceRange"
],
"properties": {
"id": {
"description": "The id of the geometry.",
"type": "string",
"format": "uuid"
},
"sourceRange": {
"description": "The source range.",
"type": "array",
"items": {
"type": "integer",
"format": "uint",
"minimum": 0.0
},
"maxItems": 2,
"minItems": 2
}
}
},
"from": {
"description": "The from point.",
"type": "array",
"items": {
"type": "number",
"format": "double"
},
"maxItems": 2,
"minItems": 2
},
"name": {
"description": "The name of the path.",
"type": "string"
},
"to": {
"description": "The to point.",
"type": "array",
"items": {
"type": "number",
"format": "double"
},
"maxItems": 2,
"minItems": 2
},
"type": {
"type": "string",
"enum": [
"ToPoint"
]
}
}
},
{
"description": "A arc that is tangential to the last path segment that goes to a point",
"type": "object",
"required": [
"__geoMeta",
"ccw",
"center",
"from",
"name",
"to",
"type"
],
"properties": {
"__geoMeta": {
"description": "Metadata.",
"type": "object",
"required": [
"id",
"sourceRange"
],
"properties": {
"id": {
"description": "The id of the geometry.",
"type": "string",
"format": "uuid"
},
"sourceRange": {
"description": "The source range.",
"type": "array",
"items": {
"type": "integer",
"format": "uint",
"minimum": 0.0
},
"maxItems": 2,
"minItems": 2
}
}
},
"ccw": {
"description": "arc's direction",
"type": "boolean"
},
"center": {
"description": "the arc's center",
"type": "array",
"items": {
"type": "number",
"format": "double"
},
"maxItems": 2,
"minItems": 2
},
"from": {
"description": "The from point.",
"type": "array",
"items": {
"type": "number",
"format": "double"
},
"maxItems": 2,
"minItems": 2
},
"name": {
"description": "The name of the path.",
"type": "string"
},
"to": {
"description": "The to point.",
"type": "array",
"items": {
"type": "number",
"format": "double"
},
"maxItems": 2,
"minItems": 2
},
"type": {
"type": "string",
"enum": [
"TangentialArcTo"
]
}
}
},
{
"description": "A arc that is tangential to the last path segment",
"type": "object",
"required": [
"__geoMeta",
"from",
"name",
"to",
"type"
],
"properties": {
"__geoMeta": {
"description": "Metadata.",
"type": "object",
"required": [
"id",
"sourceRange"
],
"properties": {
"id": {
"description": "The id of the geometry.",
"type": "string",
"format": "uuid"
},
"sourceRange": {
"description": "The source range.",
"type": "array",
"items": {
"type": "integer",
"format": "uint",
"minimum": 0.0
},
"maxItems": 2,
"minItems": 2
}
}
},
"from": {
"description": "The from point.",
"type": "array",
"items": {
"type": "number",
"format": "double"
},
"maxItems": 2,
"minItems": 2
},
"name": {
"description": "The name of the path.",
"type": "string"
},
"to": {
"description": "The to point.",
"type": "array",
"items": {
"type": "number",
"format": "double"
},
"maxItems": 2,
"minItems": 2
},
"type": {
"type": "string",
"enum": [
"TangentialArc"
]
}
}
},
{
"description": "A path that is horizontal.",
"type": "object",
"required": [
"__geoMeta",
"from",
"name",
"to",
"type",
"x"
],
"properties": {
"__geoMeta": {
"description": "Metadata.",
"type": "object",
"required": [
"id",
"sourceRange"
],
"properties": {
"id": {
"description": "The id of the geometry.",
"type": "string",
"format": "uuid"
},
"sourceRange": {
"description": "The source range.",
"type": "array",
"items": {
"type": "integer",
"format": "uint",
"minimum": 0.0
},
"maxItems": 2,
"minItems": 2
}
}
},
"from": {
"description": "The from point.",
"type": "array",
"items": {
"type": "number",
"format": "double"
},
"maxItems": 2,
"minItems": 2
},
"name": {
"description": "The name of the path.",
"type": "string"
},
"to": {
"description": "The to point.",
"type": "array",
"items": {
"type": "number",
"format": "double"
},
"maxItems": 2,
"minItems": 2
},
"type": {
"type": "string",
"enum": [
"Horizontal"
]
},
"x": {
"description": "The x coordinate.",
"type": "number",
"format": "double"
}
}
},
{
"description": "An angled line to.",
"type": "object",
"required": [
"__geoMeta",
"from",
"name",
"to",
"type"
],
"properties": {
"__geoMeta": {
"description": "Metadata.",
"type": "object",
"required": [
"id",
"sourceRange"
],
"properties": {
"id": {
"description": "The id of the geometry.",
"type": "string",
"format": "uuid"
},
"sourceRange": {
"description": "The source range.",
"type": "array",
"items": {
"type": "integer",
"format": "uint",
"minimum": 0.0
},
"maxItems": 2,
"minItems": 2
}
}
},
"from": {
"description": "The from point.",
"type": "array",
"items": {
"type": "number",
"format": "double"
},
"maxItems": 2,
"minItems": 2
},
"name": {
"description": "The name of the path.",
"type": "string"
},
"to": {
"description": "The to point.",
"type": "array",
"items": {
"type": "number",
"format": "double"
},
"maxItems": 2,
"minItems": 2
},
"type": {
"type": "string",
"enum": [
"AngledLineTo"
]
},
"x": {
"description": "The x coordinate.",
"type": "number",
"format": "double",
"nullable": true
},
"y": {
"description": "The y coordinate.",
"type": "number",
"format": "double",
"nullable": true
}
}
},
{
"description": "A base path.",
"type": "object",
"required": [
"__geoMeta",
"from",
"name",
"to",
"type"
],
"properties": {
"__geoMeta": {
"description": "Metadata.",
"type": "object",
"required": [
"id",
"sourceRange"
],
"properties": {
"id": {
"description": "The id of the geometry.",
"type": "string",
"format": "uuid"
},
"sourceRange": {
"description": "The source range.",
"type": "array",
"items": {
"type": "integer",
"format": "uint",
"minimum": 0.0
},
"maxItems": 2,
"minItems": 2
}
}
},
"from": {
"description": "The from point.",
"type": "array",
"items": {
"type": "number",
"format": "double"
},
"maxItems": 2,
"minItems": 2
},
"name": {
"description": "The name of the path.",
"type": "string"
},
"to": {
"description": "The to point.",
"type": "array",
"items": {
"type": "number",
"format": "double"
},
"maxItems": 2,
"minItems": 2
},
"type": {
"type": "string",
"enum": [
"Base"
]
}
}
}
]
}
},
"startCapId": {
"description": "The id of the extrusion start cap",
"type": "string",
"format": "uuid",
"nullable": true
},
"value": {
"description": "The extrude surfaces.",
"type": "array",
"items": {
"description": "An extrude surface.",
"oneOf": [
{
"description": "An extrude plane.",
"type": "object",
"required": [
"faceId",
"id",
"name",
"position",
"rotation",
"sourceRange",
"type"
],
"properties": {
"faceId": {
"description": "The face id for the extrude plane.",
"type": "string",
"format": "uuid"
},
"id": {
"description": "The id of the geometry.",
"type": "string",
"format": "uuid"
},
"name": {
"description": "The name.",
"type": "string"
},
"position": {
"description": "The position.",
"type": "array",
"items": {
"type": "number",
"format": "double"
},
"maxItems": 3,
"minItems": 3
},
"rotation": {
"description": "The rotation.",
"type": "array",
"items": {
"type": "number",
"format": "double"
},
"maxItems": 4,
"minItems": 4
},
"sourceRange": {
"description": "The source range.",
"type": "array",
"items": {
"type": "integer",
"format": "uint",
"minimum": 0.0
},
"maxItems": 2,
"minItems": 2
},
"type": {
"type": "string",
"enum": [
"extrudePlane"
]
}
}
},
{
"description": "An extruded arc.",
"type": "object",
"required": [
"faceId",
"id",
"name",
"position",
"rotation",
"sourceRange",
"type"
],
"properties": {
"faceId": {
"description": "The face id for the extrude plane.",
"type": "string",
"format": "uuid"
},
"id": {
"description": "The id of the geometry.",
"type": "string",
"format": "uuid"
},
"name": {
"description": "The name.",
"type": "string"
},
"position": {
"description": "The position.",
"type": "array",
"items": {
"type": "number",
"format": "double"
},
"maxItems": 3,
"minItems": 3
},
"rotation": {
"description": "The rotation.",
"type": "array",
"items": {
"type": "number",
"format": "double"
},
"maxItems": 4,
"minItems": 4
},
"sourceRange": {
"description": "The source range.",
"type": "array",
"items": {
"type": "integer",
"format": "uint",
"minimum": 0.0
},
"maxItems": 2,
"minItems": 2
},
"type": {
"type": "string",
"enum": [
"extrudeArc"
]
}
}
}
]
}
},
"xAxis": {
"description": "The x-axis of the extrude group base plane in the 3D space",
"type": "object",
"required": [
"x",
"y",
"z"
],
"properties": {
"x": {
"type": "number",
"format": "double"
},
"y": {
"type": "number",
"format": "double"
},
"z": {
"type": "number",
"format": "double"
}
}
},
"yAxis": {
"description": "The y-axis of the extrude group base plane in the 3D space",
"type": "object",
"required": [
"x",
"y",
"z"
],
"properties": {
"x": {
"type": "number",
"format": "double"
},
"y": {
"type": "number",
"format": "double"
},
"z": {
"type": "number",
"format": "double"
}
}
},
"zAxis": {
"description": "The z-axis of the extrude group base plane in the 3D space",
"type": "object",
"required": [
"x",
"y",
"z"
],
"properties": {
"x": {
"type": "number",
"format": "double"
},
"y": {
"type": "number",
"format": "double"
},
"z": {
"type": "number",
"format": "double"
}
}
}
}
},
"required": true
}
],
"returnValue": {
"name": "",
"type": "ExtrudeTransform",
"schema": {
"type": "object",
"required": [
"__meta",
"position",
"rotation"
],
"properties": {
"__meta": {
"type": "array",
"items": {
"description": "Metadata.",
"type": "object",
"required": [
"sourceRange"
],
"properties": {
"sourceRange": {
"description": "The source range.",
"type": "array",
"items": {
"type": "integer",
"format": "uint",
"minimum": 0.0
},
"maxItems": 2,
"minItems": 2
}
}
}
},
"position": {
"type": "array",
"items": {
"type": "number",
"format": "double"
},
"maxItems": 3,
"minItems": 3
},
"rotation": {
"type": "array",
"items": {
"type": "number",
"format": "double"
},
"maxItems": 4,
"minItems": 4
}
}
},
"required": true
},
"unpublished": false,
"deprecated": false,
"examples": [
"const box = startSketchOn('XY')\n |> startProfileAt([0, 0], %)\n |> line([0, 10], %)\n |> line([10, 0], %)\n |> line([0, -10], %, \"surface\")\n |> close(%)\n |> extrude(5, %)\n\nconst transform = getExtrudeWallTransform('surface', box)"
]
},
{
"name": "getNextAdjacentEdge",
"summary": "Get the next adjacent edge to the edge given.",

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

@ -12,6 +12,7 @@ import {
TEST_SETTINGS_ONBOARDING_START,
} from './storageStates'
import * as TOML from '@iarna/toml'
import { Coords2d } from 'lang/std/sketch'
/*
debug helper: unfortunately we do rely on exact coord mouse clicks in a few places
@ -130,6 +131,7 @@ test('Basic sketch', async ({ page }) => {
// selected two lines therefore there should be two cursors
await expect(page.locator('.cm-cursor')).toHaveCount(2)
await page.getByRole('button', { name: 'Constrain' }).click()
await page.getByRole('button', { name: 'Equal Length' }).click()
await expect(page.locator('.cm-content'))
@ -263,6 +265,88 @@ test('Can moving camera', async ({ page, context }) => {
}, [1, -94, -94])
})
test('if you click the format button it formats your code', async ({
page,
}) => {
const u = getUtils(page)
await page.setViewportSize({ width: 1000, height: 500 })
await page.goto('/')
await u.waitForAuthSkipAppStart()
// check no error to begin with
await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible()
await page.click('.cm-content')
await page.keyboard.type(`const part001 = startSketchOn('XY')
|> startProfileAt([-10, -10], %)
|> line([20, 0], %)
|> line([0, 20], %)
|> line([-20, 0], %)
|> close(%)`)
await page.click('#code-pane button:first-child')
await page.click('button:has-text("Format code")')
await expect(page.locator('.cm-content'))
.toHaveText(`const part001 = startSketchOn('XY')
|> startProfileAt([-10, -10], %)
|> line([20, 0], %)
|> line([0, 20], %)
|> line([-20, 0], %)
|> close(%)`)
})
test('if you use the format keyboard binding it formats your code', async ({
page,
}) => {
const u = getUtils(page)
await page.addInitScript(async () => {
localStorage.setItem(
'persistCode',
`const part001 = startSketchOn('XY')
|> startProfileAt([-10, -10], %)
|> line([20, 0], %)
|> line([0, 20], %)
|> line([-20, 0], %)
|> close(%)`
)
})
await page.setViewportSize({ width: 1000, height: 500 })
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
// check no error to begin with
await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible()
await u.openDebugPanel()
await u.expectCmdLog('[data-message-type="execution-done"]')
await u.closeDebugPanel()
// focus the editor
await page.click('.cm-line')
// Hit alt+shift+f to format the code
await page.keyboard.press('Alt+Shift+KeyF')
await expect(page.locator('.cm-content'))
.toHaveText(`const part001 = startSketchOn('XY')
|> startProfileAt([-10, -10], %)
|> line([20, 0], %)
|> line([0, 20], %)
|> line([-20, 0], %)
|> close(%)`)
})
test('if you write invalid kcl you get inlined errors', async ({ page }) => {
const u = getUtils(page)
await page.setViewportSize({ width: 1000, height: 500 })
@ -857,11 +941,14 @@ test('Selections work on fresh and edited sketch', async ({ page }) => {
// click a segment hold shift and click an axis, see that a relevant constraint is enabled
await topHorzSegmentClick()
await page.keyboard.down('Shift')
const constrainButton = page.getByRole('button', { name: 'Constrain' })
const absYButton = page.getByRole('button', { name: 'ABS Y' })
await constrainButton.click()
await expect(absYButton).toBeDisabled()
await page.waitForTimeout(100)
await xAxisClick()
await page.keyboard.up('Shift')
await constrainButton.click()
await absYButton.and(page.locator(':not([disabled])')).waitFor()
await expect(absYButton).not.toBeDisabled()
@ -871,12 +958,14 @@ test('Selections work on fresh and edited sketch', async ({ page }) => {
await page.waitForTimeout(100)
// same selection but click the axis first
await xAxisClick()
await constrainButton.click()
await expect(absYButton).toBeDisabled()
await page.keyboard.down('Shift')
await page.waitForTimeout(100)
await topHorzSegmentClick()
await page.keyboard.up('Shift')
await constrainButton.click()
await expect(absYButton).not.toBeDisabled()
// clear selection by clicking on nothing
@ -885,10 +974,12 @@ test('Selections work on fresh and edited sketch', async ({ page }) => {
// check the same selection again by putting cursor in code first then selecting axis
await page.getByText(` |> line([-${commonPoints.num2}, 0], %)`).click()
await page.keyboard.down('Shift')
await constrainButton.click()
await expect(absYButton).toBeDisabled()
await page.waitForTimeout(100)
await xAxisClick()
await page.keyboard.up('Shift')
await constrainButton.click()
await expect(absYButton).not.toBeDisabled()
// clear selection by clicking on nothing
@ -950,9 +1041,8 @@ test.describe('Command bar tests', () => {
let cmdSearchBar = page.getByPlaceholder('Search commands')
// First try opening the command bar and closing it
// It has a different label on mac and windows/linux, "Meta+K" and "Ctrl+/" respectively
await page
.getByRole('button', { name: 'Ctrl+/' })
.getByRole('button', { name: 'Commands', exact: false })
.or(page.getByRole('button', { name: '⌘K' }))
.click()
await expect(cmdSearchBar).toBeVisible()
@ -998,9 +1088,9 @@ test.describe('Command bar tests', () => {
'persistCode',
`const distance = sqrt(20)
const part001 = startSketchOn('-XZ')
|> startProfileAt([-6.95, 4.98], %)
|> startProfileAt([-6.95, 10.98], %)
|> line([25.1, 0.41], %)
|> line([0.73, -14.93], %)
|> line([0.73, -20.93], %)
|> line([-23.44, 0.52], %)
|> close(%)
`
@ -1020,7 +1110,6 @@ test.describe('Command bar tests', () => {
page.getByRole('button', { name: 'Start Sketch' })
).not.toBeDisabled()
await u.clearCommandLogs()
await page.getByText('|> line([0.73, -14.93], %)').click()
await page.getByRole('button', { name: 'Extrude' }).isEnabled()
let cmdSearchBar = page.getByPlaceholder('Search commands')
@ -1030,6 +1119,12 @@ test.describe('Command bar tests', () => {
// Search for extrude command and choose it
await page.getByRole('option', { name: 'Extrude' }).click()
// Assert that we're on the selection step
await expect(page.getByRole('button', { name: 'selection' })).toBeDisabled()
// Select a face
await page.mouse.move(700, 200)
await page.mouse.click(700, 200)
// Assert that we're on the distance step
await expect(page.getByRole('button', { name: 'distance' })).toBeDisabled()
@ -1063,9 +1158,9 @@ test.describe('Command bar tests', () => {
`const distance = sqrt(20)
const distance001 = 5 + 7
const part001 = startSketchOn('-XZ')
|> startProfileAt([-6.95, 4.98], %)
|> startProfileAt([-6.95, 10.98], %)
|> line([25.1, 0.41], %)
|> line([0.73, -14.93], %)
|> line([0.73, -20.93], %)
|> line([-23.44, 0.52], %)
|> close(%)
|> extrude(distance001, %)`.replace(/(\r\n|\n|\r)/gm, '') // remove newlines
@ -1256,6 +1351,72 @@ test('ProgramMemory can be serialised', async ({ page }) => {
})
})
test('Hovering over 3d features highlights code', async ({ page }) => {
const u = getUtils(page)
await page.addInitScript(async () => {
localStorage.setItem(
'persistCode',
`const part001 = startSketchOn('-XZ')
|> startProfileAt([20, 0], %)
|> line([7.13, 4 + 0], %)
|> angledLine({ angle: 3 + 0, length: 3.14 + 0 }, %)
|> lineTo([20.14 + 0, -0.14 + 0], %)
|> xLineTo(29 + 0, %)
|> yLine(-3.14 + 0, %, 'a')
|> xLine(1.63, %)
|> angledLineOfXLength({ angle: 3 + 0, length: 3.14 }, %)
|> angledLineOfYLength({ angle: 30, length: 3 + 0 }, %)
|> angledLineToX({ angle: 22.14 + 0, to: 12 }, %)
|> angledLineToY({ angle: 30, to: 11.14 }, %)
|> angledLineThatIntersects({
angle: 3.14,
intersectTag: 'a',
offset: 0
}, %)
|> tangentialArcTo([13.14 + 0, 13.14], %)
|> close(%)
|> extrude(5 + 7, %)
`
)
})
await page.setViewportSize({ width: 1000, height: 500 })
await page.goto('/')
await u.waitForAuthSkipAppStart()
const extrusionTop: Coords2d = [800, 240]
const flatExtrusionFace: Coords2d = [960, 160]
const arc: Coords2d = [840, 160]
const close: Coords2d = [720, 200]
const nothing: Coords2d = [600, 200]
await page.mouse.move(nothing[0], nothing[1])
await page.mouse.click(nothing[0], nothing[1])
await expect(page.getByTestId('hover-highlight')).not.toBeVisible()
await page.waitForTimeout(200)
await page.mouse.move(extrusionTop[0], extrusionTop[1])
await expect(page.getByTestId('hover-highlight')).toBeVisible()
await page.mouse.move(nothing[0], nothing[1])
await expect(page.getByTestId('hover-highlight')).not.toBeVisible()
await page.mouse.move(arc[0], arc[1])
await expect(page.getByTestId('hover-highlight')).toBeVisible()
await page.mouse.move(nothing[0], nothing[1])
await expect(page.getByTestId('hover-highlight')).not.toBeVisible()
await page.mouse.move(close[0], close[1])
await expect(page.getByTestId('hover-highlight')).toBeVisible()
await page.mouse.move(nothing[0], nothing[1])
await expect(page.getByTestId('hover-highlight')).not.toBeVisible()
await page.mouse.move(flatExtrusionFace[0], flatExtrusionFace[1])
await expect(page.getByTestId('hover-highlight')).toHaveCount(5) // multiple lines
await page.mouse.move(nothing[0], nothing[1])
await page.waitForTimeout(100)
await expect(page.getByTestId('hover-highlight')).not.toBeVisible()
})
test("Various pipe expressions should and shouldn't allow edit and or extrude", async ({
page,
}) => {
@ -1390,7 +1551,7 @@ test('Deselecting line tool should mean nothing happens on click', async ({
`const part001 = startSketchOn('-XZ')`
)
await page.waitForTimeout(300)
await page.waitForTimeout(600)
let previousCodeContent = await page.locator('.cm-content').innerText()
@ -1752,6 +1913,7 @@ test('Can code mod a line length', async ({ page }) => {
const startXPx = 500
await page.mouse.move(startXPx + PUR * 15, 250 - PUR * 10)
await page.mouse.click(615, 102)
await page.getByRole('button', { name: 'Constrain', exact: true }).click()
await page.getByRole('button', { name: 'length', exact: true }).click()
await page.getByText('Add constraining value').click()

View File

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 39 KiB

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 51 KiB

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 45 KiB

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 52 KiB

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 72 KiB

After

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 47 KiB

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 52 KiB

After

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 47 KiB

After

Width:  |  Height:  |  Size: 46 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: 50 KiB

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 46 KiB

After

Width:  |  Height:  |  Size: 45 KiB

View File

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

View File

@ -1,6 +1,6 @@
{
"name": "untitled-app",
"version": "0.20.0",
"version": "0.21.1",
"private": true,
"dependencies": {
"@codemirror/autocomplete": "^6.16.0",
@ -10,7 +10,7 @@
"@fortawesome/react-fontawesome": "^0.2.0",
"@headlessui/react": "^1.7.19",
"@headlessui/tailwindcss": "^0.2.0",
"@kittycad/lib": "^0.0.58",
"@kittycad/lib": "^0.0.60",
"@lezer/javascript": "^1.4.9",
"@open-rpc/client-js": "^1.8.1",
"@react-hook/resize-observer": "^1.2.6",

281
src-tauri/Cargo.lock generated
View File

@ -38,6 +38,17 @@ dependencies = [
"cpufeatures",
]
[[package]]
name = "ahash"
version = "0.7.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "891477e0c6a8957309ee5c45a6368af3ae14bb510732d2684ffa19af310920f9"
dependencies = [
"getrandom 0.2.14",
"once_cell",
"version_check",
]
[[package]]
name = "ahash"
version = "0.8.11"
@ -81,6 +92,24 @@ version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0"
[[package]]
name = "android_log-sys"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5ecc8056bf6ab9892dcd53216c83d1597487d7dacac16c8df6b877d127df9937"
[[package]]
name = "android_logger"
version = "0.13.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c494134f746c14dc653a35a4ea5aca24ac368529da5370ecf41fe0341c35772f"
dependencies = [
"android_log-sys",
"env_logger",
"log",
"once_cell",
]
[[package]]
name = "android_system_properties"
version = "0.1.5"
@ -154,6 +183,7 @@ dependencies = [
"anyhow",
"kcl-lib",
"kittycad",
"log",
"oauth2",
"serde_json",
"tauri",
@ -163,6 +193,7 @@ dependencies = [
"tauri-plugin-dialog",
"tauri-plugin-fs",
"tauri-plugin-http",
"tauri-plugin-log",
"tauri-plugin-os",
"tauri-plugin-process",
"tauri-plugin-shell",
@ -181,6 +212,12 @@ dependencies = [
"num-traits",
]
[[package]]
name = "arrayvec"
version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711"
[[package]]
name = "ashpd"
version = "0.8.1"
@ -507,6 +544,30 @@ dependencies = [
"tracing",
]
[[package]]
name = "borsh"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dbe5b10e214954177fb1dc9fbd20a1a2608fe99e6c832033bdc7cea287a20d77"
dependencies = [
"borsh-derive",
"cfg_aliases 0.1.1",
]
[[package]]
name = "borsh-derive"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d7a8646f94ab393e43e8b35a2558b1624bed28b97ee09c5d15456e3c9463f46d"
dependencies = [
"once_cell",
"proc-macro-crate 3.1.0",
"proc-macro2",
"quote",
"syn 2.0.60",
"syn_derive",
]
[[package]]
name = "brotli"
version = "3.5.0"
@ -534,7 +595,7 @@ version = "2.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4d43b38e074cc0de2957f10947e376a1d88b9c4dbab340b590800cc1b2e066b2"
dependencies = [
"ahash",
"ahash 0.8.11",
"base64 0.13.1",
"bitvec",
"chrono",
@ -556,6 +617,39 @@ version = "3.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c"
[[package]]
name = "byte-unit"
version = "5.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "33ac19bdf0b2665407c39d82dbc937e951e7e2001609f0fb32edd0af45a2d63e"
dependencies = [
"rust_decimal",
"serde",
"utf8-width",
]
[[package]]
name = "bytecheck"
version = "0.6.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "23cdc57ce23ac53c931e88a43d06d070a6fd142f2617be5855eb75efc9beb1c2"
dependencies = [
"bytecheck_derive",
"ptr_meta",
"simdutf8",
]
[[package]]
name = "bytecheck_derive"
version = "0.6.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3db406d29fbcd95542e92559bed4d8ad92636d1ca8b3b72ede10b4bcc010e659"
dependencies = [
"proc-macro2",
"quote",
"syn 1.0.109",
]
[[package]]
name = "bytemuck"
version = "1.15.0"
@ -1294,6 +1388,16 @@ dependencies = [
"syn 2.0.60",
]
[[package]]
name = "env_logger"
version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4cd405aab171cb85d6735e5c8d9db038c17d3ca007a4d2c25f337935c3d90580"
dependencies = [
"log",
"regex",
]
[[package]]
name = "equivalent"
version = "1.0.1"
@ -1367,6 +1471,15 @@ dependencies = [
"simd-adler32",
]
[[package]]
name = "fern"
version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9f0c14694cbd524c8720dd69b0e3179344f04ebb5f90f2e4a440c6ea3b2f1ee"
dependencies = [
"log",
]
[[package]]
name = "field-offset"
version = "0.3.6"
@ -1964,6 +2077,9 @@ name = "hashbrown"
version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
dependencies = [
"ahash 0.7.8",
]
[[package]]
name = "hashbrown"
@ -2420,7 +2536,7 @@ dependencies = [
[[package]]
name = "kcl-lib"
version = "0.1.53"
version = "0.1.54"
dependencies = [
"anyhow",
"approx",
@ -2479,9 +2595,9 @@ dependencies = [
[[package]]
name = "kittycad"
version = "0.3.0"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ddc922f0da3abc22661bf49423c9bfcc02ce6ae92dae007ede6990874789545b"
checksum = "2c6e12eb45fd9a28c8e99dbdef54556246b39acee14e4aa6f0fc43636caa62d9"
dependencies = [
"anyhow",
"async-trait",
@ -2658,6 +2774,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c"
dependencies = [
"serde",
"value-bag",
]
[[package]]
@ -2970,6 +3087,15 @@ dependencies = [
"syn 1.0.109",
]
[[package]]
name = "num_threads"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c7398b9c8b70908f6371f47ed36737907c87c52af34c268fed0bf0ceb92ead9"
dependencies = [
"libc",
]
[[package]]
name = "oauth2"
version = "4.4.2"
@ -3619,6 +3745,26 @@ dependencies = [
"unicode-ident",
]
[[package]]
name = "ptr_meta"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0738ccf7ea06b608c10564b31debd4f5bc5e197fc8bfe088f68ae5ce81e7a4f1"
dependencies = [
"ptr_meta_derive",
]
[[package]]
name = "ptr_meta_derive"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "16b845dbfca988fa33db069c0e230574d15a3088f147a87b64c7589eb662c9ac"
dependencies = [
"proc-macro2",
"quote",
"syn 1.0.109",
]
[[package]]
name = "quick-xml"
version = "0.31.0"
@ -3836,6 +3982,15 @@ version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56"
[[package]]
name = "rend"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "71fe3824f5629716b1589be05dacd749f6aa084c87e00e016714a8cdfccc997c"
dependencies = [
"bytecheck",
]
[[package]]
name = "reqwest"
version = "0.11.27"
@ -4044,6 +4199,35 @@ dependencies = [
"windows-sys 0.52.0",
]
[[package]]
name = "rkyv"
version = "0.7.44"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5cba464629b3394fc4dbc6f940ff8f5b4ff5c7aef40f29166fd4ad12acbc99c0"
dependencies = [
"bitvec",
"bytecheck",
"bytes",
"hashbrown 0.12.3",
"ptr_meta",
"rend",
"rkyv_derive",
"seahash",
"tinyvec",
"uuid",
]
[[package]]
name = "rkyv_derive"
version = "0.7.44"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a7dddfff8de25e6f62b9d64e6e432bf1c6736c57d20323e15ee10435fbda7c65"
dependencies = [
"proc-macro2",
"quote",
"syn 1.0.109",
]
[[package]]
name = "ropey"
version = "1.6.1"
@ -4054,6 +4238,22 @@ dependencies = [
"str_indices",
]
[[package]]
name = "rust_decimal"
version = "1.35.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1790d1c4c0ca81211399e0e0af16333276f375209e71a37b67698a373db5b47a"
dependencies = [
"arrayvec",
"borsh",
"bytes",
"num-traits",
"rand 0.8.5",
"rkyv",
"serde",
"serde_json",
]
[[package]]
name = "rustc-demangle"
version = "0.1.23"
@ -4249,6 +4449,12 @@ dependencies = [
"untrusted",
]
[[package]]
name = "seahash"
version = "4.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b"
[[package]]
name = "security-framework"
version = "2.10.0"
@ -4537,6 +4743,12 @@ version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe"
[[package]]
name = "simdutf8"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f27f6278552951f1f2b8cf9da965d10969b2efdea95a6ec47987ab46edfe263a"
[[package]]
name = "siphasher"
version = "0.3.11"
@ -4794,6 +5006,18 @@ dependencies = [
"unicode-ident",
]
[[package]]
name = "syn_derive"
version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1329189c02ff984e9736652b1631330da25eaa6bc639089ed4915d25446cbe7b"
dependencies = [
"proc-macro-error",
"proc-macro2",
"quote",
"syn 2.0.60",
]
[[package]]
name = "sync_wrapper"
version = "0.1.2"
@ -4938,9 +5162,9 @@ dependencies = [
[[package]]
name = "tauri"
version = "2.0.0-beta.16"
version = "2.0.0-beta.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4d411ebb670bbe5cf948f6c24978632937329748b499de1619ab55ad31512652"
checksum = "5fedd5490eddf117253945f0baedafded43474c971cba546a818f527d5c26266"
dependencies = [
"anyhow",
"bytes",
@ -4952,7 +5176,7 @@ dependencies = [
"getrandom 0.2.14",
"glob",
"gtk",
"heck 0.4.1",
"heck 0.5.0",
"http 1.1.0",
"jni",
"libc",
@ -5153,6 +5377,27 @@ dependencies = [
"urlpattern",
]
[[package]]
name = "tauri-plugin-log"
version = "2.0.0-beta.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "97718db0d981b03b7b1257c22f699ff46639220c5acb4510ac9696437afc93f8"
dependencies = [
"android_logger",
"byte-unit",
"cocoa",
"fern",
"log",
"objc",
"serde",
"serde_json",
"serde_repr",
"swift-rs",
"tauri",
"tauri-plugin",
"time",
]
[[package]]
name = "tauri-plugin-os"
version = "2.0.0-beta.3"
@ -5231,9 +5476,9 @@ dependencies = [
[[package]]
name = "tauri-runtime"
version = "2.0.0-beta.13"
version = "2.0.0-beta.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c7439729d0107c9797764919c39c4a4cc3af64306faaa48271da50d8eb4c0283"
checksum = "148b6e6aff8e63fe5d4ae1d50159d50cfc0b4309abdeca64833c887c6b5631ef"
dependencies = [
"dpi",
"gtk",
@ -5250,9 +5495,9 @@ dependencies = [
[[package]]
name = "tauri-runtime-wry"
version = "2.0.0-beta.13"
version = "2.0.0-beta.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c38dcfa7f8c2b2e344c7401972e0ddaaec4fa655666788d94b1852d6c4a7fe8"
checksum = "398d065c6e0fbf3c4304583759b6e153bc1e0daeb033bede6834ebe4df371fc3"
dependencies = [
"cocoa",
"gtk",
@ -5393,7 +5638,9 @@ checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885"
dependencies = [
"deranged",
"itoa 1.0.11",
"libc",
"num-conv",
"num_threads",
"powerfmt",
"serde",
"time-core",
@ -5963,6 +6210,12 @@ version = "0.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9"
[[package]]
name = "utf8-width"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "86bd8d4e895da8537e5315b8254664e6b769c4ff3db18321b297a1e7004392e3"
[[package]]
name = "utf8parse"
version = "0.2.1"
@ -6017,6 +6270,12 @@ version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d"
[[package]]
name = "value-bag"
version = "1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a84c137d37ab0142f0f2ddfe332651fdbf252e7b7dbb4e67b6c1f1b2e925101"
[[package]]
name = "version-compare"
version = "0.2.0"

View File

@ -17,6 +17,7 @@ tauri-build = { version = "2.0.0-beta.13", features = [] }
anyhow = "1"
kcl-lib = { version = "0.1.53", path = "../src/wasm-lib/kcl" }
kittycad = "0.3.0"
log = "0.4.21"
oauth2 = "4.4.2"
serde_json = "1.0"
tauri = { version = "2.0.0-beta.15", features = [ "devtools", "unstable"] }
@ -25,6 +26,7 @@ tauri-plugin-deep-link = { version = "2.0.0-beta.3" }
tauri-plugin-dialog = { version = "2.0.0-beta.6" }
tauri-plugin-fs = { version = "2.0.0-beta.6" }
tauri-plugin-http = { version = "2.0.0-beta.6" }
tauri-plugin-log = { version = "2.0.0-beta.4" }
tauri-plugin-os = { version = "2.0.0-beta.2" }
tauri-plugin-process = { version = "2.0.0-beta.2" }
tauri-plugin-shell = { version = "2.0.0-beta.2" }

View File

@ -9,6 +9,7 @@
"permissions": [
"cli:default",
"deep-link:default",
"log:default",
"path:default",
"event:default",
"window:default",

View File

@ -226,7 +226,7 @@ async fn read_dir_recursive(path: &str) -> Result<FileEntry, InvokeError> {
/// The string returned from this method is the access token.
#[tauri::command]
async fn login(app: tauri::AppHandle, host: &str) -> Result<String, InvokeError> {
println!("Logging in...");
log::debug!("Logging in...");
// Do an OAuth 2.0 Device Authorization Grant dance to get a token.
let device_auth_url = oauth2::DeviceAuthorizationUrl::new(format!("{host}/oauth2/device/auth"))
.map_err(|e| InvokeError::from_anyhow(e.into()))?;
@ -265,7 +265,7 @@ async fn login(app: tauri::AppHandle, host: &str) -> Result<String, InvokeError>
// and bypass the shell::open call as it fails on GitHub Actions.
let e2e_tauri_enabled = env::var("E2E_TAURI_ENABLED").is_ok();
if e2e_tauri_enabled {
println!("E2E_TAURI_ENABLED is set, won't open {} externally", auth_uri.secret());
log::warn!("E2E_TAURI_ENABLED is set, won't open {} externally", auth_uri.secret());
tokio::fs::write("/tmp/kittycad_user_code", details.user_code().secret())
.await
.map_err(|e| InvokeError::from_anyhow(e.into()))?;
@ -308,7 +308,7 @@ async fn get_user(token: &str, hostname: &str) -> Result<kittycad::types::User,
baseurl = format!("http://{host}")
}
}
println!("Getting user info...");
log::debug!("Getting user info...");
// use kittycad library to fetch the user info from /user/me
let mut client = kittycad::Client::new(token);
@ -352,10 +352,12 @@ fn show_in_folder(path: &str) -> Result<(), InvokeError> {
#[allow(dead_code)]
fn open_url_sync(app: &tauri::AppHandle, url: &url::Url) {
println!("Opening URL: {:?}", url);
log::debug!("Opening URL: {:?}", url);
let cloned_url = url.clone();
let runner: tauri::async_runtime::JoinHandle<Result<ProjectState>> = tauri::async_runtime::spawn(async move {
let url_str = cloned_url.to_string();
let url_str = cloned_url.path().to_string();
log::debug!("Opening URL path : {}", url_str);
let path = Path::new(url_str.as_str());
ProjectState::new_from_path(path.to_path_buf()).await
});
@ -367,10 +369,10 @@ fn open_url_sync(app: &tauri::AppHandle, url: &url::Url) {
app.manage(state::Store::new(store));
}
Err(e) => {
println!("Error opening URL:{} {:?}", url, e);
log::warn!("Error opening URL:{} {:?}", url, e);
}
Ok(Err(e)) => {
println!("Error opening URL:{} {:?}", url, e);
log::warn!("Error opening URL:{} {:?}", url, e);
}
}
}
@ -400,6 +402,15 @@ fn main() -> Result<()> {
.plugin(tauri_plugin_dialog::init())
.plugin(tauri_plugin_fs::init())
.plugin(tauri_plugin_http::init())
.plugin(
tauri_plugin_log::Builder::new()
.targets([
tauri_plugin_log::Target::new(tauri_plugin_log::TargetKind::Stdout),
tauri_plugin_log::Target::new(tauri_plugin_log::TargetKind::LogDir { file_name: None }),
])
.level(log::LevelFilter::Debug)
.build(),
)
.plugin(tauri_plugin_os::init())
.plugin(tauri_plugin_process::init())
.plugin(tauri_plugin_shell::init())
@ -435,7 +446,7 @@ fn main() -> Result<()> {
if let Some(source_arg) = matches.args.get("source") {
// We don't do an else here because this can be null.
if let Some(value) = source_arg.value.as_str() {
println!("Got path in cli argument: {}", value);
log::info!("Got path in cli argument: {}", value);
source_path = Some(Path::new(value).to_path_buf());
}
}
@ -446,7 +457,7 @@ fn main() -> Result<()> {
}
if verbose {
println!("Verbose mode enabled.");
log::debug!("Verbose mode enabled.");
}
// If we have a source path to open, make sure it exists.
@ -476,7 +487,7 @@ fn main() -> Result<()> {
// Listen on the deep links.
app.listen("deep-link://new-url", |event| {
println!("got deep-link url: {:?}", event);
log::info!("got deep-link url: {:?}", event);
// TODO: open_url_sync(app.handle(), event.url);
});
@ -488,10 +499,7 @@ fn main() -> Result<()> {
|app, event| {
#[cfg(any(target_os = "macos", target_os = "ios"))]
if let tauri::RunEvent::Opened { urls } = event {
if let Some(w) = app.get_webview_window("main") {
let _ = w.eval(&format!("console.log(`[tauri] Opened URLs: {:?}`)", urls));
}
println!("Opened URLs: {:?}", urls);
log::info!("Opened URLs: {:?}", urls);
// Handle the first URL.
// TODO: do we want to handle more than one URL?

View File

@ -37,8 +37,7 @@
}
},
"longDescription": "",
"macOS": {
},
"macOS": {},
"resources": [],
"shortDescription": "",
"targets": "all"
@ -75,5 +74,5 @@
}
},
"productName": "Zoo Modeling App",
"version": "0.20.0"
"version": "0.21.1"
}

View File

@ -4,7 +4,6 @@ import { engineCommandManager, kclManager } from 'lib/singletons'
import { useModelingContext } from 'hooks/useModelingContext'
import { useCommandsContext } from 'hooks/useCommandsContext'
import { ActionButton } from 'components/ActionButton'
import usePlatform from 'hooks/usePlatform'
import { isSingleCursorInPipe } from 'lang/queryAst'
import { useKclContext } from 'lang/KclProvider'
import {
@ -12,18 +11,18 @@ import {
useNetworkStatus,
} from 'components/NetworkHealthIndicator'
import { useStore } from 'useStore'
import { ActionButtonDropdown } from 'components/ActionButtonDropdown'
export const Toolbar = () => {
const platform = usePlatform()
const { commandBarSend } = useCommandsContext()
const { state, send, context } = useModelingContext()
const toolbarButtonsRef = useRef<HTMLUListElement>(null)
const iconClassName =
'group-disabled:text-chalkboard-50 group-enabled:group-hover:!text-chalkboard-10 group-pressed:!text-chalkboard-10'
'group-disabled:text-chalkboard-50 group-enabled:group-hover:!text-primary dark:group-enabled:group-hover:!text-inherit group-pressed:!text-chalkboard-10 group-ui-open:!text-chalkboard-10 dark:group-ui-open:!text-chalkboard-10'
const bgClassName =
'group-disabled:!bg-transparent group-enabled:group-hover:bg-primary group-pressed:bg-primary'
'group-disabled:!bg-transparent group-enabled:group-hover:bg-primary/10 dark:group-enabled:group-hover:bg-primary group-pressed:bg-primary group-ui-open:bg-primary'
const buttonClassName =
'bg-chalkboard-10 dark:bg-chalkboard-100 hover:bg-chalkboard-10 dark:hover:bg-chalkboard-100'
'bg-chalkboard-10 dark:bg-chalkboard-100 enabled:hover:bg-chalkboard-10 dark:enabled:hover:bg-chalkboard-100 pressed:!border-primary ui-open:!border-primary'
const pathId = useMemo(() => {
if (!isSingleCursorInPipe(context.selectionRanges, kclManager.ast)) {
return false
@ -59,10 +58,7 @@ export const Toolbar = () => {
{...props}
ref={toolbarButtonsRef}
onWheel={handleToolbarButtonsWheelEvent}
className={
'm-0 py-1 rounded-l-sm flex gap-2 items-center overflow-x-auto ' +
className
}
className={'m-0 py-1 rounded-l-sm flex gap-2 items-center ' + className}
style={{ scrollbarWidth: 'thin' }}
>
{state.nextEvents.includes('Enter sketch') && (
@ -73,7 +69,7 @@ export const Toolbar = () => {
onClick={() =>
send({ type: 'Enter sketch', data: { forceNewSketch: true } })
}
icon={{
iconStart={{
icon: 'sketch',
iconClassName,
bgClassName,
@ -90,7 +86,7 @@ export const Toolbar = () => {
className={buttonClassName}
Element="button"
onClick={() => send({ type: 'Enter sketch' })}
icon={{
iconStart={{
icon: 'sketch',
iconClassName,
bgClassName,
@ -107,7 +103,7 @@ export const Toolbar = () => {
className={buttonClassName}
Element="button"
onClick={() => send({ type: 'Cancel' })}
icon={{
iconStart={{
icon: 'arrowLeft',
iconClassName,
bgClassName,
@ -130,7 +126,7 @@ export const Toolbar = () => {
: send('Equip Line tool')
}
aria-pressed={state?.matches('Sketch.Line tool')}
icon={{
iconStart={{
icon: 'line',
iconClassName,
bgClassName,
@ -150,7 +146,7 @@ export const Toolbar = () => {
: send('Equip tangential arc to')
}
aria-pressed={state.matches('Sketch.Tangential arc to')}
icon={{
iconStart={{
icon: 'arc',
iconClassName,
bgClassName,
@ -174,7 +170,7 @@ export const Toolbar = () => {
: send('Equip rectangle tool')
}
aria-pressed={state.matches('Sketch.Rectangle tool')}
icon={{
iconStart={{
icon: 'rectangle',
iconClassName,
bgClassName,
@ -196,7 +192,13 @@ export const Toolbar = () => {
</>
)}
{state.matches('Sketch.SketchIdle') &&
state.nextEvents
state.nextEvents.filter(
(eventName) =>
eventName.includes('Make segment') ||
eventName.includes('Constrain')
).length > 0 && (
<ActionButtonDropdown
splitMenuItems={state.nextEvents
.filter(
(eventName) =>
eventName.includes('Make segment') ||
@ -217,31 +219,27 @@ export const Toolbar = () => {
}
return 0
})
.map((eventName) => (
<li className="contents" key={eventName}>
<ActionButton
className={buttonClassName}
Element="button"
key={eventName}
onClick={() => send(eventName)}
disabled={
.map((eventName) => ({
label: eventName
.replace('Make segment ', '')
.replace('Constrain ', ''),
onClick: () => send(eventName),
disabled:
!state.nextEvents
.filter((event) => state.can(event as any))
.includes(eventName) || disableAllButtons
}
title={eventName}
icon={{
icon: 'line',
.includes(eventName) || disableAllButtons,
}))}
className={buttonClassName}
Element="button"
iconStart={{
icon: 'dimension',
iconClassName,
bgClassName,
}}
>
{eventName
.replace('Make segment ', '')
.replace('Constrain ', '')}
</ActionButton>
</li>
))}
Constrain
</ActionButtonDropdown>
)}
{state.matches('idle') && (
<li className="contents">
<ActionButton
@ -259,7 +257,7 @@ export const Toolbar = () => {
? 'extrude'
: 'sketches need to be closed, or not already extruded'
}
icon={{
iconStart={{
icon: 'extrude',
iconClassName,
bgClassName,
@ -274,17 +272,8 @@ export const Toolbar = () => {
}
return (
<div className="max-w-full flex items-stretch rounded-l-sm rounded-r-full bg-chalkboard-10/80 dark:bg-chalkboard-110/70 relative">
<menu className="flex-1 pl-1 pr-2 py-0 overflow-hidden rounded-l-sm whitespace-nowrap border-solid border border-primary/30 dark:border-chalkboard-90 border-r-0">
<menu className="max-w-full whitespace-nowrap rounded px-1.5 py-0.5 backdrop-blur-sm bg-chalkboard-10/80 dark:bg-chalkboard-110/70 relative">
<ToolbarButtons />
</menu>
<ActionButton
Element="button"
onClick={() => commandBarSend({ type: 'Open' })}
className="rounded-r-full pr-4 self-stretch border-primary/30 hover:border-primary dark:border-chalkboard-80 dark:bg-chalkboard-80 text-primary"
>
{platform === 'macos' ? '⌘K' : 'Ctrl+/'}
</ActionButton>
</div>
)
}

View File

@ -90,7 +90,8 @@ export const ClientSideScene = ({
cursor = 'grabbing'
} else if (
state.matches('Sketch.Line tool') ||
state.matches('Sketch.Tangential arc to')
state.matches('Sketch.Tangential arc to') ||
state.matches('Sketch.Rectangle tool')
) {
cursor = 'crosshair'
} else {
@ -104,9 +105,9 @@ export const ClientSideScene = ({
style={{ cursor: cursor }}
className={`absolute inset-0 h-full w-full transition-all duration-300 ${
hideClient ? 'opacity-0' : 'opacity-100'
} ${hideServer ? 'bg-black' : ''} ${
} ${hideServer ? 'bg-chalkboard-10 dark:bg-chalkboard-100' : ''} ${
!hideClient && !hideServer && state.matches('Sketch')
? 'bg-black/80'
? 'bg-chalkboard-10/80 dark:bg-chalkboard-100/80'
: ''
}`}
></div>

View File

@ -97,6 +97,7 @@ import {
getRectangleCallExpressions,
updateRectangleSketch,
} from 'lib/rectangleTool'
import { getThemeColorForThreeJs } from 'lib/theme'
type DraftSegment = 'line' | 'tangentialArcTo'
@ -356,6 +357,7 @@ export class SceneEntities {
id: sketchGroup.start.__geoMeta.id,
pathToNode: segPathToNode,
scale: factor,
theme: sceneInfra._theme,
})
_profileStart.layers.set(SKETCH_LAYER)
_profileStart.traverse((child) => {
@ -406,6 +408,7 @@ export class SceneEntities {
isDraftSegment,
scale: factor,
texture: sceneInfra.extraSegmentTexture,
theme: sceneInfra._theme,
})
} else {
seg = straightSegment({
@ -417,6 +420,7 @@ export class SceneEntities {
scale: factor,
callExpName,
texture: sceneInfra.extraSegmentTexture,
theme: sceneInfra._theme,
})
}
seg.layers.set(SKETCH_LAYER)
@ -964,7 +968,7 @@ export class SceneEntities {
if (!draftInfo)
// don't want to mod the user's code yet as they have't committed to the change yet
// plus this would be the truncated ast being recast, it would be wrong
codeManager.updateCodeStateEditor(code)
codeManager.updateCodeEditor(code)
const { programMemory } = await executeAst({
ast: truncatedAst,
useFakeExecutor: true,
@ -1503,7 +1507,10 @@ export class SceneEntities {
const isSelected = parent?.userData?.isSelected
colorSegment(
selected,
isSelected ? 0x0000ff : parent?.userData?.baseColor || 0xffffff
isSelected
? 0x0000ff
: parent?.userData?.baseColor ||
getThemeColorForThreeJs(sceneInfra._theme)
)
const extraSegmentGroup = parent?.getObjectByName(EXTRA_SEGMENT_HANDLE)
if (extraSegmentGroup) {

View File

@ -30,6 +30,7 @@ import { CameraControls } from './CameraControls'
import { EngineCommandManager } from 'lang/std/engineConnection'
import { settings } from 'lib/settings/initialSettings'
import { MouseState } from 'machines/modelingMachine'
import { Themes } from 'lib/theme'
type SendType = ReturnType<typeof useModelingContext>['send']
@ -101,6 +102,7 @@ export class SceneInfra {
isFovAnimationInProgress = false
_baseUnit: BaseUnit = 'mm'
_baseUnitMultiplier = 1
_theme: Themes = Themes.System
extraSegmentTexture: Texture
lastMouseState: MouseState = { type: 'idle' }
onDragStartCallback: (arg: OnDragCallbackArgs) => void = () => {}
@ -137,6 +139,9 @@ export class SceneInfra {
this._baseUnitMultiplier
)
}
set theme(theme: Themes) {
this._theme = theme
}
resetMouseListeners = () => {
this.setCallbacks({
onDragStart: () => {},

View File

@ -37,22 +37,26 @@ import {
} from './sceneEntities'
import { getTangentPointFromPreviousArc } from 'lib/utils2d'
import { ARROWHEAD } from './sceneInfra'
import { Themes, getThemeColorForThreeJs } from 'lib/theme'
export function profileStart({
from,
id,
pathToNode,
scale = 1,
theme,
}: {
from: Coords2d
id: string
pathToNode: PathToNode
scale?: number
theme: Themes
}) {
const group = new Group()
const geometry = new BoxGeometry(12, 12, 12) // in pixels scaled later
const body = new MeshBasicMaterial({ color: 0xffffff })
const baseColor = getThemeColorForThreeJs(theme)
const body = new MeshBasicMaterial({ color: baseColor })
const mesh = new Mesh(geometry, body)
group.add(mesh)
@ -79,6 +83,7 @@ export function straightSegment({
scale = 1,
callExpName,
texture,
theme,
}: {
from: Coords2d
to: Coords2d
@ -88,6 +93,7 @@ export function straightSegment({
scale?: number
callExpName: string
texture: Texture
theme: Themes
}): Group {
const group = new Group()
@ -111,7 +117,8 @@ export function straightSegment({
})
}
const baseColor = callExpName === 'close' ? 0x444444 : 0xffffff
const baseColor =
callExpName === 'close' ? 0x444444 : getThemeColorForThreeJs(theme)
const body = new MeshBasicMaterial({ color: baseColor })
const mesh = new Mesh(geometry, body)
mesh.userData.type = isDraftSegment
@ -134,7 +141,7 @@ export function straightSegment({
const length = Math.sqrt(
Math.pow(to[0] - from[0], 2) + Math.pow(to[1] - from[1], 2)
)
const arrowGroup = createArrowhead(scale)
const arrowGroup = createArrowhead(scale, theme)
arrowGroup.position.set(to[0], to[1], 0)
const dir = new Vector3()
.subVectors(new Vector3(to[0], to[1], 0), new Vector3(from[0], from[1], 0))
@ -147,7 +154,7 @@ export function straightSegment({
group.add(mesh)
if (callExpName !== 'close') group.add(arrowGroup)
const extraSegmentGroup = createExtraSegmentHandle(scale, texture)
const extraSegmentGroup = createExtraSegmentHandle(scale, texture, theme)
const offsetFromBase = new Vector2(to[0] - from[0], to[1] - from[1])
.normalize()
.multiplyScalar(EXTRA_SEGMENT_OFFSET_PX * scale)
@ -162,8 +169,10 @@ export function straightSegment({
return group
}
function createArrowhead(scale = 1): Group {
const arrowMaterial = new MeshBasicMaterial({ color: 0xffffff })
function createArrowhead(scale = 1, theme: Themes): Group {
const arrowMaterial = new MeshBasicMaterial({
color: getThemeColorForThreeJs(theme),
})
// specify the size of the geometry in pixels (i.e. cone height = 20px, cone radius = 4.5px)
// we'll scale the group to the correct size later to match these sizes in screen space
const arrowheadMesh = new Mesh(new ConeGeometry(4.5, 20, 12), arrowMaterial)
@ -179,7 +188,11 @@ function createArrowhead(scale = 1): Group {
return arrowGroup
}
function createExtraSegmentHandle(scale: number, texture: Texture): Group {
function createExtraSegmentHandle(
scale: number,
texture: Texture,
theme: Themes
): Group {
const particleMaterial = new PointsMaterial({
size: 12, // in pixels
map: texture,
@ -189,7 +202,7 @@ function createExtraSegmentHandle(scale: number, texture: Texture): Group {
})
const mat = new MeshBasicMaterial({
transparent: true,
color: 0xffffff,
color: getThemeColorForThreeJs(theme),
opacity: 0,
})
const particleGeometry = new BufferGeometry().setFromPoints([
@ -218,6 +231,7 @@ export function tangentialArcToSegment({
isDraftSegment,
scale = 1,
texture,
theme,
}: {
prevSegment: SketchGroup['value'][number]
from: Coords2d
@ -227,6 +241,7 @@ export function tangentialArcToSegment({
isDraftSegment?: boolean
scale?: number
texture: Texture
theme: Themes
}): Group {
const group = new Group()
@ -257,7 +272,8 @@ export function tangentialArcToSegment({
scale,
})
const body = new MeshBasicMaterial({ color: 0xffffff })
const baseColor = getThemeColorForThreeJs(theme)
const body = new MeshBasicMaterial({ color: baseColor })
const mesh = new Mesh(geometry, body)
mesh.userData.type = isDraftSegment
? TANGENTIAL_ARC_TO__SEGMENT_DASH
@ -271,10 +287,11 @@ export function tangentialArcToSegment({
prevSegment,
pathToNode,
isSelected: false,
baseColor,
}
group.name = TANGENTIAL_ARC_TO_SEGMENT
const arrowGroup = createArrowhead(scale)
const arrowGroup = createArrowhead(scale, theme)
arrowGroup.position.set(to[0], to[1], 0)
const arrowheadAngle = endAngle + (Math.PI / 2) * (ccw ? 1 : -1)
arrowGroup.quaternion.setFromUnitVectors(
@ -285,7 +302,7 @@ export function tangentialArcToSegment({
const shouldHide = pxLength < HIDE_SEGMENT_LENGTH
arrowGroup.visible = !shouldHide
const extraSegmentGroup = createExtraSegmentHandle(scale, texture)
const extraSegmentGroup = createExtraSegmentHandle(scale, texture, theme)
const circumferenceInPx = (2 * Math.PI * radius) / scale
const extraSegmentAngleDelta =
(EXTRA_SEGMENT_OFFSET_PX / circumferenceInPx) * Math.PI * 2

View File

@ -1,11 +1,12 @@
import { ActionIcon, ActionIconProps } from './ActionIcon'
import React from 'react'
import React, { ForwardedRef, forwardRef } from 'react'
import { paths } from 'lib/paths'
import { Link } from 'react-router-dom'
import type { LinkProps } from 'react-router-dom'
interface BaseActionButtonProps {
icon?: ActionIconProps
iconStart?: ActionIconProps
iconEnd?: ActionIconProps
className?: string
}
@ -32,15 +33,15 @@ type ActionButtonAsElement = BaseActionButtonProps &
Element: React.ComponentType<React.HTMLAttributes<HTMLButtonElement>>
}
type ActionButtonProps =
export type ActionButtonProps =
| ActionButtonAsButton
| ActionButtonAsLink
| ActionButtonAsExternal
| ActionButtonAsElement
export const ActionButton = (props: ActionButtonProps) => {
const classNames = `action-button m-0 group mono text-sm flex items-center gap-2 rounded-sm border-solid border border-chalkboard-30 hover:border-chalkboard-40 dark:border-chalkboard-70 dark:hover:border-chalkboard-60 dark:bg-chalkboard-90/50 p-[3px] text-chalkboard-100 dark:text-chalkboard-10 ${
props.icon ? 'pr-2' : 'px-2'
export const ActionButton = forwardRef((props: ActionButtonProps, ref) => {
const classNames = `action-button p-0 m-0 group mono text-xs leading-none flex items-center gap-2 rounded-sm border-solid border border-chalkboard-30 hover:border-chalkboard-40 enabled:dark:border-chalkboard-70 dark:hover:border-chalkboard-60 dark:bg-chalkboard-90/50 text-chalkboard-100 dark:text-chalkboard-10 ${
props.iconStart ? (props.iconEnd ? 'px-0' : 'pr-2') : 'px-2'
} ${props.className ? props.className : ''}`
switch (props.Element) {
@ -48,11 +49,23 @@ export const ActionButton = (props: ActionButtonProps) => {
// Note we have to destructure 'className' and 'Element' out of props
// because we don't want to pass them to the button element;
// the same is true for the other cases below.
const { Element, icon, children, className: _className, ...rest } = props
const {
Element,
iconStart,
iconEnd,
children,
className: _className,
...rest
} = props
return (
<button className={classNames} {...rest}>
{props.icon && <ActionIcon {...icon} />}
<button
ref={ref as ForwardedRef<HTMLButtonElement>}
className={classNames}
{...rest}
>
{iconStart && <ActionIcon {...iconStart} />}
{children}
{iconEnd && <ActionIcon {...iconEnd} />}
</button>
)
}
@ -60,15 +73,22 @@ export const ActionButton = (props: ActionButtonProps) => {
const {
Element,
to,
icon,
iconStart,
iconEnd,
children,
className: _className,
...rest
} = props
return (
<Link to={to || paths.INDEX} className={classNames} {...rest}>
{icon && <ActionIcon {...icon} />}
<Link
ref={ref as ForwardedRef<HTMLAnchorElement>}
to={to || paths.INDEX}
className={classNames}
{...rest}
>
{iconStart && <ActionIcon {...iconStart} />}
{children}
{iconEnd && <ActionIcon {...iconEnd} />}
</Link>
)
}
@ -76,33 +96,42 @@ export const ActionButton = (props: ActionButtonProps) => {
const {
Element,
to,
icon,
iconStart,
iconEnd,
children,
className: _className,
...rest
} = props
return (
<Link
ref={ref as ForwardedRef<HTMLAnchorElement>}
to={to || paths.INDEX}
className={classNames}
{...rest}
target="_blank"
>
{icon && <ActionIcon {...icon} />}
{iconStart && <ActionIcon {...iconStart} />}
{children}
{iconEnd && <ActionIcon {...iconEnd} />}
</Link>
)
}
default: {
const { Element, icon, children, className: _className, ...rest } = props
if (!Element) throw new Error('Element is required')
const {
Element,
iconStart,
children,
className: _className,
...rest
} = props
return (
<Element className={classNames} {...rest}>
{props.icon && <ActionIcon {...props.icon} />}
{props.iconStart && <ActionIcon {...props.iconStart} />}
{children}
{props.iconEnd && <ActionIcon {...props.iconEnd} />}
</Element>
)
}
}
}
})

View File

@ -0,0 +1,55 @@
import { Popover } from '@headlessui/react'
import { ActionButton, ActionButtonProps } from './ActionButton'
type ActionButtonSplitProps = Omit<ActionButtonProps, 'iconEnd'> & {
splitMenuItems: {
label: string
shortcut?: string
onClick: () => void
disabled?: boolean
}[]
}
export function ActionButtonDropdown({
splitMenuItems,
className,
...props
}: ActionButtonSplitProps) {
return (
<Popover className="relative">
<Popover.Button
as={ActionButton}
className={className}
{...props}
Element="button"
iconEnd={{
icon: 'caretDown',
className: 'ui-open:rotate-180',
bgClassName:
'bg-chalkboard-20 dark:bg-chalkboard-80 ui-open:bg-primary ui-open:text-chalkboard-10',
}}
/>
<Popover.Panel
as="ul"
className="absolute z-20 left-1/2 -translate-x-1/2 top-full mt-1 w-fit max-h-[80vh] overflow-y-auto py-2 flex flex-col gap-1 align-stretch text-inherit dark:text-chalkboard-10 bg-chalkboard-10 dark:bg-chalkboard-100 rounded shadow-lg border border-solid border-chalkboard-30 dark:border-chalkboard-80 text-sm m-0 p-0"
>
{splitMenuItems.map((item) => (
<li className="contents" key={item.label}>
<button
onClick={item.onClick}
className="block px-3 py-1 hover:bg-primary/10 dark:hover:bg-chalkboard-80 border-0 m-0 text-sm w-full rounded-none text-left disabled:!bg-transparent dark:disabled:text-chalkboard-60"
disabled={item.disabled}
>
<span className="capitalize">{item.label}</span>
{item.shortcut && (
<kbd className="bg-primary/10 dark:bg-chalkboard-80 dark:group-hover:bg-primary font-mono rounded-sm dark:text-inherit inline-block px-1 border-primary dark:border-chalkboard-90">
{item.shortcut}
</kbd>
)}
</button>
</li>
))}
</Popover.Panel>
</Popover>
)
}

View File

@ -29,10 +29,8 @@ export const ActionIcon = ({
size = 'md',
children,
}: ActionIconProps) => {
// By default, we reverse the icon color and background color in dark mode
const computedIconClassName = `h-auto text-primary dark:text-current !group-disabled:text-chalkboard-60 !group-disabled:text-chalkboard-60 ${iconClassName}`
const computedBgClassName = `bg-primary/10 dark:bg-chalkboard-90 !group-disabled:bg-chalkboard-30 !dark:group-disabled:bg-chalkboard-80 ${bgClassName}`
const computedIconClassName = `h-auto text-inherit dark:text-current !group-disabled:text-chalkboard-60 !group-disabled:text-chalkboard-60 ${iconClassName}`
const computedBgClassName = `bg-chalkboard-20 dark:bg-chalkboard-80 !group-disabled:bg-chalkboard-30 !dark:group-disabled:bg-chalkboard-80 ${bgClassName}`
return (
<div

View File

@ -1,12 +1,11 @@
import { Toolbar } from '../Toolbar'
import UserSidebarMenu from './UserSidebarMenu'
import UserSidebarMenu from 'components/UserSidebarMenu'
import { type IndexLoaderData } from 'lib/types'
import ProjectSidebarMenu from './ProjectSidebarMenu'
import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext'
import styles from './AppHeader.module.css'
import { useCommandsContext } from 'hooks/useCommandsContext'
import { ActionButton } from './ActionButton'
import usePlatform from 'hooks/usePlatform'
import { RefreshButton } from 'components/RefreshButton'
import { CommandBarOpenButton } from './CommandBarOpenButton'
interface AppHeaderProps extends React.PropsWithChildren {
showToolbar?: boolean
@ -22,8 +21,6 @@ export const AppHeader = ({
className = '',
enableMenu = false,
}: AppHeaderProps) => {
const platform = usePlatform()
const { commandBarSend } = useCommandsContext()
const { auth } = useSettingsAuthContext()
const user = auth?.context?.user
@ -43,24 +40,17 @@ export const AppHeader = ({
/>
{/* Toolbar if the context deems it */}
<div className="flex-grow flex justify-center max-w-lg md:max-w-xl lg:max-w-2xl xl:max-w-4xl 2xl:max-w-5xl">
{showToolbar ? (
<Toolbar />
) : (
<ActionButton
Element="button"
onClick={() => commandBarSend({ type: 'Open' })}
className="text-sm self-center flex items-center w-fit gap-3"
>
Command Palette{' '}
<kbd className="bg-primary/10 dark:bg-chalkboard-100 dark:text-primary inline-block px-1 py-0.5 border-primary dark:border-chalkboard-90">
{platform === 'macos' ? '⌘K' : 'Ctrl+/'}
</kbd>
</ActionButton>
)}
{showToolbar && <Toolbar />}
</div>
<div className="flex items-center gap-1 py-1 ml-auto">
{/* If there are children, show them, otherwise show User menu */}
{children || <UserSidebarMenu user={user} />}
{children || (
<>
<CommandBarOpenButton />
<RefreshButton />
<UserSidebarMenu user={user} />
</>
)}
</div>
</header>
)

View File

@ -175,7 +175,7 @@ function ReviewingButton() {
type="submit"
form="review-form"
className="w-fit !p-0 rounded-sm border !border-primary hover:shadow"
icon={{
iconStart={{
icon: 'checkmark',
bgClassName: 'p-1 rounded-sm !bg-primary hover:brightness-110',
iconClassName: '!text-chalkboard-10',
@ -193,7 +193,7 @@ function GatheringArgsButton() {
type="submit"
form="arg-form"
className="w-fit !p-0 rounded-sm border !border-primary hover:shadow"
icon={{
iconStart={{
icon: 'arrowRight',
bgClassName: 'p-1 rounded-sm !bg-primary hover:brightness-110',
iconClassName: '!text-chalkboard-10',

View File

@ -7,6 +7,7 @@ import {
getSelectionType,
getSelectionTypeDisplayText,
} from 'lib/selections'
import { kclManager } from 'lib/singletons'
import { modelingMachine } from 'machines/modelingMachine'
import { useCallback, useEffect, useRef, useState } from 'react'
import { useHotkeys } from 'react-hotkeys-hook'
@ -50,6 +51,14 @@ function CommandBarSelectionInput({
inputRef.current?.focus()
}, [selection, inputRef])
// Exit engine's edit mode when this input step is active,
// and re-enter it when it's not.
// In future the engine's edit mode will go away and this will be handled differently.
useEffect(() => {
kclManager.exitEditMode()
return () => kclManager.enterEditMode()
}, [])
// Fast-forward through this arg if it's marked as skippable
// and we have a valid selection already
useEffect(() => {

View File

@ -0,0 +1,19 @@
import { useCommandsContext } from 'hooks/useCommandsContext'
import usePlatform from 'hooks/usePlatform'
export function CommandBarOpenButton() {
const { commandBarSend } = useCommandsContext()
const platform = usePlatform()
return (
<button
className="group rounded-full flex items-center justify-center gap-2 px-2 py-1 bg-primary/10 dark:bg-chalkboard-90 dark:backdrop-blur-sm border-primary hover:border-primary dark:border-chalkboard-50 dark:hover:border-inherit text-primary dark:text-inherit"
onClick={() => commandBarSend({ type: 'Open' })}
>
<span>Commands</span>
<kbd className="bg-primary/10 dark:bg-chalkboard-80 dark:group-hover:bg-primary font-mono rounded-sm dark:text-inherit inline-block px-1 border-primary dark:border-chalkboard-90">
{platform === 'macos' ? '⌘K' : '^/'}
</kbd>
</button>
)
}

View File

@ -38,7 +38,7 @@ function CommandComboBox({
<div className="flex items-center gap-2 px-4 pb-2 border-solid border-0 border-b border-b-chalkboard-20 dark:border-b-chalkboard-80">
<CustomIcon
name="search"
className="w-5 h-5 bg-primary/10 text-primary"
className="w-5 h-5 bg-primary/10 dark:bg-primary text-primary dark:text-inherit"
/>
<Combobox.Input
onChange={(event) => setQuery(event.target.value)}

View File

@ -41,6 +41,16 @@ const CustomIconMap = {
/>
</svg>
),
arrowRotateRight: (
<svg viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
fillRule="evenodd"
clipRule="evenodd"
d="M15.5 7.59684L15.5 8.09684L15 8.09684L10.7931 8.09684L10.7931 7.09684L13.769 7.09684C13.3052 6.54751 12.7147 6.11526 12.0452 5.83941C11.2133 5.49662 10.2977 5.41109 9.41668 5.59387C8.53566 5.77666 7.72967 6.21935 7.10277 6.8648C6.47588 7.51025 6.05687 8.32881 5.89986 9.21478C5.74284 10.1008 5.85503 11.0134 6.22194 11.835C6.58884 12.6566 7.19361 13.3493 7.95816 13.8237C8.7227 14.2981 9.61192 14.5325 10.511 14.4964C11.41 14.4604 12.2776 14.1557 13.0018 13.6216L13.5953 14.4264C12.7103 15.0792 11.6499 15.4516 10.551 15.4956C9.45216 15.5397 8.36535 15.2533 7.4309 14.6734C6.49646 14.0936 5.75729 13.2469 5.30885 12.2428C4.86041 11.2386 4.7233 10.1231 4.9152 9.04027C5.10711 7.95742 5.61923 6.95696 6.38543 6.16808C7.15164 5.3792 8.13674 4.83812 9.21354 4.61472C10.2903 4.39132 11.4094 4.49586 12.4262 4.91483C13.2286 5.24545 13.9382 5.7599 14.5 6.41286L14.5 3.38998L15.5 3.38998L15.5 7.59684Z"
fill="currentColor"
/>
</svg>
),
arrowUp: (
<svg viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
@ -61,6 +71,16 @@ const CustomIconMap = {
/>
</svg>
),
caretDown: (
<svg viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
fillRule="evenodd"
clipRule="evenodd"
d="M10 11.2929L6.35346 7.64642L5.64636 8.35354L9.64648 12.3536L10.3536 12.3536L14.3535 8.35353L13.6464 7.64643L10 11.2929Z"
fill="currentColor"
/>
</svg>
),
clipboardCheckmark: (
<svg viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
@ -91,6 +111,16 @@ const CustomIconMap = {
/>
</svg>
),
dimension: (
<svg viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
fillRule="evenodd"
clipRule="evenodd"
d="M15.6455 3.6455L14 2V5.291L5.291 14H2L6 18V14.7052L14.7052 6H18L16.3526 4.35261L16.3546 4.35065L15.6475 3.64354L15.6455 3.6455Z"
fill="currentColor"
/>
</svg>
),
equal: (
<svg viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path

View File

@ -25,7 +25,7 @@ const DownloadAppBanner = () => {
<ActionButton
Element="button"
onClick={() => setIsBannerDismissed(true)}
icon={{
iconStart={{
icon: 'close',
className: 'p-1',
bgClassName:

View File

@ -29,7 +29,7 @@ export const ErrorPage = () => {
<ActionButton
Element="link"
to={'/'}
icon={{ icon: faHome }}
iconStart={{ icon: faHome }}
data-testid="unexpected-error-home"
>
Go Home
@ -37,14 +37,14 @@ export const ErrorPage = () => {
)}
<ActionButton
Element="button"
icon={{ icon: faRefresh }}
iconStart={{ icon: faRefresh }}
onClick={() => window.location.reload()}
>
Reload
</ActionButton>
<ActionButton
Element="button"
icon={{ icon: faTrash }}
iconStart={{ icon: faTrash }}
onClick={() => {
window.localStorage.clear()
}}
@ -53,7 +53,7 @@ export const ErrorPage = () => {
</ActionButton>
<ActionButton
Element="externalLink"
icon={{ icon: faBug }}
iconStart={{ icon: faBug }}
to="https://github.com/KittyCAD/modeling-app/issues/new"
>
Report Bug

View File

@ -109,7 +109,7 @@ function DeleteConfirmationDialog({
send({ type: 'Delete file', data: fileOrDir })
setIsOpen(false)
}}
icon={{
iconStart={{
icon: faTrashAlt,
bgClassName: 'bg-destroy-80',
iconClassName:
@ -340,7 +340,7 @@ export const FileTreeMenu = () => {
<>
<ActionButton
Element="button"
icon={{
iconStart={{
icon: 'filePlus',
iconClassName: '!text-current',
bgClassName: 'bg-transparent',
@ -355,7 +355,7 @@ export const FileTreeMenu = () => {
<ActionButton
Element="button"
icon={{
iconStart={{
icon: 'folderPlus',
iconClassName: '!text-current',
bgClassName: 'bg-transparent',

View File

@ -2,7 +2,7 @@ import ReactCodeMirror from '@uiw/react-codemirror'
import { TEST } from 'env'
import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext'
import { Themes, getSystemTheme } from 'lib/theme'
import { useEffect, useMemo } from 'react'
import { useEffect, useMemo, useRef } from 'react'
import { highlightSelectionMatches, searchKeymap } from '@codemirror/search'
import { lineHighlightField } from 'editor/highlightextension'
import { roundOff } from 'lib/utils'
@ -190,13 +190,15 @@ export const KclEditorPane = () => {
return extensions
}, [kclLSP, copilotLSP, textWrapping.current, cursorBlinking.current])
const initialCode = useRef(codeManager.code)
return (
<div
id="code-mirror-override"
className={'absolute inset-0 ' + (cursorBlinking.current ? 'blink' : '')}
>
<ReactCodeMirror
value={codeManager.code}
value={initialCode.current}
extensions={editorExtensions}
theme={theme}
onCreateEditor={(_editorView) =>

View File

@ -85,7 +85,7 @@ function ProjectCard({
<ActionButton
Element="button"
type="submit"
icon={{
iconStart={{
icon: faCheck,
size: 'sm',
className: 'p-1',
@ -99,7 +99,7 @@ function ProjectCard({
</ActionButton>
<ActionButton
Element="button"
icon={{
iconStart={{
icon: faX,
size: 'sm',
iconClassName: 'dark:!text-chalkboard-20',
@ -141,7 +141,7 @@ function ProjectCard({
<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"
icon={{
iconStart={{
icon: faPenAlt,
className: 'p-1',
iconClassName: 'dark:!text-chalkboard-20',
@ -161,7 +161,7 @@ function ProjectCard({
</ActionButton>
<ActionButton
Element="button"
icon={{
iconStart={{
icon: faTrashAlt,
className: 'p-1',
size: 'xs',
@ -207,7 +207,7 @@ function ProjectCard({
await handleDeleteProject(project)
setIsConfirmingDelete(false)
}}
icon={{
iconStart={{
icon: faTrashAlt,
bgClassName: 'bg-destroy-80',
className: 'p-1',

View File

@ -165,7 +165,7 @@ function ProjectMenuPopover({
<div className="flex flex-col gap-2 p-4 dark:bg-chalkboard-90">
<ActionButton
Element="button"
icon={{ icon: 'exportFile', className: 'p-1' }}
iconStart={{ icon: 'exportFile', className: 'p-1' }}
className="border-transparent dark:border-transparent"
disabled={!findCommand(exportCommandInfo)}
onClick={() =>
@ -185,7 +185,7 @@ function ProjectMenuPopover({
// Clear the scene and end the session.
engineCommandManager.endSession()
}}
icon={{
iconStart={{
icon: 'arrowLeft',
className: 'p-1',
}}

View File

@ -0,0 +1,37 @@
import { CustomIcon } from './CustomIcon'
import Tooltip from './Tooltip'
export function RefreshButton() {
async function refresh() {
if (window && 'plausible' in window) {
const p = window.plausible as (
event: string,
options?: { props: Record<string, string> }
) => Promise<void>
// Send a refresh event to Plausible so we can track how often users get stuck
await p('Refresh', {
props: {
method: 'UI button',
// TODO: add more coredump data here
},
})
}
// Window may not be available in some environments
window?.location.reload()
}
return (
<button
onClick={refresh}
className="p-1 m-0 bg-chalkboard-10/80 dark:bg-chalkboard-100/50 hover:bg-chalkboard-10 dark:hover:bg-chalkboard-100 rounded-full border border-solid border-chalkboard-10 dark:border-chalkboard-100"
>
<CustomIcon name="arrowRotateRight" className="w-5 h-5" />
<Tooltip position="bottom-right">
<span>Refresh and report</span>
<br />
<span className="text-xs">Send us data on how you got stuck</span>
</Tooltip>
</button>
)
}

View File

@ -78,7 +78,7 @@ export const SetVarNameModal = ({
Element="button"
type="submit"
disabled={!isNewVariableNameUnique}
icon={{ icon: faPlus }}
iconStart={{ icon: faPlus }}
>
Add variable
</ActionButton>

View File

@ -134,6 +134,10 @@ export const SettingsAuthProviderBase = ({
},
})
},
setClientTheme: (context) => {
const opposingTheme = getOppositeTheme(context.app.theme.current)
sceneInfra.theme = opposingTheme
},
setEngineEdges: (context) => {
engineCommandManager.sendSceneCommand({
cmd_id: uuidv4(),

View File

@ -59,7 +59,7 @@ export const UpdaterModal = ({
<ActionButton
Element="button"
onClick={() => onResolve({ wantUpdate: false })}
icon={{
iconStart={{
icon: 'close',
bgClassName: 'bg-destroy-80',
iconClassName: 'text-destroy-20 group-hover:text-destroy-10',
@ -72,7 +72,10 @@ export const UpdaterModal = ({
<ActionButton
Element="button"
onClick={() => onResolve({ wantUpdate: true })}
icon={{ icon: 'arrowRight', bgClassName: 'dark:bg-chalkboard-80' }}
iconStart={{
icon: 'arrowRight',
bgClassName: 'dark:bg-chalkboard-80',
}}
className="dark:hover:bg-chalkboard-80/50"
data-testid="update-button-update"
>

View File

@ -31,7 +31,7 @@ export const UpdaterRestartModal = ({
<ActionButton
Element="button"
onClick={() => onResolve({ wantRestart: false })}
icon={{
iconStart={{
icon: 'close',
bgClassName: 'bg-destroy-80',
iconClassName: 'text-destroy-20 group-hover:text-destroy-10',
@ -44,7 +44,10 @@ export const UpdaterRestartModal = ({
<ActionButton
Element="button"
onClick={() => onResolve({ wantRestart: true })}
icon={{ icon: 'arrowRight', bgClassName: 'dark:bg-chalkboard-80' }}
iconStart={{
icon: 'arrowRight',
bgClassName: 'dark:bg-chalkboard-80',
}}
className="dark:hover:bg-chalkboard-80/50"
data-testid="update-restrart-button-update"
>

View File

@ -55,7 +55,7 @@ const UserSidebarMenu = ({ user }: { user?: User }) => {
) : (
<ActionButton
Element={Popover.Button}
icon={{ icon: 'menu' }}
iconStart={{ icon: 'menu' }}
className="border-transparent !px-0"
data-testid="user-sidebar-toggle"
>
@ -120,7 +120,7 @@ const UserSidebarMenu = ({ user }: { user?: User }) => {
<div className="p-4 flex flex-col gap-2">
<ActionButton
Element="button"
icon={{ icon: 'settings' }}
iconStart={{ icon: 'settings' }}
className="border-transparent dark:border-transparent hover:bg-transparent"
onClick={() => {
// since /settings is a nested route the sidebar doesn't close
@ -138,7 +138,7 @@ const UserSidebarMenu = ({ user }: { user?: User }) => {
<ActionButton
Element="externalLink"
to="https://github.com/KittyCAD/modeling-app/discussions"
icon={{ icon: faGithub, className: 'p-1', size: 'sm' }}
iconStart={{ icon: faGithub, className: 'p-1', size: 'sm' }}
className="border-transparent dark:border-transparent"
>
Request a feature
@ -146,7 +146,7 @@ const UserSidebarMenu = ({ user }: { user?: User }) => {
<ActionButton
Element="externalLink"
to="https://github.com/KittyCAD/modeling-app/issues/new/choose"
icon={{ icon: faBug, className: 'p-1', size: 'sm' }}
iconStart={{ icon: faBug, className: 'p-1', size: 'sm' }}
className="border-transparent dark:border-transparent"
>
Report a bug
@ -154,7 +154,7 @@ const UserSidebarMenu = ({ user }: { user?: User }) => {
<ActionButton
Element="button"
onClick={() => send('Log out')}
icon={{
iconStart={{
icon: faSignOutAlt,
className: 'p-1',
bgClassName: '!bg-transparent',

View File

@ -24,7 +24,7 @@ export function WasmErrBanner() {
<ActionButton
Element="button"
onClick={() => setBannerDismissed(true)}
icon={{
iconStart={{
icon: 'close',
className: 'p-1',
bgClassName:

View File

@ -189,6 +189,7 @@ export default class EditorManager {
const ignoreEvents: ModelingMachineEvent['type'][] = [
'Equip Line tool',
'Equip tangential arc to',
'Equip rectangle tool',
]
if (!this._modelingEvent) {

View File

@ -264,8 +264,12 @@ export class LanguageServerPlugin implements PluginValue {
)
return null
this.sendChange({
documentText: this.view.state.doc.toString(),
this.client.textDocumentDidChange({
textDocument: {
uri: this.documentUri,
version: this.documentVersion++,
},
contentChanges: [{ text: this.view.state.doc.toString() }],
})
const result = await this.client.textDocumentFormatting({

View File

@ -1,57 +0,0 @@
import useResizeObserver from '@react-hook/resize-observer'
import { useEffect, useState } from 'react'
interface Rect {
top: number
left: number
height: number
width: number
}
/**
* Takes an element id and uses React refs to create a CSS clip-path rule to apply to a backdrop element
* which excludes the element with the given id, creating a "highlight" effect.
* @param highlightId
*/
export function useBackdropHighlight(target: string): string {
const [clipPath, setClipPath] = useState('')
const [elem, setElem] = useState(document.getElementById(target))
// Build the actual clip path string, cutting out the target element
function buildClipPath({ top, left, height, width }: Rect) {
const windowWidth = window.innerWidth
const windowHeight = window.innerHeight
return `
path(evenodd, "M0 0 l${windowWidth} 0 l0 ${windowHeight} l-${windowWidth} 0 Z \
M${left} ${top} l${width} 0 l0 ${height} l-${width} 0 Z")
`
}
// initial setup of clip path
useEffect(() => {
if (!elem) {
const newElem = document.getElementById(target)
if (newElem === null) {
throw new Error(
`Could not find element with id "${target}" to highlight`
)
}
setElem(document.getElementById(target))
return
}
const { top, left, height, width } = elem.getBoundingClientRect()
setClipPath(buildClipPath({ top, left, height, width }))
}, [elem, target])
// update clip path on resize
useResizeObserver(elem, (entry) => {
const { height, width } = entry.contentRect
// the top and left are relative to the viewport, so we need to get the target's position
const { top, left } = entry.target.getBoundingClientRect()
setClipPath(buildClipPath({ top, left, height, width }))
})
return clipPath
}

View File

@ -14,8 +14,8 @@ export function useEngineConnectionSubscriptions() {
event: 'highlight_set_entity',
callback: ({ data }) => {
if (data?.entity_id) {
const sourceRange =
engineCommandManager.artifactMap?.[data.entity_id]?.range
const sourceRange = engineCommandManager.artifactMap?.[data.entity_id]
?.range || [0, 0]
editorManager.setHighlightRange(sourceRange)
} else if (
!editorManager.highlightRange ||

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