Compare commits

...

30 Commits

Author SHA1 Message Date
a947e23ef9 Merge branch 'main' into achalmers/extrude-in-batch 2024-04-11 15:30:55 -07:00
76de64780c Bump yarn from 1.22.19 to 1.22.22 (#2083)
Bumps [yarn](https://github.com/yarnpkg/yarn) from 1.22.19 to 1.22.22.
- [Release notes](https://github.com/yarnpkg/yarn/releases)
- [Changelog](https://github.com/yarnpkg/yarn/blob/master/CHANGELOG.md)
- [Commits](https://github.com/yarnpkg/yarn/compare/v1.22.19...v1.22.22)

---
updated-dependencies:
- dependency-name: yarn
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-04-11 15:27:53 -07:00
2d804dee2b Bump three and @types/three (#2081)
Bumps [three](https://github.com/mrdoob/three.js) and [@types/three](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/three). These dependencies needed to be updated together.

Updates `three` from 0.160.0 to 0.163.0
- [Release notes](https://github.com/mrdoob/three.js/releases)
- [Commits](https://github.com/mrdoob/three.js/commits)

Updates `@types/three` from 0.160.0 to 0.163.0
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/three)

---
updated-dependencies:
- dependency-name: three
  dependency-type: direct:production
  update-type: version-update:semver-minor
- dependency-name: "@types/three"
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-04-11 15:27:44 -07:00
c094d0ced1 Screenshot for core dump (#2066)
* start of screenshot, need uploader

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

* cleanup

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

* some cleanup

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

* most things working

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

* bump the world

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>

* mime type

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

---------

Signed-off-by: Jess Frazelle <github@jessfraz.com>
2024-04-11 20:15:49 +00:00
a90fe3c066 add asssignees (#2079)
* add asssignee

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

* add asssignee

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

---------

Signed-off-by: Jess Frazelle <github@jessfraz.com>
2024-04-11 18:43:33 +00:00
f3ea7fd0e2 fixing onboarding bracket with fillet changes (#2069)
* fixing fillet changes

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

* Trigger CI with empty commit

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

* Rerun CI

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Frank Noirot <frank@kittycad.io>
2024-04-11 14:15:48 -04:00
704ff0df62 Bump tar from 6.1.15 to 6.2.1 (#2048)
Bumps [tar](https://github.com/isaacs/node-tar) from 6.1.15 to 6.2.1.
- [Release notes](https://github.com/isaacs/node-tar/releases)
- [Changelog](https://github.com/isaacs/node-tar/blob/main/CHANGELOG.md)
- [Commits](https://github.com/isaacs/node-tar/compare/v6.1.15...v6.2.1)

---
updated-dependencies:
- dependency-name: tar
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-04-11 10:51:27 -07:00
eba17e92b7 Bump @types/node from 18.19.26 to 18.19.31 (#2055)
Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 18.19.26 to 18.19.31.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-04-11 10:48:56 -07:00
d9d700624e Bump @playwright/test from 1.39.0 to 1.43.0 (#2058)
Bumps [@playwright/test](https://github.com/microsoft/playwright) from 1.39.0 to 1.43.0.
- [Release notes](https://github.com/microsoft/playwright/releases)
- [Commits](https://github.com/microsoft/playwright/compare/v1.39.0...v1.43.0)

---
updated-dependencies:
- dependency-name: "@playwright/test"
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-04-11 10:48:32 -07:00
1e547aeef0 Bump swr from 2.2.2 to 2.2.5 (#2057)
Bumps [swr](https://github.com/vercel/swr) from 2.2.2 to 2.2.5.
- [Release notes](https://github.com/vercel/swr/releases)
- [Commits](https://github.com/vercel/swr/compare/v2.2.2...v2.2.5)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-04-11 10:48:18 -07:00
22899c9e69 Bump quote from 1.0.35 to 1.0.36 in /src/wasm-lib (#2074)
Bumps [quote](https://github.com/dtolnay/quote) from 1.0.35 to 1.0.36.
- [Release notes](https://github.com/dtolnay/quote/releases)
- [Commits](https://github.com/dtolnay/quote/compare/1.0.35...1.0.36)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-04-11 10:47:54 -07:00
25702f454c Bump anyhow from 1.0.81 to 1.0.82 in /src/wasm-lib (#2071)
Bumps [anyhow](https://github.com/dtolnay/anyhow) from 1.0.81 to 1.0.82.
- [Release notes](https://github.com/dtolnay/anyhow/releases)
- [Commits](https://github.com/dtolnay/anyhow/compare/1.0.81...1.0.82)

---
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-04-11 10:47:44 -07:00
11faf86983 Bump kittycad-modeling-cmds from 0.2.10 to 0.2.17 in /src/wasm-lib (#2073)
Bumps [kittycad-modeling-cmds](https://github.com/KittyCAD/modeling-api) from 0.2.10 to 0.2.17.
- [Commits](https://github.com/KittyCAD/modeling-api/compare/kittycad-modeling-cmds-0.2.10...kittycad-modeling-cmds-0.2.17)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-04-11 10:47:23 -07:00
67d73382b1 Bump kittycad from 0.2.63 to 0.2.66 in /src-tauri (#2076)
Bumps [kittycad](https://github.com/KittyCAD/kittycad.rs) from 0.2.63 to 0.2.66.
- [Release notes](https://github.com/KittyCAD/kittycad.rs/releases)
- [Commits](https://github.com/KittyCAD/kittycad.rs/compare/v0.2.63...v0.2.66)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-04-11 10:39:44 -07:00
15b9f43f2c Revert Playwright tests to use addInitScript to adjust storage state (#2077)
* Revert Playwright tests to use addInitScript to adjust storage state

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

* Fix tsc

* Rerun CI

* Rerun CI

* Only use page.addInitScript within tests
because technically adding multiple init scripts to the context has an indeterminate run order, per the [Playwright docs](https://playwright.dev/docs/api/class-page#page-add-init-script)

* Rerun CI

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-04-11 13:37:49 -04:00
d28555a070 fix source range error when end of file (#2070)
Signed-off-by: Jess Frazelle <github@jessfraz.com>
2024-04-11 01:51:09 +00:00
7bf116629f clean up very old ast mod (#2060)
* clean up very old ast mod

* typo
2024-04-11 10:35:23 +10:00
fe45b5b54d Bump anyhow from 1.0.81 to 1.0.82 in /src-tauri (#2059)
Bumps [anyhow](https://github.com/dtolnay/anyhow) from 1.0.81 to 1.0.82.
- [Release notes](https://github.com/dtolnay/anyhow/releases)
- [Commits](https://github.com/dtolnay/anyhow/compare/1.0.81...1.0.82)

---
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-04-10 09:45:11 -05:00
bcbd3f5bfd playwright snapshot stability (#2053)
stability attempt
2024-04-10 04:55:29 +00:00
959433e357 start of coredump (#2050)
* start of coredump

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

* add local

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

* more coredummp

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

* add arch

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

* os info

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

* fix app version

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

* more webrtc stats

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

* webrtc data

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

* webrtc stats

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

* add wasm function

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

* add coredump things

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

* add hotkey

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>

* fixes

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

* cleanup

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

* updates

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

* updates

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

* fixes

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

* fixes

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

* fixes

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

* updates

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

* clippy

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

---------

Signed-off-by: Jess Frazelle <github@jessfraz.com>
2024-04-09 18:05:36 -07:00
b3695c060d Unit test which draws 3 cubes 2024-04-09 16:46:39 -05:00
09374081ea Batch the Extrude, ObjectBringToFront and Solid3DGetExtrusionFaceInfo calls into one batch
This means sketching a cube takes 2 batches, not 1.
2024-04-09 15:19:10 -05:00
3d40650cab Debugging prints 2024-04-09 15:19:09 -05:00
d18e35b7ea Bump derive-docs (#2047) 2024-04-09 15:02:04 -05:00
596c9a0ee6 Bump @fortawesome/free-brands-svg-icons from 6.4.2 to 6.5.2 (#2012)
Bumps [@fortawesome/free-brands-svg-icons](https://github.com/FortAwesome/Font-Awesome) from 6.4.2 to 6.5.2.
- [Release notes](https://github.com/FortAwesome/Font-Awesome/releases)
- [Changelog](https://github.com/FortAwesome/Font-Awesome/blob/6.x/CHANGELOG.md)
- [Commits](https://github.com/FortAwesome/Font-Awesome/compare/6.4.2...6.5.2)

---
updated-dependencies:
- dependency-name: "@fortawesome/free-brands-svg-icons"
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-04-09 19:07:21 +00:00
9106a81c77 Bump formik from 2.4.3 to 2.4.5 (#2013)
Bumps [formik](https://github.com/jaredpalmer/formik) from 2.4.3 to 2.4.5.
- [Release notes](https://github.com/jaredpalmer/formik/releases)
- [Commits](https://github.com/jaredpalmer/formik/compare/formik@2.4.3...formik@2.4.5)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Jess Frazelle <jessfraz@users.noreply.github.com>
2024-04-09 19:05:46 +00:00
8b5ebe67b2 fix js string undefined (#2045)
* fix js string undefined

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

* fixes for tests

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

---------

Signed-off-by: Jess Frazelle <github@jessfraz.com>
2024-04-09 18:51:41 +00:00
a7f539eca6 Bump ws and @types/ws (#2010)
Bumps [ws](https://github.com/websockets/ws) and [@types/ws](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/ws). These dependencies needed to be updated together.

Updates `ws` from 8.13.0 to 8.16.0
- [Release notes](https://github.com/websockets/ws/releases)
- [Commits](https://github.com/websockets/ws/compare/8.13.0...8.16.0)

Updates `@types/ws` from 8.5.5 to 8.5.10
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/ws)

---
updated-dependencies:
- dependency-name: ws
  dependency-type: direct:production
  update-type: version-update:semver-minor
- dependency-name: "@types/ws"
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-04-09 10:55:07 -07:00
f4c87c994c Bump @fortawesome/fontawesome-svg-core from 6.4.2 to 6.5.2 (#2014)
Bumps [@fortawesome/fontawesome-svg-core](https://github.com/FortAwesome/Font-Awesome) from 6.4.2 to 6.5.2.
- [Release notes](https://github.com/FortAwesome/Font-Awesome/releases)
- [Changelog](https://github.com/FortAwesome/Font-Awesome/blob/6.x/CHANGELOG.md)
- [Commits](https://github.com/FortAwesome/Font-Awesome/compare/6.4.2...6.5.2)

---
updated-dependencies:
- dependency-name: "@fortawesome/fontawesome-svg-core"
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-04-09 10:54:57 -07:00
3d4ae05145 Bump @types/react from 18.2.73 to 18.2.75 (#2042)
Bumps [@types/react](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/react) from 18.2.73 to 18.2.75.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/react)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-04-09 10:53:30 -07:00
59 changed files with 1337 additions and 437 deletions

View File

@ -9,15 +9,27 @@ updates:
directory: '/' # Location of package manifests
schedule:
interval: 'daily'
reviewers:
- franknoirot
- irev-dev
- package-ecosystem: 'github-actions' # See documentation for possible values
directory: '/' # Location of package manifests
schedule:
interval: 'daily'
reviewers:
- adamchalmers
- jessfraz
- package-ecosystem: 'cargo' # See documentation for possible values
directory: '/src/wasm-lib/' # Location of package manifests
schedule:
interval: 'daily'
reviewers:
- adamchalmers
- jessfraz
- package-ecosystem: 'cargo' # See documentation for possible values
directory: '/src-tauri/' # Location of package manifests
schedule:
interval: 'daily'
reviewers:
- adamchalmers
- jessfraz

View File

@ -15,7 +15,7 @@ concurrency:
cancel-in-progress: true
jobs:
generate-website-docs:
name: generate-website-docs
name: generate-website-docs
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
@ -56,6 +56,9 @@ jobs:
gh pr create --title "Update KCL docs" \
--body "Updating the generated kcl docs cc @jessfraz @franknoirot merge this" \
--head "$NEW_BRANCH" \
--reviewer jessfraz \
--reviewer irev-dev \
--reviewer franknoirot \
--base main || true
env:
GITHUB_TOKEN: ${{ steps.app-token.outputs.token }}

View File

@ -34,7 +34,7 @@ const part = startSketchOn('XY')
{
// The arc angle (in degrees) to place the repetitions. Must be greater than 0.
arcDegrees: number,
// The center about which to make th pattern. This is a 2D vector.
// The center about which to make the pattern. This is a 2D vector.
center: [number, number],
// The number of repetitions. Must be greater than 0. This excludes the original entity. For example, if `repetitions` is 1, the original entity will be copied once.
repetitions: number,

View File

@ -42,7 +42,7 @@ const part = startSketchOn('XY')
arcDegrees: number,
// The axis around which to make the pattern. This is a 3D vector.
axis: [number, number, number],
// The center about which to make th pattern. This is a 3D vector.
// The center about which to make the pattern. This is a 3D vector.
center: [number, number, number],
// The number of repetitions. Must be greater than 0. This excludes the original entity. For example, if `repetitions` is 1, the original entity will be copied once.
repetitions: number,

View File

@ -42165,7 +42165,7 @@
"format": "double"
},
"center": {
"description": "The center about which to make th pattern. This is a 2D vector.",
"description": "The center about which to make the pattern. This is a 2D vector.",
"type": "array",
"items": {
"type": "number",
@ -44168,7 +44168,7 @@
"minItems": 3
},
"center": {
"description": "The center about which to make th pattern. This is a 3D vector.",
"description": "The center about which to make the pattern. This is a 3D vector.",
"type": "array",
"items": {
"type": "number",

View File

@ -2,10 +2,15 @@ import { test, expect } from '@playwright/test'
import { getUtils } from './test-utils'
import waitOn from 'wait-on'
import { roundOff } from 'lib/utils'
import { basicStorageState } from './storageStates'
import * as TOML from '@iarna/toml'
import { SaveSettingsPayload } from 'lib/settings/settingsTypes'
import { Themes } from 'lib/theme'
import { secrets } from './secrets'
import {
TEST_SETTINGS,
TEST_SETTINGS_KEY,
TEST_SETTINGS_CORRUPTED,
TEST_SETTINGS_ONBOARDING,
} from './storageStates'
/*
debug helper: unfortunately we do rely on exact coord mouse clicks in a few places
@ -32,13 +37,25 @@ test.beforeEach(async ({ context, page }) => {
timeout: 5000,
})
await context.addInitScript(
async ({ token, settingsKey, settings }) => {
localStorage.setItem('TOKEN_PERSIST_KEY', token)
localStorage.setItem('persistCode', ``)
localStorage.setItem(settingsKey, settings)
},
{
token: secrets.token,
settingsKey: TEST_SETTINGS_KEY,
settings: TOML.stringify({ settings: TEST_SETTINGS }),
}
)
// kill animations, speeds up tests and reduced flakiness
await page.emulateMedia({ reducedMotion: 'reduce' })
})
test.setTimeout(60000)
test('Basic sketch', async ({ page, context }) => {
test('Basic sketch', async ({ page }) => {
const u = getUtils(page)
await page.setViewportSize({ width: 1200, height: 500 })
const PUR = 400 / 37.5 //pixeltoUnitRatio
@ -308,9 +325,9 @@ test('if you write invalid kcl you get inlined errors', async ({ page }) => {
await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible()
})
test('executes on load', async ({ page, context }) => {
test('executes on load', async ({ page }) => {
const u = getUtils(page)
await context.addInitScript(async () => {
await page.addInitScript(async () => {
localStorage.setItem(
'persistCode',
`const part001 = startSketchOn('-XZ')
@ -340,9 +357,9 @@ test('executes on load', async ({ page, context }) => {
).toBeVisible()
})
test('re-executes', async ({ page, context }) => {
test('re-executes', async ({ page }) => {
const u = getUtils(page)
await context.addInitScript(async (token) => {
await page.addInitScript(async () => {
localStorage.setItem('persistCode', `const myVar = 5`)
})
await page.setViewportSize({ width: 1000, height: 500 })
@ -512,134 +529,131 @@ test('Auto complete works', async ({ page }) => {
|> xLine(5, %) // lin`)
})
// Stored settings validation test
test.describe('Settings persistence and validation tests', () => {
// Override test setup
test('Stored settings are validated and fall back to defaults', async ({
page,
}) => {
// Override beforeEach test setup
// with corrupted settings
const storageState = structuredClone(basicStorageState)
const s = TOML.parse(storageState.origins[0].localStorage[2].value) as {
settings: SaveSettingsPayload
}
s.settings.app.theme = Themes.Dark
s.settings.app.projectDirectory = 123 as any
s.settings.modeling.defaultUnit = 'invalid' as any
s.settings.modeling.mouseControls = `() => alert('hack the planet')` as any
s.settings.projects.defaultProjectName = false as any
storageState.origins[0].localStorage[2].value = TOML.stringify(s)
await page.addInitScript(
async ({ settingsKey, settings }) => {
localStorage.setItem(settingsKey, settings)
},
{
settingsKey: TEST_SETTINGS_KEY,
settings: TOML.stringify({ settings: TEST_SETTINGS_CORRUPTED }),
}
)
test.use({ storageState })
await page.setViewportSize({ width: 1200, height: 500 })
await page.goto('/')
test('Stored settings are validated and fall back to defaults', async ({
page,
}) => {
const u = getUtils(page)
await page.setViewportSize({ width: 1200, height: 500 })
await page.goto('/')
await u.waitForAuthSkipAppStart()
// Check the settings were reset
const storedSettings = TOML.parse(
await page.evaluate(
({ settingsKey }) => localStorage.getItem(settingsKey) || '{}',
{ settingsKey: TEST_SETTINGS_KEY }
)
) as { settings: SaveSettingsPayload }
// Check the settings were reset
const storedSettings = TOML.parse(
await page.evaluate(() => localStorage.getItem('/user.toml') || '{}')
) as { settings: SaveSettingsPayload }
expect(storedSettings.settings.app?.theme).toBe('dark')
expect(storedSettings.settings.app?.theme).toBe('dark')
// Check that the invalid settings were removed
expect(storedSettings.settings.modeling?.defaultUnit).toBe(undefined)
expect(storedSettings.settings.modeling?.mouseControls).toBe(undefined)
expect(storedSettings.settings.app?.projectDirectory).toBe(undefined)
expect(storedSettings.settings.projects?.defaultProjectName).toBe(undefined)
})
test('Project settings can be set and override user settings', async ({
page,
}) => {
const u = getUtils(page)
await page.setViewportSize({ width: 1200, height: 500 })
await page.goto('/')
await u.waitForAuthSkipAppStart()
// Open the settings modal with the browser keyboard shortcut
await page.keyboard.press('Meta+Shift+,')
await expect(
page.getByRole('heading', { name: 'Settings', exact: true })
).toBeVisible()
await page
.locator('select[name="app-theme"]')
.selectOption({ value: 'light' })
// Verify the toast appeared
await expect(
page.getByText(`Set theme to "light" for this project`)
).toBeVisible()
// Check that the theme changed
await expect(page.locator('body')).not.toHaveClass(`body-bg dark`)
// Check that the user setting was not changed
await page.getByRole('radio', { name: 'User' }).click()
await expect(page.locator('select[name="app-theme"]')).toHaveValue('dark')
// Roll back to default "system" theme
await page
.getByText(
'themeRoll back themeRoll back to match defaultThe overall appearance of the appl'
)
.hover()
await page
.getByRole('button', {
name: 'Roll back theme ; Has tooltip: Roll back to match default',
})
.click()
await expect(page.locator('select[name="app-theme"]')).toHaveValue('system')
// Check that the project setting did not change
await page.getByRole('radio', { name: 'Project' }).click()
await expect(page.locator('select[name="app-theme"]')).toHaveValue('light')
})
// Check that the invalid settings were removed
expect(storedSettings.settings.modeling?.defaultUnit).toBe(undefined)
expect(storedSettings.settings.modeling?.mouseControls).toBe(undefined)
expect(storedSettings.settings.app?.projectDirectory).toBe(undefined)
expect(storedSettings.settings.projects?.defaultProjectName).toBe(undefined)
})
// Onboarding tests
test.describe('Onboarding tests', () => {
// Override test setup
const storageState = structuredClone(basicStorageState)
const s = TOML.parse(storageState.origins[0].localStorage[2].value) as {
settings: SaveSettingsPayload
}
s.settings.app.onboardingStatus = '/export'
storageState.origins[0].localStorage[2].value = TOML.stringify(s)
test.use({ storageState })
test('Project settings can be set and override user settings', async ({
page,
}) => {
await page.setViewportSize({ width: 1200, height: 500 })
await page.goto('/', { waitUntil: 'domcontentloaded' })
await page
.getByRole('button', { name: 'Start Sketch' })
.waitFor({ state: 'visible' })
test('Onboarding redirects and code updating', async ({ page, context }) => {
const u = getUtils(page)
// Open the settings modal with the browser keyboard shortcut
await page.keyboard.press('Meta+Shift+,')
await page.setViewportSize({ width: 1200, height: 500 })
await page.goto('/')
await u.waitForAuthSkipAppStart()
await expect(
page.getByRole('heading', { name: 'Settings', exact: true })
).toBeVisible()
await page
.locator('select[name="app-theme"]')
.selectOption({ value: 'light' })
// Test that the redirect happened
await expect(page.url().split(':3000').slice(-1)[0]).toBe(
`/file/%2Fbrowser%2Fmain.kcl/onboarding/export`
// Verify the toast appeared
await expect(
page.getByText(`Set theme to "light" for this project`)
).toBeVisible()
// Check that the theme changed
await expect(page.locator('body')).not.toHaveClass(`body-bg dark`)
// Check that the user setting was not changed
await page.getByRole('radio', { name: 'User' }).click()
await expect(page.locator('select[name="app-theme"]')).toHaveValue('dark')
// Roll back to default "system" theme
await page
.getByText(
'themeRoll back themeRoll back to match defaultThe overall appearance of the appl'
)
.hover()
await page
.getByRole('button', {
name: 'Roll back theme ; Has tooltip: Roll back to match default',
})
.click()
await expect(page.locator('select[name="app-theme"]')).toHaveValue('system')
// Test that you come back to this page when you refresh
await page.reload()
await expect(page.url().split(':3000').slice(-1)[0]).toBe(
`/file/%2Fbrowser%2Fmain.kcl/onboarding/export`
)
// Check that the project setting did not change
await page.getByRole('radio', { name: 'Project' }).click()
await expect(page.locator('select[name="app-theme"]')).toHaveValue('light')
})
// Test that the onboarding pane loaded
const title = page.locator('[data-testid="onboarding-content"]')
await expect(title).toBeAttached()
test('Onboarding redirects and code updating', async ({ page }) => {
const u = getUtils(page)
// Test that the code changes when you advance to the next step
await page.locator('[data-testid="onboarding-next"]').click()
await expect(page.locator('.cm-content')).toHaveText('')
// Override beforeEach test setup
await page.addInitScript(
async ({ settingsKey, settings }) => {
// Give some initial code, so we can test that it's cleared
localStorage.setItem('persistCode', 'const sigmaAllow = 15000')
localStorage.setItem(settingsKey, settings)
},
{
settingsKey: TEST_SETTINGS_KEY,
settings: TOML.stringify({ settings: TEST_SETTINGS_ONBOARDING }),
}
)
// Test that the code is not empty when you click on the next step
await page.locator('[data-testid="onboarding-next"]').click()
await expect(page.locator('.cm-content')).toHaveText(/.+/)
})
await page.setViewportSize({ width: 1200, height: 500 })
await page.goto('/')
await u.waitForAuthSkipAppStart()
// Test that the redirect happened
await expect(page.url().split(':3000').slice(-1)[0]).toBe(
`/file/%2Fbrowser%2Fmain.kcl/onboarding/export`
)
// Test that you come back to this page when you refresh
await page.reload()
await expect(page.url().split(':3000').slice(-1)[0]).toBe(
`/file/%2Fbrowser%2Fmain.kcl/onboarding/export`
)
// Test that the onboarding pane loaded
const title = page.locator('[data-testid="onboarding-content"]')
await expect(title).toBeAttached()
// Test that the code changes when you advance to the next step
await page.locator('[data-testid="onboarding-next"]').click()
await expect(page.locator('.cm-content')).toHaveText('')
// Test that the code is not empty when you click on the next step
await page.locator('[data-testid="onboarding-next"]').click()
await expect(page.locator('.cm-content')).toHaveText(/.+/)
})
test('Selections work on fresh and edited sketch', async ({ page }) => {
@ -851,19 +865,22 @@ test.describe('Command bar tests', () => {
await expect(page.locator('body')).not.toHaveClass(`body-bg dark`)
})
// Override test setup code
const storageState = structuredClone(basicStorageState)
storageState.origins[0].localStorage[1].value = `const distance = sqrt(20)
const part001 = startSketchOn('-XZ')
|> startProfileAt([-6.95, 4.98], %)
|> line([25.1, 0.41], %)
|> line([0.73, -14.93], %)
|> line([-23.44, 0.52], %)
|> close(%)
`
test.use({ storageState })
test('Can extrude from the command bar', async ({ page }) => {
await page.addInitScript(async () => {
localStorage.setItem(
'persistCode',
`
const distance = sqrt(20)
const part001 = startSketchOn('-XZ')
|> startProfileAt([-6.95, 4.98], %)
|> line([25.1, 0.41], %)
|> line([0.73, -14.93], %)
|> line([-23.44, 0.52], %)
|> close(%)
`
)
})
test('Can extrude from the command bar', async ({ page, context }) => {
const u = getUtils(page)
await page.setViewportSize({ width: 1200, height: 500 })
await page.goto('/')
@ -1055,9 +1072,9 @@ const part002 = startSketchOn('XY')
)
})
test('ProgramMemory can be serialised', async ({ page, context }) => {
test('ProgramMemory can be serialised', async ({ page }) => {
const u = getUtils(page)
await context.addInitScript(async () => {
await page.addInitScript(async () => {
localStorage.setItem(
'persistCode',
`const part = startSketchOn('XY')
@ -1096,7 +1113,6 @@ test('ProgramMemory can be serialised', async ({ page, context }) => {
test("Various pipe expressions should and shouldn't allow edit and or extrude", async ({
page,
context,
}) => {
const u = getUtils(page)
const selectionsSnippets = {
@ -1105,7 +1121,7 @@ test("Various pipe expressions should and shouldn't allow edit and or extrude",
extrudeAndEditAllowed: '|> startProfileAt([15.72, 4.7], %)',
editOnly: '|> startProfileAt([15.79, -14.6], %)',
}
await context.addInitScript(
await page.addInitScript(
async ({
extrudeAndEditBlocked,
extrudeAndEditBlockedInFunction,
@ -1265,12 +1281,9 @@ test('Deselecting line tool should mean nothing happens on click', async ({
previousCodeContent = await page.locator('.cm-content').innerText()
})
test('Can edit segments by dragging their handles', async ({
page,
context,
}) => {
test('Can edit segments by dragging their handles', async ({ page }) => {
const u = getUtils(page)
await context.addInitScript(async () => {
await page.addInitScript(async () => {
localStorage.setItem(
'persistCode',
`const part001 = startSketchOn('-XZ')
@ -1422,9 +1435,9 @@ test('Snap to close works (at any scale)', async ({ page }) => {
await doSnapAtDifferentScales([0, 10000, 10000], codeTemplate())
})
test('Sketch on face', async ({ page, context }) => {
test('Sketch on face', async ({ page }) => {
const u = getUtils(page)
await context.addInitScript(async () => {
await page.addInitScript(async () => {
localStorage.setItem(
'persistCode',
`const part001 = startSketchOn('-XZ')

View File

@ -7,16 +7,26 @@ import { spawn } from 'child_process'
import { APP_NAME } from 'lib/constants'
import JSZip from 'jszip'
import path from 'path'
import { basicSettings, basicStorageState } from './storageStates'
import { TEST_SETTINGS, TEST_SETTINGS_KEY } from './storageStates'
import * as TOML from '@iarna/toml'
test.beforeEach(async ({ page }) => {
// reducedMotion kills animations, which speeds up tests and reduces flakiness
await page.emulateMedia({ reducedMotion: 'reduce' })
})
test.use({
storageState: structuredClone(basicStorageState),
// set the default settings
await page.addInitScript(
async ({ token, settingsKey, settings }) => {
localStorage.setItem('TOKEN_PERSIST_KEY', token)
localStorage.setItem('persistCode', ``)
localStorage.setItem(settingsKey, settings)
},
{
token: secrets.token,
settingsKey: TEST_SETTINGS_KEY,
settings: TOML.stringify({ settings: TEST_SETTINGS }),
}
)
})
test.setTimeout(60_000)
@ -364,13 +374,15 @@ test('extrude on each default plane should be stable', async ({
await u.removeCurrentCode()
// add makeCode('XZ')
await u.openAndClearDebugPanel()
await page.locator('.cm-content').fill(makeCode(plane))
await u.doAndWaitForImageDiff(
() => page.locator('.cm-content').fill(makeCode(plane)),
200
)
// wait for execution done
await u.expectCmdLog('[data-message-type="execution-done"]')
await u.clearAndCloseDebugPanel()
await page.getByText('Code').click()
await page.waitForTimeout(150)
await expect(page).toHaveScreenshot({
maxDiffPixels: 100,
})
@ -445,105 +457,108 @@ test('Draft segments should look right', async ({ page, context }) => {
})
})
test('Client side scene scale should match engine scale - Inch', async ({
page,
}) => {
const u = getUtils(page)
await page.setViewportSize({ width: 1200, height: 500 })
const PUR = 400 / 37.5 //pixeltoUnitRatio
await page.goto('/')
await u.waitForAuthSkipAppStart()
await u.openDebugPanel()
test.describe('Client side scene scale should match engine scale', () => {
test('Inch', async ({ page }) => {
const u = getUtils(page)
await page.setViewportSize({ width: 1200, height: 500 })
const PUR = 400 / 37.5 //pixeltoUnitRatio
await page.goto('/')
await u.waitForAuthSkipAppStart()
await u.openDebugPanel()
await expect(
page.getByRole('button', { name: 'Start Sketch' })
).not.toBeDisabled()
await expect(page.getByRole('button', { name: 'Start Sketch' })).toBeVisible()
await expect(
page.getByRole('button', { name: 'Start Sketch' })
).not.toBeDisabled()
await expect(
page.getByRole('button', { name: 'Start Sketch' })
).toBeVisible()
// click on "Start Sketch" button
await u.clearCommandLogs()
await u.doAndWaitForImageDiff(
() => page.getByRole('button', { name: 'Start Sketch' }).click(),
200
)
// click on "Start Sketch" button
await u.clearCommandLogs()
await u.doAndWaitForImageDiff(
() => page.getByRole('button', { name: 'Start Sketch' }).click(),
200
)
// select a plane
await page.mouse.click(700, 200)
// select a plane
await page.mouse.click(700, 200)
await expect(page.locator('.cm-content')).toHaveText(
`const part001 = startSketchOn('-XZ')`
)
await expect(page.locator('.cm-content')).toHaveText(
`const part001 = startSketchOn('-XZ')`
)
await page.waitForTimeout(300) // TODO detect animation ending, or disable animation
await page.waitForTimeout(300) // TODO detect animation ending, or disable animation
const startXPx = 600
await page.mouse.click(startXPx + PUR * 10, 500 - PUR * 10)
await expect(page.locator('.cm-content'))
.toHaveText(`const part001 = startSketchOn('-XZ')
|> startProfileAt([9.06, -12.22], %)`)
await page.waitForTimeout(100)
const startXPx = 600
await page.mouse.click(startXPx + PUR * 10, 500 - PUR * 10)
await expect(page.locator('.cm-content'))
.toHaveText(`const part001 = startSketchOn('-XZ')
|> startProfileAt([9.06, -12.22], %)`)
await page.waitForTimeout(100)
await u.closeDebugPanel()
await u.closeDebugPanel()
await page.mouse.click(startXPx + PUR * 20, 500 - PUR * 10)
await page.waitForTimeout(100)
await page.mouse.click(startXPx + PUR * 20, 500 - PUR * 10)
await page.waitForTimeout(100)
await expect(page.locator('.cm-content'))
.toHaveText(`const part001 = startSketchOn('-XZ')
|> startProfileAt([9.06, -12.22], %)
|> line([9.14, 0], %)`)
await expect(page.locator('.cm-content'))
.toHaveText(`const part001 = startSketchOn('-XZ')
|> startProfileAt([9.06, -12.22], %)
|> line([9.14, 0], %)`)
await page.getByRole('button', { name: 'Tangential Arc' }).click()
await page.waitForTimeout(100)
await page.getByRole('button', { name: 'Tangential Arc' }).click()
await page.waitForTimeout(100)
await page.mouse.click(startXPx + PUR * 30, 500 - PUR * 20)
await page.mouse.click(startXPx + PUR * 30, 500 - PUR * 20)
await expect(page.locator('.cm-content'))
.toHaveText(`const part001 = startSketchOn('-XZ')
|> startProfileAt([9.06, -12.22], %)
|> line([9.14, 0], %)
|> tangentialArcTo([27.34, -3.08], %)`)
await expect(page.locator('.cm-content'))
.toHaveText(`const part001 = startSketchOn('-XZ')
|> startProfileAt([9.06, -12.22], %)
|> line([9.14, 0], %)
|> tangentialArcTo([27.34, -3.08], %)`)
// click tangential arc tool again to unequip it
await page.getByRole('button', { name: 'Tangential Arc' }).click()
await page.waitForTimeout(100)
// click tangential arc tool again to unequip it
await page.getByRole('button', { name: 'Tangential Arc' }).click()
await page.waitForTimeout(100)
// screen shot should show the sketch
await expect(page).toHaveScreenshot({
maxDiffPixels: 100,
})
// screen shot should show the sketch
await expect(page).toHaveScreenshot({
maxDiffPixels: 100,
})
// exit sketch
await u.openAndClearDebugPanel()
await page.getByRole('button', { name: 'Exit Sketch' }).click()
// exit sketch
await u.openAndClearDebugPanel()
await page.getByRole('button', { name: 'Exit Sketch' }).click()
// wait for execution done
await u.expectCmdLog('[data-message-type="execution-done"]')
await u.clearAndCloseDebugPanel()
await page.waitForTimeout(200)
// wait for execution done
await u.expectCmdLog('[data-message-type="execution-done"]')
await u.clearAndCloseDebugPanel()
await page.waitForTimeout(200)
// second screen shot should look almost identical, i.e. scale should be the same.
await expect(page).toHaveScreenshot({
maxDiffPixels: 100,
})
})
test.describe('Client side scene scale should match engine scale - Millimeters', () => {
const storageState = structuredClone(basicStorageState)
storageState.origins[0].localStorage[2].value = TOML.stringify({
settings: {
...basicSettings,
modeling: {
...basicSettings.modeling,
defaultUnit: 'mm',
},
},
})
test.use({
storageState,
// second screen shot should look almost identical, i.e. scale should be the same.
await expect(page).toHaveScreenshot({
maxDiffPixels: 100,
})
})
test('Millimeters', async ({ page }) => {
await page.addInitScript(
async ({ settingsKey, settings }) => {
localStorage.setItem(settingsKey, settings)
},
{
settingsKey: TEST_SETTINGS_KEY,
settings: TOML.stringify({
settings: {
...TEST_SETTINGS,
modeling: {
...TEST_SETTINGS.modeling,
defaultUnit: 'mm',
},
},
}),
}
)
const u = getUtils(page)
await page.setViewportSize({ width: 1200, height: 500 })
const PUR = 400 / 37.5 //pixeltoUnitRatio

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

After

Width:  |  Height:  |  Size: 43 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: 48 KiB

After

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

After

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

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 48 KiB

After

Width:  |  Height:  |  Size: 48 KiB

View File

@ -1,9 +1,8 @@
import { SaveSettingsPayload } from 'lib/settings/settingsTypes'
import { secrets } from './secrets'
import * as TOML from '@iarna/toml'
import { Themes } from 'lib/theme'
export const basicSettings = {
export const TEST_SETTINGS_KEY = '/user.toml'
export const TEST_SETTINGS = {
app: {
theme: Themes.Dark,
onboardingStatus: 'dismissed',
@ -22,19 +21,26 @@ export const basicSettings = {
},
} satisfies Partial<SaveSettingsPayload>
export const basicStorageState = {
cookies: [],
origins: [
{
origin: 'http://localhost:3000',
localStorage: [
{ name: 'TOKEN_PERSIST_KEY', value: secrets.token },
{ name: 'persistCode', value: '' },
{
name: '/user.toml',
value: TOML.stringify({ settings: basicSettings }),
},
],
},
],
}
export const TEST_SETTINGS_ONBOARDING = {
...TEST_SETTINGS,
app: { ...TEST_SETTINGS.app, onboardingStatus: '/export ' },
} satisfies Partial<SaveSettingsPayload>
export const TEST_SETTINGS_CORRUPTED = {
app: {
theme: Themes.Dark,
onboardingStatus: 'dismissed',
projectDirectory: 123 as any,
},
modeling: {
defaultUnit: 'invalid' as any,
mouseControls: `() => alert('hack the planet')` as any,
showDebugPanel: true,
},
projects: {
defaultProjectName: false as any,
},
textEditor: {
textWrapping: true,
},
} satisfies Partial<SaveSettingsPayload>

View File

@ -4,8 +4,8 @@
"private": true,
"dependencies": {
"@codemirror/autocomplete": "^6.15.0",
"@fortawesome/fontawesome-svg-core": "^6.4.2",
"@fortawesome/free-brands-svg-icons": "^6.4.2",
"@fortawesome/fontawesome-svg-core": "^6.5.2",
"@fortawesome/free-brands-svg-icons": "^6.5.2",
"@fortawesome/free-solid-svg-icons": "^6.4.2",
"@fortawesome/react-fontawesome": "^0.2.0",
"@headlessui/react": "^1.7.18",
@ -16,7 +16,7 @@
"@open-rpc/client-js": "^1.8.1",
"@react-hook/resize-observer": "^1.2.6",
"@replit/codemirror-interact": "^6.3.0",
"@tauri-apps/api": "^2.0.0-beta.7",
"@tauri-apps/api": "2.0.0-beta.7",
"@tauri-apps/plugin-dialog": "^2.0.0-beta.2",
"@tauri-apps/plugin-fs": "^2.0.0-beta.2",
"@tauri-apps/plugin-http": "^2.0.0-beta.2",
@ -27,8 +27,8 @@
"@testing-library/user-event": "^14.5.2",
"@ts-stack/markdown": "^1.5.0",
"@tweenjs/tween.js": "^23.1.1",
"@types/node": "^18.19.26",
"@types/react": "^18.2.73",
"@types/node": "^18.19.31",
"@types/react": "^18.2.75",
"@types/react-dom": "^18.2.22",
"@uiw/react-codemirror": "^4.21.25",
"@xstate/inspect": "^0.8.0",
@ -36,8 +36,9 @@
"crypto-js": "^4.2.0",
"debounce-promise": "^3.1.2",
"decamelize": "^6.0.0",
"formik": "^2.4.3",
"formik": "^2.4.5",
"fuse.js": "^7.0.0",
"html2canvas-pro": "^1.4.3",
"http-server": "^14.1.1",
"json-rpc-2.0": "^1.6.0",
"jszip": "^3.10.1",
@ -52,18 +53,19 @@
"react-modal-promise": "^1.0.2",
"react-router-dom": "^6.22.3",
"sketch-helpers": "^0.0.4",
"swr": "^2.2.2",
"three": "^0.160.0",
"swr": "^2.2.5",
"three": "^0.163.0",
"toml": "^3.0.0",
"ts-node": "^10.9.2",
"typescript": "^5.4.3",
"ua-parser-js": "^1.0.37",
"uuid": "^9.0.1",
"vitest": "^1.4.0",
"vscode-jsonrpc": "^8.1.0",
"vscode-languageserver-protocol": "^3.17.5",
"wasm-pack": "^0.12.1",
"web-vitals": "^3.5.2",
"ws": "^8.13.0",
"ws": "^8.16.0",
"xstate": "^4.38.2",
"zustand": "^4.5.2"
},
@ -115,18 +117,19 @@
"devDependencies": {
"@babel/plugin-proposal-private-property-in-object": "^7.21.11",
"@babel/preset-env": "^7.24.3",
"@playwright/test": "^1.39.0",
"@playwright/test": "^1.43.0",
"@tauri-apps/cli": "^2.0.0-beta.12",
"@types/crypto-js": "^4.2.2",
"@types/debounce-promise": "^3.1.9",
"@types/pixelmatch": "^5.2.6",
"@types/pngjs": "^6.0.4",
"@types/react-modal": "^3.16.3",
"@types/three": "^0.160.0",
"@types/three": "^0.163.0",
"@types/ua-parser-js": "^0.7.39",
"@types/uuid": "^9.0.8",
"@types/wait-on": "^5.3.4",
"@types/wicg-file-system-access": "^2023.10.5",
"@types/ws": "^8.5.5",
"@types/ws": "^8.5.10",
"@vitejs/plugin-react": "^4.2.1",
"@wdio/cli": "^8.24.3",
"@wdio/globals": "^8.24.3",
@ -153,6 +156,6 @@
"vite-tsconfig-paths": "^4.3.2",
"vitest-webgl-canvas-mock": "^1.1.0",
"wait-on": "^7.2.0",
"yarn": "^1.22.19"
"yarn": "^1.22.22"
}
}

View File

@ -1,5 +1,4 @@
import { defineConfig, devices } from '@playwright/test'
import { basicStorageState } from './e2e/playwright/storageStates'
/**
* Read environment variables from file.
@ -29,9 +28,6 @@ export default defineConfig({
/* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
trace: 'on-first-retry',
/* Use a common shared localStorage */
storageState: basicStorageState,
},
/* Configure projects for major browsers */

10
src-tauri/Cargo.lock generated
View File

@ -69,9 +69,9 @@ dependencies = [
[[package]]
name = "anyhow"
version = "1.0.81"
version = "1.0.82"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0952808a6c2afd1aa8947271f3a60f1a6763c7b912d210184c5149b5cf147247"
checksum = "f538837af36e6f6a9be0faa67f9a314f8119e4e4b5867c6ab40ed60360142519"
[[package]]
name = "app"
@ -2231,9 +2231,9 @@ dependencies = [
[[package]]
name = "kittycad"
version = "0.2.63"
version = "0.2.66"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "93a332250e08fd715ad3d5826e04d36da1c5bb42d0c1b1ff1f0598278b9ebf3c"
checksum = "9e2897244f4600f863115561a0fd1cd7c87fca20253ffecfebc53ef642d0aceb"
dependencies = [
"anyhow",
"async-trait",
@ -2333,7 +2333,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c2a198fb6b0eada2a8df47933734e6d35d350665a33a3593d7164fa52c75c19"
dependencies = [
"cfg-if",
"windows-targets 0.48.0",
"windows-targets 0.52.4",
]
[[package]]

View File

@ -16,7 +16,7 @@ tauri-build = { version = "2.0.0-beta", features = [] }
[dependencies]
anyhow = "1"
kittycad = "0.2.63"
kittycad = "0.2.66"
oauth2 = "4.4.2"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"

View File

@ -1,4 +1,4 @@
import { useCallback, MouseEventHandler, useEffect } from 'react'
import { useCallback, MouseEventHandler, useEffect, useRef } from 'react'
import { DebugPanel } from './components/DebugPanel'
import { uuidv4 } from 'lib/utils'
import { PaneType, useStore } from './useStore'
@ -41,6 +41,9 @@ export function App() {
const navigate = useNavigate()
const filePath = useAbsoluteFilePath()
const { onProjectOpen } = useLspContext()
// We need the ref for the outermost div so we can screenshot the app for
// the coredump.
const ref = useRef<HTMLDivElement>(null)
const projectName = project?.name || null
const projectPath = project?.path || null
@ -55,14 +58,20 @@ export function App() {
setOpenPanes,
didDragInStream,
streamDimensions,
setHtmlRef,
} = useStore((s) => ({
buttonDownInStream: s.buttonDownInStream,
openPanes: s.openPanes,
setOpenPanes: s.setOpenPanes,
didDragInStream: s.didDragInStream,
streamDimensions: s.streamDimensions,
setHtmlRef: s.setHtmlRef,
}))
useEffect(() => {
setHtmlRef(ref)
}, [ref])
const { settings } = useSettingsAuthContext()
const {
modeling: { showDebugPanel },
@ -140,6 +149,7 @@ export function App() {
<div
className="relative h-full flex flex-col"
onMouseMove={handleMouseMove}
ref={ref}
>
<AppHeader
className={

View File

@ -38,7 +38,7 @@ import {
getSketchQuaternion,
} from 'clientSideScene/sceneEntities'
import { sketchOnExtrudedFace, startSketchOnDefault } from 'lang/modifyAst'
import { Program, parse } from 'lang/wasm'
import { Program, coreDump, parse } from 'lang/wasm'
import { getNodePathFromSourceRange, isSingleCursorInPipe } from 'lang/queryAst'
import { TEST } from 'env'
import { exportFromEngine } from 'lib/exportFromEngine'
@ -47,6 +47,8 @@ import toast from 'react-hot-toast'
import { EditorSelection } from '@uiw/react-codemirror'
import { Vector3 } from 'three'
import { quaternionFromUpNForward } from 'clientSideScene/helpers'
import { CoreDumpManager } from 'lib/coredump'
import { useHotkeys } from 'react-hotkeys-hook'
type MachineContext<T extends AnyStateMachine> = {
state: StateFrom<T>
@ -76,6 +78,15 @@ export const ModelingMachineProvider = ({
const token = auth?.context?.token
const streamRef = useRef<HTMLDivElement>(null)
useSetupEngineManager(streamRef, token, theme.current)
const { htmlRef } = useStore((s) => ({
htmlRef: s.htmlRef,
}))
const coreDumpManager = new CoreDumpManager(
engineCommandManager,
htmlRef,
token
)
useHotkeys('meta + shift + .', () => coreDump(coreDumpManager, true))
const {
isShiftDown,

View File

@ -241,7 +241,6 @@ export function extrudeSketch(
pathToExtrudeArg: PathToNode
} {
const _node = { ...node }
const dumbyStartend = { start: 0, end: 0 }
const { node: sketchExpression } = getNodeFromPath(
_node,
pathToNode,
@ -256,18 +255,14 @@ export function extrudeSketch(
)
const isInPipeExpression = pipeExpression.type === 'PipeExpression'
const { node: variableDeclorator, shallowPath: pathToDecleration } =
const { node: variableDeclarator, shallowPath: pathToDecleration } =
getNodeFromPath<VariableDeclarator>(_node, pathToNode, 'VariableDeclarator')
const extrudeCall = createCallExpressionStdLib('extrude', [
distance,
shouldPipe
? createPipeSubstitution()
: {
type: 'Identifier',
...dumbyStartend,
name: variableDeclorator.id.name,
},
: createIdentifier(variableDeclarator.id.name),
])
if (shouldPipe) {
@ -277,7 +272,7 @@ export function extrudeSketch(
: [sketchExpression as any, extrudeCall]
)
variableDeclorator.init = pipeChain
variableDeclarator.init = pipeChain
const pathToExtrudeArg: PathToNode = [
...pathToDecleration,
['init', 'VariableDeclarator'],

View File

@ -25,7 +25,8 @@ interface CommandInfo {
}
}
type WebSocketResponse = Models['OkWebSocketResponseData_type']
type WebSocketResponse = Models['WebSocketResponse_type']
type OkWebSocketResponseData = Models['OkWebSocketResponseData_type']
interface ResultCommand extends CommandInfo {
type: 'result'
@ -37,10 +38,19 @@ interface FailedCommand extends CommandInfo {
type: 'failed'
errors: Models['FailureWebSocketResponse_type']['errors']
}
interface ResolveCommand {
id: string
commandType: CommandTypes
range: SourceRange
// We ALWAYS need the raw response because we pass it back to the rust side.
raw: WebSocketResponse
data?: Models['OkModelingCmdResponse_type']
errors?: Models['FailureWebSocketResponse_type']['errors']
}
interface PendingCommand extends CommandInfo {
type: 'pending'
promise: Promise<any>
resolve: (val: any) => void
resolve: (val: ResolveCommand) => void
}
export interface ArtifactMap {
@ -59,6 +69,15 @@ type Timeout = ReturnType<typeof setTimeout>
type ClientMetrics = Models['ClientMetrics_type']
interface WebRTCClientMetrics extends ClientMetrics {
rtc_frame_height: number
rtc_frame_width: number
rtc_packets_lost: number
rtc_pli_count: number
rtc_pause_count: number
rtc_total_pauses_duration_sec: number
}
type Value<T, U> = U extends undefined
? { type: T; value: U }
: U extends void
@ -224,7 +243,7 @@ class EngineConnection {
private onNewTrack: (track: NewTrackArgs) => void
// TODO: actual type is ClientMetrics
private webrtcStatsCollector?: () => Promise<ClientMetrics>
public webrtcStatsCollector?: () => Promise<WebRTCClientMetrics>
private engineCommandManager: EngineCommandManager
constructor({
@ -396,7 +415,7 @@ class EngineConnection {
},
}
this.webrtcStatsCollector = (): Promise<ClientMetrics> => {
this.webrtcStatsCollector = (): Promise<WebRTCClientMetrics> => {
return new Promise((resolve, reject) => {
if (mediaStream.getVideoTracks().length !== 1) {
reject(new Error('too many video tracks to report'))
@ -405,7 +424,7 @@ class EngineConnection {
let videoTrack = mediaStream.getVideoTracks()[0]
void this.pc?.getStats(videoTrack).then((videoTrackStats) => {
let client_metrics: ClientMetrics = {
let client_metrics: WebRTCClientMetrics = {
rtc_frames_decoded: 0,
rtc_frames_dropped: 0,
rtc_frames_received: 0,
@ -414,6 +433,12 @@ class EngineConnection {
rtc_jitter_sec: 0.0,
rtc_keyframes_decoded: 0,
rtc_total_freezes_duration_sec: 0.0,
rtc_frame_height: 0,
rtc_frame_width: 0,
rtc_packets_lost: 0,
rtc_pli_count: 0,
rtc_pause_count: 0,
rtc_total_pauses_duration_sec: 0.0,
}
// TODO(paultag): Since we can technically have multiple WebRTC
@ -439,6 +464,13 @@ class EngineConnection {
videoTrackReport.keyFramesDecoded || 0
client_metrics.rtc_total_freezes_duration_sec =
videoTrackReport.totalFreezesDuration || 0
client_metrics.rtc_frame_height =
videoTrackReport.frameHeight || 0
client_metrics.rtc_frame_width =
videoTrackReport.frameWidth || 0
client_metrics.rtc_packets_lost =
videoTrackReport.packetsLost || 0
client_metrics.rtc_pli_count = videoTrackReport.pliCount || 0
} else if (videoTrackReport.type === 'transport') {
// videoTrackReport.bytesReceived,
// videoTrackReport.bytesSent,
@ -827,7 +859,7 @@ export type CommandLog =
}
| {
type: 'receive-reliable'
data: WebSocketResponse
data: OkWebSocketResponseData
id: string
cmd_type?: string
}
@ -1020,7 +1052,11 @@ export class EngineCommandManager {
message.resp.type === 'modeling' &&
message.request_id
) {
this.handleModelingCommand(message.resp, message.request_id)
this.handleModelingCommand(
message.resp,
message.request_id,
message
)
} else if (
!message.success &&
message.request_id &&
@ -1069,7 +1105,11 @@ export class EngineCommandManager {
}
this.engineConnection?.send(resizeCmd)
}
handleModelingCommand(message: WebSocketResponse, id: string) {
handleModelingCommand(
message: OkWebSocketResponseData,
id: string,
raw: WebSocketResponse
) {
if (message.type !== 'modeling') {
return
}
@ -1081,7 +1121,7 @@ export class EngineCommandManager {
command?.additionalData?.type === 'batch-ids'
) {
command.additionalData.ids.forEach((id) => {
this.handleModelingCommand(message, id)
this.handleModelingCommand(message, id, raw)
})
// batch artifact is just a container, we don't need to keep it
// once we process all the commands inside it
@ -1092,7 +1132,7 @@ export class EngineCommandManager {
commandType: command.commandType,
range: command.range,
data: modelingResponse,
raw: message,
raw,
})
return
}
@ -1116,7 +1156,7 @@ export class EngineCommandManager {
commandType: command.commandType,
parentId: command.parentId ? command.parentId : undefined,
data: modelingResponse,
raw: message,
raw,
} as const
this.artifactMap[id] = artifact
if (
@ -1161,7 +1201,7 @@ export class EngineCommandManager {
commandType: command.commandType,
range: command.range,
data: modelingResponse,
raw: message,
raw,
})
} else if (sceneCommand && sceneCommand.type === 'pending') {
const resolve = sceneCommand.resolve
@ -1172,7 +1212,7 @@ export class EngineCommandManager {
commandType: sceneCommand.commandType,
parentId: sceneCommand.parentId ? sceneCommand.parentId : undefined,
data: modelingResponse,
raw: message,
raw,
} as const
this.sceneCommandArtifacts[id] = artifact
resolve({
@ -1180,6 +1220,7 @@ export class EngineCommandManager {
commandType: sceneCommand.commandType,
range: sceneCommand.range,
data: modelingResponse,
raw,
})
} else if (command) {
this.artifactMap[id] = {
@ -1188,7 +1229,7 @@ export class EngineCommandManager {
range: command?.range,
pathToNode: command?.pathToNode,
data: modelingResponse,
raw: message,
raw,
}
} else {
this.sceneCommandArtifacts[id] = {
@ -1197,15 +1238,14 @@ export class EngineCommandManager {
range: sceneCommand?.range,
pathToNode: sceneCommand?.pathToNode,
data: modelingResponse,
raw: message,
raw,
}
}
}
handleFailedModelingCommand({
request_id,
errors,
}: Models['FailureWebSocketResponse_type']) {
const id = request_id
handleFailedModelingCommand(raw: WebSocketResponse) {
const id = raw.request_id
const failed = raw as Models['FailureWebSocketResponse_type']
const errors = failed.errors
if (!id) return
const command = this.artifactMap[id]
if (command && command.type === 'pending') {
@ -1223,6 +1263,7 @@ export class EngineCommandManager {
commandType: command.commandType,
range: command.range,
errors,
raw,
})
} else {
this.artifactMap[id] = {
@ -1573,7 +1614,14 @@ export class EngineCommandManager {
command: commandStr,
ast: this.getAst(),
idToRangeMap,
}).then(({ raw }) => JSON.stringify(raw))
}).then(({ raw }: { raw: WebSocketResponse | undefined | null }) => {
if (raw === undefined || raw === null) {
throw new Error(
'returning modeling cmd response to the rust side is undefined or null'
)
}
return JSON.stringify(raw)
})
}
commandResult(id: string): Promise<any> {
const command = this.artifactMap[id]

View File

@ -10,6 +10,7 @@ import init, {
ServerConfig,
copilot_lsp_run,
kcl_lsp_run,
coredump,
} from '../wasm-lib/pkg/wasm_lib'
import { KCLError } from './errors'
import { KclError as RustKclError } from '../wasm-lib/kcl/bindings/KclError'
@ -21,6 +22,9 @@ import type { Token } from '../wasm-lib/kcl/bindings/Token'
import { Coords2d } from './std/sketch'
import { fileSystemManager } from 'lang/std/fileSystemManager'
import { DEV } from 'env'
import { AppInfo } from 'wasm-lib/kcl/bindings/AppInfo'
import { CoreDumpManager } from 'lib/coredump'
import openWindow from 'lib/openWindow'
export type { Program } from '../wasm-lib/kcl/bindings/Program'
export type { Value } from '../wasm-lib/kcl/bindings/Value'
@ -311,3 +315,18 @@ export async function kclLspRun(config: ServerConfig, token: string) {
// We can't restart here because a moved value, we should do this another way.
}
}
export async function coreDump(
coreDumpManager: CoreDumpManager,
openGithubIssue: boolean = false
): Promise<AppInfo> {
try {
const dump: AppInfo = await coredump(coreDumpManager)
if (openGithubIssue && dump.github_issue_url) {
openWindow(dump.github_issue_url)
}
return dump
} catch (e: any) {
throw new Error(`Error getting core dump: ${e}`)
}
}

150
src/lib/coredump.ts Normal file
View File

@ -0,0 +1,150 @@
import { EngineCommandManager } from 'lang/std/engineConnection'
import { WebrtcStats } from 'wasm-lib/kcl/bindings/WebrtcStats'
import { OsInfo } from 'wasm-lib/kcl/bindings/OsInfo'
import { isTauri } from 'lib/isTauri'
import {
platform as tauriPlatform,
arch as tauriArch,
version as tauriKernelVersion,
} from '@tauri-apps/plugin-os'
import { APP_VERSION } from 'routes/Settings'
import { UAParser } from 'ua-parser-js'
import screenshot from 'lib/screenshot'
import React from 'react'
import { VITE_KC_API_BASE_URL } from 'env'
// This is a class for getting all the values from the JS world to pass to the Rust world
// for a core dump.
export class CoreDumpManager {
engineCommandManager: EngineCommandManager
htmlRef: React.RefObject<HTMLDivElement> | null
token: string | undefined
baseUrl: string = VITE_KC_API_BASE_URL
constructor(
engineCommandManager: EngineCommandManager,
htmlRef: React.RefObject<HTMLDivElement> | null,
token: string | undefined
) {
this.engineCommandManager = engineCommandManager
this.htmlRef = htmlRef
this.token = token
}
// Get the token.
authToken(): string {
if (!this.token) {
throw new Error('Token not set')
}
return this.token
}
// Get the base url.
baseApiUrl(): string {
return this.baseUrl
}
// Get the version of the app from the package.json.
version(): string {
return APP_VERSION
}
// Get the os information.
getOsInfo(): Promise<string> {
if (this.isTauri()) {
return tauriArch()
.catch((error: any) => {
throw new Error(`Error getting arch: ${error}`)
})
.then((arch: string) => {
return tauriPlatform()
.catch((error: any) => {
throw new Error(`Error getting platform: ${error}`)
})
.then((platform: string) => {
return tauriKernelVersion()
.catch((error: any) => {
throw new Error(`Error getting kernel version: ${error}`)
})
.then((kernelVersion: string) => {
const osinfo: OsInfo = {
platform,
arch,
version: kernelVersion,
}
return JSON.stringify(osinfo)
})
})
})
}
const userAgent = window.navigator.userAgent || 'unknown browser'
if (userAgent === 'unknown browser') {
const osinfo: OsInfo = {
platform: userAgent,
arch: userAgent,
version: userAgent,
}
return new Promise((resolve) => resolve(JSON.stringify(osinfo)))
}
const parser = new UAParser(userAgent)
const parserResults = parser.getResult()
const osinfo: OsInfo = {
platform: parserResults.os.name,
arch: parserResults.cpu.architecture,
version: parserResults.os.version,
browser: userAgent,
}
return new Promise((resolve) => resolve(JSON.stringify(osinfo)))
}
isTauri(): boolean {
return isTauri()
}
getWebrtcStats(): Promise<string> {
if (!this.engineCommandManager.engineConnection) {
throw new Error('Engine connection not initialized')
}
if (!this.engineCommandManager.engineConnection.webrtcStatsCollector) {
throw new Error('Engine webrtcStatsCollector not initialized')
}
return this.engineCommandManager.engineConnection
.webrtcStatsCollector()
.catch((error: any) => {
throw new Error(`Error getting webrtc stats: ${error}`)
})
.then((stats: any) => {
const webrtcStats: WebrtcStats = {
packets_lost: stats.rtc_packets_lost,
frames_received: stats.rtc_frames_received,
frame_width: stats.rtc_frame_width,
frame_height: stats.rtc_frame_height,
frame_rate: stats.rtc_frames_per_second,
key_frames_decoded: stats.rtc_keyframes_decoded,
frames_dropped: stats.rtc_frames_dropped,
pause_count: stats.rtc_pause_count,
total_pauses_duration: stats.rtc_total_pauses_duration_sec,
freeze_count: stats.rtc_freeze_count,
total_freezes_duration: stats.rtc_total_freezes_duration_sec,
pli_count: stats.rtc_pli_count,
jitter: stats.rtc_jitter_sec,
}
return JSON.stringify(webrtcStats)
})
}
// Return a data URL (png format) of the screenshot of the current page.
screenshot(): Promise<string> {
return screenshot(this.htmlRef)
.then((screenshot: string) => {
return screenshot
})
.catch((error: any) => {
throw new Error(`Error getting screenshot: ${error}`)
})
}
}

View File

@ -29,11 +29,11 @@ const bracket = startSketchOn('XY')
|> extrude(width, %)
|> fillet({
radius: filletR,
tags: [getNextAdjacentEdge('innerEdge', %)]
tags: [getPreviousAdjacentEdge('innerEdge', %)]
}, %)
|> fillet({
radius: filletR + thickness,
tags: [getNextAdjacentEdge('outerEdge', %)]
tags: [getPreviousAdjacentEdge('outerEdge', %)]
}, %)`
function findLineInExampleCode({

11
src/lib/openWindow.ts Normal file
View File

@ -0,0 +1,11 @@
import { isTauri } from 'lib/isTauri'
import { open as tauriOpen } from '@tauri-apps/plugin-shell'
// Open a new browser window tauri style or browser style.
export default async function openWindow(url: string) {
if (isTauri()) {
await tauriOpen(url)
} else {
window.open(url, '_blank')
}
}

21
src/lib/screenshot.ts Normal file
View File

@ -0,0 +1,21 @@
import React from 'react'
import html2canvas from 'html2canvas-pro'
// Return a data URL (png format) of the screenshot of the current page.
export default async function screenshot(
htmlRef: React.RefObject<HTMLDivElement> | null
): Promise<string> {
if (htmlRef === null) {
throw new Error('htmlRef is null')
}
if (htmlRef.current === null) {
throw new Error('htmlRef is null')
}
return html2canvas(htmlRef.current)
.then((canvas) => {
return canvas.toDataURL()
})
.catch((error) => {
throw error
})
}

View File

@ -6,7 +6,7 @@ import {
import { Models } from '@kittycad/lib'
import { Themes } from './theme'
type WebSocketResponse = Models['OkWebSocketResponseData_type']
type WebSocketResponse = Models['WebSocketResponse_type']
class MockEngineCommandManager {
// eslint-disable-next-line @typescript-eslint/no-useless-constructor
@ -27,9 +27,12 @@ class MockEngineCommandManager {
command: EngineCommand
}): Promise<any> {
const response: WebSocketResponse = {
type: 'modeling',
data: {
modeling_response: { type: 'empty' },
success: true,
resp: {
type: 'modeling',
data: {
modeling_response: { type: 'empty' },
},
},
}
return Promise.resolve(JSON.stringify(response))

View File

@ -37,8 +37,9 @@ import {
shouldShowSettingInput,
} from 'lib/settings/settingsUtils'
export const APP_VERSION = import.meta.env.PACKAGE_VERSION || 'unknown'
export const Settings = () => {
const APP_VERSION = import.meta.env.PACKAGE_VERSION || 'unknown'
const navigate = useNavigate()
const close = () => navigate(location.pathname.replace(paths.SETTINGS, ''))
const location = useLocation()

View File

@ -81,6 +81,8 @@ export interface StoreState {
streamWidth: number
streamHeight: number
}) => void
setHtmlRef: (ref: React.RefObject<HTMLDivElement>) => void
htmlRef: React.RefObject<HTMLDivElement> | null
showHomeMenu: boolean
setHomeShowMenu: (showMenu: boolean) => void
@ -132,6 +134,10 @@ export const useStore = create<StoreState>()(
setButtonDownInStream: (buttonDownInStream) => {
set({ buttonDownInStream })
},
setHtmlRef: (htmlRef) => {
set({ htmlRef })
},
htmlRef: null,
didDragInStream: false,
setDidDragInStream: (didDragInStream) => {
set({ didDragInStream })

View File

@ -155,9 +155,9 @@ dependencies = [
[[package]]
name = "anyhow"
version = "1.0.81"
version = "1.0.82"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0952808a6c2afd1aa8947271f3a60f1a6763c7b912d210184c5149b5cf147247"
checksum = "f538837af36e6f6a9be0faa67f9a314f8119e4e4b5867c6ab40ed60360142519"
dependencies = [
"backtrace",
]
@ -1373,6 +1373,12 @@ version = "0.28.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253"
[[package]]
name = "git_rev"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "60884563ea313b5037683cd5d44f1e14e9cb07b08543756242a65887f9cff48e"
[[package]]
name = "gloo-utils"
version = "0.2.0"
@ -1864,7 +1870,9 @@ dependencies = [
"databake",
"derive-docs",
"expectorate",
"form_urlencoded",
"futures",
"git_rev",
"gltf-json",
"iai",
"image",
@ -1912,9 +1920,9 @@ dependencies = [
[[package]]
name = "kittycad"
version = "0.2.63"
version = "0.2.66"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "93a332250e08fd715ad3d5826e04d36da1c5bb42d0c1b1ff1f0598278b9ebf3c"
checksum = "9e2897244f4600f863115561a0fd1cd7c87fca20253ffecfebc53ef642d0aceb"
dependencies = [
"anyhow",
"async-trait",
@ -1950,9 +1958,9 @@ dependencies = [
[[package]]
name = "kittycad-execution-plan"
version = "0.1.3"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e913f8e5f3ef7928cddca2e7b53c6582d7be6a8f900d18ce6c31c04083056270"
checksum = "acf8ffb148bd09de8889a8a2b3075a23ee86446c3a6e1c6dcf66b40fdc778158"
dependencies = [
"bytes",
"gltf-json",
@ -1995,9 +2003,9 @@ dependencies = [
[[package]]
name = "kittycad-modeling-cmds"
version = "0.2.10"
version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b41beb7d9b776df93fd449604de5c447e33c7bd3326fd590002dc18cf5f08166"
checksum = "e94b75afb2ab9fe824bb483d2475d419bb15fbe850466c45962999b54456173a"
dependencies = [
"anyhow",
"chrono",
@ -2035,9 +2043,9 @@ dependencies = [
[[package]]
name = "kittycad-modeling-session"
version = "0.1.2"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6ee3a24232a086ec12ae4cfee443485c22e6c6959936d861006fa13bebef0904"
checksum = "bae9bc47fcc3cc30727b35e738c35666b97e1e5f48f3f4c60ddaeccb69b66559"
dependencies = [
"futures",
"kittycad",
@ -2815,9 +2823,9 @@ dependencies = [
[[package]]
name = "quote"
version = "1.0.35"
version = "1.0.36"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef"
checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7"
dependencies = [
"proc-macro2",
]

View File

@ -59,12 +59,12 @@ members = [
]
[workspace.dependencies]
kittycad = { version = "0.2.63", default-features = false, features = ["js", "requests"] }
kittycad-execution-plan = "0.1.3"
kittycad = { version = "0.2.66", default-features = false, features = ["js", "requests"] }
kittycad-execution-plan = "0.1.4"
kittycad-execution-plan-macros = "0.1.9"
kittycad-execution-plan-traits = "0.1.14"
kittycad-modeling-cmds = "0.2.10"
kittycad-modeling-session = "0.1.2"
kittycad-modeling-cmds = "0.2.17"
kittycad-modeling-session = "0.1.3"
[[test]]
name = "executor"

View File

@ -23,7 +23,7 @@ serde_tokenstream = "0.2"
syn = { version = "2.0.58", features = ["full"] }
[dev-dependencies]
anyhow = "1.0.81"
anyhow = "1.0.82"
expectorate = "1.1.0"
pretty_assertions = "1.4.0"
rustfmt-wrapper = "0.2.1"

View File

@ -777,7 +777,7 @@ fn generate_code_block_test(
}
let ws = client
.modeling()
.commands_ws(None, None, None, None, None, Some(false))
.commands_ws(None, None, None, None, None,None, Some(false))
.await.unwrap();
let tokens = crate::token::lexer(#code_block);

View File

@ -11,15 +11,18 @@ keywords = ["kcl", "KittyCAD", "CAD"]
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
anyhow = { version = "1.0.81", features = ["backtrace"] }
anyhow = { version = "1.0.82", features = ["backtrace"] }
async-recursion = "1.1.0"
async-trait = "0.1.79"
base64 = "0.22.0"
chrono = "0.4.37"
clap = { version = "4.5.4", features = ["cargo", "derive", "env", "unicode"], optional = true }
dashmap = "5.5.3"
databake = { version = "0.1.7", features = ["derive"] }
derive-docs = { version = "0.1.12", path = "../derive-docs" }
derive-docs = { version = "0.1.13", path = "../derive-docs" }
form_urlencoded = "1.2.1"
futures = { version = "0.3.30" }
git_rev = "0.1.0"
gltf-json = "1.4.0"
kittycad = { workspace = true }
kittycad-execution-plan-macros = { workspace = true }

View File

@ -0,0 +1,58 @@
//! Functions for getting core dump information via local rust.
use anyhow::Result;
use crate::coredump::CoreDump;
#[derive(Debug, Clone)]
pub struct CoreDumper {}
impl CoreDumper {
pub fn new() -> Self {
CoreDumper {}
}
}
impl Default for CoreDumper {
fn default() -> Self {
Self::new()
}
}
#[async_trait::async_trait(?Send)]
impl CoreDump for CoreDumper {
fn token(&self) -> Result<String> {
Ok(std::env::var("KITTYCAD_API_TOKEN").unwrap_or_default())
}
fn base_api_url(&self) -> Result<String> {
Ok("https://api.zoo.dev".to_string())
}
fn version(&self) -> Result<String> {
Ok(env!("CARGO_PKG_VERSION").to_string())
}
async fn os(&self) -> Result<crate::coredump::OsInfo> {
Ok(crate::coredump::OsInfo {
platform: Some(std::env::consts::OS.to_string()),
arch: Some(std::env::consts::ARCH.to_string()),
version: None,
browser: None,
})
}
fn is_tauri(&self) -> Result<bool> {
Ok(false)
}
async fn get_webrtc_stats(&self) -> Result<crate::coredump::WebrtcStats> {
// TODO: we could actually implement this.
Ok(crate::coredump::WebrtcStats::default())
}
async fn screenshot(&self) -> Result<String> {
// Take a screenshot of the engine.
todo!()
}
}

View File

@ -0,0 +1,193 @@
//! Core dump related structures and functions.
#[cfg(not(target_arch = "wasm32"))]
pub mod local;
#[cfg(target_arch = "wasm32")]
pub mod wasm;
use anyhow::Result;
use base64::Engine;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
#[async_trait::async_trait(?Send)]
pub trait CoreDump: Clone {
/// Return the authentication token.
fn token(&self) -> Result<String>;
fn base_api_url(&self) -> Result<String>;
fn version(&self) -> Result<String>;
async fn os(&self) -> Result<OsInfo>;
fn is_tauri(&self) -> Result<bool>;
async fn get_webrtc_stats(&self) -> Result<WebrtcStats>;
/// Return a screenshot of the app.
async fn screenshot(&self) -> Result<String>;
/// Get a screenshot of the app and upload it to public cloud storage.
async fn upload_screenshot(&self) -> Result<String> {
let screenshot = self.screenshot().await?;
let cleaned = screenshot.trim_start_matches("data:image/png;base64,");
// Create the zoo client.
let mut zoo = kittycad::Client::new(self.token()?);
zoo.set_base_url(&self.base_api_url()?);
// Base64 decode the screenshot.
let data = base64::engine::general_purpose::STANDARD.decode(cleaned)?;
// Upload the screenshot.
let links = zoo
.meta()
.create_debug_uploads(vec![kittycad::types::multipart::Attachment {
name: "".to_string(),
filename: Some("modeling-app/core-dump-screenshot.png".to_string()),
content_type: Some("image/png".to_string()),
data,
}])
.await
.map_err(|e| anyhow::anyhow!(e.to_string()))?;
if links.is_empty() {
anyhow::bail!("Failed to upload screenshot");
}
Ok(links[0].clone())
}
/// Dump the app info.
async fn dump(&self) -> Result<AppInfo> {
let webrtc_stats = self.get_webrtc_stats().await?;
let os = self.os().await?;
let screenshot_url = self.upload_screenshot().await?;
let mut app_info = AppInfo {
version: self.version()?,
git_rev: git_rev::try_revision_string!().map_or_else(|| "unknown".to_string(), |s| s.to_string()),
timestamp: chrono::Utc::now(),
tauri: self.is_tauri()?,
os,
webrtc_stats,
github_issue_url: None,
};
app_info.set_github_issue_url(&screenshot_url)?;
Ok(app_info)
}
}
/// The app info structure.
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
#[ts(export)]
#[serde(rename_all = "snake_case")]
pub struct AppInfo {
/// The version of the app.
pub version: String,
/// The git revision of the app.
pub git_rev: String,
/// A timestamp of the core dump.
#[ts(type = "string")]
pub timestamp: chrono::DateTime<chrono::Utc>,
/// If the app is running in tauri or the browser.
pub tauri: bool,
/// The os info.
pub os: OsInfo,
/// The webrtc stats.
pub webrtc_stats: WebrtcStats,
/// A GitHub issue url to report the core dump.
/// This gets prepoulated with all the core dump info.
#[serde(skip_serializing_if = "Option::is_none")]
pub github_issue_url: Option<String>,
}
impl AppInfo {
/// Set the github issue url.
pub fn set_github_issue_url(&mut self, screenshot_url: &str) -> Result<()> {
let tauri_or_browser_label = if self.tauri { "tauri" } else { "browser" };
let labels = ["coredump", "bug", tauri_or_browser_label];
let body = format!(
r#"[Insert a description of the issue here]
![Screenshot]({})
<details>
<summary><b>Core Dump</b></summary>
```json
{}
```
</details>
"#,
screenshot_url,
serde_json::to_string_pretty(&self)?
);
let urlencoded: String = form_urlencoded::byte_serialize(body.as_bytes()).collect();
self.github_issue_url = Some(format!(
r#"https://github.com/{}/{}/issues/new?body={}&labels={}"#,
"KittyCAD",
"modeling-app",
urlencoded,
labels.join(",")
));
Ok(())
}
}
/// The os info structure.
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
#[ts(export)]
#[serde(rename_all = "snake_case")]
pub struct OsInfo {
/// The platform the app is running on.
#[serde(skip_serializing_if = "Option::is_none")]
pub platform: Option<String>,
/// The architecture the app is running on.
#[serde(skip_serializing_if = "Option::is_none")]
pub arch: Option<String>,
/// The kernel version.
#[serde(skip_serializing_if = "Option::is_none")]
pub version: Option<String>,
/// Information about the browser.
#[serde(skip_serializing_if = "Option::is_none")]
pub browser: Option<String>,
}
/// The webrtc stats structure.
#[derive(Default, Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
#[ts(export)]
#[serde(rename_all = "snake_case")]
pub struct WebrtcStats {
/// The packets lost.
pub packets_lost: u32,
/// The frames received.
pub frames_received: u32,
/// The frame width.
pub frame_width: f32,
/// The frame height.
pub frame_height: f32,
/// The frame rate.
pub frame_rate: f32,
/// The number of key frames decoded.
pub key_frames_decoded: u32,
/// The number of frames dropped.
pub frames_dropped: u32,
/// The pause count.
pub pause_count: u32,
/// The total pauses duration.
pub total_pauses_duration: f32,
/// The freeze count.
pub freeze_count: u32,
/// The total freezes duration.
pub total_freezes_duration: f32,
/// The pli count.
pub pli_count: u32,
/// Packet jitter for this synchronizing source, measured in seconds.
pub jitter: f32,
}

View File

@ -0,0 +1,134 @@
//! Functions for getting core dump information via wasm.
use anyhow::Result;
use wasm_bindgen::prelude::wasm_bindgen;
use crate::{coredump::CoreDump, wasm::JsFuture};
#[wasm_bindgen(module = "/../../lib/coredump.ts")]
extern "C" {
#[derive(Debug, Clone)]
pub type CoreDumpManager;
#[wasm_bindgen(method, js_name = authToken, catch)]
fn auth_token(this: &CoreDumpManager) -> Result<String, js_sys::Error>;
#[wasm_bindgen(method, js_name = baseApiUrl, catch)]
fn baseApiUrl(this: &CoreDumpManager) -> Result<String, js_sys::Error>;
#[wasm_bindgen(method, js_name = version, catch)]
fn version(this: &CoreDumpManager) -> Result<String, js_sys::Error>;
#[wasm_bindgen(method, js_name = getOsInfo, catch)]
fn get_os_info(this: &CoreDumpManager) -> Result<js_sys::Promise, js_sys::Error>;
#[wasm_bindgen(method, js_name = isTauri, catch)]
fn is_tauri(this: &CoreDumpManager) -> Result<bool, js_sys::Error>;
#[wasm_bindgen(method, js_name = getWebrtcStats, catch)]
fn get_webrtc_stats(this: &CoreDumpManager) -> Result<js_sys::Promise, js_sys::Error>;
#[wasm_bindgen(method, js_name = screenshot, catch)]
fn screenshot(this: &CoreDumpManager) -> Result<js_sys::Promise, js_sys::Error>;
}
#[derive(Debug, Clone)]
pub struct CoreDumper {
manager: CoreDumpManager,
}
impl CoreDumper {
pub fn new(manager: CoreDumpManager) -> Self {
CoreDumper { manager }
}
}
unsafe impl Send for CoreDumper {}
unsafe impl Sync for CoreDumper {}
#[async_trait::async_trait(?Send)]
impl CoreDump for CoreDumper {
fn token(&self) -> Result<String> {
self.manager
.auth_token()
.map_err(|e| anyhow::anyhow!("Failed to get response from token: {:?}", e))
}
fn base_api_url(&self) -> Result<String> {
self.manager
.baseApiUrl()
.map_err(|e| anyhow::anyhow!("Failed to get response from base api url: {:?}", e))
}
fn version(&self) -> Result<String> {
self.manager
.version()
.map_err(|e| anyhow::anyhow!("Failed to get response from version: {:?}", e))
}
async fn os(&self) -> Result<crate::coredump::OsInfo> {
let promise = self
.manager
.get_os_info()
.map_err(|e| anyhow::anyhow!("Failed to get promise from get os info: {:?}", e))?;
let value = JsFuture::from(promise)
.await
.map_err(|e| anyhow::anyhow!("Failed to get response from os info: {:?}", e))?;
// Parse the value as a string.
let s = value
.as_string()
.ok_or_else(|| anyhow::anyhow!("Failed to get string from response from os info: `{:?}`", value))?;
let os: crate::coredump::OsInfo =
serde_json::from_str(&s).map_err(|e| anyhow::anyhow!("Failed to parse os info: {:?}", e))?;
Ok(os)
}
fn is_tauri(&self) -> Result<bool> {
self.manager
.is_tauri()
.map_err(|e| anyhow::anyhow!("Failed to get response from is tauri: {:?}", e))
}
async fn get_webrtc_stats(&self) -> Result<crate::coredump::WebrtcStats> {
let promise = self
.manager
.get_webrtc_stats()
.map_err(|e| anyhow::anyhow!("Failed to get promise from get webrtc stats: {:?}", e))?;
let value = JsFuture::from(promise)
.await
.map_err(|e| anyhow::anyhow!("Failed to get response from webrtc stats: {:?}", e))?;
// Parse the value as a string.
let s = value
.as_string()
.ok_or_else(|| anyhow::anyhow!("Failed to get string from response from webrtc stats: `{:?}`", value))?;
let stats: crate::coredump::WebrtcStats =
serde_json::from_str(&s).map_err(|e| anyhow::anyhow!("Failed to parse webrtc stats: {:?}", e))?;
Ok(stats)
}
async fn screenshot(&self) -> Result<String> {
let promise = self
.manager
.screenshot()
.map_err(|e| anyhow::anyhow!("Failed to get promise from get screenshot: {:?}", e))?;
let value = JsFuture::from(promise)
.await
.map_err(|e| anyhow::anyhow!("Failed to get response from screenshot: {:?}", e))?;
// Parse the value as a string.
let s = value
.as_string()
.ok_or_else(|| anyhow::anyhow!("Failed to get string from response from screenshot: `{:?}`", value))?;
Ok(s)
}
}

View File

@ -99,13 +99,25 @@ impl crate::engine::EngineManager for EngineConnection {
})
})?;
let modeling_result: kittycad::types::OkWebSocketResponseData = serde_json::from_str(&s).map_err(|e| {
let ws_result: kittycad::types::WebSocketResponse = serde_json::from_str(&s).map_err(|e| {
KclError::Engine(KclErrorDetails {
message: format!("Failed to deserialize response from engine: {:?}", e),
source_ranges: vec![source_range],
})
})?;
Ok(modeling_result)
if let Some(data) = &ws_result.resp {
Ok(data.clone())
} else if let Some(errors) = &ws_result.errors {
Err(KclError::Engine(KclErrorDetails {
message: format!("Modeling command failed: {:?}", errors),
source_ranges: vec![source_range],
}))
} else {
Err(KclError::Engine(KclErrorDetails {
message: format!("Modeling command failed: {:?}", ws_result),
source_ranges: vec![source_range],
}))
}
}
}

View File

@ -28,6 +28,16 @@ pub trait EngineManager: std::fmt::Debug + Send + Sync + 'static {
id_to_source_range: std::collections::HashMap<uuid::Uuid, crate::executor::SourceRange>,
) -> Result<kittycad::types::OkWebSocketResponseData, crate::errors::KclError>;
fn push_to_batch(
&self,
cmd_id: uuid::Uuid,
source_range: crate::executor::SourceRange,
cmd: kittycad::types::ModelingCmd,
) {
let req = WebSocketRequest::ModelingCmdReq { cmd, cmd_id };
self.batch().lock().unwrap().push((req, source_range))
}
async fn send_modeling_cmd(
&self,
id: uuid::Uuid,
@ -85,6 +95,7 @@ pub trait EngineManager: std::fmt::Debug + Send + Sync + 'static {
let batched_requests = WebSocketRequest::ModelingCmdBatchReq {
requests,
batch_id: uuid::Uuid::new_v4(),
responses: Some(false),
};
let final_req = if self.batch().lock().unwrap().len() == 1 {
@ -93,7 +104,8 @@ pub trait EngineManager: std::fmt::Debug + Send + Sync + 'static {
} else {
batched_requests
};
// println!("Running batch: {final_req:#?}");
// TODO: Uncomment this.
debug_batch(&final_req);
// Create the map of original command IDs to source range.
// This is for the wasm side, kurt needs it for selections.
@ -117,7 +129,11 @@ pub trait EngineManager: std::fmt::Debug + Send + Sync + 'static {
// We pop off the responses to cleanup our mappings.
let id_final = match final_req {
WebSocketRequest::ModelingCmdBatchReq { requests: _, batch_id } => batch_id,
WebSocketRequest::ModelingCmdBatchReq {
requests: _,
batch_id,
responses: _,
} => batch_id,
WebSocketRequest::ModelingCmdReq { cmd: _, cmd_id } => cmd_id,
_ => {
return Err(KclError::Engine(KclErrorDetails {
@ -186,3 +202,18 @@ pub fn is_cmd_with_return_values(cmd: &kittycad::types::ModelingCmd) -> bool {
true
}
#[allow(dead_code)] // Only used in debugging.
fn debug_batch(msg: &WebSocketRequest) {
match msg {
WebSocketRequest::ModelingCmdReq { cmd, .. } => {
println!("[ {:?} ]", cmd);
}
WebSocketRequest::ModelingCmdBatchReq { requests, .. } => {
let names: Vec<_> = requests.iter().map(|req| format!("{:?}", req.cmd)).collect();
println!("[ {} ]", names.join(", "))
}
other => panic!("this isn't a modeling command or batch: {other:?}"),
}
}

View File

@ -1226,7 +1226,7 @@ pub(crate) async fn execute(
}
// Flush the batch queue.
ctx.engine.flush_batch(SourceRange::default()).await?;
ctx.engine.flush_batch(SourceRange([program.end, program.end])).await?;
Ok(memory.clone())
}

View File

@ -8,6 +8,7 @@ pub use local::FileManager;
#[cfg(target_arch = "wasm32")]
#[cfg(not(test))]
pub mod wasm;
use anyhow::Result;
#[cfg(target_arch = "wasm32")]
#[cfg(not(test))]

View File

@ -5,6 +5,7 @@
#![recursion_limit = "1024"]
pub mod ast;
pub mod coredump;
pub mod docs;
pub mod engine;
pub mod errors;

View File

@ -38,15 +38,14 @@ async fn inner_extrude(length: f64, sketch_group: Box<SketchGroup>, args: Args)
let id = uuid::Uuid::new_v4();
// Extrude the element.
args.send_modeling_cmd(
args.push_to_batch(
id,
kittycad::types::ModelingCmd::Extrude {
target: sketch_group.id,
distance: length,
cap: true,
},
)
.await?;
);
do_post_extrude(sketch_group, length, id, args).await
}
@ -66,13 +65,12 @@ pub(crate) async fn do_post_extrude(
// Bring the object to the front of the scene.
// See: https://github.com/KittyCAD/modeling-app/issues/806
args.send_modeling_cmd(
args.push_to_batch(
uuid::Uuid::new_v4(),
kittycad::types::ModelingCmd::ObjectBringToFront {
object_id: sketch_group.id,
},
)
.await?;
);
if sketch_group.value.is_empty() {
return Err(KclError::Type(KclErrorDetails {

View File

@ -214,6 +214,10 @@ impl Args {
self.ctx.engine.send_modeling_cmd(id, self.source_range, cmd).await
}
pub fn push_to_batch(&self, id: uuid::Uuid, cmd: kittycad::types::ModelingCmd) {
self.ctx.engine.push_to_batch(id, self.source_range, cmd)
}
fn make_user_val_from_json(&self, j: serde_json::Value) -> Result<MemoryItem, KclError> {
Ok(MemoryItem::UserVal(crate::executor::UserVal {
value: j,

View File

@ -235,7 +235,7 @@ pub struct CircularPattern2dData {
/// This excludes the original entity. For example, if `repetitions` is 1,
/// the original entity will be copied once.
pub repetitions: u32,
/// The center about which to make th pattern. This is a 2D vector.
/// The center about which to make the pattern. This is a 2D vector.
pub center: [f64; 2],
/// The arc angle (in degrees) to place the repetitions. Must be greater than 0.
pub arc_degrees: f64,
@ -254,7 +254,7 @@ pub struct CircularPattern3dData {
pub repetitions: u32,
/// The axis around which to make the pattern. This is a 3D vector.
pub axis: [f64; 3],
/// The center about which to make th pattern. This is a 3D vector.
/// The center about which to make the pattern. This is a 3D vector.
pub center: [f64; 3],
/// The arc angle (in degrees) to place the repetitions. Must be greater than 0.
pub arc_degrees: f64,

View File

@ -224,7 +224,7 @@ async fn inner_revolve(
match data.axis {
RevolveAxis::Axis(axis) => {
let (axis, origin) = axis.axis_and_origin()?;
args.send_modeling_cmd(
args.push_to_batch(
id,
ModelingCmd::Revolve {
angle,
@ -234,8 +234,7 @@ async fn inner_revolve(
tolerance: DEFAULT_TOLERANCE,
axis_is_2d: true,
},
)
.await?;
);
}
RevolveAxis::Edge(edge) => {
let edge_id = match edge {
@ -256,7 +255,7 @@ async fn inner_revolve(
.id
}
};
args.send_modeling_cmd(
args.push_to_batch(
id,
ModelingCmd::RevolveAboutEdge {
angle,
@ -264,8 +263,7 @@ async fn inner_revolve(
edge_id,
tolerance: DEFAULT_TOLERANCE,
},
)
.await?;
);
}
}

View File

@ -7,7 +7,7 @@ use std::{
use futures::stream::TryStreamExt;
use gloo_utils::format::JsValueSerdeExt;
use kcl_lib::engine::EngineManager;
use kcl_lib::{coredump::CoreDump, engine::EngineManager};
use tower_lsp::{LspService, Server};
use wasm_bindgen::prelude::*;
@ -379,3 +379,16 @@ pub fn program_memory_init() -> Result<JsValue, String> {
// gloo-serialize crate instead.
JsValue::from_serde(&memory).map_err(|e| e.to_string())
}
/// Get a coredump.
#[wasm_bindgen]
pub async fn coredump(core_dump_manager: kcl_lib::coredump::wasm::CoreDumpManager) -> Result<JsValue, String> {
console_error_panic_hook::set_once();
let core_dumper = kcl_lib::coredump::wasm::CoreDumper::new(core_dump_manager);
let dump = core_dumper.dump().await.map_err(|e| e.to_string())?;
// The serde-wasm-bindgen does not work here because of weird HashMap issues so we use the
// gloo-serialize crate instead.
JsValue::from_serde(&dump).map_err(|e| e.to_string())
}

View File

@ -0,0 +1,18 @@
const width = 20
fn cube = (cx, cy) => {
let d = width/2
startSketchAt([cx-d, cy-d])
|> lineTo([cx+d, cy-d], %)
|> lineTo([cx+d, cy+d], %)
|> lineTo([cx-d, cy+d], %)
|> lineTo([cx-d, cy-d], %)
|> close(%)
|> extrude(width, %)
}
let interval = 30
cube(interval,interval)
cube(0,0)
cube(-interval,interval)

View File

@ -29,7 +29,7 @@ async fn execute_and_snapshot(code: &str, units: kittycad::types::UnitLength) ->
let ws = client
.modeling()
.commands_ws(None, None, None, None, None, Some(false))
.commands_ws(None, None, None, None, None, None, Some(false))
.await?;
// Create a temporary file to write the output to.
@ -110,6 +110,15 @@ const part002 = startSketchOn(part001, "here")
twenty_twenty::assert_image("tests/executor/outputs/sketch_on_face.png", &result, 0.999);
}
#[tokio::test(flavor = "multi_thread")]
async fn serial_test_three_cubes() {
let code = include_str!("inputs/three_cubes.kcl");
let result = execute_and_snapshot(code, kittycad::types::UnitLength::Mm)
.await
.unwrap();
twenty_twenty::assert_image("tests/executor/outputs/three_cubes.png", &result, 0.999);
}
#[tokio::test(flavor = "multi_thread")]
async fn serial_test_riddle_small() {
let code = include_str!("inputs/riddle_small.kcl");
@ -1959,3 +1968,52 @@ capScrew([0, 0.5, 0], 50, 37.5, 50, 25)
.unwrap();
twenty_twenty::assert_image("tests/executor/outputs/member_expression_in_params.png", &result, 1.0);
}
#[tokio::test(flavor = "multi_thread")]
async fn serial_test_bracket_with_fillets_ensure_fail_on_flush_source_ranges() {
let code = r#"// Shelf Bracket
// This is a shelf bracket made out of 6061-T6 aluminum sheet metal. The required thickness is calculated based on a point load of 300 lbs applied to the end of the shelf. There are two brackets holding up the shelf, so the moment experienced is divided by 2. The shelf is 1 foot long from the wall.
const sigmaAllow = 35000 // psi
const width = 6 // inch
const p = 300 // Force on shelf - lbs
const distance = 12 // inches
const M = 12 * 300 / 2 // Moment experienced at fixed end of bracket
const FOS = 2 // Factor of safety of 2
const shelfMountL = 8 // The length of the bracket holding up the shelf is 6 inches
const wallMountL = 8 // the length of the bracket
// Calculate the thickness off the allowable bending stress and factor of safety
const thickness = sqrt(6 * M * FOS / (width * sigmaAllow))
// 0.25 inch fillet radius
const filletR = 0.25
// Sketch the bracket and extrude with fillets
const bracket = startSketchOn('XY')
|> startProfileAt([0, 0], %)
|> line([0, wallMountL], %, 'outerEdge')
|> line([-shelfMountL, 0], %)
|> line([0, -thickness], %)
|> line([shelfMountL - thickness, 0], %, 'innerEdge')
|> line([0, -wallMountL + thickness], %)
|> close(%)
|> extrude(width, %)
|> fillet({
radius: filletR,
tags: [getNextAdjacentEdge('innerEdge', %)]
}, %)
|> fillet({
radius: filletR + thickness,
tags: [getNextAdjacentEdge('outerEdge', %)]
}, %)
"#;
let result = execute_and_snapshot(code, kittycad::types::UnitLength::Mm).await;
assert!(result.is_err());
assert_eq!(
result.err().unwrap().to_string(),
r#"engine: KclErrorDetails { source_ranges: [SourceRange([1443, 1443])], message: "Modeling command failed: Some([ApiError { error_code: BadRequest, message: \"Fillet failed\" }])" }"#
);
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 116 KiB

View File

@ -33,7 +33,7 @@ async fn setup(code: &str, name: &str) -> Result<(ExecutorContext, Program, uuid
let ws = client
.modeling()
.commands_ws(None, None, None, None, None, Some(false))
.commands_ws(None, None, None, None, None, None, Some(false))
.await?;
let tokens = kcl_lib::token::lexer(code);

211
yarn.lock
View File

@ -1630,19 +1630,24 @@
resolved "https://registry.yarnpkg.com/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-6.4.2.tgz#1766039cad33f8ad87f9467b98e0d18fbc8f01c5"
integrity sha512-1DgP7f+XQIJbLFCTX1V2QnxVmpLdKdzzo2k8EmvDOePfchaIGQ9eCHj2up3/jNEbZuBqel5OxiaOJf37TWauRA==
"@fortawesome/fontawesome-svg-core@^6.4.2":
version "6.4.2"
resolved "https://registry.yarnpkg.com/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-6.4.2.tgz#37f4507d5ec645c8b50df6db14eced32a6f9be09"
integrity sha512-gjYDSKv3TrM2sLTOKBc5rH9ckje8Wrwgx1CxAPbN5N3Fm4prfi7NsJVWd1jklp7i5uSCVwhZS5qlhMXqLrpAIg==
dependencies:
"@fortawesome/fontawesome-common-types" "6.4.2"
"@fortawesome/fontawesome-common-types@6.5.2":
version "6.5.2"
resolved "https://registry.yarnpkg.com/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-6.5.2.tgz#eaf2f5699f73cef198454ebc0c414e3688898179"
integrity sha512-gBxPg3aVO6J0kpfHNILc+NMhXnqHumFxOmjYCFfOiLZfwhnnfhtsdA2hfJlDnj+8PjAs6kKQPenOTKj3Rf7zHw==
"@fortawesome/free-brands-svg-icons@^6.4.2":
version "6.4.2"
resolved "https://registry.yarnpkg.com/@fortawesome/free-brands-svg-icons/-/free-brands-svg-icons-6.4.2.tgz#9b8e78066ea6dd563da5dfa686615791d0f7cc71"
integrity sha512-LKOwJX0I7+mR/cvvf6qIiqcERbdnY+24zgpUSouySml+5w8B4BJOx8EhDR/FTKAu06W12fmUIcv6lzPSwYKGGg==
"@fortawesome/fontawesome-svg-core@^6.5.2":
version "6.5.2"
resolved "https://registry.yarnpkg.com/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-6.5.2.tgz#4b42de71e196039b0d5ccf88559b8044e3296c21"
integrity sha512-5CdaCBGl8Rh9ohNdxeeTMxIj8oc3KNBgIeLMvJosBMdslK/UnEB8rzyDRrbKdL1kDweqBPo4GT9wvnakHWucZw==
dependencies:
"@fortawesome/fontawesome-common-types" "6.4.2"
"@fortawesome/fontawesome-common-types" "6.5.2"
"@fortawesome/free-brands-svg-icons@^6.5.2":
version "6.5.2"
resolved "https://registry.yarnpkg.com/@fortawesome/free-brands-svg-icons/-/free-brands-svg-icons-6.5.2.tgz#bfca0cebd2c4713dc93244e1fa8b384f1f023587"
integrity sha512-zi5FNYdmKLnEc0jc0uuHH17kz/hfYTg4Uei0wMGzcoCL/4d3WM3u1VMc0iGGa31HuhV5i7ZK8ZlTCQrHqRHSGQ==
dependencies:
"@fortawesome/fontawesome-common-types" "6.5.2"
"@fortawesome/free-solid-svg-icons@^6.4.2":
version "6.4.2"
@ -1913,12 +1918,12 @@
resolved "https://registry.yarnpkg.com/@pkgjs/parseargs/-/parseargs-0.11.0.tgz#a77ea742fab25775145434eb1d2328cf5013ac33"
integrity sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==
"@playwright/test@^1.39.0":
version "1.39.0"
resolved "https://registry.yarnpkg.com/@playwright/test/-/test-1.39.0.tgz#d10ba8e38e44104499e25001945f07faa9fa91cd"
integrity sha512-3u1iFqgzl7zr004bGPYiN/5EZpRUSFddQBra8Rqll5N0/vfpqlP9I9EXqAoGacuAbX6c9Ulg/Cjqglp5VkK6UQ==
"@playwright/test@^1.43.0":
version "1.43.0"
resolved "https://registry.yarnpkg.com/@playwright/test/-/test-1.43.0.tgz#5d90f247b26d404dd5d81c60f9c7c5e5159eb664"
integrity sha512-Ebw0+MCqoYflop7wVKj711ccbNlrwTBCtjY5rlbiY9kHL2bCYxq+qltK6uPsVBGGAOb033H2VO0YobcQVxoW7Q==
dependencies:
playwright "1.39.0"
playwright "1.43.0"
"@puppeteer/browsers@1.4.6":
version "1.4.6"
@ -2106,7 +2111,7 @@
resolved "https://registry.yarnpkg.com/@tauri-apps/api/-/api-2.0.0-beta.4.tgz#7688950f6e03f38b3bac73585f8f4cdd61be6aa6"
integrity sha512-Nxtj28NYUo5iwYkpYslxmOPkdI2WkELU2e3UH9nbJm9Ydki2CQwJVGQxx4EANtdZcMNsEsUzRqaDTvEUYH1l6w==
"@tauri-apps/api@^2.0.0-beta.7":
"@tauri-apps/api@2.0.0-beta.7":
version "2.0.0-beta.7"
resolved "https://registry.yarnpkg.com/@tauri-apps/api/-/api-2.0.0-beta.7.tgz#a80a3ffc24e6ec8dbdc8131dc3e68d8f4342293d"
integrity sha512-cM7SJQP4DBkLLMOdybLFYUURWn/tng2FEdAnXlu42f3NhFxKL4KVeeQTkuwlgC7ePwwwvDSqiXGiF+dKOadY7w==
@ -2287,7 +2292,7 @@
resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.4.tgz#0b92dcc0cc1c81f6f306a381f28e31b1a56536e9"
integrity sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==
"@tweenjs/tween.js@^23.1.1":
"@tweenjs/tween.js@^23.1.1", "@tweenjs/tween.js@~23.1.1":
version "23.1.1"
resolved "https://registry.yarnpkg.com/@tweenjs/tween.js/-/tween.js-23.1.1.tgz#0ae28ed9c635805557f78c2626464018d5f1b5e2"
integrity sha512-ZpboH7pCPPeyBWKf8c7TJswtCEQObFo3bOBYalm99NzZarATALYCo5OhbCa/n4RQyJyHfhkdx+hNrdL5ByFYDw==
@ -2358,6 +2363,14 @@
resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.5.tgz#a6ce3e556e00fd9895dd872dd172ad0d4bd687f4"
integrity sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==
"@types/hoist-non-react-statics@^3.3.1":
version "3.3.5"
resolved "https://registry.yarnpkg.com/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.5.tgz#dab7867ef789d87e2b4b0003c9d65c49cc44a494"
integrity sha512-SbcrWzkKBw2cdwRTwQAswfpB9g9LJWfjtUeW/jvNwbhC8cpmmNYVePa+ncbUe0rGTQ7G3Ff6mYUN2VMfLVr+Sg==
dependencies:
"@types/react" "*"
hoist-non-react-statics "^3.3.0"
"@types/http-cache-semantics@^4.0.2":
version "4.0.4"
resolved "https://registry.yarnpkg.com/@types/http-cache-semantics/-/http-cache-semantics-4.0.4.tgz#b979ebad3919799c979b17c72621c0bc0a31c6c4"
@ -2412,10 +2425,10 @@
dependencies:
undici-types "~5.26.4"
"@types/node@^18.19.26":
version "18.19.26"
resolved "https://registry.yarnpkg.com/@types/node/-/node-18.19.26.tgz#18991279d0a0e53675285e8cf4a0823766349729"
integrity sha512-+wiMJsIwLOYCvUqSdKTrfkS8mpTp+MPINe6+Np4TAGFWWRWiBQ5kSq9nZGCSPkzx9mvT+uEukzpX4MOSCydcvw==
"@types/node@^18.19.31":
version "18.19.31"
resolved "https://registry.yarnpkg.com/@types/node/-/node-18.19.31.tgz#b7d4a00f7cb826b60a543cebdbda5d189aaecdcd"
integrity sha512-ArgCD39YpyyrtFKIqMDvjz79jto5fcI/SVUs2HwB+f0dAzq68yqOdyaSivLiLugSziTpNXLQrVb7RZFmdZzbhA==
dependencies:
undici-types "~5.26.4"
@ -2462,10 +2475,10 @@
dependencies:
"@types/react" "*"
"@types/react@*", "@types/react@^18.2.73":
version "18.2.73"
resolved "https://registry.yarnpkg.com/@types/react/-/react-18.2.73.tgz#0579548ad122660d99e00499d22e33b81e73ed94"
integrity sha512-XcGdod0Jjv84HOC7N5ziY3x+qL0AfmubvKOZ9hJjJ2yd5EE+KYjWhdOjt387e9HPheHkdggF9atTifMRtyAaRA==
"@types/react@*", "@types/react@^18.2.75":
version "18.2.75"
resolved "https://registry.yarnpkg.com/@types/react/-/react-18.2.75.tgz#45d18f384939306d35312def1bf532eb38a68562"
integrity sha512-+DNnF7yc5y0bHkBTiLKqXFe+L4B3nvOphiMY3tuA5X10esmjqk7smyBZzbGTy2vsiy/Bnzj8yFIBL8xhRacoOg==
dependencies:
"@types/prop-types" "*"
csstype "^3.0.2"
@ -2492,16 +2505,22 @@
dependencies:
"@types/jest" "*"
"@types/three@^0.160.0":
version "0.160.0"
resolved "https://registry.yarnpkg.com/@types/three/-/three-0.160.0.tgz#7915a97e0a14ccaa9ccbb9f190c5730b04a23075"
integrity sha512-jWlbUBovicUKaOYxzgkLlhkiEQJkhCVvg4W2IYD2trqD2om3VK4DGLpHH5zQHNr7RweZK/5re/4IVhbhvxbV9w==
"@types/three@^0.163.0":
version "0.163.0"
resolved "https://registry.yarnpkg.com/@types/three/-/three-0.163.0.tgz#96f5440fcd39452d2c84dfe0c9b7a9cf0247b9e6"
integrity sha512-uIdDhsXRpQiBUkflBS/i1l3JX14fW6Ot9csed60nfbZNXHDTRsnV2xnTVwXcgbvTiboAR4IW+t+lTL5f1rqIqA==
dependencies:
"@tweenjs/tween.js" "~23.1.1"
"@types/stats.js" "*"
"@types/webxr" "*"
fflate "~0.6.10"
fflate "~0.8.2"
meshoptimizer "~0.18.1"
"@types/ua-parser-js@^0.7.39":
version "0.7.39"
resolved "https://registry.yarnpkg.com/@types/ua-parser-js/-/ua-parser-js-0.7.39.tgz#832c58e460c9435e4e34bb866e85e9146e12cdbb"
integrity sha512-P/oDfpofrdtF5xw433SPALpdSchtJmY7nsJItf8h3KXqOslkbySh8zq4dSWXH2oTjRvJ5PczVEoCZPow6GicLg==
"@types/uuid@^9.0.8":
version "9.0.8"
resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-9.0.8.tgz#7545ba4fc3c003d6c756f651f3bf163d8f0f29ba"
@ -2529,20 +2548,13 @@
resolved "https://registry.yarnpkg.com/@types/wicg-file-system-access/-/wicg-file-system-access-2023.10.5.tgz#14b3c25eb4d914b5734795bdea71da229f918b9d"
integrity sha512-e9kZO9kCdLqT2h9Tw38oGv9UNzBBWaR1MzuAavxPcsV/7FJ3tWbU6RI3uB+yKIDPGLkGVbplS52ub0AcRLvrhA==
"@types/ws@^8.5.3":
"@types/ws@^8.5.10", "@types/ws@^8.5.3":
version "8.5.10"
resolved "https://registry.yarnpkg.com/@types/ws/-/ws-8.5.10.tgz#4acfb517970853fa6574a3a6886791d04a396787"
integrity sha512-vmQSUcfalpIq0R9q7uTo2lXs6eGIpt9wtnLdMv9LVpIjCA/+ufZRozlVoVelIYixx1ugCBKDhn89vnsEGOCx9A==
dependencies:
"@types/node" "*"
"@types/ws@^8.5.5":
version "8.5.5"
resolved "https://registry.yarnpkg.com/@types/ws/-/ws-8.5.5.tgz#af587964aa06682702ee6dcbc7be41a80e4b28eb"
integrity sha512-lwhs8hktwxSjf9UaZ9tG5M03PGogvFaH8gUgLNbN9HKIg0dvv6q+gkSuJ8HN4/VbyxkuLzCjlN7GquQ0gUJfIg==
dependencies:
"@types/node" "*"
"@types/yargs-parser@*":
version "21.0.3"
resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-21.0.3.tgz#815e30b786d2e8f0dcd85fd5bcf5e1a04d008f15"
@ -3505,6 +3517,11 @@ base16@^1.0.0:
resolved "https://registry.yarnpkg.com/base16/-/base16-1.0.0.tgz#e297f60d7ec1014a7a971a39ebc8a98c0b681e70"
integrity sha512-pNdYkNPiJUnEhnfXV56+sQy8+AaPcG3POZAUnwr4EeqCUZFz4u2PePbo3e5Gj4ziYPCWGUZT9RHisvJKnwFuBQ==
base64-arraybuffer@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz#1c37589a7c4b0746e34bd1feb951da2df01c1bdc"
integrity sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ==
base64-js@^1.3.1:
version "1.5.1"
resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a"
@ -4022,6 +4039,13 @@ crypto-js@^4.2.0:
resolved "https://registry.yarnpkg.com/crypto-js/-/crypto-js-4.2.0.tgz#4d931639ecdfd12ff80e8186dba6af2c2e856631"
integrity sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==
css-line-break@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/css-line-break/-/css-line-break-2.1.0.tgz#bfef660dfa6f5397ea54116bb3cb4873edbc4fa0"
integrity sha512-FHcKFCZcAha3LwfVBhCQbW2nCNbkZXn7KVUJcsT5/P8YmfsVja0FMPJr0B903j/E69HUphKiV9iQArX8SDYA4w==
dependencies:
utrie "^1.0.2"
css-shorthand-properties@^1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/css-shorthand-properties/-/css-shorthand-properties-1.1.1.tgz#1c808e63553c283f289f2dd56fcee8f3337bd935"
@ -4993,10 +5017,10 @@ fetch-blob@^3.1.2, fetch-blob@^3.1.4:
node-domexception "^1.0.0"
web-streams-polyfill "^3.0.3"
fflate@~0.6.10:
version "0.6.10"
resolved "https://registry.yarnpkg.com/fflate/-/fflate-0.6.10.tgz#5f40f9659205936a2d18abf88b2e7781662b6d43"
integrity sha512-IQrh3lEPM93wVCEczc9SaAOvkmcoQn/G8Bo1e8ZPlY3X3bnAxWaBdvTdvM1hP62iZp0BXWDy4vTAy4fF0+Dlpg==
fflate@~0.8.2:
version "0.8.2"
resolved "https://registry.yarnpkg.com/fflate/-/fflate-0.8.2.tgz#fc8631f5347812ad6028bbe4a2308b2792aa1dea"
integrity sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==
figures@^5.0.0:
version "5.0.0"
@ -5110,11 +5134,12 @@ formdata-polyfill@^4.0.10:
dependencies:
fetch-blob "^3.1.2"
formik@^2.4.3:
version "2.4.3"
resolved "https://registry.yarnpkg.com/formik/-/formik-2.4.3.tgz#6020e85eb3e3e8415b3b19d6f4f65793ab754b24"
integrity sha512-2Dy79Szw3zlXmZiokUdKsn+n1ow4G8hRrC/n92cOWHNTWXCRpQXlyvz6HcjW7aSQZrldytvDOavYjhfmDnUq8Q==
formik@^2.4.5:
version "2.4.5"
resolved "https://registry.yarnpkg.com/formik/-/formik-2.4.5.tgz#f899b5b7a6f103a8fabb679823e8fafc7e0ee1b4"
integrity sha512-Gxlht0TD3vVdzMDHwkiNZqJ7Mvg77xQNfmBRrNtvzcHZs72TJppSTDKHpImCMJZwcWPBJ8jSQQ95GJzXFf1nAQ==
dependencies:
"@types/hoist-non-react-statics" "^3.3.1"
deepmerge "^2.1.1"
hoist-non-react-statics "^3.3.0"
lodash "^4.17.21"
@ -5608,6 +5633,14 @@ html-encoding-sniffer@^3.0.0:
dependencies:
whatwg-encoding "^2.0.0"
html2canvas-pro@^1.4.3:
version "1.4.3"
resolved "https://registry.yarnpkg.com/html2canvas-pro/-/html2canvas-pro-1.4.3.tgz#100124e2d17d4de483700ce03176d7447e90d49f"
integrity sha512-RB36SrUGxT9PTjImC7BsGxTinaI3y8cEne76ACdw+E7nRmeJ0jgDntxUP15B9Q9AM2mvEPN6SZo6zmkzwk8HKg==
dependencies:
css-line-break "^2.1.0"
text-segmentation "^1.0.3"
http-cache-semantics@^4.1.1:
version "4.1.1"
resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz#abe02fcb2985460bf0323be664436ec3476a6d5a"
@ -7231,17 +7264,17 @@ pkg-types@^1.0.3:
mlly "^1.2.0"
pathe "^1.1.0"
playwright-core@1.39.0:
version "1.39.0"
resolved "https://registry.yarnpkg.com/playwright-core/-/playwright-core-1.39.0.tgz#efeaea754af4fb170d11845b8da30b2323287c63"
integrity sha512-+k4pdZgs1qiM+OUkSjx96YiKsXsmb59evFoqv8SKO067qBA+Z2s/dCzJij/ZhdQcs2zlTAgRKfeiiLm8PQ2qvw==
playwright-core@1.43.0:
version "1.43.0"
resolved "https://registry.yarnpkg.com/playwright-core/-/playwright-core-1.43.0.tgz#d8079acb653abebb0b63062e432479647a4e1271"
integrity sha512-iWFjyBUH97+pUFiyTqSLd8cDMMOS0r2ZYz2qEsPjH8/bX++sbIJT35MSwKnp1r/OQBAqC5XO99xFbJ9XClhf4w==
playwright@1.39.0:
version "1.39.0"
resolved "https://registry.yarnpkg.com/playwright/-/playwright-1.39.0.tgz#184c81cd6478f8da28bcd9e60e94fcebf566e077"
integrity sha512-naE5QT11uC/Oiq0BwZ50gDmy8c8WLPRTEWuSSFVG2egBka/1qMoSqYQcROMT9zLwJ86oPofcTH2jBY/5wWOgIw==
playwright@1.43.0:
version "1.43.0"
resolved "https://registry.yarnpkg.com/playwright/-/playwright-1.43.0.tgz#2c2efd4ee2a25defd8c24c98ccb342bdd9d435f5"
integrity sha512-SiOKHbVjTSf6wHuGCbqrEyzlm6qvXcv7mENP+OZon1I07brfZLGdfWV0l/efAzVx7TF3Z45ov1gPEkku9q25YQ==
dependencies:
playwright-core "1.39.0"
playwright-core "1.43.0"
optionalDependencies:
fsevents "2.3.2"
@ -8327,10 +8360,10 @@ supports-preserve-symlinks-flag@^1.0.0:
resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09"
integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==
swr@^2.2.2:
version "2.2.2"
resolved "https://registry.yarnpkg.com/swr/-/swr-2.2.2.tgz#abcb1f9c97e10527789884169d58b878472d4c98"
integrity sha512-CbR41AoMD4TQBQw9ic3GTXspgfM9Y8Mdhb5Ob4uIKXhWqnRLItwA5fpGvB7SmSw3+zEjb0PdhiEumtUvYoQ+bQ==
swr@^2.2.5:
version "2.2.5"
resolved "https://registry.yarnpkg.com/swr/-/swr-2.2.5.tgz#063eea0e9939f947227d5ca760cc53696f46446b"
integrity sha512-QtxqyclFeAsxEUeZIYmsaQ0UjimSq1RZ9Un7I68/0ClKK/U3LoyQunwkQfJZr2fc22DfIXLNDc2wFyTEikCUpg==
dependencies:
client-only "^0.0.1"
use-sync-external-store "^1.2.0"
@ -8393,9 +8426,9 @@ tar-stream@^3.0.0, tar-stream@^3.1.5:
streamx "^2.15.0"
tar@^6.1.11:
version "6.1.15"
resolved "https://registry.yarnpkg.com/tar/-/tar-6.1.15.tgz#c9738b0b98845a3b344d334b8fa3041aaba53a69"
integrity sha512-/zKt9UyngnxIT/EAGYuxaMYgOIJiP81ab9ZfkILq4oNLPFX50qyYmu7jRj9qeXoxmJHjGlbH0+cm2uy1WCs10A==
version "6.2.1"
resolved "https://registry.yarnpkg.com/tar/-/tar-6.2.1.tgz#717549c541bc3c2af15751bea94b1dd068d4b03a"
integrity sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==
dependencies:
chownr "^2.0.0"
fs-minipass "^2.0.0"
@ -8404,6 +8437,13 @@ tar@^6.1.11:
mkdirp "^1.0.3"
yallist "^4.0.0"
text-segmentation@^1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/text-segmentation/-/text-segmentation-1.0.3.tgz#52a388159efffe746b24a63ba311b6ac9f2d7943"
integrity sha512-iOiPUo/BGnZ6+54OsWxZidGCsdU8YbE4PSpdPinp7DeMtUJNJBoJ/ouUSTJjHkh1KntHaltHl/gDs2FC4i5+Nw==
dependencies:
utrie "^1.0.2"
text-table@^0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4"
@ -8423,10 +8463,10 @@ thenify-all@^1.0.0:
dependencies:
any-promise "^1.0.0"
three@^0.160.0:
version "0.160.0"
resolved "https://registry.yarnpkg.com/three/-/three-0.160.0.tgz#cd1e4dbd01aee0719280a9086d75545db52b7a8f"
integrity sha512-DLU8lc0zNIPkM7rH5/e1Ks1Z8tWCGRq6g8mPowdDJpw1CFBJMU7UoJjC6PefXW7z//SSl0b2+GCw14LB+uDhng==
three@^0.163.0:
version "0.163.0"
resolved "https://registry.yarnpkg.com/three/-/three-0.163.0.tgz#cbfefbfd64a1353ab7cc8bf0fc396ddca1875a49"
integrity sha512-HlMgCb2TF/dTLRtknBnjUTsR8FsDqBY43itYop2+Zg822I+Kd0Ua2vs8CvfBVefXkBdNDrLMoRTGCIIpfCuDew==
through@^2.3.8:
version "2.3.8"
@ -8531,12 +8571,7 @@ tslib@^1.8.1:
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00"
integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==
tslib@^2.0.0:
version "2.6.1"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.1.tgz#fd8c9a0ff42590b25703c0acb3de3d3f4ede0410"
integrity sha512-t0hLfiEKfMUoqhG+U1oid7Pva4bbDPHYfJNiB7BiIjRkj1pyC++4N3huJfqY6aRH6VTB0rvtzQwjM4K6qpfOig==
tslib@^2.0.1, tslib@^2.1.0, tslib@^2.3.0:
tslib@^2.0.0, tslib@^2.0.1, tslib@^2.1.0, tslib@^2.3.0:
version "2.6.2"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.2.tgz#703ac29425e7b37cd6fd456e92404d46d1f3e4ae"
integrity sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==
@ -8644,6 +8679,11 @@ ua-parser-js@^1.0.35:
resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-1.0.35.tgz#c4ef44343bc3db0a3cbefdf21822f1b1fc1ab011"
integrity sha512-fKnGuqmTBnIE+/KXSzCn4db8RTigUzw1AN0DmdU6hJovUTbYJKyqj+8Mt1c4VfRDnOVJnENmfYkIPZ946UrSAA==
ua-parser-js@^1.0.37:
version "1.0.37"
resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-1.0.37.tgz#b5dc7b163a5c1f0c510b08446aed4da92c46373f"
integrity sha512-bhTyI94tZofjo+Dn8SN6Zv8nBDvyXTymAdM3LDI/0IboIUwTu1rEhW7v2TfiVsoYWgkQ4kOVqnI8APUFbIQIFQ==
ufo@^1.1.2:
version "1.2.0"
resolved "https://registry.yarnpkg.com/ufo/-/ufo-1.2.0.tgz#28d127a087a46729133fdc89cb1358508b3f80ba"
@ -8791,6 +8831,13 @@ util@^0.12.5:
is-typed-array "^1.1.3"
which-typed-array "^1.1.2"
utrie@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/utrie/-/utrie-1.0.2.tgz#d42fe44de9bc0119c25de7f564a6ed1b2c87a645"
integrity sha512-1MLa5ouZiOmQzUbjbu9VmjLzn1QLXBhwpUa7kdLUQK+KQ5KA9I1vk5U4YHe/X2Ch7PYnJfWuWT+VbuxbGwljhw==
dependencies:
base64-arraybuffer "^1.0.2"
uuid@^9.0.1:
version "9.0.1"
resolved "https://registry.yarnpkg.com/uuid/-/uuid-9.0.1.tgz#e188d4c8853cc722220392c424cd637f32293f30"
@ -9193,7 +9240,7 @@ wrappy@1:
resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==
ws@8.13.0, ws@^8.13.0:
ws@8.13.0:
version "8.13.0"
resolved "https://registry.yarnpkg.com/ws/-/ws-8.13.0.tgz#9a9fb92f93cf41512a0735c8f4dd09b8a1211cd0"
integrity sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA==
@ -9203,10 +9250,10 @@ ws@^7.0.0:
resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.9.tgz#54fa7db29f4c7cec68b1ddd3a89de099942bb591"
integrity sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==
ws@^8.8.0:
version "8.14.2"
resolved "https://registry.yarnpkg.com/ws/-/ws-8.14.2.tgz#6c249a806eb2db7a20d26d51e7709eab7b2e6c7f"
integrity sha512-wEBG1ftX4jcglPxgFCMJmZ2PLtSbJ2Peg6TmpJFTbe9GZYOQCDPdMYu/Tm0/bGZkw8paZnJY45J4K2PZrLYq8g==
ws@^8.16.0, ws@^8.8.0:
version "8.16.0"
resolved "https://registry.yarnpkg.com/ws/-/ws-8.16.0.tgz#d1cd774f36fbc07165066a60e40323eab6446fd4"
integrity sha512-HS0c//TP7Ina87TfiPUz1rQzMhHrl/SG2guqRcTOIUYD2q8uhUdNHZYJUaQ8aTGPzCh+c6oawMKW35nFl1dxyQ==
"xstate-beta@npm:xstate@beta":
version "5.0.0-beta.54"
@ -9307,10 +9354,10 @@ yargs@17.7.2, yargs@^17.7.2:
y18n "^5.0.5"
yargs-parser "^21.1.1"
yarn@^1.22.19:
version "1.22.19"
resolved "https://registry.yarnpkg.com/yarn/-/yarn-1.22.19.tgz#4ba7fc5c6e704fce2066ecbfb0b0d8976fe62447"
integrity sha512-/0V5q0WbslqnwP91tirOvldvYISzaqhClxzyUKXYxs07yUILIs5jx/k6CFe8bvKSkds5w+eiOqta39Wk3WxdcQ==
yarn@^1.22.22:
version "1.22.22"
resolved "https://registry.yarnpkg.com/yarn/-/yarn-1.22.22.tgz#ac34549e6aa8e7ead463a7407e1c7390f61a6610"
integrity sha512-prL3kGtyG7o9Z9Sv8IPfBNrWTDmXB4Qbes8A9rEzt6wkJV8mUvoirjU0Mp3GGAU06Y0XQyA3/2/RQFVuK7MTfg==
yauzl@^2.10.0:
version "2.10.0"