Compare commits

...

21 Commits

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

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

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

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

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

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

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

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

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

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

* trigger ci

* trigger ci

---------

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

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

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

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

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

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

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

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

This reverts commit e7ab645267.

* rejig out side of xstate

* test tweak

* more test tweak

* refactor some codeManager stuff

* tsc

* try and fix tests

* revert small uneeded change

* fix

* snapshot tweak

* more test tweak

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

* small tweak

* disable bad test

* fmt

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-05-06 19:28:30 +10:00
55 changed files with 412 additions and 284 deletions

View File

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

View File

@ -12,6 +12,7 @@ import {
TEST_SETTINGS_ONBOARDING_START, TEST_SETTINGS_ONBOARDING_START,
} from './storageStates' } from './storageStates'
import * as TOML from '@iarna/toml' 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 debug helper: unfortunately we do rely on exact coord mouse clicks in a few places
@ -528,6 +529,10 @@ test.describe('Can create sketches on all planes and their back sides', () => {
}) })
test('Auto complete works', async ({ page }) => { test('Auto complete works', async ({ page }) => {
test.skip(
true,
'CORS issue stopping the kcl lsp from working, enable again later'
)
const u = getUtils(page) const u = getUtils(page)
// const PUR = 400 / 37.5 //pixeltoUnitRatio // const PUR = 400 / 37.5 //pixeltoUnitRatio
await page.setViewportSize({ width: 1200, height: 500 }) await page.setViewportSize({ width: 1200, height: 500 })
@ -950,9 +955,8 @@ test.describe('Command bar tests', () => {
let cmdSearchBar = page.getByPlaceholder('Search commands') let cmdSearchBar = page.getByPlaceholder('Search commands')
// First try opening the command bar and closing it // 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 await page
.getByRole('button', { name: 'Ctrl+/' }) .getByRole('button', { name: 'Commands', exact: false })
.or(page.getByRole('button', { name: '⌘K' })) .or(page.getByRole('button', { name: '⌘K' }))
.click() .click()
await expect(cmdSearchBar).toBeVisible() await expect(cmdSearchBar).toBeVisible()
@ -997,13 +1001,13 @@ test.describe('Command bar tests', () => {
localStorage.setItem( localStorage.setItem(
'persistCode', 'persistCode',
`const distance = sqrt(20) `const distance = sqrt(20)
const part001 = startSketchOn('-XZ') const part001 = startSketchOn('-XZ')
|> startProfileAt([-6.95, 4.98], %) |> startProfileAt([-6.95, 10.98], %)
|> line([25.1, 0.41], %) |> line([25.1, 0.41], %)
|> line([0.73, -14.93], %) |> line([0.73, -20.93], %)
|> line([-23.44, 0.52], %) |> line([-23.44, 0.52], %)
|> close(%) |> close(%)
` `
) )
}) })
@ -1020,7 +1024,6 @@ test.describe('Command bar tests', () => {
page.getByRole('button', { name: 'Start Sketch' }) page.getByRole('button', { name: 'Start Sketch' })
).not.toBeDisabled() ).not.toBeDisabled()
await u.clearCommandLogs() await u.clearCommandLogs()
await page.getByText('|> line([0.73, -14.93], %)').click()
await page.getByRole('button', { name: 'Extrude' }).isEnabled() await page.getByRole('button', { name: 'Extrude' }).isEnabled()
let cmdSearchBar = page.getByPlaceholder('Search commands') let cmdSearchBar = page.getByPlaceholder('Search commands')
@ -1030,6 +1033,12 @@ test.describe('Command bar tests', () => {
// Search for extrude command and choose it // Search for extrude command and choose it
await page.getByRole('option', { name: 'Extrude' }).click() 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 // Assert that we're on the distance step
await expect(page.getByRole('button', { name: 'distance' })).toBeDisabled() await expect(page.getByRole('button', { name: 'distance' })).toBeDisabled()
@ -1063,9 +1072,9 @@ test.describe('Command bar tests', () => {
`const distance = sqrt(20) `const distance = sqrt(20)
const distance001 = 5 + 7 const distance001 = 5 + 7
const part001 = startSketchOn('-XZ') const part001 = startSketchOn('-XZ')
|> startProfileAt([-6.95, 4.98], %) |> startProfileAt([-6.95, 10.98], %)
|> line([25.1, 0.41], %) |> line([25.1, 0.41], %)
|> line([0.73, -14.93], %) |> line([0.73, -20.93], %)
|> line([-23.44, 0.52], %) |> line([-23.44, 0.52], %)
|> close(%) |> close(%)
|> extrude(distance001, %)`.replace(/(\r\n|\n|\r)/gm, '') // remove newlines |> extrude(distance001, %)`.replace(/(\r\n|\n|\r)/gm, '') // remove newlines
@ -1256,6 +1265,72 @@ test('ProgramMemory can be serialised', async ({ page }) => {
}) })
}) })
test('Hovering over 3d features highlights code', async ({ page }) => {
const u = getUtils(page)
await page.addInitScript(async () => {
localStorage.setItem(
'persistCode',
`const part001 = startSketchOn('-XZ')
|> startProfileAt([20, 0], %)
|> line([7.13, 4 + 0], %)
|> angledLine({ angle: 3 + 0, length: 3.14 + 0 }, %)
|> lineTo([20.14 + 0, -0.14 + 0], %)
|> xLineTo(29 + 0, %)
|> yLine(-3.14 + 0, %, 'a')
|> xLine(1.63, %)
|> angledLineOfXLength({ angle: 3 + 0, length: 3.14 }, %)
|> angledLineOfYLength({ angle: 30, length: 3 + 0 }, %)
|> angledLineToX({ angle: 22.14 + 0, to: 12 }, %)
|> angledLineToY({ angle: 30, to: 11.14 }, %)
|> angledLineThatIntersects({
angle: 3.14,
intersectTag: 'a',
offset: 0
}, %)
|> tangentialArcTo([13.14 + 0, 13.14], %)
|> close(%)
|> extrude(5 + 7, %)
`
)
})
await page.setViewportSize({ width: 1000, height: 500 })
await page.goto('/')
await u.waitForAuthSkipAppStart()
const extrusionTop: Coords2d = [800, 240]
const flatExtrusionFace: Coords2d = [960, 160]
const arc: Coords2d = [840, 160]
const close: Coords2d = [720, 200]
const nothing: Coords2d = [600, 200]
await page.mouse.move(nothing[0], nothing[1])
await page.mouse.click(nothing[0], nothing[1])
await expect(page.getByTestId('hover-highlight')).not.toBeVisible()
await page.waitForTimeout(200)
await page.mouse.move(extrusionTop[0], extrusionTop[1])
await expect(page.getByTestId('hover-highlight')).toBeVisible()
await page.mouse.move(nothing[0], nothing[1])
await expect(page.getByTestId('hover-highlight')).not.toBeVisible()
await page.mouse.move(arc[0], arc[1])
await expect(page.getByTestId('hover-highlight')).toBeVisible()
await page.mouse.move(nothing[0], nothing[1])
await expect(page.getByTestId('hover-highlight')).not.toBeVisible()
await page.mouse.move(close[0], close[1])
await expect(page.getByTestId('hover-highlight')).toBeVisible()
await page.mouse.move(nothing[0], nothing[1])
await expect(page.getByTestId('hover-highlight')).not.toBeVisible()
await page.mouse.move(flatExtrusionFace[0], flatExtrusionFace[1])
await expect(page.getByTestId('hover-highlight')).toHaveCount(5) // multiple lines
await page.mouse.move(nothing[0], nothing[1])
await page.waitForTimeout(100)
await expect(page.getByTestId('hover-highlight')).not.toBeVisible()
})
test("Various pipe expressions should and shouldn't allow edit and or extrude", async ({ test("Various pipe expressions should and shouldn't allow edit and or extrude", async ({
page, page,
}) => { }) => {
@ -1390,7 +1465,7 @@ test('Deselecting line tool should mean nothing happens on click', async ({
`const part001 = startSketchOn('-XZ')` `const part001 = startSketchOn('-XZ')`
) )
await page.waitForTimeout(300) await page.waitForTimeout(600)
let previousCodeContent = await page.locator('.cm-content').innerText() let previousCodeContent = await page.locator('.cm-content').innerText()

View File

@ -507,7 +507,7 @@ test('Draft rectangles should look right', async ({ page, context }) => {
`const part001 = startSketchOn('-XZ')` `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() await u.closeDebugPanel()
const startXPx = 600 const startXPx = 600
@ -597,12 +597,15 @@ test.describe('Client side scene scale should match engine scale', () => {
// exit sketch // exit sketch
await u.openAndClearDebugPanel() 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 // wait for execution done
await u.expectCmdLog('[data-message-type="execution-done"]') await u.expectCmdLog('[data-message-type="execution-done"]')
await u.clearAndCloseDebugPanel() 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. // second screen shot should look almost identical, i.e. scale should be the same.
await expect(page).toHaveScreenshot({ await expect(page).toHaveScreenshot({
@ -696,12 +699,15 @@ test.describe('Client side scene scale should match engine scale', () => {
// exit sketch // exit sketch
await u.openAndClearDebugPanel() 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 // wait for execution done
await u.expectCmdLog('[data-message-type="execution-done"]') await u.expectCmdLog('[data-message-type="execution-done"]')
await u.clearAndCloseDebugPanel() 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. // second screen shot should look almost identical, i.e. scale should be the same.
await expect(page).toHaveScreenshot({ await expect(page).toHaveScreenshot({

Binary file not shown.

Before

Width:  |  Height:  |  Size: 39 KiB

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 51 KiB

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 45 KiB

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 52 KiB

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

After

Width:  |  Height:  |  Size: 34 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: 49 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 52 KiB

After

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 47 KiB

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 48 KiB

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 50 KiB

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 46 KiB

After

Width:  |  Height:  |  Size: 48 KiB

View File

@ -15,7 +15,7 @@
<script <script
defer defer
data-domain="app.zoo.dev" 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> ></script>
<title>Zoo Modeling App</title> <title>Zoo Modeling App</title>
</head> </head>

View File

@ -1,6 +1,6 @@
{ {
"name": "untitled-app", "name": "untitled-app",
"version": "0.20.1", "version": "0.21.0",
"private": true, "private": true,
"dependencies": { "dependencies": {
"@codemirror/autocomplete": "^6.16.0", "@codemirror/autocomplete": "^6.16.0",

View File

@ -74,5 +74,5 @@
} }
}, },
"productName": "Zoo Modeling App", "productName": "Zoo Modeling App",
"version": "0.20.1" "version": "0.21.0"
} }

View File

@ -4,7 +4,6 @@ import { engineCommandManager, kclManager } from 'lib/singletons'
import { useModelingContext } from 'hooks/useModelingContext' import { useModelingContext } from 'hooks/useModelingContext'
import { useCommandsContext } from 'hooks/useCommandsContext' import { useCommandsContext } from 'hooks/useCommandsContext'
import { ActionButton } from 'components/ActionButton' import { ActionButton } from 'components/ActionButton'
import usePlatform from 'hooks/usePlatform'
import { isSingleCursorInPipe } from 'lang/queryAst' import { isSingleCursorInPipe } from 'lang/queryAst'
import { useKclContext } from 'lang/KclProvider' import { useKclContext } from 'lang/KclProvider'
import { import {
@ -14,7 +13,6 @@ import {
import { useStore } from 'useStore' import { useStore } from 'useStore'
export const Toolbar = () => { export const Toolbar = () => {
const platform = usePlatform()
const { commandBarSend } = useCommandsContext() const { commandBarSend } = useCommandsContext()
const { state, send, context } = useModelingContext() const { state, send, context } = useModelingContext()
const toolbarButtonsRef = useRef<HTMLUListElement>(null) const toolbarButtonsRef = useRef<HTMLUListElement>(null)
@ -274,17 +272,8 @@ export const Toolbar = () => {
} }
return ( 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="max-w-full overflow-hidden whitespace-nowrap rounded px-1.5 py-0.5 backdrop-blur-sm bg-chalkboard-10/80 dark:bg-chalkboard-110/70 relative">
<menu className="flex-1 pl-1 pr-2 py-0 overflow-hidden rounded-l-sm whitespace-nowrap border-solid border border-primary/30 dark:border-chalkboard-90 border-r-0"> <ToolbarButtons />
<ToolbarButtons /> </menu>
</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' cursor = 'grabbing'
} else if ( } else if (
state.matches('Sketch.Line tool') || state.matches('Sketch.Line tool') ||
state.matches('Sketch.Tangential arc to') state.matches('Sketch.Tangential arc to') ||
state.matches('Sketch.Rectangle tool')
) { ) {
cursor = 'crosshair' cursor = 'crosshair'
} else { } else {
@ -104,9 +105,9 @@ export const ClientSideScene = ({
style={{ cursor: cursor }} style={{ cursor: cursor }}
className={`absolute inset-0 h-full w-full transition-all duration-300 ${ className={`absolute inset-0 h-full w-full transition-all duration-300 ${
hideClient ? 'opacity-0' : 'opacity-100' hideClient ? 'opacity-0' : 'opacity-100'
} ${hideServer ? 'bg-black' : ''} ${ } ${hideServer ? 'bg-chalkboard-10 dark:bg-chalkboard-100' : ''} ${
!hideClient && !hideServer && state.matches('Sketch') !hideClient && !hideServer && state.matches('Sketch')
? 'bg-black/80' ? 'bg-chalkboard-10/80 dark:bg-chalkboard-100/80'
: '' : ''
}`} }`}
></div> ></div>

View File

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

View File

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

View File

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

View File

@ -1,12 +1,11 @@
import { Toolbar } from '../Toolbar' import { Toolbar } from '../Toolbar'
import UserSidebarMenu from './UserSidebarMenu' import UserSidebarMenu from 'components/UserSidebarMenu'
import { type IndexLoaderData } from 'lib/types' import { type IndexLoaderData } from 'lib/types'
import ProjectSidebarMenu from './ProjectSidebarMenu' import ProjectSidebarMenu from './ProjectSidebarMenu'
import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext' import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext'
import styles from './AppHeader.module.css' import styles from './AppHeader.module.css'
import { useCommandsContext } from 'hooks/useCommandsContext' import { RefreshButton } from 'components/RefreshButton'
import { ActionButton } from './ActionButton' import { CommandBarOpenButton } from './CommandBarOpenButton'
import usePlatform from 'hooks/usePlatform'
interface AppHeaderProps extends React.PropsWithChildren { interface AppHeaderProps extends React.PropsWithChildren {
showToolbar?: boolean showToolbar?: boolean
@ -22,8 +21,6 @@ export const AppHeader = ({
className = '', className = '',
enableMenu = false, enableMenu = false,
}: AppHeaderProps) => { }: AppHeaderProps) => {
const platform = usePlatform()
const { commandBarSend } = useCommandsContext()
const { auth } = useSettingsAuthContext() const { auth } = useSettingsAuthContext()
const user = auth?.context?.user const user = auth?.context?.user
@ -43,24 +40,17 @@ export const AppHeader = ({
/> />
{/* Toolbar if the context deems it */} {/* 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"> <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 ? ( {showToolbar && <Toolbar />}
<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>
)}
</div> </div>
<div className="flex items-center gap-1 py-1 ml-auto"> <div className="flex items-center gap-1 py-1 ml-auto">
{/* If there are children, show them, otherwise show User menu */} {/* If there are children, show them, otherwise show User menu */}
{children || <UserSidebarMenu user={user} />} {children || (
<>
<CommandBarOpenButton />
<RefreshButton />
<UserSidebarMenu user={user} />
</>
)}
</div> </div>
</header> </header>
) )

View File

@ -7,6 +7,7 @@ import {
getSelectionType, getSelectionType,
getSelectionTypeDisplayText, getSelectionTypeDisplayText,
} from 'lib/selections' } from 'lib/selections'
import { kclManager } from 'lib/singletons'
import { modelingMachine } from 'machines/modelingMachine' import { modelingMachine } from 'machines/modelingMachine'
import { useCallback, useEffect, useRef, useState } from 'react' import { useCallback, useEffect, useRef, useState } from 'react'
import { useHotkeys } from 'react-hotkeys-hook' import { useHotkeys } from 'react-hotkeys-hook'
@ -50,6 +51,14 @@ function CommandBarSelectionInput({
inputRef.current?.focus() inputRef.current?.focus()
}, [selection, inputRef]) }, [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 // Fast-forward through this arg if it's marked as skippable
// and we have a valid selection already // and we have a valid selection already
useEffect(() => { 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"> <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 <CustomIcon
name="search" 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 <Combobox.Input
onChange={(event) => setQuery(event.target.value)} onChange={(event) => setQuery(event.target.value)}

View File

@ -41,6 +41,16 @@ const CustomIconMap = {
/> />
</svg> </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: ( arrowUp: (
<svg viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg"> <svg viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path <path

View File

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

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

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

View File

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

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', event: 'highlight_set_entity',
callback: ({ data }) => { callback: ({ data }) => {
if (data?.entity_id) { if (data?.entity_id) {
const sourceRange = const sourceRange = engineCommandManager.artifactMap?.[data.entity_id]
engineCommandManager.artifactMap?.[data.entity_id]?.range ?.range || [0, 0]
editorManager.setHighlightRange(sourceRange) editorManager.setHighlightRange(sourceRange)
} else if ( } else if (
!editorManager.highlightRange || !editorManager.highlightRange ||

View File

@ -17,7 +17,7 @@ import {
ExtrudeGroup, ExtrudeGroup,
} from 'lang/wasm' } from 'lang/wasm'
import { getNodeFromPath } from './queryAst' import { getNodeFromPath } from './queryAst'
import { codeManager, editorManager } from 'lib/singletons' import { codeManager, editorManager, sceneInfra } from 'lib/singletons'
export class KclManager { export class KclManager {
private _ast: Program = { private _ast: Program = {
@ -187,6 +187,7 @@ export class KclManager {
ast, ast,
engineCommandManager: this.engineCommandManager, engineCommandManager: this.engineCommandManager,
}) })
sceneInfra.modelingSend({ type: 'code edit during sketch' })
enterEditMode(programMemory, this.engineCommandManager) enterEditMode(programMemory, this.engineCommandManager)
this.isExecuting = false this.isExecuting = false
// Check the cancellation token for this execution before applying side effects // Check the cancellation token for this execution before applying side effects
@ -219,7 +220,7 @@ export class KclManager {
const newCode = recast(ast) const newCode = recast(ast)
const newAst = this.safeParse(newCode) const newAst = this.safeParse(newCode)
if (!newAst) return if (!newAst) return
codeManager.updateCodeStateEditor(newCode) codeManager.updateCodeEditor(newCode)
// Write the file to disk. // Write the file to disk.
await codeManager.writeToFile() await codeManager.writeToFile()
await this?.engineCommandManager?.waitForReady await this?.engineCommandManager?.waitForReady
@ -316,7 +317,7 @@ export class KclManager {
if (execute) { if (execute) {
// Call execute on the set ast. // Call execute on the set ast.
// Update the code state and editor. // Update the code state and editor.
codeManager.updateCodeStateEditor(newCode) codeManager.updateCodeEditor(newCode)
// Write the file to disk. // Write the file to disk.
await codeManager.writeToFile() await codeManager.writeToFile()
await this.executeAst(astWithUpdatedSource) await this.executeAst(astWithUpdatedSource)

View File

@ -11,8 +11,7 @@ const PERSIST_CODE_TOKEN = 'persistCode'
export default class CodeManager { export default class CodeManager {
private _code: string = bracket private _code: string = bracket
private _updateState: (arg: string) => void = () => {} #updateState: (arg: string) => void = () => {}
private _updateEditor: (arg: string) => void = () => {}
private _currentFilePath: string | null = null private _currentFilePath: string | null = null
constructor() { constructor() {
@ -46,7 +45,7 @@ export default class CodeManager {
} }
registerCallBacks({ setCode }: { setCode: (arg: string) => void }) { registerCallBacks({ setCode }: { setCode: (arg: string) => void }) {
this._updateState = setCode this.#updateState = setCode
} }
updateCurrentFilePath(path: string) { updateCurrentFilePath(path: string) {
@ -57,18 +56,20 @@ export default class CodeManager {
updateCodeState(code: string): void { updateCodeState(code: string): void {
if (this._code !== code) { if (this._code !== code) {
this.code = code this.code = code
this._updateState(code) this.#updateState(code)
} }
} }
// Update the code in the editor. // Update the code in the editor.
updateCodeEditor(code: string): void { updateCodeEditor(code: string): void {
const lastCode = this._code
this.code = code this.code = code
this._updateEditor(code)
if (editorManager.editorView) { if (editorManager.editorView) {
editorManager.editorView.dispatch({ editorManager.editorView.dispatch({
changes: { from: 0, to: lastCode.length, insert: code }, changes: {
from: 0,
to: editorManager.editorView.state.doc.length,
insert: code,
},
}) })
} }
} }
@ -77,8 +78,7 @@ export default class CodeManager {
updateCodeStateEditor(code: string): void { updateCodeStateEditor(code: string): void {
if (this._code !== code) { if (this._code !== code) {
this.code = code this.code = code
this._updateState(code) this.#updateState(code)
this._updateEditor(code)
} }
} }

View File

@ -542,6 +542,27 @@ class EngineConnection {
}, },
} }
}) })
this.unreliableDataChannel.addEventListener('message', (event) => {
const result: UnreliableResponses = JSON.parse(event.data)
Object.values(
this.engineCommandManager.unreliableSubscriptions[result.type] || {}
).forEach(
// TODO: There is only one response that uses the unreliable channel atm,
// highlight_set_entity, if there are more it's likely they will all have the same
// sequence logic, but I'm not sure if we use a single global sequence or a sequence
// per unreliable subscription.
(callback) => {
if (
result.type === 'highlight_set_entity' &&
result?.data?.sequence &&
result?.data.sequence > this.engineCommandManager.inSequence
) {
this.engineCommandManager.inSequence = result.data.sequence
callback(result)
}
}
)
})
}) })
} }
@ -853,7 +874,7 @@ type CommandTypes = Models['ModelingCmd_type']['type'] | 'batch'
type UnreliableResponses = Extract< type UnreliableResponses = Extract<
Models['OkModelingCmdResponse_type'], Models['OkModelingCmdResponse_type'],
{ type: 'highlight_set_entity' } { type: 'highlight_set_entity' | 'camera_drag_move' }
> >
interface UnreliableSubscription<T extends UnreliableResponses['type']> { interface UnreliableSubscription<T extends UnreliableResponses['type']> {
event: T event: T
@ -1061,32 +1082,6 @@ export class EngineCommandManager {
setIsStreamReady(false) setIsStreamReady(false)
}, },
onConnectionStarted: (engineConnection) => { onConnectionStarted: (engineConnection) => {
engineConnection?.pc?.addEventListener('datachannel', (event) => {
let unreliableDataChannel = event.channel
unreliableDataChannel.addEventListener('message', (event) => {
const result: UnreliableResponses = JSON.parse(event.data)
Object.values(
this.unreliableSubscriptions[result.type] || {}
).forEach(
// TODO: There is only one response that uses the unreliable channel atm,
// highlight_set_entity, if there are more it's likely they will all have the same
// sequence logic, but I'm not sure if we use a single global sequence or a sequence
// per unreliable subscription.
(callback) => {
if (
result?.data?.sequence &&
result?.data.sequence > this.inSequence &&
result.type === 'highlight_set_entity'
) {
this.inSequence = result.data.sequence
callback(result)
}
}
)
})
})
// When the EngineConnection starts a connection, we want to register // When the EngineConnection starts a connection, we want to register
// callbacks into the WebSocket/PeerConnection. // callbacks into the WebSocket/PeerConnection.
engineConnection.websocket?.addEventListener('message', (event) => { engineConnection.websocket?.addEventListener('message', (event) => {
@ -1366,14 +1361,11 @@ export class EngineCommandManager {
callback, callback,
}: Subscription<T>): () => void { }: Subscription<T>): () => void {
const localUnsubscribeId = uuidv4() const localUnsubscribeId = uuidv4()
const otherEventCallbacks = this.subscriptions[event] if (!this.subscriptions[event]) {
if (otherEventCallbacks) { this.subscriptions[event] = {}
otherEventCallbacks[localUnsubscribeId] = callback
} else {
this.subscriptions[event] = {
[localUnsubscribeId]: callback,
}
} }
this.subscriptions[event][localUnsubscribeId] = callback
return () => this.unSubscribeTo(event, localUnsubscribeId) return () => this.unSubscribeTo(event, localUnsubscribeId)
} }
private unSubscribeTo(event: ModelTypes, id: string) { private unSubscribeTo(event: ModelTypes, id: string) {
@ -1384,14 +1376,10 @@ export class EngineCommandManager {
callback, callback,
}: UnreliableSubscription<T>): () => void { }: UnreliableSubscription<T>): () => void {
const localUnsubscribeId = uuidv4() const localUnsubscribeId = uuidv4()
const otherEventCallbacks = this.unreliableSubscriptions[event] if (!this.unreliableSubscriptions[event]) {
if (otherEventCallbacks) { this.unreliableSubscriptions[event] = {}
otherEventCallbacks[localUnsubscribeId] = callback
} else {
this.unreliableSubscriptions[event] = {
[localUnsubscribeId]: callback,
}
} }
this.unreliableSubscriptions[event][localUnsubscribeId] = callback
return () => this.unSubscribeToUnreliable(event, localUnsubscribeId) return () => this.unSubscribeToUnreliable(event, localUnsubscribeId)
} }
private unSubscribeToUnreliable( private unSubscribeToUnreliable(

View File

@ -14,9 +14,21 @@ interface ModelingAppFile {
const save_ = async (file: ModelingAppFile) => { const save_ = async (file: ModelingAppFile) => {
try { try {
if (isTauri()) { if (isTauri()) {
const extension = file.name.split('.').pop() || null
let extensions: string[] = []
if (extension !== null) {
extensions.push(extension)
}
// Open a dialog to save the file. // Open a dialog to save the file.
const filePath = await save({ const filePath = await save({
defaultPath: file.name, defaultPath: file.name,
filters: [
{
name: 'model',
extensions: extensions,
},
],
}) })
if (filePath === null) { if (filePath === null) {
@ -48,7 +60,7 @@ export async function exportSave(data: ArrayBuffer) {
// This converts the ArrayBuffer to a Rust equivalent Vec<u8>. // This converts the ArrayBuffer to a Rust equivalent Vec<u8>.
let uintArray = new Uint8Array(data) let uintArray = new Uint8Array(data)
const files: ModelingAppFile[] = deserialize_files(uintArray) let files: ModelingAppFile[] = deserialize_files(uintArray)
if (files.length > 1) { if (files.length > 1) {
let zip = new JSZip() let zip = new JSZip()

View File

@ -65,3 +65,15 @@ export function getThemeColorForEngine(theme: Themes) {
? { r: dark, g: dark, b: dark, a: 1 } ? { r: dark, g: dark, b: dark, a: 1 }
: { r: light, g: light, b: light, a: 1 } : { r: light, g: light, b: light, a: 1 }
} }
/**
* ThreeJS uses hex values for colors
* @param theme
* @returns
*/
export function getThemeColorForThreeJs(theme: Themes) {
const resolvedTheme = getResolvedTheme(theme)
const dark = 0x1c1c1c
const light = 0xf9f9f9
return resolvedTheme === Themes.Dark ? dark : light
}

File diff suppressed because one or more lines are too long

View File

@ -11,7 +11,7 @@ import {
export const settingsMachine = createMachine( export const settingsMachine = createMachine(
{ {
/** @xstate-layout N4IgpgJg5mDOIC5QGUwBc0EsB2VYDpMIAbMAYlnXwFsB7CMYnKfAV20zVgG0AGAXUSgADrVidMtbEJAAPRABYAHAFZ8vAEwA2FVq0aNAZgCcARl0B2ADQgAnolO8LvfBYUXT+48YW+tCgF8Am1QMZgIiUgoqAENhYXw0AAswajA+QSQQUXEsKRl5BCM1DQUDMo0VQwVjJxt7BFMDJXwNWo0LFSU3c0DgkFCsXAiScgAlOHQAAkow4YyZHIl8rMKAWn18Q39q3ScVFQ1NFXqHXiVTfGNqhSdeXi1DJQ1TIJD0IbxCUbIAKgWsks8tJVopjFp8KZjEpqi9lKZDE0TnYzgZXO5PG0fH4+u85l9IuRQlMYsRiFNsGAAO4zD7hAEiMTLEGgda6fAKJoIiyIkwWYwWawoxpGFxaHkKFS1a7woL9bD0OAyQbhRZM4EFRBrUyOLY7SVafaHTSnBDGDqQiz6DRKbwqTxvAZ04bfUhq3KSFlyBxHdQIhR6HTQmoC02OUwKPW7GrPdy8QxygJAA */ /** @xstate-layout N4IgpgJg5mDOIC5QGUwBc0EsB2VYDpMIAbMAYlnXwEMAHW-Ae2wCNHqAnCHKZNatAFdYAbQAMAXUShajWJizNpIAB6IAbAFZN+AOwAWAIwAOYwE4AzGYBM+-ZosAaEAE9Eh62LP51ls+v0LMWt1awMAX3DnVAweAiJSCio6BjQACzAAWzAAYUZiRg5xKSQQWXlFbGU1BA9vQ0N1CwCxdVbdY1DnNwQzPp8zTTFje1D1QwtjSOj0LFx4knJKNHxMxggwYh58DYAzakFiNABVbAVi5XKFTCVSmotNY3w7YysHRuNDTXV1bvdG7w-IKTcbaazWCzTEAxOZ4QiLJIrFL4dJZMAXUpXSrVDTGazPMQPR4GXRBAx-XoGfDWIadMx4n6EqEwuLwxLLVbrTbbNKYKBpLb8tAAUWgcAxMjk11uoHumn0+DEw2sJkMulCgWsFL6YnwfX0ELsYg61jMumZs1ZCXIACU4OgAATLWGiSSXKXYu4aLz4UwWBr6DqBYYUzSePXqUlBLxmyZmC2xeZs8gxB3UYjEJ2W+YSsoem5VL0IKy6z6EsJifQ2Czq0MTHzq8Yjfz6MQmBMu5NkABUuaxBZxCDD+DD5lJgUjxssFP0xl0I+0pm06uMmg8kSiIGwXPgpRZ83dFQHRYAtA0LCO2tZjGIHJNdB5fq5EK3dc1OsNGkrA+oO1bFoe0qFrKiAnlol7BDed5zo+FK+Doc7qhCNaVv4UwbkAA */
id: 'Settings', id: 'Settings',
predictableActionArguments: true, predictableActionArguments: true,
context: {} as ReturnType<typeof createSettings>, context: {} as ReturnType<typeof createSettings>,
@ -59,6 +59,7 @@ export const settingsMachine = createMachine(
'setThemeClass', 'setThemeClass',
'setEngineTheme', 'setEngineTheme',
'persistSettings', 'persistSettings',
'setClientTheme',
], ],
}, },
@ -83,6 +84,7 @@ export const settingsMachine = createMachine(
'setClientSideSceneUnits', 'setClientSideSceneUnits',
'Execute AST', 'Execute AST',
'persistSettings', 'persistSettings',
'setClientTheme',
], ],
}, },
@ -96,6 +98,7 @@ export const settingsMachine = createMachine(
'setClientSideSceneUnits', 'setClientSideSceneUnits',
'Execute AST', 'Execute AST',
'persistSettings', 'persistSettings',
'setClientTheme',
], ],
}, },
}, },

View File

@ -1,7 +1,6 @@
import { OnboardingButtons, useDismiss, useNextClick } from '.' import { OnboardingButtons, useDismiss, useNextClick } from '.'
import { onboardingPaths } from 'routes/Onboarding/paths' import { onboardingPaths } from 'routes/Onboarding/paths'
import { useStore } from '../../useStore' import { useStore } from '../../useStore'
import { useBackdropHighlight } from 'hooks/useBackdropHighlight'
export default function CodeEditor() { export default function CodeEditor() {
const { buttonDownInStream } = useStore((s) => ({ const { buttonDownInStream } = useStore((s) => ({
@ -12,10 +11,6 @@ export default function CodeEditor() {
return ( return (
<div className="fixed grid justify-end items-center inset-0 z-50 pointer-events-none"> <div className="fixed grid justify-end items-center inset-0 z-50 pointer-events-none">
<div
className="fixed inset-0 bg-black opacity-50 dark:opacity-80 pointer-events-none"
style={{ clipPath: useBackdropHighlight('code-pane') }}
></div>
<div <div
className={ className={
'z-10 max-w-xl border border-chalkboard-50 dark:border-chalkboard-80 shadow-lg h-3/4 flex flex-col justify-center bg-chalkboard-10 dark:bg-chalkboard-90 p-8 rounded' + 'z-10 max-w-xl border border-chalkboard-50 dark:border-chalkboard-80 shadow-lg h-3/4 flex flex-col justify-center bg-chalkboard-10 dark:bg-chalkboard-90 p-8 rounded' +

View File

@ -12,7 +12,7 @@ export default function FutureWork() {
useEffect(() => { useEffect(() => {
// We do want to update both the state and editor here. // We do want to update both the state and editor here.
codeManager.updateCodeStateEditor(bracket) codeManager.updateCodeEditor(bracket)
if (kclManager.engineCommandManager.engineConnection?.isReady()) { if (kclManager.engineCommandManager.engineConnection?.isReady()) {
// If the engine is ready, promptly execute the loaded code // If the engine is ready, promptly execute the loaded code
kclManager.executeCode(true) kclManager.executeCode(true)

View File

@ -1,7 +1,6 @@
import { OnboardingButtons, kbdClasses, useDismiss, useNextClick } from '.' import { OnboardingButtons, kbdClasses, useDismiss, useNextClick } from '.'
import { onboardingPaths } from 'routes/Onboarding/paths' import { onboardingPaths } from 'routes/Onboarding/paths'
import { useStore } from '../../useStore' import { useStore } from '../../useStore'
import { useBackdropHighlight } from 'hooks/useBackdropHighlight'
import { bracketWidthConstantLine } from 'lib/exampleKcl' import { bracketWidthConstantLine } from 'lib/exampleKcl'
export default function InteractiveNumbers() { export default function InteractiveNumbers() {
@ -13,10 +12,6 @@ export default function InteractiveNumbers() {
return ( return (
<div className="fixed grid justify-end items-center inset-0 z-50 pointer-events-none"> <div className="fixed grid justify-end items-center inset-0 z-50 pointer-events-none">
<div
className="fixed inset-0 bg-black opacity-50 pointer-events-none"
style={{ clipPath: useBackdropHighlight('code-pane') }}
></div>
<div <div
className={ className={
'z-10 max-w-xl border border-chalkboard-50 dark:border-chalkboard-80 shadow-lg h-3/4 flex flex-col justify-center bg-chalkboard-10 dark:bg-chalkboard-90 p-8 rounded' + 'z-10 max-w-xl border border-chalkboard-50 dark:border-chalkboard-80 shadow-lg h-3/4 flex flex-col justify-center bg-chalkboard-10 dark:bg-chalkboard-90 p-8 rounded' +

View File

@ -56,7 +56,7 @@ function OnboardingWithNewFile() {
dismiss={dismiss} dismiss={dismiss}
next={() => { next={() => {
// We do want to update both the state and editor here. // We do want to update both the state and editor here.
codeManager.updateCodeStateEditor(bracket) codeManager.updateCodeEditor(bracket)
kclManager.executeCode(true) kclManager.executeCode(true)
next() next()
}} }}
@ -80,7 +80,7 @@ function OnboardingWithNewFile() {
dismiss={dismiss} dismiss={dismiss}
next={() => { next={() => {
void createAndOpenNewProject() void createAndOpenNewProject()
codeManager.updateCodeStateEditor(bracket) codeManager.updateCodeEditor(bracket)
dismiss() dismiss()
}} }}
nextText="Make a new project" nextText="Make a new project"
@ -113,7 +113,7 @@ export default function Introduction() {
const isStarterCode = currentCode === '' || currentCode === bracket const isStarterCode = currentCode === '' || currentCode === bracket
useEffect(() => { useEffect(() => {
if (codeManager.code === '') codeManager.updateCodeStateEditor(bracket) if (codeManager.code === '') codeManager.updateCodeEditor(bracket)
}, []) }, [])
return isStarterCode ? ( return isStarterCode ? (

View File

@ -1,7 +1,6 @@
import { OnboardingButtons, useDismiss, useNextClick } from '.' import { OnboardingButtons, useDismiss, useNextClick } from '.'
import { onboardingPaths } from 'routes/Onboarding/paths' import { onboardingPaths } from 'routes/Onboarding/paths'
import { useStore } from '../../useStore' import { useStore } from '../../useStore'
import { useBackdropHighlight } from 'hooks/useBackdropHighlight'
import { Themes, getSystemTheme } from 'lib/theme' import { Themes, getSystemTheme } from 'lib/theme'
import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext' import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext'
import { bracketThicknessCalculationLine } from 'lib/exampleKcl' import { bracketThicknessCalculationLine } from 'lib/exampleKcl'
@ -29,10 +28,6 @@ export default function ParametricModeling() {
return ( return (
<div className="fixed grid justify-end items-center inset-0 z-50 pointer-events-none"> <div className="fixed grid justify-end items-center inset-0 z-50 pointer-events-none">
<div
className="fixed inset-0 bg-black dark:bg-black-80 opacity-50 pointer-events-none"
style={{ clipPath: useBackdropHighlight('code-pane') }}
></div>
<div <div
className={ className={
'z-10 max-w-xl border border-chalkboard-50 dark:border-chalkboard-80 shadow-lg h-3/4 flex flex-col justify-center bg-chalkboard-10 dark:bg-chalkboard-90 p-8 rounded' + 'z-10 max-w-xl border border-chalkboard-50 dark:border-chalkboard-80 shadow-lg h-3/4 flex flex-col justify-center bg-chalkboard-10 dark:bg-chalkboard-90 p-8 rounded' +

View File

@ -11,7 +11,7 @@ export default function Sketching() {
useEffect(() => { useEffect(() => {
// We do want to update both the state and editor here. // We do want to update both the state and editor here.
codeManager.updateCodeStateEditor('') codeManager.updateCodeEditor('')
if (kclManager.engineCommandManager.engineConnection?.isReady()) { if (kclManager.engineCommandManager.engineConnection?.isReady()) {
// If the engine is ready, promptly execute the loaded code // If the engine is ready, promptly execute the loaded code
kclManager.executeCode(true) kclManager.executeCode(true)

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

@ -155,9 +155,9 @@ dependencies = [
[[package]] [[package]]
name = "anyhow" name = "anyhow"
version = "1.0.82" version = "1.0.83"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f538837af36e6f6a9be0faa67f9a314f8119e4e4b5867c6ab40ed60360142519" checksum = "25bdb32cbbdce2b519a9cd7df3a678443100e265d5e25ca763b7572a5104f5f3"
dependencies = [ dependencies = [
"backtrace", "backtrace",
] ]
@ -246,7 +246,7 @@ checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.60", "syn 2.0.61",
] ]
[[package]] [[package]]
@ -257,7 +257,7 @@ checksum = "c6fa2087f2753a7da8cc1c0dbfcf89579dd57458e36769de5ac750b4671737ca"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.60", "syn 2.0.61",
] ]
[[package]] [[package]]
@ -280,7 +280,7 @@ checksum = "3c87f3f15e7794432337fc718554eaa4dc8f04c9677a950ffe366f20a162ae42"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.60", "syn 2.0.61",
] ]
[[package]] [[package]]
@ -596,7 +596,7 @@ dependencies = [
"heck 0.5.0", "heck 0.5.0",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.60", "syn 2.0.61",
] ]
[[package]] [[package]]
@ -846,7 +846,7 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.60", "syn 2.0.61",
] ]
[[package]] [[package]]
@ -870,7 +870,7 @@ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"strsim 0.10.0", "strsim 0.10.0",
"syn 2.0.60", "syn 2.0.61",
] ]
[[package]] [[package]]
@ -881,7 +881,7 @@ checksum = "a668eda54683121533a393014d8692171709ff57a7d61f187b6e782719f8933f"
dependencies = [ dependencies = [
"darling_core", "darling_core",
"quote", "quote",
"syn 2.0.60", "syn 2.0.61",
] ]
[[package]] [[package]]
@ -922,7 +922,7 @@ checksum = "377af281d8f23663862a7c84623bc5dcf7f8c44b13c7496a590bdc157f941a43"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.60", "syn 2.0.61",
"synstructure 0.13.1", "synstructure 0.13.1",
] ]
@ -976,7 +976,7 @@ dependencies = [
"rustfmt-wrapper", "rustfmt-wrapper",
"serde", "serde",
"serde_tokenstream", "serde_tokenstream",
"syn 2.0.60", "syn 2.0.61",
] ]
[[package]] [[package]]
@ -988,7 +988,7 @@ dependencies = [
"diesel_table_macro_syntax", "diesel_table_macro_syntax",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.60", "syn 2.0.61",
] ]
[[package]] [[package]]
@ -997,7 +997,7 @@ version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc5557efc453706fed5e4fa85006fe9817c224c3f480a34c7e5959fd700921c5" checksum = "fc5557efc453706fed5e4fa85006fe9817c224c3f480a34c7e5959fd700921c5"
dependencies = [ dependencies = [
"syn 2.0.60", "syn 2.0.61",
] ]
[[package]] [[package]]
@ -1026,7 +1026,7 @@ checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.60", "syn 2.0.61",
] ]
[[package]] [[package]]
@ -1108,7 +1108,7 @@ checksum = "03cdc46ec28bd728e67540c528013c6a10eb69a02eb31078a1bda695438cbfb8"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.60", "syn 2.0.61",
] ]
[[package]] [[package]]
@ -1325,7 +1325,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.60", "syn 2.0.61",
] ]
[[package]] [[package]]
@ -1436,7 +1436,7 @@ dependencies = [
"inflections", "inflections",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.60", "syn 2.0.61",
] ]
[[package]] [[package]]
@ -1959,7 +1959,7 @@ dependencies = [
"pretty_assertions", "pretty_assertions",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.60", "syn 2.0.61",
] ]
[[package]] [[package]]
@ -2032,7 +2032,7 @@ checksum = "0611fc9b9786175da21d895ffa0f65039e19c9111e94a41b7af999e3b95f045f"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.60", "syn 2.0.61",
] ]
[[package]] [[package]]
@ -2048,9 +2048,9 @@ dependencies = [
[[package]] [[package]]
name = "kittycad-modeling-cmds" name = "kittycad-modeling-cmds"
version = "0.2.23" version = "0.2.24"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1c1f8bdab7f09b4f4a954dafe0694e96f6f2b73733cf7cddccc42ee493e2c1e9" checksum = "c390a9f643aa00d2c3ffe880f1810066f52a13877ec3ecab858ff53cad84bd5e"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"chrono", "chrono",
@ -2083,7 +2083,7 @@ checksum = "385775cc9d5bf25579f3029824ca1a6e7ab1b7c338e972ec8e8fcefff801f353"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.60", "syn 2.0.61",
] ]
[[package]] [[package]]
@ -2457,7 +2457,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.60", "syn 2.0.61",
] ]
[[package]] [[package]]
@ -2614,7 +2614,7 @@ dependencies = [
"regex", "regex",
"regex-syntax 0.7.5", "regex-syntax 0.7.5",
"structmeta 0.2.0", "structmeta 0.2.0",
"syn 2.0.60", "syn 2.0.61",
] ]
[[package]] [[package]]
@ -2628,7 +2628,7 @@ dependencies = [
"regex", "regex",
"regex-syntax 0.8.2", "regex-syntax 0.8.2",
"structmeta 0.3.0", "structmeta 0.3.0",
"syn 2.0.60", "syn 2.0.61",
] ]
[[package]] [[package]]
@ -2694,7 +2694,7 @@ checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.60", "syn 2.0.61",
] ]
[[package]] [[package]]
@ -2841,9 +2841,9 @@ dependencies = [
[[package]] [[package]]
name = "proc-macro2" name = "proc-macro2"
version = "1.0.81" version = "1.0.82"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d1597b0c024618f09a9c3b8655b7e430397a36d23fdafec26d6965e9eec3eba" checksum = "8ad3d49ab951a01fbaafe34f2ec74122942fe18a3f9814c3268f1bb72042131b"
dependencies = [ dependencies = [
"unicode-ident", "unicode-ident",
] ]
@ -3443,7 +3443,7 @@ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"serde_derive_internals", "serde_derive_internals",
"syn 2.0.60", "syn 2.0.61",
] ]
[[package]] [[package]]
@ -3519,9 +3519,9 @@ checksum = "92d43fe69e652f3df9bdc2b85b2854a0825b86e4fb76bc44d945137d053639ca"
[[package]] [[package]]
name = "serde" name = "serde"
version = "1.0.200" version = "1.0.201"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ddc6f9cc94d67c0e21aaf7eda3a010fd3af78ebf6e096aa6e2e13c79749cce4f" checksum = "780f1cebed1629e4753a1a38a3c72d30b97ec044f0aef68cb26650a3c5cf363c"
dependencies = [ dependencies = [
"serde_derive", "serde_derive",
] ]
@ -3537,13 +3537,13 @@ dependencies = [
[[package]] [[package]]
name = "serde_derive" name = "serde_derive"
version = "1.0.200" version = "1.0.201"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "856f046b9400cee3c8c94ed572ecdb752444c24528c035cd35882aad6f492bcb" checksum = "c5e405930b9796f1c00bee880d03fc7e0bb4b9a11afc776885ffe84320da2865"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.60", "syn 2.0.61",
] ]
[[package]] [[package]]
@ -3554,7 +3554,7 @@ checksum = "330f01ce65a3a5fe59a60c82f3c9a024b573b8a6e875bd233fe5f934e71d54e3"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.60", "syn 2.0.61",
] ]
[[package]] [[package]]
@ -3577,7 +3577,7 @@ checksum = "0b2e6b945e9d3df726b65d6ee24060aff8e3533d431f677a9695db04eff9dfdb"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.60", "syn 2.0.61",
] ]
[[package]] [[package]]
@ -3598,7 +3598,7 @@ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"serde", "serde",
"syn 2.0.60", "syn 2.0.61",
] ]
[[package]] [[package]]
@ -3773,7 +3773,7 @@ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"structmeta-derive 0.2.0", "structmeta-derive 0.2.0",
"syn 2.0.60", "syn 2.0.61",
] ]
[[package]] [[package]]
@ -3785,7 +3785,7 @@ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"structmeta-derive 0.3.0", "structmeta-derive 0.3.0",
"syn 2.0.60", "syn 2.0.61",
] ]
[[package]] [[package]]
@ -3796,7 +3796,7 @@ checksum = "a60bcaff7397072dca0017d1db428e30d5002e00b6847703e2e42005c95fbe00"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.60", "syn 2.0.61",
] ]
[[package]] [[package]]
@ -3807,7 +3807,7 @@ checksum = "152a0b65a590ff6c3da95cabe2353ee04e6167c896b28e3b14478c2636c922fc"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.60", "syn 2.0.61",
] ]
[[package]] [[package]]
@ -3879,9 +3879,9 @@ dependencies = [
[[package]] [[package]]
name = "syn" name = "syn"
version = "2.0.60" version = "2.0.61"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "909518bc7b1c9b779f1bbf07f2929d35af9f0f37e47c6e9ef7f9dddc1e1821f3" checksum = "c993ed8ccba56ae856363b1845da7266a7cb78e1d146c8a32d54b45a8b831fc9"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -3914,7 +3914,7 @@ checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.60", "syn 2.0.61",
] ]
[[package]] [[package]]
@ -4000,22 +4000,22 @@ dependencies = [
[[package]] [[package]]
name = "thiserror" name = "thiserror"
version = "1.0.59" version = "1.0.60"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0126ad08bff79f29fc3ae6a55cc72352056dfff61e3ff8bb7129476d44b23aa" checksum = "579e9083ca58dd9dcf91a9923bb9054071b9ebbd800b342194c9feb0ee89fc18"
dependencies = [ dependencies = [
"thiserror-impl", "thiserror-impl",
] ]
[[package]] [[package]]
name = "thiserror-impl" name = "thiserror-impl"
version = "1.0.59" version = "1.0.60"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d1cd413b5d558b4c5bf3680e324a6fa5014e7b7c067a51e69dbdf47eb7148b66" checksum = "e2470041c06ec3ac1ab38d0356a6119054dedaea53e12fbefc0de730a1c08524"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.60", "syn 2.0.61",
] ]
[[package]] [[package]]
@ -4122,7 +4122,7 @@ checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.60", "syn 2.0.61",
] ]
[[package]] [[package]]
@ -4285,7 +4285,7 @@ checksum = "84fd902d4e0b9a4b27f2f440108dc034e1758628a9b702f8ec61ad66355422fa"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.60", "syn 2.0.61",
] ]
[[package]] [[package]]
@ -4313,7 +4313,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.60", "syn 2.0.61",
] ]
[[package]] [[package]]
@ -4390,7 +4390,7 @@ dependencies = [
"Inflector", "Inflector",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.60", "syn 2.0.61",
"termcolor", "termcolor",
] ]
@ -4586,7 +4586,7 @@ dependencies = [
"proc-macro-error", "proc-macro-error",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.60", "syn 2.0.61",
] ]
[[package]] [[package]]
@ -4662,7 +4662,7 @@ dependencies = [
"once_cell", "once_cell",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.60", "syn 2.0.61",
"wasm-bindgen-shared", "wasm-bindgen-shared",
] ]
@ -4697,7 +4697,7 @@ checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.60", "syn 2.0.61",
"wasm-bindgen-backend", "wasm-bindgen-backend",
"wasm-bindgen-shared", "wasm-bindgen-shared",
] ]
@ -5269,7 +5269,7 @@ checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.60", "syn 2.0.61",
] ]
[[package]] [[package]]
@ -5289,7 +5289,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.60", "syn 2.0.61",
] ]
[[package]] [[package]]

View File

@ -66,7 +66,7 @@ kittycad = { version = "0.3.1", default-features = false, features = ["js", "req
kittycad-execution-plan = "0.1.6" kittycad-execution-plan = "0.1.6"
kittycad-execution-plan-macros = "0.1.9" kittycad-execution-plan-macros = "0.1.9"
kittycad-execution-plan-traits = "0.1.14" kittycad-execution-plan-traits = "0.1.14"
kittycad-modeling-cmds = "0.2.23" kittycad-modeling-cmds = "0.2.24"
kittycad-modeling-session = "0.1.4" kittycad-modeling-session = "0.1.4"
[[test]] [[test]]

View File

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

View File

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

View File

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

View File

@ -11,7 +11,7 @@ keywords = ["kcl", "KittyCAD", "CAD"]
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies] [dependencies]
anyhow = { version = "1.0.82", features = ["backtrace"] } anyhow = { version = "1.0.83", features = ["backtrace"] }
async-recursion = "1.1.1" async-recursion = "1.1.1"
async-trait = "0.1.80" async-trait = "0.1.80"
base64 = "0.22.1" base64 = "0.22.1"
@ -33,10 +33,10 @@ parse-display = "0.9.0"
reqwest = { version = "0.11.26", default-features = false, features = ["stream", "rustls-tls"] } reqwest = { version = "0.11.26", default-features = false, features = ["stream", "rustls-tls"] }
ropey = "1.6.1" ropey = "1.6.1"
schemars = { version = "0.8.17", features = ["impl_json_schema", "url", "uuid1"] } schemars = { version = "0.8.17", features = ["impl_json_schema", "url", "uuid1"] }
serde = { version = "1.0.200", features = ["derive"] } serde = { version = "1.0.201", features = ["derive"] }
serde_json = "1.0.116" serde_json = "1.0.116"
sha2 = "0.10.8" sha2 = "0.10.8"
thiserror = "1.0.59" thiserror = "1.0.60"
toml = "0.8.12" toml = "0.8.12"
ts-rs = { version = "7.1.1", features = ["uuid-impl", "url-impl", "chrono-impl"] } ts-rs = { version = "7.1.1", features = ["uuid-impl", "url-impl", "chrono-impl"] }
url = { version = "2.5.0", features = ["serde"] } url = { version = "2.5.0", features = ["serde"] }