Compare commits

..

1 Commits

Author SHA1 Message Date
3e0c44e689 add segment proof of concept 2024-02-28 20:20:00 +11:00
65 changed files with 561 additions and 1490 deletions

View File

@ -3,3 +3,4 @@ VITE_KC_API_BASE_URL=https://api.dev.zoo.dev
VITE_KC_SITE_BASE_URL=https://dev.zoo.dev
VITE_KC_SKIP_AUTH=false
VITE_KC_CONNECTION_TIMEOUT_MS=5000
VITE_KC_SENTRY_DSN=

View File

@ -3,3 +3,4 @@ VITE_KC_API_BASE_URL=https://api.zoo.dev
VITE_KC_SITE_BASE_URL=https://zoo.dev
VITE_KC_SKIP_AUTH=false
VITE_KC_CONNECTION_TIMEOUT_MS=15000
VITE_KC_SENTRY_DSN=

View File

@ -40,20 +40,6 @@ jobs:
run: |
sudo apt-get update
sudo apt-get install -y libgtk-3-dev libwebkit2gtk-4.0-dev libappindicator3-dev librsvg2-dev patchelf
- name: Install vector
run: |
curl --proto '=https' --tlsv1.2 -sSfL https://sh.vector.dev > /tmp/vector.sh
chmod +x /tmp/vector.sh
/tmp/vector.sh -y -no-modify-path
mkdir -p /tmp/vector
cp .github/workflows/vector.toml /tmp/vector.toml
sed -i "s#GITHUB_WORKFLOW#${GITHUB_WORKFLOW}#g" /tmp/vector.toml
sed -i "s#GITHUB_REPOSITORY#${GITHUB_REPOSITORY}#g" /tmp/vector.toml
sed -i "s#GITHUB_SHA#${GITHUB_SHA}#g" /tmp/vector.toml
sed -i "s#GITHUB_REF_NAME#${GITHUB_REF_NAME}#g" /tmp/vector.toml
sed -i "s#GH_ACTIONS_AXIOM_TOKEN#${{secrets.GH_ACTIONS_AXIOM_TOKEN}}#g" /tmp/vector.toml
cat /tmp/vector.toml
${HOME}/.vector/bin/vector --config /tmp/vector.toml &
- uses: taiki-e/install-action@cargo-llvm-cov
- uses: taiki-e/install-action@nextest
- name: Rust Cache
@ -62,7 +48,7 @@ jobs:
shell: bash
run: |-
cd "${{ matrix.dir }}"
cargo nextest run --workspace --no-fail-fast -P ci 2>&1 | tee /tmp/github-actions.log
cargo nextest run --workspace --no-fail-fast -P ci
env:
KITTYCAD_API_TOKEN: ${{secrets.KITTYCAD_API_TOKEN}}
RUST_MIN_STACK: 10485760000

View File

@ -336,7 +336,7 @@ jobs:
cat last_download.json
- name: Authenticate to Google Cloud
uses: 'google-github-actions/auth@v2.1.2'
uses: 'google-github-actions/auth@v2.1.1'
with:
credentials_json: '${{ secrets.GOOGLE_CLOUD_DL_SA }}'

View File

@ -4,11 +4,6 @@ on:
branches: [ main ]
pull_request:
branches: [ main ]
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
jobs:
playwright-ubuntu:
timeout-minutes: 60

View File

@ -1,21 +0,0 @@
[sources.github-actions-file]
type = "file"
data_dir = "/tmp/vector"
include = ["/tmp/github-actions.log"]
# Modify the logs to include the action name.
[transforms.add-action-name]
type = "remap"
inputs = [ "github-actions-file" ]
source = '''
.action = "GITHUB_WORKFLOW"
.repo = "GITHUB_REPOSITORY"
.sha = "GITHUB_SHA"
.ref = "GITHUB_REF_NAME"
'''
[sinks.axiom]
type = "axiom"
inputs = ["add-action-name"]
token = "GH_ACTIONS_AXIOM_TOKEN"
dataset = "github-actions"

3
.gitignore vendored
View File

@ -33,7 +33,6 @@ src/wasm-lib/bindings
src/wasm-lib/kcl/bindings
public/wasm_lib_bg.wasm
src/wasm-lib/lcov.info
src/wasm-lib/grackle/*.test.json
e2e/playwright/playwright-secrets.env
e2e/playwright/temp1.png
@ -55,5 +54,3 @@ e2e/playwright/export-snapshots/*embedded.gltf
## generated files
src/**/*.typegen.ts
src/wasm-lib/grackle/stdlib_cube_partial.json

View File

@ -8,6 +8,10 @@ once fixed in engine will just start working here with no language changes.
model for that sketch and its underlying 3D object.
If you see a red line around your model, it means this is happening.
- **Patterns**: If you try and pass a pattern to `hole` currently only the first
item in the pattern is being subtracted. This is an engine bug that is being
worked on.
- **Import**: Right now you can import a file, even if that file has brep data
you cannot edit it, after v1, the engine will account for this. You also cannot
currently move or transform the imported objects at all, once we have assemblies

View File

@ -32506,14 +32506,14 @@
"format": "double"
},
"axis": {
"description": "The axis around which to make the pattern. This is a 2D vector.",
"description": "The axis around which to make the pattern. This is a 3D vector.",
"type": "array",
"items": {
"type": "number",
"format": "double"
},
"maxItems": 2,
"minItems": 2
"maxItems": 3,
"minItems": 3
},
"center": {
"description": "The center about which to make th pattern. This is a 3D vector.",
@ -35128,14 +35128,14 @@
],
"properties": {
"axis": {
"description": "The axis of the pattern. This is a 2D vector.",
"description": "The axis of the pattern. This is a 3D vector.",
"type": "array",
"items": {
"type": "number",
"format": "double"
},
"maxItems": 2,
"minItems": 2
"maxItems": 3,
"minItems": 3
},
"distance": {
"description": "The distance between each repetition. This can also be referred to as spacing.",

View File

@ -6086,8 +6086,8 @@ patternCircular(data: CircularPatternData, geometry: Geometry) -> Geometries
{
// The arc angle (in degrees) to place the repetitions. Must be greater than 0.
arcDegrees: number,
// The axis around which to make the pattern. This is a 2D vector.
axis: [number, 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.
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.
@ -6355,8 +6355,8 @@ patternLinear(data: LinearPatternData, geometry: Geometry) -> Geometries
* `data`: `LinearPatternData` - Data for a linear pattern.
```
{
// The axis of the pattern. This is a 2D vector.
axis: [number, number],
// The axis of the pattern. This is a 3D vector.
axis: [number, number, number],
// The distance between each repetition. This can also be referred to as spacing.
distance: 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.

View File

@ -3,7 +3,6 @@ import { secrets } from './secrets'
import { getUtils } from './test-utils'
import waitOn from 'wait-on'
import { Themes } from '../../src/lib/theme'
import { roundOff } from 'lib/utils'
/*
debug helper: unfortunately we do rely on exact coord mouse clicks in a few places
@ -16,9 +15,9 @@ document.addEventListener('mousemove', (e) =>
*/
const commonPoints = {
startAt: '[0.93, -1.26]',
num1: 0.95,
num2: 1.88,
startAt: '[26.38, -35.59]',
num1: 26.63,
num2: 53.01,
}
test.beforeEach(async ({ context, page }) => {
@ -102,13 +101,13 @@ test('Basic sketch', async ({ page }) => {
.toHaveText(`const part001 = startSketchOn('-XZ')
|> startProfileAt(${commonPoints.startAt}, %)
|> line([${commonPoints.num1}, 0], %)
|> line([0, ${commonPoints.num1 - 0.01}], %)`)
|> line([0, ${commonPoints.num1}], %)`)
await page.mouse.click(startXPx, 500 - PUR * 20)
await expect(page.locator('.cm-content'))
.toHaveText(`const part001 = startSketchOn('-XZ')
|> startProfileAt(${commonPoints.startAt}, %)
|> line([${commonPoints.num1}, 0], %)
|> line([0, ${commonPoints.num1 - 0.01}], %)
|> line([0, ${commonPoints.num1}], %)
|> line([-${commonPoints.num2}, 0], %)`)
// deselect line tool
@ -133,7 +132,7 @@ test('Basic sketch', async ({ page }) => {
.toHaveText(`const part001 = startSketchOn('-XZ')
|> startProfileAt(${commonPoints.startAt}, %)
|> line({ to: [${commonPoints.num1}, 0], tag: 'seg01' }, %)
|> line([0, ${commonPoints.num1 - 0.01}], %)
|> line([0, ${commonPoints.num1}], %)
|> angledLine([180, segLen('seg01', %)], %)`)
})
@ -285,9 +284,10 @@ test('Can create sketches on all planes and their back sides', async ({
}) => {
await u.openDebugPanel()
await u.updateCamPosition(viewCmd)
await u.clearCommandLogs()
await page.getByRole('button', { name: 'Start Sketch' }).click()
await u.updateCamPosition(viewCmd)
await u.closeDebugPanel()
await page.mouse.click(clickCoords.x, clickCoords.y)
@ -315,7 +315,7 @@ test('Can create sketches on all planes and their back sides', async ({
const codeTemplate = (
plane = 'XY'
) => `const part001 = startSketchOn('${plane}')
|> startProfileAt([1.14, -1.54], %)`
|> startProfileAt([32.13, -43.34], %)`
await TestSinglePlane({
viewCmd: camPos,
expectedCode: codeTemplate('XY'),
@ -325,7 +325,7 @@ test('Can create sketches on all planes and their back sides', async ({
await TestSinglePlane({
viewCmd: camPos,
expectedCode: codeTemplate('YZ'),
clickCoords: { x: 700, y: 250 }, // green plane
clickCoords: { x: 700, y: 300 }, // green plane
})
await TestSinglePlane({
viewCmd: camPos,
@ -386,16 +386,12 @@ test('Auto complete works', async ({ page }) => {
await page.keyboard.press('ArrowDown')
await page.keyboard.press('ArrowDown')
await page.keyboard.press('Enter')
// finish line with comment
await page.keyboard.type('(5, %) // lin')
await page.waitForTimeout(100)
// there shouldn't be any auto complete options for 'lin' in the comment
await expect(page.locator('.cm-completionLabel')).not.toBeVisible()
await page.keyboard.type('(5, %)')
await expect(page.locator('.cm-content'))
.toHaveText(`const part001 = startSketchOn('XY')
|> startProfileAt([0,0], %)
|> xLine(5, %) // lin`)
|> xLine(5, %)`)
})
// Onboarding tests
@ -492,13 +488,13 @@ test('Selections work on fresh and edited sketch', async ({ page }) => {
.toHaveText(`const part001 = startSketchOn('-XZ')
|> startProfileAt(${commonPoints.startAt}, %)
|> line([${commonPoints.num1}, 0], %)
|> line([0, ${commonPoints.num1 - 0.01}], %)`)
|> line([0, ${commonPoints.num1}], %)`)
await page.mouse.click(startXPx, 500 - PUR * 20)
await expect(page.locator('.cm-content'))
.toHaveText(`const part001 = startSketchOn('-XZ')
|> startProfileAt(${commonPoints.startAt}, %)
|> line([${commonPoints.num1}, 0], %)
|> line([0, ${commonPoints.num1 - 0.01}], %)
|> line([0, ${commonPoints.num1}], %)
|> line([-${commonPoints.num2}, 0], %)`)
// deselect line tool
@ -703,8 +699,6 @@ test('Can extrude from the command bar', async ({ page, context }) => {
).toBeDisabled()
await page.keyboard.press('Enter')
await expect(page.getByText('Confirm Extrude')).toBeVisible()
// Check that the code was updated
await page.keyboard.press('Enter')
// Unfortunately this indentation seems to matter for the test
@ -771,12 +765,12 @@ test('Can add multiple sketches', async ({ page }) => {
.toHaveText(`const part001 = startSketchOn('-XZ')
|> startProfileAt(${commonPoints.startAt}, %)
|> line([${commonPoints.num1}, 0], %)
|> line([0, ${commonPoints.num1 - 0.01}], %)`)
|> line([0, ${commonPoints.num1}], %)`)
await page.mouse.click(startXPx, 500 - PUR * 20)
const finalCodeFirstSketch = `const part001 = startSketchOn('-XZ')
|> startProfileAt(${commonPoints.startAt}, %)
|> line([${commonPoints.num1}, 0], %)
|> line([0, ${commonPoints.num1 - 0.01}], %)
|> line([0, ${commonPoints.num1}], %)
|> line([-${commonPoints.num2}, 0], %)`
await expect(page.locator('.cm-content')).toHaveText(finalCodeFirstSketch)
@ -799,7 +793,7 @@ test('Can add multiple sketches', async ({ page }) => {
await u.clearAndCloseDebugPanel()
await page.mouse.click(startXPx + PUR * 10, 500 - PUR * 10)
const startAt2 = '[0.93,-1.25]'
const startAt2 = '[26.23, -35.39]'
await expect(
(await page.locator('.cm-content').innerText()).replace(/\s/g, '')
).toBe(
@ -813,7 +807,7 @@ const part002 = startSketchOn('XY')
await page.mouse.click(startXPx + PUR * 20, 500 - PUR * 10)
await page.waitForTimeout(100)
const num2 = 0.94
const num2 = 26.48
await expect(
(await page.locator('.cm-content').innerText()).replace(/\s/g, '')
).toBe(
@ -831,7 +825,7 @@ const part002 = startSketchOn('XY')
const part002 = startSketchOn('XY')
|> startProfileAt(${startAt2}, %)
|> line([${num2}, 0], %)
|> line([0, ${roundOff(num2 - 0.01)}], %)`.replace(/\s/g, '')
|> line([0, ${num2}], %)`.replace(/\s/g, '')
)
await page.mouse.click(startXPx, 500 - PUR * 20)
await expect(
@ -841,8 +835,8 @@ const part002 = startSketchOn('XY')
const part002 = startSketchOn('XY')
|> startProfileAt(${startAt2}, %)
|> line([${num2}, 0], %)
|> line([0, ${roundOff(num2 - 0.01)}], %)
|> line([-1.87, 0], %)`.replace(/\s/g, '')
|> line([0, ${num2}], %)
|> line([-52.71, 0], %)`.replace(/\s/g, '')
)
})
@ -936,7 +930,7 @@ fn yohey = (pos) => {
|> line([-15.79, 17.08], %)
return ''
}
yohey([15.79, -34.6])
`
)

View File

@ -435,23 +435,7 @@ test('extrude on each default plane should be stable', async ({
await runSnapshotsForOtherPlanes('-YZ')
})
test('Draft segments should look right', async ({ page, context }) => {
await context.addInitScript(async () => {
localStorage.setItem(
'SETTINGS_PERSIST_KEY',
JSON.stringify({
baseUnit: 'in',
cameraControls: 'KittyCAD',
defaultDirectory: '',
defaultProjectName: 'project-$nnn',
onboardingStatus: 'dismissed',
showDebugPanel: true,
textWrapping: 'On',
theme: 'system',
unitSystem: 'imperial',
})
)
})
test('Draft segments should look right', async ({ page }) => {
const u = getUtils(page)
await page.setViewportSize({ width: 1200, height: 500 })
const PUR = 400 / 37.5 //pixeltoUnitRatio
@ -484,7 +468,7 @@ test('Draft segments should look right', async ({ page, context }) => {
await page.mouse.click(startXPx + PUR * 10, 500 - PUR * 10)
await expect(page.locator('.cm-content'))
.toHaveText(`const part001 = startSketchOn('-XZ')
|> startProfileAt([0.93, -1.26], %)`)
|> startProfileAt(${commonPoints.startAt}, %)`)
await page.waitForTimeout(100)
await u.closeDebugPanel()
@ -498,8 +482,8 @@ test('Draft segments should look right', async ({ page, context }) => {
await expect(page.locator('.cm-content'))
.toHaveText(`const part001 = startSketchOn('-XZ')
|> startProfileAt([0.93, -1.26], %)
|> line([0.95, 0], %)`)
|> startProfileAt(${commonPoints.startAt}, %)
|> line([${commonPoints.num1}, 0], %)`)
await page.getByRole('button', { name: 'Tangential Arc' }).click()
@ -509,195 +493,3 @@ test('Draft segments should look right', async ({ page, context }) => {
maxDiffPixels: 100,
})
})
test('Client side scene scale should match engine scale inch', async ({
page,
context,
}) => {
await context.addInitScript(async () => {
localStorage.setItem(
'SETTINGS_PERSIST_KEY',
JSON.stringify({
baseUnit: 'in',
cameraControls: 'KittyCAD',
defaultDirectory: '',
defaultProjectName: 'project-$nnn',
onboardingStatus: 'dismissed',
showDebugPanel: true,
textWrapping: 'On',
theme: 'system',
unitSystem: 'imperial',
})
)
})
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()
// 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)
await expect(page.locator('.cm-content')).toHaveText(
`const part001 = startSketchOn('-XZ')`
)
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([0.93, -1.26], %)`)
await page.waitForTimeout(100)
await u.closeDebugPanel()
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([0.93, -1.26], %)
|> line([0.95, 0], %)`)
await page.getByRole('button', { name: 'Tangential Arc' }).click()
await page.waitForTimeout(100)
await page.mouse.click(startXPx + PUR * 30, 500 - PUR * 20)
await expect(page.locator('.cm-content'))
.toHaveText(`const part001 = startSketchOn('-XZ')
|> startProfileAt([0.93, -1.26], %)
|> line([0.95, 0], %)
|> tangentialArcTo([2.82, -0.32], %)`)
// 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()
// 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('Client side scene scale should match engine scale mm', async ({
page,
context,
}) => {
await context.addInitScript(async () => {
localStorage.setItem(
'SETTINGS_PERSIST_KEY',
JSON.stringify({
baseUnit: 'mm',
cameraControls: 'KittyCAD',
defaultDirectory: '',
defaultProjectName: 'project-$nnn',
onboardingStatus: 'dismissed',
showDebugPanel: true,
textWrapping: 'On',
theme: 'system',
unitSystem: 'metric',
})
)
})
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()
// 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)
await expect(page.locator('.cm-content')).toHaveText(
`const part001 = startSketchOn('-XZ')`
)
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([0.93, -1.26], %)`)
await page.waitForTimeout(100)
await u.closeDebugPanel()
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([0.93, -1.26], %)
|> line([0.95, 0], %)`)
await page.getByRole('button', { name: 'Tangential Arc' }).click()
await page.waitForTimeout(100)
await page.mouse.click(startXPx + PUR * 30, 500 - PUR * 20)
await expect(page.locator('.cm-content'))
.toHaveText(`const part001 = startSketchOn('-XZ')
|> startProfileAt([0.93, -1.26], %)
|> line([0.95, 0], %)
|> tangentialArcTo([2.82, -0.32], %)`)
// 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()
// 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,
})
})

Binary file not shown.

Before

Width:  |  Height:  |  Size: 35 KiB

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 38 KiB

After

Width:  |  Height:  |  Size: 44 KiB

View File

@ -1,6 +1,6 @@
{
"name": "untitled-app",
"version": "0.15.4",
"version": "0.15.2",
"private": true,
"dependencies": {
"@codemirror/autocomplete": "^6.10.2",
@ -10,11 +10,12 @@
"@fortawesome/react-fontawesome": "^0.2.0",
"@headlessui/react": "^1.7.17",
"@headlessui/tailwindcss": "^0.2.0",
"@kittycad/lib": "^0.0.54",
"@kittycad/lib": "^0.0.53",
"@lezer/javascript": "^1.4.9",
"@open-rpc/client-js": "^1.8.1",
"@react-hook/resize-observer": "^1.2.6",
"@replit/codemirror-interact": "^6.3.0",
"@sentry/react": "^7.77.0",
"@tauri-apps/api": "^1.5.1",
"@testing-library/jest-dom": "^5.14.1",
"@testing-library/react": "^14.0.0",

22
src-tauri/Cargo.lock generated
View File

@ -67,9 +67,9 @@ dependencies = [
[[package]]
name = "anyhow"
version = "1.0.80"
version = "1.0.79"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5ad32ce52e4161730f7098c077cd2ed6229b5804ccf99e5366be1ab72a98b4e1"
checksum = "080e9890a082662b09c1ad45f567faeeb47f22b5fb23895fbe1e651e718e25ca"
[[package]]
name = "app"
@ -1664,9 +1664,9 @@ dependencies = [
[[package]]
name = "kittycad"
version = "0.2.58"
version = "0.2.53"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "049c3881ffbe77bf1c3a968372a246ce906eceb79f61cd0bc5fa229bec3504cb"
checksum = "a086e1a1bbddb3b38959c0f0ce6de6b3a3b7566e38e0b7d5fb101e91911beed4"
dependencies = [
"anyhow",
"async-trait",
@ -3235,9 +3235,9 @@ dependencies = [
[[package]]
name = "serde"
version = "1.0.197"
version = "1.0.196"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3fb1c873e1b9b056a4dc4c0c198b24c3ffa059243875552b2bd0933b1aee4ce2"
checksum = "870026e60fa08c69f064aa766c10f10b1d62db9ccd4d0abb206472bee0ce3b32"
dependencies = [
"serde_derive",
]
@ -3253,9 +3253,9 @@ dependencies = [
[[package]]
name = "serde_derive"
version = "1.0.197"
version = "1.0.196"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b"
checksum = "33c85360c95e7d137454dc81d9a4ed2b8efd8fbe19cee57357b32b9771fccb67"
dependencies = [
"proc-macro2",
"quote",
@ -3275,9 +3275,9 @@ dependencies = [
[[package]]
name = "serde_json"
version = "1.0.114"
version = "1.0.113"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c5f09b1bd632ef549eaa9f60a1f8de742bdbc698e6cee2095fc84dde5f549ae0"
checksum = "69801b70b1c3dac963ecb03a364ba0ceda9cf60c71cfe475e99864759c8b8a79"
dependencies = [
"itoa 1.0.6",
"ryu",
@ -3872,7 +3872,7 @@ dependencies = [
[[package]]
name = "tauri-plugin-fs-extra"
version = "0.0.0"
source = "git+https://github.com/tauri-apps/plugins-workspace?branch=v1#ed682dd96eb765e7cd3cdbc3cc64f794a0d6f9df"
source = "git+https://github.com/tauri-apps/plugins-workspace?branch=v1#01211ff0759d578e0e9ac8c98c31fdf09077eb34"
dependencies = [
"log",
"serde",

View File

@ -16,7 +16,7 @@ tauri-build = { version = "1.5.1", features = [] }
[dependencies]
anyhow = "1"
kittycad = "0.2.58"
kittycad = "0.2.53"
oauth2 = "4.4.2"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"

View File

@ -7,7 +7,7 @@
},
"package": {
"productName": "zoo-modeling-app",
"version": "0.15.4"
"version": "0.15.2"
},
"tauri": {
"allowlist": {

View File

@ -38,6 +38,8 @@ import { ContextFrom } from 'xstate'
import CommandBarProvider, {
CommandBar,
} from 'components/CommandBar/CommandBar'
import { TEST, VITE_KC_SENTRY_DSN } from './env'
import * as Sentry from '@sentry/react'
import ModelingMachineProvider from 'components/ModelingMachineProvider'
import { KclContextProvider, kclManager } from 'lang/KclSingleton'
import FileMachineProvider from 'components/FileMachineProvider'
@ -46,6 +48,38 @@ import { paths } from 'lib/paths'
import { IndexLoaderData, HomeLoaderData } from 'lib/types'
import { fileSystemManager } from 'lang/std/fileSystemManager'
if (VITE_KC_SENTRY_DSN && !TEST) {
Sentry.init({
dsn: VITE_KC_SENTRY_DSN,
// TODO(paultag): pass in the right env here.
// environment: "production",
integrations: [
new Sentry.BrowserTracing({
routingInstrumentation: Sentry.reactRouterV6Instrumentation(
useEffect,
useLocation,
useNavigationType,
createRoutesFromChildren,
matchRoutes
),
}),
new Sentry.Replay(),
],
// Set tracesSampleRate to 1.0 to capture 100%
// of transactions for performance monitoring.
tracesSampleRate: 1.0,
// TODO: Add in kittycad.io endpoints
tracePropagationTargets: ['localhost'],
// Capture Replay for 10% of all sessions,
// plus for 100% of sessions with an error
replaysSessionSampleRate: 0.1,
replaysOnErrorSampleRate: 1.0,
})
}
export const BROWSER_FILE_NAME = 'new'
type CreateBrowserRouterArg = Parameters<typeof createBrowserRouter>[0]

View File

@ -151,17 +151,6 @@ export class CameraControls {
get isPerspective() {
return this.camera instanceof PerspectiveCamera
}
private debounceTimer = 0
handleStart = () => {
if (this.debounceTimer) clearTimeout(this.debounceTimer)
this._isCamMovingCallback(true, false)
}
handleEnd = () => {
this.debounceTimer = setTimeout(() => {
this._isCamMovingCallback(false, false)
}, 400) as any as number
}
// reacts hooks into some of this singleton's properties
reactCameraProperties: ReactCameraProperties = {
@ -220,7 +209,6 @@ export class CameraControls {
this.onWindowResize()
this.update()
this._usePerspectiveCamera()
}
private _isCamMovingCallback: (isMoving: boolean, isTween: boolean) => void =
@ -254,7 +242,6 @@ export class CameraControls {
onMouseDown = (event: MouseEvent) => {
this.isDragging = true
this.mouseDownPosition.set(event.clientX, event.clientY)
this.handleStart()
}
onMouseMove = (event: MouseEvent) => {
@ -310,18 +297,15 @@ export class CameraControls {
onMouseUp = (event: MouseEvent) => {
this.isDragging = false
this.handleEnd()
}
onMouseWheel = (event: WheelEvent) => {
// Assume trackpad if the deltas are small and integers
this.handleStart()
const isTrackpad = Math.abs(event.deltaY) <= 1 || event.deltaY % 1 === 0
const zoomSpeed = isTrackpad ? 0.02 : 0.1 // Reduced zoom speed for trackpad
this.pendingZoom = this.pendingZoom ? this.pendingZoom : 1
this.pendingZoom *= 1 + (event.deltaY > 0 ? zoomSpeed : -zoomSpeed)
this.handleEnd()
}
useOrthographicCamera = () => {
@ -374,7 +358,7 @@ export class CameraControls {
return this.camera
}
_usePerspectiveCamera = () => {
usePerspectiveCamera = () => {
const { x: px, y: py, z: pz } = this.camera.position
const { x: qx, y: qy, z: qz, w: qw } = this.camera.quaternion
const zoom = this.camera.zoom
@ -390,17 +374,14 @@ export class CameraControls {
)
direction.normalize()
this.camera.position.copy(this.target).addScaledVector(direction, distance)
}
usePerspectiveCamera = () => {
this._usePerspectiveCamera()
engineCommandManager.sendSceneCommand({
type: 'modeling_cmd_req',
cmd_id: uuidv4(),
cmd: {
type: 'default_camera_set_perspective',
parameters: {
fov_y:
this.camera instanceof PerspectiveCamera ? this.camera.fov : 45,
fov_y: this.camera.fov,
...calculateNearFarFromFOV(this.lastPerspectiveFov),
},
},

View File

@ -13,6 +13,7 @@ import {
Quaternion,
Scene,
Shape,
SphereGeometry,
Vector2,
Vector3,
} from 'three'
@ -85,6 +86,7 @@ export const TANGENTIAL_ARC_TO_SEGMENT = 'tangential-arc-to-segment'
export const TANGENTIAL_ARC_TO_SEGMENT_BODY = 'tangential-arc-to-segment-body'
export const TANGENTIAL_ARC_TO__SEGMENT_DASH =
'tangential-arc-to-segment-body-dashed'
export const EXTRA_SEGMENT_HANDLE = 'extraSegmentHandle'
// This singleton Class is responsible for all of the things the user sees and interacts with.
// That mostly mean sketch elements.
@ -106,10 +108,9 @@ class SceneEntities {
Object.values(this.activeSegments).forEach((segment) => {
const factor =
(sceneInfra.camControls.camera instanceof OrthographicCamera
sceneInfra.camControls.camera instanceof OrthographicCamera
? orthoFactor
: perspScale(sceneInfra.camControls.camera, segment)) /
sceneInfra._baseUnitMultiplier
: perspScale(sceneInfra.camControls.camera, segment)
if (
segment.userData.from &&
segment.userData.to &&
@ -144,9 +145,9 @@ class SceneEntities {
? orthoFactor
: perspScale(sceneInfra.camControls.camera, this.axisGroup)
const x = this.axisGroup.getObjectByName(X_AXIS)
x?.scale.set(1, factor / sceneInfra._baseUnitMultiplier, 1)
x?.scale.set(1, factor, 1)
const y = this.axisGroup.getObjectByName(Y_AXIS)
y?.scale.set(factor / sceneInfra._baseUnitMultiplier, 1, 1)
y?.scale.set(factor, 1, 1)
}
}
@ -170,7 +171,6 @@ class SceneEntities {
this.scene.add(this.intersectionPlane)
}
createSketchAxis(sketchPathToNode: PathToNode) {
const orthoFactor = orthoScale(sceneInfra.camControls.camera)
const baseXColor = 0x000055
const baseYColor = 0x550000
const xAxisGeometry = new BoxGeometry(100000, 0.3, 0.01)
@ -210,14 +210,6 @@ class SceneEntities {
sceneInfra.camControls.target
)
gridHelper.scale.set(sceneScale, sceneScale, sceneScale)
const factor =
sceneInfra.camControls.camera instanceof OrthographicCamera
? orthoFactor
: perspScale(sceneInfra.camControls.camera, this.axisGroup)
xAxisMesh?.scale.set(1, factor / sceneInfra._baseUnitMultiplier, 1)
yAxisMesh?.scale.set(factor / sceneInfra._baseUnitMultiplier, 1, 1)
this.axisGroup.add(xAxisMesh, yAxisMesh, gridHelper)
this.currentSketchQuaternion &&
this.axisGroup.setRotationFromQuaternion(this.currentSketchQuaternion)
@ -249,12 +241,16 @@ class SceneEntities {
ast,
// is draft line assumes the last segment is a draft line, and mods it as the user moves the mouse
draftSegment,
skipListeners,
}: {
sketchPathToNode: PathToNode
ast?: Program
draftSegment?: DraftSegment
skipListeners?: boolean
}) {
sceneInfra.resetMouseListeners()
if (!skipListeners) {
sceneInfra.resetMouseListeners()
}
this.createIntersectionPlane()
const { truncatedAst, programMemoryOverride, variableDeclarationName } =
@ -289,10 +285,9 @@ class SceneEntities {
)
const orthoFactor = orthoScale(sceneInfra.camControls.camera)
const factor =
(sceneInfra.camControls.camera instanceof OrthographicCamera
sceneInfra.camControls.camera instanceof OrthographicCamera
? orthoFactor
: perspScale(sceneInfra.camControls.camera, dummy)) /
sceneInfra._baseUnitMultiplier
: perspScale(sceneInfra.camControls.camera, dummy)
sketchGroup.value.forEach((segment, index) => {
let segPathToNode = getNodePathFromSourceRange(
draftSegment ? truncatedAst : kclManager.ast,
@ -337,11 +332,60 @@ class SceneEntities {
this.currentSketchQuaternion
)
let addingNewSegmentStatus: 'nothing' | 'pending' | 'added' = 'nothing'
this.scene.add(group)
if (!draftSegment) {
if (!draftSegment && !skipListeners) {
sceneInfra.setCallbacks({
onDrag: (args) => {
onDragEnd: async () => {
if (addingNewSegmentStatus !== 'nothing') {
await this.tearDownSketch({ removeAxis: false })
this.setupSketch({ sketchPathToNode })
}
},
onDrag: async (args) => {
if (args.event.which !== 1) return
const group = getParentGroup(args.object, [EXTRA_SEGMENT_HANDLE])
if (group?.name === EXTRA_SEGMENT_HANDLE) {
const segGroup = getParentGroup(args.object)
const pathToNode: PathToNode = segGroup?.userData?.pathToNode
const pathToNodeIndex = pathToNode.findIndex(
(x) => x[1] === 'PipeExpression'
)
const pipeIndex = pathToNode[pathToNodeIndex + 1][0] as number
if (addingNewSegmentStatus === 'nothing') {
const prevSegment = sketchGroup.value[pipeIndex - 2]
const yo = addNewSketchLn({
node: kclManager.ast,
programMemory: kclManager.programMemory,
to: [args.intersection2d.x, args.intersection2d.y],
from: [prevSegment.from[0], prevSegment.from[1]],
fnName:
prevSegment.type === 'TangentialArcTo'
? 'tangentialArcTo'
: 'line',
pathToNode: pathToNode,
})
addingNewSegmentStatus = 'pending'
await kclManager.executeAstMock(yo.modifiedAst, {
updates: 'code',
})
await this.tearDownSketch({ removeAxis: false })
this.setupSketch({ sketchPathToNode, skipListeners: true })
addingNewSegmentStatus = 'added'
} else if (addingNewSegmentStatus === 'added') {
const pathToNodeForNewSegment = pathToNode.slice(
0,
pathToNodeIndex
)
pathToNodeForNewSegment.push([pipeIndex - 2, 'index'])
this.onDragSegment({
...args,
sketchPathToNode: pathToNodeForNewSegment,
})
}
return
}
this.onDragSegment({
...args,
sketchPathToNode,
@ -402,7 +446,7 @@ class SceneEntities {
}
},
})
} else {
} else if (draftSegment && !skipListeners) {
sceneInfra.setCallbacks({
onDrag: () => {},
onClick: async (args) => {
@ -415,7 +459,7 @@ class SceneEntities {
const isClosingSketch = compareVec2Epsilon2(
firstSeg.from,
[intersection2d.x, intersection2d.y],
0.5
1
)
let modifiedAst
if (isClosingSketch) {
@ -511,6 +555,7 @@ class SceneEntities {
variableDeclarationName: string
}
}) {
if (object.name === STRAIGHT_SEGMENT_BODY) return
const group = getParentGroup(object)
if (!group) return
const pathToNode: PathToNode = JSON.parse(
@ -578,10 +623,9 @@ class SceneEntities {
// const prevSegment = sketchGroup.slice(index - 1)[0]
const type = group?.userData?.type
const factor =
(sceneInfra.camControls.camera instanceof OrthographicCamera
sceneInfra.camControls.camera instanceof OrthographicCamera
? orthoFactor
: perspScale(sceneInfra.camControls.camera, group)) /
sceneInfra._baseUnitMultiplier
: perspScale(sceneInfra.camControls.camera, group)
if (type === TANGENTIAL_ARC_TO_SEGMENT) {
this.updateTangentialArcToSegment({
prevSegment: sketchGroup[index - 1],
@ -618,9 +662,7 @@ class SceneEntities {
group.userData.from = from
group.userData.to = to
group.userData.prevSegment = prevSegment
const arrowGroup = group.children.find(
(child) => child.userData.type === ARROWHEAD
) as Group
const arrowGroup = group.getObjectByName(ARROWHEAD) as Group
arrowGroup.position.set(to[0], to[1], 0)
@ -695,9 +737,7 @@ class SceneEntities {
const shape = new Shape()
shape.moveTo(0, -0.08 * scale)
shape.lineTo(0, 0.08 * scale) // The width of the line
const arrowGroup = group.children.find(
(child) => child.userData.type === ARROWHEAD
) as Group
const arrowGroup = group.getObjectByName(ARROWHEAD) as Group
arrowGroup.position.set(to[0], to[1], 0)
@ -710,6 +750,32 @@ class SceneEntities {
arrowGroup.quaternion.setFromUnitVectors(new Vector3(0, 1, 0), dir)
arrowGroup.scale.set(scale, scale, scale)
// TODO this should be created in setupSketch, not updateStraightSegment
// it should only be updated here
const extraSegmentHandle = (group.getObjectByName(EXTRA_SEGMENT_HANDLE) ||
(() => {
const mat = new MeshBasicMaterial({ color: 0xffffff })
const sphereMesh = new Mesh(new SphereGeometry(0.6, 12, 12), mat)
const handleGroup = new Group()
handleGroup.userData.type = EXTRA_SEGMENT_HANDLE
handleGroup.name = EXTRA_SEGMENT_HANDLE
handleGroup.add(sphereMesh)
handleGroup.layers.set(SKETCH_LAYER)
handleGroup.traverse((child) => {
child.layers.set(SKETCH_LAYER)
})
return handleGroup
})()) as Group
extraSegmentHandle.position.set(
from[0] + 0.08 * (to[0] - from[0]),
from[1] + 0.08 * (to[1] - from[1]),
0
)
extraSegmentHandle.scale.set(scale, scale, scale)
group.add(extraSegmentHandle)
const straightSegmentBody = group.children.find(
(child) => child.userData.type === STRAIGHT_SEGMENT_BODY
) as Mesh

View File

@ -18,8 +18,9 @@ import {
Intersection,
Object3D,
Object3DEventMap,
BoxGeometry,
} from 'three'
import { Coords2d, compareVec2Epsilon2 } from 'lang/std/sketch'
import { compareVec2Epsilon2 } from 'lang/std/sketch'
import { useModelingContext } from 'hooks/useModelingContext'
import * as TWEEN from '@tweenjs/tween.js'
import { SourceRange } from 'lang/wasm'
@ -87,20 +88,25 @@ class SceneInfra {
fov = 45
fovBeforeAnimate = 45
isFovAnimationInProgress = false
_baseUnit: BaseUnit = 'mm'
_baseUnitMultiplier = 1
onDragStartCallback: (arg: OnDragCallbackArgs) => void = () => {}
onDragEndCallback: (arg: OnDragCallbackArgs) => void = () => {}
onDragCallback: (arg: OnDragCallbackArgs) => void = () => {}
onMoveCallback: (arg: onMoveCallbackArgs) => void = () => {}
onClickCallback: (arg?: OnClickCallbackArgs) => void = () => {}
onMouseEnter: (arg: BaseCallbackArgs2) => void = () => {}
onMouseLeave: (arg: BaseCallbackArgs2) => void = () => {}
setCallbacks = (callbacks: {
onDragStart?: (arg: OnDragCallbackArgs) => void
onDragEnd?: (arg: OnDragCallbackArgs) => void
onDrag?: (arg: OnDragCallbackArgs) => void
onMove?: (arg: onMoveCallbackArgs) => void
onClick?: (arg?: OnClickCallbackArgs) => void
onMouseEnter?: (arg: BaseCallbackArgs2) => void
onMouseLeave?: (arg: BaseCallbackArgs2) => void
}) => {
console.trace('setting callbacks')
this.onDragStartCallback = callbacks.onDragStart || this.onDragStartCallback
this.onDragEndCallback = callbacks.onDragEnd || this.onDragEndCallback
this.onDragCallback = callbacks.onDrag || this.onDragCallback
this.onMoveCallback = callbacks.onMove || this.onMoveCallback
this.onClickCallback = callbacks.onClick || this.onClickCallback
@ -108,17 +114,10 @@ class SceneInfra {
this.onMouseLeave = callbacks.onMouseLeave || this.onMouseLeave
this.selected = null // following selections between callbacks being set is too tricky
}
set baseUnit(unit: BaseUnit) {
this._baseUnit = unit
this._baseUnitMultiplier = baseUnitTomm(unit)
this.scene.scale.set(
this._baseUnitMultiplier,
this._baseUnitMultiplier,
this._baseUnitMultiplier
)
}
resetMouseListeners = () => {
sceneInfra.setCallbacks({
onDragStart: () => {},
onDragEnd: () => {},
onDrag: () => {},
onMove: () => {},
onClick: () => {},
@ -212,12 +211,7 @@ class SceneInfra {
const axisGroup = this.scene
.getObjectByName(AXIS_GROUP)
?.getObjectByName('gridHelper')
planesGroup &&
planesGroup.scale.set(
scale / sceneInfra._baseUnitMultiplier,
scale / sceneInfra._baseUnitMultiplier,
scale / sceneInfra._baseUnitMultiplier
)
planesGroup && planesGroup.scale.set(scale, scale, scale)
axisGroup?.name === 'gridHelper' && axisGroup.scale.set(scale, scale, scale)
}
@ -285,11 +279,8 @@ class SceneInfra {
}
return {
intersection2d: new Vector2(
transformedPoint.x / this._baseUnitMultiplier,
transformedPoint.y / this._baseUnitMultiplier
), // z should be 0
intersectPoint: intersectPoint.divideScalar(this._baseUnitMultiplier),
intersection2d: new Vector2(transformedPoint.x, transformedPoint.y), // z should be 0
intersectPoint,
intersection: planeIntersects[0],
}
}
@ -438,8 +429,13 @@ class SceneInfra {
if (this.selected) {
if (this.selected.hasBeenDragged) {
// this is where we could fire a onDragEnd event
// console.log('onDragEnd', this.selected)
// TODO do the types properly here
this.onDragEndCallback({
object: this.selected.object,
event,
intersection2d: planeIntersectPoint?.intersection2d,
...planeIntersectPoint,
} as any)
} else if (planeIntersectPoint) {
// fire onClick event as there was no drags
this.onClickCallback({
@ -501,11 +497,7 @@ class SceneInfra {
this.camControls.camera,
this.camControls.target
)
planesGroup.scale.set(
sceneScale / sceneInfra._baseUnitMultiplier,
sceneScale / sceneInfra._baseUnitMultiplier,
sceneScale / sceneInfra._baseUnitMultiplier
)
planesGroup.scale.set(sceneScale, sceneScale, sceneScale)
this.scene.add(planesGroup)
}
removeDefaultPlanes() {

View File

@ -81,6 +81,7 @@ export function straightSegment({
pathToNode,
isSelected: false,
}
group.name = STRAIGHT_SEGMENT
const arrowGroup = createArrowhead(scale)
arrowGroup.position.set(to[0], to[1], 0)
@ -169,6 +170,7 @@ export function tangentialArcToSegment({
pathToNode,
isSelected: false,
}
group.name = TANGENTIAL_ARC_TO_SEGMENT
const arrowGroup = createArrowhead(scale)
arrowGroup.position.set(to[0], to[1], 0)

View File

@ -18,7 +18,6 @@ import {
import { isTauri } from 'lib/isTauri'
import { settingsCommandBarConfig } from 'lib/commandBarConfigs/settingsCommandConfig'
import { authCommandBarConfig } from 'lib/commandBarConfigs/authCommandConfig'
import { sceneInfra } from 'clientSideScene/sceneInfra'
type MachineContext<T extends AnyStateMachine> = {
state: StateFrom<T>
@ -98,7 +97,6 @@ export const GlobalStateProvider = ({
if (settingsState.context.theme !== 'system') return
setThemeClass(e.matches ? Themes.Dark : Themes.Light)
}
sceneInfra.baseUnit = settingsState?.context?.baseUnit || 'mm'
matcher.addEventListener('change', listener)
return () => matcher.removeEventListener('change', listener)

View File

@ -27,8 +27,6 @@ describe('UserSidebarMenu tests', () => {
phone: '555-555-5555',
first_name: 'Test',
last_name: 'User',
can_train_on_data: false,
is_service_account: false,
}
render(
@ -59,8 +57,6 @@ describe('UserSidebarMenu tests', () => {
first_name: '',
last_name: '',
name: '',
can_train_on_data: false,
is_service_account: false,
}
render(
@ -88,8 +84,6 @@ describe('UserSidebarMenu tests', () => {
first_name: 'Test',
last_name: 'User',
image: '',
can_train_on_data: false,
is_service_account: false,
}
render(

View File

@ -4,7 +4,6 @@ import { ViewPlugin, hoverTooltip, tooltips } from '@codemirror/view'
import { CompletionTriggerKind } from 'vscode-languageserver-protocol'
import { offsetToPos } from 'editor/plugins/lsp/util'
import { LanguageServerOptions } from 'editor/plugins/lsp'
import { syntaxTree } from '@codemirror/language'
import {
LanguageServerPlugin,
documentUri,
@ -41,14 +40,6 @@ export function kclPlugin(options: LanguageServerOptions): Extension {
if (plugin == null) return null
const { state, pos, explicit } = context
let nodeBefore = syntaxTree(state).resolveInner(pos, -1)
if (
nodeBefore.name === 'BlockComment' ||
nodeBefore.name === 'LineComment'
)
return null
const line = state.doc.lineAt(pos)
let trigKind: CompletionTriggerKind = CompletionTriggerKind.Invoked
let trigChar: string | undefined
@ -69,7 +60,6 @@ export function kclPlugin(options: LanguageServerOptions): Extension {
) {
return null
}
return await plugin.requestCompletion(
context,
offsetToPos(state.doc, pos),

View File

@ -7,5 +7,6 @@ export const VITE_KC_API_BASE_URL = import.meta.env.VITE_KC_API_BASE_URL
export const VITE_KC_SITE_BASE_URL = import.meta.env.VITE_KC_SITE_BASE_URL
export const VITE_KC_CONNECTION_TIMEOUT_MS = import.meta.env
.VITE_KC_CONNECTION_TIMEOUT_MS
export const VITE_KC_SENTRY_DSN = import.meta.env.VITE_KC_SENTRY_DSN
export const TEST = import.meta.env.TEST
export const DEV = import.meta.env.DEV

View File

@ -93,7 +93,7 @@ class KclManager {
// Note that PROJECT_ENTRYPOINT is hardcoded until we support multiple files
this._params.id &&
writeTextFile(this._params.id, code).catch((err) => {
// TODO: add tracing per GH issue #254 (https://github.com/KittyCAD/modeling-app/issues/254)
// TODO: add Sentry per GH issue #254 (https://github.com/KittyCAD/modeling-app/issues/254)
console.error('error saving file', err)
toast.error('Error saving file, please check file permissions')
})

View File

@ -3,6 +3,7 @@ import { VITE_KC_API_WS_MODELING_URL, VITE_KC_CONNECTION_TIMEOUT_MS } from 'env'
import { Models } from '@kittycad/lib'
import { exportSave } from 'lib/exportSave'
import { v4 as uuidv4 } from 'uuid'
import * as Sentry from '@sentry/react'
import { getNodePathFromSourceRange } from 'lang/queryAst'
import { sceneInfra } from 'clientSideScene/sceneInfra'
@ -289,6 +290,12 @@ class EngineConnection {
}
}
// shouldTrace will return true when Sentry should be used to instrument
// the Engine.
shouldTrace() {
return Sentry.getCurrentHub()?.getClient()?.getOptions()?.sendClientReports
}
// connect will attempt to connect to the Engine over a WebSocket, and
// establish the WebRTC connections.
//
@ -301,6 +308,41 @@ class EngineConnection {
// Information on the connect transaction
class SpanPromise {
span: Sentry.Span
promise: Promise<void>
resolve?: (v: void) => void
constructor(span: Sentry.Span) {
this.span = span
this.promise = new Promise((resolve) => {
this.resolve = (v: void) => {
// here we're going to invoke finish before resolving the
// promise so that a `.then()` will order strictly after
// all spans have -- for sure -- been resolved, rather than
// doing a `then` on this promise.
this.span.finish()
resolve(v)
}
})
}
}
let webrtcMediaTransaction: Sentry.Transaction
let websocketSpan: SpanPromise
let mediaTrackSpan: SpanPromise
let dataChannelSpan: SpanPromise
let handshakeSpan: SpanPromise
let iceSpan: SpanPromise
const spanStart = (op: string) =>
new SpanPromise(webrtcMediaTransaction.startChild({ op }))
if (this.shouldTrace()) {
webrtcMediaTransaction = Sentry.startTransaction({ name: 'webrtc-media' })
websocketSpan = spanStart('websocket')
}
const createPeerConnection = () => {
this.pc = new RTCPeerConnection()
@ -351,6 +393,10 @@ class EngineConnection {
// From what I understand, only after have we done the ICE song and
// dance is it safest to connect the video tracks / stream
case 'connected':
if (this.shouldTrace()) {
iceSpan.resolve?.()
}
// Let the browser attach to the video stream now
this.onNewTrack({ conn: this, mediaStream: this.mediaStream! })
break
@ -383,6 +429,17 @@ class EngineConnection {
},
}
if (this.shouldTrace()) {
let mediaStreamTrack = mediaStream.getVideoTracks()[0]
mediaStreamTrack.addEventListener('unmute', () => {
// let settings = mediaStreamTrack.getSettings()
// mediaTrackSpan.span.setTag("fps", settings.frameRate)
// mediaTrackSpan.span.setTag("width", settings.width)
// mediaTrackSpan.span.setTag("height", settings.height)
mediaTrackSpan.resolve?.()
})
}
this.webrtcStatsCollector = (): Promise<ClientMetrics> => {
return new Promise((resolve, reject) => {
if (mediaStream.getVideoTracks().length !== 1) {
@ -465,6 +522,10 @@ class EngineConnection {
},
}
if (this.shouldTrace()) {
dataChannelSpan.resolve?.()
}
// Everything is now connected.
this.state = { type: EngineConnectionStateType.ConnectionEstablished }
@ -516,6 +577,27 @@ class EngineConnection {
if (this.token) {
this.send({ headers: { Authorization: `Bearer ${this.token}` } })
}
if (this.shouldTrace()) {
websocketSpan.resolve?.()
handshakeSpan = spanStart('handshake')
iceSpan = spanStart('ice')
dataChannelSpan = spanStart('data-channel')
mediaTrackSpan = spanStart('media-track')
}
if (this.shouldTrace()) {
void Promise.all([
handshakeSpan.promise,
iceSpan.promise,
dataChannelSpan.promise,
mediaTrackSpan.promise,
]).then(() => {
console.log('All spans finished, reporting')
webrtcMediaTransaction?.finish()
})
}
})
this.websocket.addEventListener('close', (event) => {
@ -704,6 +786,13 @@ failed cmd type was ${artifactThatFailed?.commandType}`
type: ConnectingType.WebRTCConnecting,
},
}
if (this.shouldTrace()) {
// When both ends have a local and remote SDP, we've been able to
// set up successfully. We'll still need to find the right ICE
// servers, but this is hand-shook.
handshakeSpan.resolve?.()
}
break
case 'trickle_ice':

View File

@ -181,6 +181,10 @@ export const line: SketchLineHelper = {
pathToNode,
'PipeExpression'
)
const { node: callExpression } = getNodeFromPath<
PipeExpression | CallExpression
>(_node, pathToNode, 'CallExpression')
const { node: varDec } = getNodeFromPath<VariableDeclarator>(
_node,
pathToNode,
@ -190,6 +194,38 @@ export const line: SketchLineHelper = {
const newXVal = createLiteral(roundOff(to[0] - from[0], 2))
const newYVal = createLiteral(roundOff(to[1] - from[1], 2))
const isAddingSegmentBetween =
callExpression.type === 'CallExpression' &&
callExpression.start >= pipe.start &&
callExpression.end <= pipe.end
if (
isAddingSegmentBetween &&
!createCallback &&
pipe.type === 'PipeExpression'
) {
const callExp = createCallExpression('line', [
createArrayExpression([newXVal, newYVal]),
createPipeSubstitution(),
])
const pathToNodeIndex = pathToNode.findIndex(
(x) => x[1] === 'PipeExpression'
)
const pipeIndex = pathToNode[pathToNodeIndex + 1][0]
if (typeof pipeIndex === 'undefined' || typeof pipeIndex === 'string') {
console.warn('pipeIndex is undefined')
return
}
pipe.body = [
...pipe.body.slice(0, pipeIndex),
callExp,
...pipe.body.slice(pipeIndex),
]
return {
modifiedAst: _node,
pathToNode,
}
}
if (replaceExisting && createCallback && pipe.type !== 'CallExpression') {
const { index: callIndex } = splitPathAtPipeExpression(pathToNode)
const { callExp, valueUsedInTransform } = createCallback(
@ -1011,15 +1047,6 @@ export function changeSketchArguments(
throw new Error(`not a sketch line helper: ${callExpression?.callee?.name}`)
}
interface CreateLineFnCallArgs {
node: Program
programMemory: ProgramMemory
to: [number, number]
from: [number, number]
fnName: ToolTip
pathToNode: PathToNode
}
export function compareVec2Epsilon(
vec1: [number, number],
vec2: [number, number],
@ -1044,6 +1071,15 @@ export function compareVec2Epsilon2(
return distance < compareEpsilon
}
interface CreateLineFnCallArgs {
node: Program
programMemory: ProgramMemory
to: [number, number]
from: [number, number]
fnName: ToolTip
pathToNode: PathToNode
}
export function addNewSketchLn({
node: _node,
programMemory: previousProgramMemory,

View File

@ -20,8 +20,6 @@ const LOCAL_USER: Models['User_type'] = {
phone: '555-555-5555',
first_name: 'Test',
last_name: 'User',
can_train_on_data: false,
is_service_account: false,
}
export interface UserContext {

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

@ -246,7 +246,7 @@ checksum = "5fd55a5ba1179988837d24ab4c7cc8ed6efdeff578ede0416b4225a5fca35bd0"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.52",
"syn 2.0.49",
]
[[package]]
@ -257,7 +257,7 @@ checksum = "c980ee35e870bd1a4d2c8294d4c04d0499e67bca1e4b5cefcc693c2fa00caea9"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.52",
"syn 2.0.49",
]
[[package]]
@ -574,9 +574,9 @@ dependencies = [
[[package]]
name = "clap"
version = "4.5.1"
version = "4.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c918d541ef2913577a0f9566e9ce27cb35b6df072075769e0b26cb5a554520da"
checksum = "80c21025abd42669a92efc996ef13cfb2c5c627858421ea58d5c3b331a6c134f"
dependencies = [
"clap_builder",
"clap_derive",
@ -584,9 +584,9 @@ dependencies = [
[[package]]
name = "clap_builder"
version = "4.5.1"
version = "4.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9f3e7391dad68afb0c2ede1bf619f579a3dc9c2ec67f089baa397123a2f3d1eb"
checksum = "458bf1f341769dfcf849846f65dffdf9146daa56bcd2a47cb4e1de9915567c99"
dependencies = [
"anstream",
"anstyle",
@ -606,7 +606,7 @@ dependencies = [
"heck",
"proc-macro2",
"quote",
"syn 2.0.52",
"syn 2.0.49",
]
[[package]]
@ -856,7 +856,7 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.52",
"syn 2.0.49",
]
[[package]]
@ -897,7 +897,7 @@ checksum = "377af281d8f23663862a7c84623bc5dcf7f8c44b13c7496a590bdc157f941a43"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.52",
"syn 2.0.49",
"synstructure 0.13.0",
]
@ -949,7 +949,7 @@ dependencies = [
"regex",
"serde",
"serde_tokenstream",
"syn 2.0.52",
"syn 2.0.49",
]
[[package]]
@ -965,7 +965,7 @@ dependencies = [
"regex",
"serde",
"serde_tokenstream",
"syn 2.0.52",
"syn 2.0.49",
]
[[package]]
@ -977,7 +977,7 @@ dependencies = [
"diesel_table_macro_syntax",
"proc-macro2",
"quote",
"syn 2.0.52",
"syn 2.0.49",
]
[[package]]
@ -986,7 +986,7 @@ version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc5557efc453706fed5e4fa85006fe9817c224c3f480a34c7e5959fd700921c5"
dependencies = [
"syn 2.0.52",
"syn 2.0.49",
]
[[package]]
@ -1036,7 +1036,7 @@ checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.52",
"syn 2.0.49",
]
[[package]]
@ -1118,7 +1118,7 @@ checksum = "eecf8589574ce9b895052fa12d69af7a233f99e6107f5cb8dd1044f2a17bfdcb"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.52",
"syn 2.0.49",
]
[[package]]
@ -1335,7 +1335,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.52",
"syn 2.0.49",
]
[[package]]
@ -1404,9 +1404,9 @@ dependencies = [
[[package]]
name = "gif"
version = "0.13.1"
version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3fb2d69b19215e18bb912fa30f7ce15846e301408695e44e0ef719f1da9e19f2"
checksum = "80792593675e051cf94a4b111980da2ba60d4a83e43e0048c5693baab3977045"
dependencies = [
"color_quant",
"weezl",
@ -1440,7 +1440,7 @@ dependencies = [
"inflections",
"proc-macro2",
"quote",
"syn 2.0.52",
"syn 2.0.49",
]
[[package]]
@ -1460,17 +1460,13 @@ name = "grackle"
version = "0.1.0"
dependencies = [
"kcl-lib",
"kittycad",
"kittycad-execution-plan",
"kittycad-execution-plan-macros",
"kittycad-execution-plan-traits",
"kittycad-modeling-cmds",
"kittycad-modeling-session",
"pretty_assertions",
"serde_json",
"thiserror",
"tokio",
"uuid",
]
[[package]]
@ -1715,9 +1711,9 @@ dependencies = [
[[package]]
name = "image"
version = "0.24.9"
version = "0.24.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5690139d2f55868e080017335e4b94cb7414274c74f1669c84fb5feba2c9f69d"
checksum = "034bbe799d1909622a74d1193aa50147769440040ff36cb2baa947609b0a4e23"
dependencies = [
"bytemuck",
"byteorder",
@ -1916,7 +1912,7 @@ dependencies = [
"itertools 0.12.1",
"js-sys",
"kittycad",
"kittycad-execution-plan-macros",
"kittycad-execution-plan-macros 0.1.4 (git+https://github.com/KittyCAD/modeling-api?branch=main)",
"kittycad-execution-plan-traits",
"lazy_static",
"parse-display 0.9.0",
@ -1947,14 +1943,14 @@ dependencies = [
"pretty_assertions",
"proc-macro2",
"quote",
"syn 2.0.52",
"syn 2.0.49",
]
[[package]]
name = "kittycad"
version = "0.2.58"
version = "0.2.54"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "049c3881ffbe77bf1c3a968372a246ce906eceb79f61cd0bc5fa229bec3504cb"
checksum = "13958174d876353f429ea8230dc92fe86f164819cea2e51bbf22e01a4c2a496e"
dependencies = [
"anyhow",
"async-trait",
@ -1990,7 +1986,7 @@ dependencies = [
[[package]]
name = "kittycad-execution-plan"
version = "0.1.0"
source = "git+https://github.com/KittyCAD/modeling-api?branch=main#9c96767289139f03036c2ba40f889f974ca3e976"
source = "git+https://github.com/KittyCAD/modeling-api?branch=main#08f05d91062380fe3a69f4baa1f1301532d31977"
dependencies = [
"bytes",
"insta",
@ -2008,18 +2004,30 @@ dependencies = [
[[package]]
name = "kittycad-execution-plan-macros"
version = "0.1.6"
source = "git+https://github.com/KittyCAD/modeling-api?branch=main#9c96767289139f03036c2ba40f889f974ca3e976"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "71d31b689c944d00aadda2ef83d8422a6efff97e1be5654a61f9d95496f0c19e"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.52",
"syn 2.0.49",
]
[[package]]
name = "kittycad-execution-plan-macros"
version = "0.1.4"
source = "git+https://github.com/KittyCAD/modeling-api?branch=main#632b75a0242400fa34373d7973b9149b0e08aa3f"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.49",
]
[[package]]
name = "kittycad-execution-plan-traits"
version = "0.1.11"
source = "git+https://github.com/KittyCAD/modeling-api?branch=main#9c96767289139f03036c2ba40f889f974ca3e976"
version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a3ec8efd57b59697eb140b63c0ffe7db44fdfe5a55f14e45513411eba2280ba5"
dependencies = [
"serde",
"thiserror",
@ -2028,8 +2036,8 @@ dependencies = [
[[package]]
name = "kittycad-modeling-cmds"
version = "0.1.27"
source = "git+https://github.com/KittyCAD/modeling-api?branch=main#9c96767289139f03036c2ba40f889f974ca3e976"
version = "0.1.18"
source = "git+https://github.com/KittyCAD/modeling-api?branch=main#08f05d91062380fe3a69f4baa1f1301532d31977"
dependencies = [
"anyhow",
"chrono",
@ -2039,9 +2047,8 @@ dependencies = [
"enum-iterator-derive",
"euler",
"http 0.2.9",
"kittycad-execution-plan-macros",
"kittycad-execution-plan-macros 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
"kittycad-execution-plan-traits",
"kittycad-modeling-cmds-macros",
"kittycad-unit-conversion-derive",
"measurements",
"parse-display 0.8.2",
@ -2054,20 +2061,10 @@ dependencies = [
"webrtc",
]
[[package]]
name = "kittycad-modeling-cmds-macros"
version = "0.1.2"
source = "git+https://github.com/KittyCAD/modeling-api?branch=main#9c96767289139f03036c2ba40f889f974ca3e976"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.52",
]
[[package]]
name = "kittycad-modeling-session"
version = "0.1.0"
source = "git+https://github.com/KittyCAD/modeling-api?branch=main#9c96767289139f03036c2ba40f889f974ca3e976"
source = "git+https://github.com/KittyCAD/modeling-api?branch=main#08f05d91062380fe3a69f4baa1f1301532d31977"
dependencies = [
"futures",
"kittycad",
@ -2499,7 +2496,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.52",
"syn 2.0.49",
]
[[package]]
@ -2656,7 +2653,7 @@ dependencies = [
"regex",
"regex-syntax 0.7.5",
"structmeta 0.2.0",
"syn 2.0.52",
"syn 2.0.49",
]
[[package]]
@ -2670,7 +2667,7 @@ dependencies = [
"regex",
"regex-syntax 0.8.2",
"structmeta 0.3.0",
"syn 2.0.52",
"syn 2.0.49",
]
[[package]]
@ -2736,7 +2733,7 @@ checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.52",
"syn 2.0.49",
]
[[package]]
@ -3604,7 +3601,7 @@ checksum = "33c85360c95e7d137454dc81d9a4ed2b8efd8fbe19cee57357b32b9771fccb67"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.52",
"syn 2.0.49",
]
[[package]]
@ -3638,7 +3635,7 @@ checksum = "8725e1dfadb3a50f7e5ce0b1a540466f6ed3fe7a0fca2ac2b8b831d31316bd00"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.52",
"syn 2.0.49",
]
[[package]]
@ -3659,7 +3656,7 @@ dependencies = [
"proc-macro2",
"quote",
"serde",
"syn 2.0.52",
"syn 2.0.49",
]
[[package]]
@ -3916,7 +3913,7 @@ dependencies = [
"proc-macro2",
"quote",
"structmeta-derive 0.2.0",
"syn 2.0.52",
"syn 2.0.49",
]
[[package]]
@ -3928,7 +3925,7 @@ dependencies = [
"proc-macro2",
"quote",
"structmeta-derive 0.3.0",
"syn 2.0.52",
"syn 2.0.49",
]
[[package]]
@ -3939,7 +3936,7 @@ checksum = "a60bcaff7397072dca0017d1db428e30d5002e00b6847703e2e42005c95fbe00"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.52",
"syn 2.0.49",
]
[[package]]
@ -3950,7 +3947,7 @@ checksum = "152a0b65a590ff6c3da95cabe2353ee04e6167c896b28e3b14478c2636c922fc"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.52",
"syn 2.0.49",
]
[[package]]
@ -4022,9 +4019,9 @@ dependencies = [
[[package]]
name = "syn"
version = "2.0.52"
version = "2.0.49"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b699d15b36d1f02c3e7c69f8ffef53de37aefae075d8488d4ba1a7788d574a07"
checksum = "915aea9e586f80826ee59f8453c1101f9d1c4b3964cd2460185ee8e299ada496"
dependencies = [
"proc-macro2",
"quote",
@ -4057,7 +4054,7 @@ checksum = "285ba80e733fac80aa4270fbcdf83772a79b80aa35c97075320abfee4a915b06"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.52",
"syn 2.0.49",
"unicode-xid",
]
@ -4187,7 +4184,7 @@ checksum = "a953cb265bef375dae3de6663da4d3804eee9682ea80d8e2542529b73c531c81"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.52",
"syn 2.0.49",
]
[[package]]
@ -4294,7 +4291,7 @@ checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.52",
"syn 2.0.49",
]
[[package]]
@ -4457,7 +4454,7 @@ checksum = "84fd902d4e0b9a4b27f2f440108dc034e1758628a9b702f8ec61ad66355422fa"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.52",
"syn 2.0.49",
]
[[package]]
@ -4485,7 +4482,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.52",
"syn 2.0.49",
]
[[package]]
@ -4569,7 +4566,7 @@ dependencies = [
"Inflector",
"proc-macro2",
"quote",
"syn 2.0.52",
"syn 2.0.49",
"termcolor",
]
@ -4816,7 +4813,7 @@ dependencies = [
"once_cell",
"proc-macro2",
"quote",
"syn 2.0.52",
"syn 2.0.49",
"wasm-bindgen-shared",
]
@ -4851,7 +4848,7 @@ checksum = "642f325be6301eb8107a83d12a8ac6c1e1c54345a7ef1a9261962dfefda09e66"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.52",
"syn 2.0.49",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]
@ -5137,9 +5134,9 @@ dependencies = [
[[package]]
name = "weezl"
version = "0.1.8"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "53a85b86a771b1c87058196170769dd264f66c0782acf1ae6cc51bfd64b39082"
checksum = "9193164d4de03a926d909d3bc7c30543cecb35400c02114792c2cae20d5e2dbb"
[[package]]
name = "winapi"
@ -5469,7 +5466,7 @@ checksum = "b3c129550b3e6de3fd0ba67ba5c81818f9805e58b8d7fee80a3a59d2c9fc601a"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.52",
"syn 2.0.49",
]
[[package]]
@ -5489,7 +5486,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.52",
"syn 2.0.49",
]
[[package]]

View File

@ -21,7 +21,7 @@ wasm-bindgen-futures = "0.4.41"
[dev-dependencies]
anyhow = "1"
image = "0.24.9"
image = "0.24.8"
kittycad = { workspace = true, default-features = true }
pretty_assertions = "1.4.0"
reqwest = { version = "0.11.24", default-features = false }
@ -58,12 +58,11 @@ members = [
]
[workspace.dependencies]
kittycad = { version = "0.2.58", default-features = false, features = ["js", "requests"] }
kittycad = { version = "0.2.54", default-features = false, features = ["js", "requests"] }
kittycad-execution-plan = { git = "https://github.com/KittyCAD/modeling-api", branch = "main" }
kittycad-execution-plan-macros = { git = "https://github.com/KittyCAD/modeling-api", branch = "main" }
kittycad-execution-plan-traits = { git = "https://github.com/KittyCAD/modeling-api", branch = "main" }
kittycad-modeling-cmds = { git = "https://github.com/KittyCAD/modeling-api", branch = "main" }
kittycad-execution-plan-traits = "0.1.10"
kittycad-modeling-session = { git = "https://github.com/KittyCAD/modeling-api", branch = "main" }
kittycad-execution-plan-macros = { git = "https://github.com/KittyCAD/modeling-api", branch = "main" }
[[test]]
name = "executor"
@ -74,9 +73,6 @@ name = "modify"
path = "tests/modify/main.rs"
# Example: how to point modeling-api at a different repo (e.g. a branch or a local clone)
#[patch."https://github.com/KittyCAD/modeling-api"]
#kittycad-execution-plan = { path = "../../../modeling-api/execution-plan" }
#kittycad-execution-plan-macros = { path = "../../../modeling-api/execution-plan-macros" }
#kittycad-execution-plan-traits = { path = "../../../modeling-api/execution-plan-traits" }
#kittycad-modeling-cmds = { path = "../../../modeling-api/modeling-cmds" }
#kittycad-modeling-session = { path = "../../../modeling-api/modeling-session" }
# [patch."https://github.com/KittyCAD/modeling-api"]
# kittycad-execution-plan = { path = "../../../modeling-api/execution-plan" }
# kittycad-modeling-session = { path = "../../../modeling-api/modeling-session" }

View File

@ -19,7 +19,7 @@ quote = "1"
regex = "1.10"
serde = { version = "1.0.193", features = ["derive"] }
serde_tokenstream = "0.2"
syn = { version = "2.0.52", features = ["full"] }
syn = { version = "2.0.49", features = ["full"] }
[dev-dependencies]
expectorate = "1.1.0"

View File

@ -7,15 +7,11 @@ description = "A new executor for KCL which compiles to Execution Plans"
[dependencies]
kcl-lib = { path = "../kcl" }
kittycad = { workspace = true }
kittycad-execution-plan = { workspace = true }
kittycad-execution-plan-traits = { workspace = true }
kittycad-execution-plan-macros = { workspace = true }
kittycad-modeling-cmds = { workspace = true }
kittycad-modeling-session = { workspace = true }
thiserror = "1.0.57"
tokio = { version = "1.36.0", features = ["macros", "rt"] }
uuid = "1.7"
[dev-dependencies]
pretty_assertions = "1"

View File

@ -103,11 +103,7 @@ impl BindingScope {
("add".into(), EpBinding::from(KclFunction::Add(native_functions::Add))),
(
"startSketchAt".into(),
EpBinding::from(KclFunction::StartSketchAt(native_functions::sketch::StartSketchAt)),
),
(
"lineTo".into(),
EpBinding::from(KclFunction::LineTo(native_functions::sketch::LineTo)),
EpBinding::from(KclFunction::StartSketchAt(native_functions::StartSketchAt)),
),
]),
parent: None,

View File

@ -45,13 +45,6 @@ pub enum CompileError {
NoReturnStmt,
#[error("You used the %, which means \"substitute this argument for the value to the left in this |> pipeline\". But there is no such value, because you're not calling a pipeline.")]
NotInPipeline,
#[error("The function '{fn_name}' expects a parameter of type {expected} as argument number {arg_number} but you supplied {actual}")]
ArgWrongType {
fn_name: &'static str,
expected: &'static str,
actual: String,
arg_number: usize,
},
}
#[derive(Debug, thiserror::Error)]

View File

@ -252,7 +252,6 @@ impl Planner {
} = match callee {
KclFunction::Id(f) => f.call(&mut self.next_addr, args)?,
KclFunction::StartSketchAt(f) => f.call(&mut self.next_addr, args)?,
KclFunction::LineTo(f) => f.call(&mut self.next_addr, args)?,
KclFunction::Add(f) => f.call(&mut self.next_addr, args)?,
KclFunction::UserDefined(f) => {
let UserDefinedFunction {
@ -619,8 +618,7 @@ impl Eq for UserDefinedFunction {}
#[cfg_attr(test, derive(Eq, PartialEq))]
enum KclFunction {
Id(native_functions::Id),
StartSketchAt(native_functions::sketch::StartSketchAt),
LineTo(native_functions::sketch::LineTo),
StartSketchAt(native_functions::StartSketchAt),
Add(native_functions::Add),
UserDefined(UserDefinedFunction),
}

View File

@ -2,13 +2,12 @@
//! This includes some of the stdlib, e.g. `startSketchAt`.
//! But some other stdlib functions will be written in KCL.
use kcl_lib::std::sketch::PlaneData;
use kittycad_execution_plan::{BinaryArithmetic, Destination, Instruction};
use kittycad_execution_plan_traits::Address;
use kittycad_execution_plan_traits::{Address, Value};
use crate::{CompileError, EpBinding, EvalPlan};
pub mod sketch;
/// The identity function. Always returns its first input.
#[derive(Debug, Clone)]
#[cfg_attr(test, derive(Eq, PartialEq))]
@ -42,6 +41,34 @@ impl Callable for Id {
}
}
#[derive(Debug, Clone)]
#[cfg_attr(test, derive(Eq, PartialEq))]
pub struct StartSketchAt;
impl Callable for StartSketchAt {
fn call(&self, next_addr: &mut Address, _args: Vec<EpBinding>) -> Result<EvalPlan, CompileError> {
let mut instructions = Vec::new();
// Store the plane.
let plane = PlaneData::XY.into_parts();
instructions.push(Instruction::SetValue {
address: next_addr.offset_by(plane.len()),
value_parts: plane,
});
// TODO: Get the plane ID from global context.
// TODO: Send this command:
// ModelingCmd::SketchModeEnable {
// animated: false,
// ortho: false,
// plane_id: plane.id,
// // We pass in the normal for the plane here.
// disable_camera_with_plane: Some(plane.z_axis.clone().into()),
// },
// TODO: Send ModelingCmd::StartPath at the given point.
// TODO (maybe): Store the SketchGroup in KCEP memory.
todo!()
}
}
/// A test function that adds two numbers.
#[derive(Debug, Clone)]
#[cfg_attr(test, derive(Eq, PartialEq))]

View File

@ -1,7 +0,0 @@
//! Native functions for sketching on the plane.
pub mod helpers;
pub mod stdlib_functions;
pub mod types;
pub use stdlib_functions::{LineTo, StartSketchAt};

View File

@ -1,137 +0,0 @@
use kittycad_execution_plan::{api_request::ApiRequest, Destination, Instruction};
use kittycad_execution_plan_traits::{Address, InMemory};
use kittycad_modeling_cmds::{id::ModelingCmdId, ModelingCmdEndpoint};
use crate::{binding_scope::EpBinding, error::CompileError};
/// Emit instructions for an API call with no parameters.
pub fn no_arg_api_call(instrs: &mut Vec<Instruction>, endpoint: ModelingCmdEndpoint, cmd_id: ModelingCmdId) {
instrs.push(Instruction::ApiRequest(ApiRequest {
endpoint,
store_response: None,
arguments: vec![],
cmd_id,
}))
}
/// Emit instructions for an API call with the given parameters.
/// The API parameters are stored in the EP memory stack.
/// So, they have to be pushed onto the stack in the right order,
/// i.e. the reverse order in which the API call's Rust struct defines the fields.
pub fn stack_api_call<const N: usize>(
instrs: &mut Vec<Instruction>,
endpoint: ModelingCmdEndpoint,
store_response: Option<Address>,
cmd_id: ModelingCmdId,
data: [Vec<kittycad_execution_plan_traits::Primitive>; N],
) {
let arguments = vec![InMemory::StackPop; data.len()];
instrs.extend(data.map(|data| Instruction::StackPush { data }));
instrs.push(Instruction::ApiRequest(ApiRequest {
endpoint,
store_response,
arguments,
cmd_id,
}))
}
pub fn single_binding(
b: EpBinding,
fn_name: &'static str,
expected: &'static str,
arg_number: usize,
) -> Result<Address, CompileError> {
match b {
EpBinding::Single(a) => Ok(a),
EpBinding::Sequence { .. } => Err(CompileError::ArgWrongType {
fn_name,
expected,
actual: "array".to_owned(),
arg_number,
}),
EpBinding::Map { .. } => Err(CompileError::ArgWrongType {
fn_name,
expected,
actual: "object".to_owned(),
arg_number,
}),
EpBinding::Function(_) => Err(CompileError::ArgWrongType {
fn_name,
expected,
actual: "function".to_owned(),
arg_number,
}),
}
}
pub fn sequence_binding(
b: EpBinding,
fn_name: &'static str,
expected: &'static str,
arg_number: usize,
) -> Result<Vec<EpBinding>, CompileError> {
match b {
EpBinding::Sequence { elements, .. } => Ok(elements),
EpBinding::Single(_) => Err(CompileError::ArgWrongType {
fn_name,
expected,
actual: "single".to_owned(),
arg_number,
}),
EpBinding::Map { .. } => Err(CompileError::ArgWrongType {
fn_name,
expected,
actual: "object".to_owned(),
arg_number,
}),
EpBinding::Function(_) => Err(CompileError::ArgWrongType {
fn_name,
expected,
actual: "function".to_owned(),
arg_number,
}),
}
}
/// Extract a 2D point from an argument to a Cabble.
pub fn arg_point2d(
arg: EpBinding,
fn_name: &'static str,
instructions: &mut Vec<Instruction>,
next_addr: &mut Address,
arg_number: usize,
) -> Result<Address, CompileError> {
let expected = "2D point (array with length 2)";
let elements = sequence_binding(arg, "startSketchAt", "an array of length 2", arg_number)?;
if elements.len() != 2 {
return Err(CompileError::ArgWrongType {
fn_name,
expected,
actual: format!("array of length {}", elements.len()),
arg_number: 0,
});
}
// KCL stores points as an array.
// KC API stores them as Rust objects laid flat out in memory.
let start = next_addr.offset_by(2);
let start_x = start;
let start_y = start + 1;
let start_z = start + 2;
instructions.extend([
Instruction::Copy {
source: single_binding(elements[0].clone(), "startSketchAt", "number", arg_number)?,
destination: Destination::Address(start_x),
length: 1,
},
Instruction::Copy {
source: single_binding(elements[1].clone(), "startSketchAt", "number", arg_number)?,
destination: Destination::Address(start_y),
length: 1,
},
Instruction::SetPrimitive {
address: start_z,
value: 0.0.into(),
},
]);
Ok(start)
}

View File

@ -1,210 +0,0 @@
use kittycad_execution_plan::{api_request::ApiRequest, Destination, Instruction};
use kittycad_execution_plan_traits::{Address, InMemory, Value};
use kittycad_modeling_cmds::{
shared::{Point3d, Point4d},
ModelingCmdEndpoint,
};
use uuid::Uuid;
use super::{
helpers::{arg_point2d, no_arg_api_call, single_binding, stack_api_call},
types::{Axes, BasePath, Plane, SketchGroup},
};
use crate::{binding_scope::EpBinding, error::CompileError, native_functions::Callable, EvalPlan};
#[derive(Debug, Clone)]
#[cfg_attr(test, derive(Eq, PartialEq))]
pub struct LineTo;
impl Callable for LineTo {
fn call(&self, next_addr: &mut Address, args: Vec<EpBinding>) -> Result<EvalPlan, CompileError> {
let mut instructions = Vec::new();
let fn_name = "lineTo";
// Get both required params.
let mut args_iter = args.into_iter();
let Some(to) = args_iter.next() else {
return Err(CompileError::NotEnoughArgs {
fn_name: fn_name.into(),
required: 2,
actual: 0,
});
};
let Some(sketch_group) = args_iter.next() else {
return Err(CompileError::NotEnoughArgs {
fn_name: fn_name.into(),
required: 2,
actual: 1,
});
};
// Check the type of both required params.
let to = arg_point2d(to, fn_name, &mut instructions, next_addr, 0)?;
let sg = single_binding(sketch_group, fn_name, "sketch group", 1)?;
let id = Uuid::new_v4();
let start_of_line = next_addr.offset(1);
let length_of_3d_point = Point3d::<f64>::default().into_parts().len();
instructions.extend([
// Push the `to` 2D point onto the stack.
Instruction::Copy {
source: to,
length: 2,
destination: Destination::StackPush,
},
// Make it a 3D point.
Instruction::StackExtend { data: vec![0.0.into()] },
// Append the new path segment to memory.
// First comes its tag.
Instruction::SetPrimitive {
address: start_of_line,
value: "Line".to_owned().into(),
},
// Then its end
Instruction::StackPop {
destination: Some(start_of_line + 1),
},
// Then its `relative` field.
Instruction::SetPrimitive {
address: start_of_line + 1 + length_of_3d_point,
value: false.into(),
},
// Send the ExtendPath request
Instruction::ApiRequest(ApiRequest {
endpoint: ModelingCmdEndpoint::ExtendPath,
store_response: None,
arguments: vec![
// Path ID
InMemory::Address(sg + SketchGroup::path_id_offset()),
// Segment
InMemory::Address(start_of_line),
],
cmd_id: id.into(),
}),
]);
// TODO: Create a new SketchGroup from the old one + add the new path, then store it.
Ok(EvalPlan {
instructions,
binding: EpBinding::Single(Address::ZERO + 9999),
})
}
}
#[derive(Debug, Clone)]
#[cfg_attr(test, derive(Eq, PartialEq))]
pub struct StartSketchAt;
impl Callable for StartSketchAt {
fn call(&self, next_addr: &mut Address, args: Vec<EpBinding>) -> Result<EvalPlan, CompileError> {
let mut instructions = Vec::new();
// First, before we send any API calls, let's validate the arguments to this function.
let mut args_iter = args.into_iter();
let Some(start) = args_iter.next() else {
return Err(CompileError::NotEnoughArgs {
fn_name: "startSketchAt".into(),
required: 1,
actual: 0,
});
};
let start_point = arg_point2d(start, "startSketchAt", &mut instructions, next_addr, 0)?;
let tag = match args_iter.next() {
None => None,
Some(b) => Some(single_binding(b, "startSketchAt", "a single string", 1)?),
};
// Define some constants:
let axes = Axes {
x: Point3d { x: 1.0, y: 0.0, z: 0.0 },
y: Point3d { x: 0.0, y: 1.0, z: 0.0 },
z: Point3d { x: 0.0, y: 0.0, z: 1.0 },
};
let origin = Point3d::default();
// Now the function can start.
// First API call: make the plane.
let plane_id = Uuid::new_v4();
stack_api_call(
&mut instructions,
ModelingCmdEndpoint::MakePlane,
None,
plane_id.into(),
[
Some(true).into_parts(), // hide
vec![false.into()], // clobber
vec![60.0.into()], // size
axes.y.into_parts(),
axes.x.into_parts(),
origin.into_parts(),
],
);
// Next, enter sketch mode.
stack_api_call(
&mut instructions,
ModelingCmdEndpoint::SketchModeEnable,
None,
Uuid::new_v4().into(),
[
Some(axes.z).into_parts(),
vec![false.into()], // animated
vec![false.into()], // ortho mode
vec![plane_id.into()],
],
);
// Then start a path
let path_id = Uuid::new_v4();
no_arg_api_call(&mut instructions, ModelingCmdEndpoint::StartPath, path_id.into());
// Move the path pen to the given point.
instructions.push(Instruction::StackPush {
data: vec![path_id.into()],
});
instructions.push(Instruction::ApiRequest(ApiRequest {
endpoint: ModelingCmdEndpoint::MovePathPen,
store_response: None,
arguments: vec![InMemory::StackPop, InMemory::Address(start_point)],
cmd_id: Uuid::new_v4().into(),
}));
// Starting a sketch creates a sketch group.
// Updating the sketch will update this sketch group later.
let sketch_group = SketchGroup {
id: path_id,
position: origin,
rotation: Point4d {
x: 0.0,
y: 0.0,
z: 0.0,
w: 1.0,
},
// TODO: Must copy the existing data (from the arguments to this KCL function)
// over these values after writing to memory.
path_first: BasePath {
from: Default::default(),
to: Default::default(),
name: Default::default(),
},
path_rest: Vec::new(),
on: super::types::SketchSurface::Plane(Plane {
id: plane_id,
value: super::types::PlaneType::XY,
origin,
axes,
}),
axes,
entity_id: Some(plane_id),
};
let sketch_group_primitives = sketch_group.clone().into_parts();
let sketch_group_addr = next_addr.offset_by(sketch_group_primitives.len());
instructions.push(Instruction::SetValue {
address: sketch_group_addr,
value_parts: sketch_group_primitives,
});
instructions.extend(sketch_group.set_base_path(sketch_group_addr, start_point, tag));
Ok(EvalPlan {
instructions,
binding: EpBinding::Single(sketch_group_addr),
})
}
}

View File

@ -1,142 +0,0 @@
use kittycad_execution_plan::{Destination, Instruction};
use kittycad_execution_plan_macros::ExecutionPlanValue;
use kittycad_execution_plan_traits::{Address, Value};
use kittycad_modeling_cmds::shared::{Point2d, Point3d, Point4d};
use uuid::Uuid;
/// A sketch group is a collection of paths.
#[derive(Clone, ExecutionPlanValue)]
pub struct SketchGroup {
// NOTE to developers
// Do NOT reorder these fields without updating the _offset() methods below.
/// The id of the sketch group.
pub id: Uuid,
/// What the sketch is on (can be a plane or a face).
pub on: SketchSurface,
/// The position of the sketch group.
pub position: Point3d,
/// The rotation of the sketch group base plane.
pub rotation: Point4d,
/// The X, Y and Z axes of this sketch's base plane, in 3D space.
pub axes: Axes,
/// The plane id or face id of the sketch group.
pub entity_id: Option<Uuid>,
/// The base path.
pub path_first: BasePath,
/// Paths after the first path, if any.
pub path_rest: Vec<Path>,
}
impl SketchGroup {
/// Get the offset for the `id` field.
pub fn path_id_offset() -> usize {
0
}
pub fn set_base_path(&self, sketch_group: Address, start_point: Address, tag: Option<Address>) -> Vec<Instruction> {
let base_path_addr = sketch_group
+ self.id.into_parts().len()
+ self.on.into_parts().len()
+ self.position.into_parts().len()
+ self.rotation.into_parts().len()
+ self.axes.into_parts().len()
+ self.entity_id.into_parts().len()
+ self.entity_id.into_parts().len();
let mut out = vec![
// Copy over the `from` field.
Instruction::Copy {
source: start_point,
destination: Destination::Address(base_path_addr),
length: 1,
},
// Copy over the `to` field.
Instruction::Copy {
source: start_point,
destination: Destination::Address(base_path_addr + self.path_first.from.into_parts().len()),
length: 1,
},
];
if let Some(tag) = tag {
// Copy over the `name` field.
out.push(Instruction::Copy {
source: tag,
destination: Destination::Address(
base_path_addr + self.path_first.from.into_parts().len() + self.path_first.to.into_parts().len(),
),
length: 1,
});
}
out
}
}
/// The X, Y and Z axes.
#[derive(Clone, Copy, ExecutionPlanValue)]
pub struct Axes {
pub x: Point3d,
pub y: Point3d,
pub z: Point3d,
}
#[derive(Clone, ExecutionPlanValue)]
pub struct BasePath {
pub from: Point2d<f64>,
pub to: Point2d<f64>,
pub name: String,
}
/// A path.
#[derive(Clone, ExecutionPlanValue)]
pub enum Path {
/// A path that goes to a point.
ToPoint { base: BasePath },
/// A arc that is tangential to the last path segment that goes to a point
TangentialArcTo {
base: BasePath,
/// the arc's center
center: Point2d,
/// arc's direction
ccw: bool,
},
/// A path that is horizontal.
Horizontal {
base: BasePath,
/// The x coordinate.
x: f64,
},
/// An angled line to.
AngledLineTo {
base: BasePath,
/// The x coordinate.
x: Option<f64>,
/// The y coordinate.
y: Option<f64>,
},
/// A base path.
Base { base: BasePath },
}
#[derive(Clone, Copy, ExecutionPlanValue)]
pub enum SketchSurface {
Plane(Plane),
}
/// A plane.
#[derive(Clone, Copy, ExecutionPlanValue)]
pub struct Plane {
/// The id of the plane.
pub id: Uuid,
// The code for the plane either a string or custom.
pub value: PlaneType,
/// Origin of the plane.
pub origin: Point3d,
pub axes: Axes,
}
/// Type for a plane.
#[derive(Clone, Copy, ExecutionPlanValue)]
pub enum PlaneType {
XY,
XZ,
YZ,
Custom,
}

View File

@ -1,8 +1,7 @@
use std::{collections::HashMap, env};
use std::collections::HashMap;
use ep::{Destination, UnaryArithmetic};
use ept::{ListHeader, ObjectHeader};
use kittycad_modeling_session::SessionBuilder;
use pretty_assertions::assert_eq;
use super::*;
@ -1045,91 +1044,6 @@ fn store_object_with_array_property() {
)
}
#[tokio::test]
async fn stdlib_cube_partial() {
let program = r#"
let cube = startSketchAt([0.0, 0.0])
|> lineTo([4.0, 0.0], %)
"#;
let (plan, _scope) = must_plan(program);
std::fs::write(
"stdlib_cube_partial.test.json",
serde_json::to_string_pretty(&plan).unwrap(),
)
.unwrap();
let ast = kcl_lib::parser::Parser::new(kcl_lib::token::lexer(program))
.ast()
.unwrap();
let client = test_client().await;
let _mem = crate::execute(ast, Some(client)).await.unwrap();
// use kittycad_modeling_cmds::{each_cmd, ok_response::OkModelingCmdResponse, ImageFormat, ModelingCmd};
// let out = client
// .run_command(
// uuid::Uuid::new_v4().into(),
// each_cmd::TakeSnapshot {
// format: ImageFormat::Png,
// },
// )
// .await
// .unwrap();
// let out = match out {
// OkModelingCmdResponse::TakeSnapshot(b) => b,
// other => panic!("wrong output: {other:?}"),
// };
// let out: Vec<u8> = out.contents.into();
}
async fn test_client() -> Session {
let kittycad_api_token = env::var("KITTYCAD_API_TOKEN").expect("You must set $KITTYCAD_API_TOKEN");
let kittycad_api_client = kittycad::Client::new(kittycad_api_token);
let session_builder = SessionBuilder {
client: kittycad_api_client,
fps: Some(10),
unlocked_framerate: Some(false),
video_res_height: Some(720),
video_res_width: Some(1280),
buffer_reqs: None,
await_response_timeout: None,
};
match Session::start(session_builder).await {
Err(e) => match e {
kittycad::types::error::Error::InvalidRequest(s) => panic!("Request did not meet requirements {s}"),
kittycad::types::error::Error::CommunicationError(e) => {
panic!(" A server error either due to the data, or with the connection: {e}")
}
kittycad::types::error::Error::RequestError(e) => panic!("Could not build request: {e}"),
kittycad::types::error::Error::SerdeError { error, status } => {
panic!("Serde error (HTTP {status}): {error}")
}
kittycad::types::error::Error::InvalidResponsePayload { error, response } => {
panic!("Invalid response payload. Error {error}, response {response:?}")
}
kittycad::types::error::Error::Server { body, status } => panic!("Server error (HTTP {status}): {body}"),
kittycad::types::error::Error::UnexpectedResponse(resp) => {
let status = resp.status();
let url = resp.url().to_owned();
match resp.text().await {
Ok(body) => panic!(
"Unexpected response from KittyCAD API.
URL:{url}
HTTP {status}
---Body----
{body}"
),
Err(e) => panic!(
"Unexpected response from KittyCAD API.
URL:{url}
HTTP {status}
---Body could not be read, the error is----
{e}"
),
}
}
},
Ok(x) => x,
}
}
#[ignore = "haven't done API calls or stdlib yet"]
#[test]
fn stdlib_api_calls() {

View File

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

View File

@ -14,7 +14,7 @@ keywords = ["kcl", "KittyCAD", "CAD"]
anyhow = { version = "1.0.79", features = ["backtrace"] }
async-recursion = "1.0.5"
async-trait = "0.1.77"
clap = { version = "4.5.1", features = ["cargo", "derive", "env", "unicode"], optional = true }
clap = { version = "4.5.0", features = ["cargo", "derive", "env", "unicode"], optional = true }
dashmap = "5.5.3"
databake = { version = "0.1.7", features = ["derive"] }
derive-docs = { version = "0.1.8" }

View File

@ -96,19 +96,7 @@ impl Program {
let custom_white_space_or_comment = match self.non_code_meta.non_code_nodes.get(&index) {
Some(noncodes) => noncodes
.iter()
.enumerate()
.map(|(i, custom_white_space_or_comment)| {
let formatted = custom_white_space_or_comment.format(&indentation);
if i == 0 && !formatted.trim().is_empty() {
if let NonCodeValue::BlockComment { .. } = custom_white_space_or_comment.value {
format!("\n{}", formatted)
} else {
formatted
}
} else {
formatted
}
})
.map(|custom_white_space_or_comment| custom_white_space_or_comment.format(&indentation))
.collect::<String>(),
None => String::new(),
};
@ -171,35 +159,6 @@ impl Program {
}
}
/// Returns a non code meta that includes the given character position.
pub fn get_non_code_meta_for_position(&self, pos: usize) -> Option<&NonCodeMeta> {
// Check if its in the body.
if self.non_code_meta.contains(pos) {
return Some(&self.non_code_meta);
}
let Some(item) = self.get_body_item_for_position(pos) else {
return None;
};
// Recurse over the item.
let value = match item {
BodyItem::ExpressionStatement(expression_statement) => Some(&expression_statement.expression),
BodyItem::VariableDeclaration(variable_declaration) => variable_declaration.get_value_for_position(pos),
BodyItem::ReturnStatement(return_statement) => Some(&return_statement.argument),
};
// Check if the value's non code meta contains the position.
if let Some(value) = value {
if let Some(non_code_meta) = value.get_non_code_meta() {
if non_code_meta.contains(pos) {
return Some(non_code_meta);
}
}
}
None
}
/// Returns all the lsp symbols in the program.
pub fn get_lsp_symbols(&self, code: &str) -> Vec<DocumentSymbol> {
let mut symbols = vec![];
@ -472,24 +431,6 @@ impl Value {
}
}
// Get the non code meta for the value.
pub fn get_non_code_meta(&self) -> Option<&NonCodeMeta> {
match self {
Value::BinaryExpression(_bin_exp) => None,
Value::ArrayExpression(_array_exp) => None,
Value::ObjectExpression(_obj_exp) => None,
Value::MemberExpression(_mem_exp) => None,
Value::Literal(_literal) => None,
Value::FunctionExpression(_func_exp) => None,
Value::CallExpression(_call_exp) => None,
Value::Identifier(_ident) => None,
Value::PipeExpression(pipe_exp) => Some(&pipe_exp.non_code_meta),
Value::UnaryExpression(_unary_exp) => None,
Value::PipeSubstitution(_pipe_substitution) => None,
Value::None(_none) => None,
}
}
pub fn replace_value(&mut self, source_range: SourceRange, new_value: Value) {
if source_range == self.clone().into() {
*self = new_value;
@ -795,10 +736,6 @@ pub struct NonCodeNode {
}
impl NonCodeNode {
pub fn contains(&self, pos: usize) -> bool {
self.start <= pos && pos <= self.end
}
pub fn value(&self) -> String {
match &self.value {
NonCodeValue::InlineComment { value, style: _ } => value.clone(),
@ -818,27 +755,18 @@ impl NonCodeNode {
value,
style: CommentStyle::Block,
} => format!(" /* {} */", value),
NonCodeValue::BlockComment { value, style } => match style {
CommentStyle::Block => format!("{}/* {} */", indentation, value),
CommentStyle::Line => {
if value.trim().is_empty() {
format!("{}//\n", indentation)
} else {
format!("{}// {}\n", indentation, value.trim())
}
NonCodeValue::BlockComment { value, style } => {
let add_start_new_line = if self.start == 0 { "" } else { "\n" };
match style {
CommentStyle::Block => format!("{}{}/* {} */", add_start_new_line, indentation, value),
CommentStyle::Line => format!("{}{}// {}\n", add_start_new_line, indentation, value),
}
},
}
NonCodeValue::NewLineBlockComment { value, style } => {
let add_start_new_line = if self.start == 0 { "" } else { "\n\n" };
match style {
CommentStyle::Block => format!("{}{}/* {} */\n", add_start_new_line, indentation, value),
CommentStyle::Line => {
if value.trim().is_empty() {
format!("{}{}//\n", add_start_new_line, indentation)
} else {
format!("{}{}// {}\n", add_start_new_line, indentation, value.trim())
}
}
CommentStyle::Line => format!("{}{}// {}\n", add_start_new_line, indentation, value),
}
}
NonCodeValue::NewLine => "\n\n".to_string(),
@ -935,16 +863,6 @@ impl NonCodeMeta {
pub fn insert(&mut self, i: usize, new: NonCodeNode) {
self.non_code_nodes.entry(i).or_default().push(new);
}
pub fn contains(&self, pos: usize) -> bool {
if self.start.iter().any(|node| node.contains(pos)) {
return true;
}
self.non_code_nodes
.iter()
.any(|(_, nodes)| nodes.iter().any(|node| node.contains(pos)))
}
}
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema, Bake)]
@ -2615,13 +2533,7 @@ impl PipeExpression {
let non_code_meta = self.non_code_meta.clone();
if let Some(non_code_meta_value) = non_code_meta.non_code_nodes.get(&index) {
for val in non_code_meta_value {
let formatted = val.format(&indentation).trim_end_matches('\n').to_string();
if let NonCodeValue::BlockComment { .. } = val.value {
s += "\n";
s += &formatted;
} else {
s += &formatted;
}
s += val.format(&indentation).trim_end_matches('\n')
}
}
@ -3194,109 +3106,6 @@ show(part001)"#;
"#
);
}
#[test]
fn test_recast_comment_under_variable() {
let some_program_string = r#"const key = 'c'
// this is also a comment
const thing = 'foo'
"#;
let tokens = crate::token::lexer(some_program_string);
let parser = crate::parser::Parser::new(tokens);
let program = parser.ast().unwrap();
let recasted = program.recast(&Default::default(), 0);
assert_eq!(
recasted,
r#"const key = 'c'
// this is also a comment
const thing = 'foo'
"#
);
}
#[test]
fn test_recast_multiline_comment_start_file() {
let some_program_string = r#"// hello world
// I am a comment
const key = 'c'
// this is also a comment
// hello
const thing = 'foo'
"#;
let tokens = crate::token::lexer(some_program_string);
let parser = crate::parser::Parser::new(tokens);
let program = parser.ast().unwrap();
let recasted = program.recast(&Default::default(), 0);
assert_eq!(
recasted,
r#"// hello world
// I am a comment
const key = 'c'
// this is also a comment
// hello
const thing = 'foo'
"#
);
}
#[test]
fn test_recast_empty_comment() {
let some_program_string = r#"// hello world
//
// I am a comment
const key = 'c'
//
// I am a comment
const thing = 'c'
const foo = 'bar' //
"#;
let tokens = crate::token::lexer(some_program_string);
let parser = crate::parser::Parser::new(tokens);
let program = parser.ast().unwrap();
let recasted = program.recast(&Default::default(), 0);
assert_eq!(
recasted,
r#"// hello world
//
// I am a comment
const key = 'c'
//
// I am a comment
const thing = 'c'
const foo = 'bar' //
"#
);
}
#[test]
fn test_recast_multiline_comment_under_variable() {
let some_program_string = r#"const key = 'c'
// this is also a comment
// hello
const thing = 'foo'
"#;
let tokens = crate::token::lexer(some_program_string);
let parser = crate::parser::Parser::new(tokens);
let program = parser.ast().unwrap();
let recasted = program.recast(&Default::default(), 0);
assert_eq!(
recasted,
r#"const key = 'c'
// this is also a comment
// hello
const thing = 'foo'
"#
);
}
#[test]
fn test_recast_comment_at_start() {
let test_program = r#"
@ -3572,97 +3381,6 @@ show(mySuperCoolPart)
);
}
#[test]
fn test_recast_trailing_comma() {
let some_program_string = r#"startSketchOn('XY')
|> startProfileAt([0, 0], %)
|> arc({
radius: 1,
angle_start: 0,
angle_end: 180,
}, %)"#;
let tokens = crate::token::lexer(some_program_string);
let parser = crate::parser::Parser::new(tokens);
let program = parser.ast().unwrap();
let recasted = program.recast(&Default::default(), 0);
assert_eq!(
recasted,
r#"startSketchOn('XY')
|> startProfileAt([0, 0], %)
|> arc({
radius: 1,
angle_start: 0,
angle_end: 180
}, %)
"#
);
}
#[test]
fn test_ast_get_non_code_node() {
let some_program_string = r#"const r = 20 / pow(pi(), 1 / 3)
const h = 30
// st
const cylinder = startSketchOn('-XZ')
|> startProfileAt([50, 0], %)
|> arc({
angle_end: 360,
angle_start: 0,
radius: r
}, %)
|> extrude(h, %)
"#;
let tokens = crate::token::lexer(some_program_string);
let parser = crate::parser::Parser::new(tokens);
let program = parser.ast().unwrap();
let value = program.get_non_code_meta_for_position(50);
assert!(value.is_some());
}
#[test]
fn test_ast_get_non_code_node_pipe() {
let some_program_string = r#"const r = 20 / pow(pi(), 1 / 3)
const h = 30
// st
const cylinder = startSketchOn('-XZ')
|> startProfileAt([50, 0], %)
// comment
|> arc({
angle_end: 360,
angle_start: 0,
radius: r
}, %)
|> extrude(h, %)
"#;
let tokens = crate::token::lexer(some_program_string);
let parser = crate::parser::Parser::new(tokens);
let program = parser.ast().unwrap();
let value = program.get_non_code_meta_for_position(124);
assert!(value.is_some());
}
#[test]
fn test_ast_get_non_code_node_inline_comment() {
let some_program_string = r#"const part001 = startSketchOn('XY')
|> startProfileAt([0,0], %)
|> xLine(5, %) // lin
"#;
let tokens = crate::token::lexer(some_program_string);
let parser = crate::parser::Parser::new(tokens);
let program = parser.ast().unwrap();
let value = program.get_non_code_meta_for_position(86);
assert!(value.is_some());
}
#[test]
fn test_recast_negative_var() {
let some_program_string = r#"const w = 20
@ -3689,51 +3407,6 @@ show(firstExtrude)"#;
const l = 8
const h = 10
const firstExtrude = startSketchOn('XY')
|> startProfileAt([0, 0], %)
|> line([0, l], %)
|> line([w, 0], %)
|> line([0, -l], %)
|> close(%)
|> extrude(h, %)
show(firstExtrude)
"#
);
}
#[test]
fn test_recast_multiline_comment() {
let some_program_string = r#"const w = 20
const l = 8
const h = 10
// This is my comment
// It has multiple lines
// And it's really long
const firstExtrude = startSketchOn('XY')
|> startProfileAt([0,0], %)
|> line([0, l], %)
|> line([w, 0], %)
|> line([0, -l], %)
|> close(%)
|> extrude(h, %)
show(firstExtrude)"#;
let tokens = crate::token::lexer(some_program_string);
let parser = crate::parser::Parser::new(tokens);
let program = parser.ast().unwrap();
let recasted = program.recast(&Default::default(), 0);
assert_eq!(
recasted,
r#"const w = 20
const l = 8
const h = 10
// This is my comment
// It has multiple lines
// And it's really long
const firstExtrude = startSketchOn('XY')
|> startProfileAt([0, 0], %)
|> line([0, l], %)

View File

@ -440,7 +440,6 @@ fn object(i: TokenSlice) -> PResult<ObjectExpression> {
"a comma-separated list of key-value pairs, e.g. 'height: 4, width: 3'",
))
.parse_next(i)?;
ignore_trailing_comma(i);
ignore_whitespace(i);
let end = close_brace(i)?.end;
Ok(ObjectExpression { start, end, properties })
@ -978,11 +977,6 @@ fn ignore_whitespace(i: TokenSlice) {
let _: PResult<()> = repeat(0.., whitespace).parse_next(i);
}
// A helper function to ignore a trailing comma.
fn ignore_trailing_comma(i: TokenSlice) {
let _ = opt(comma).parse_next(i);
}
/// Matches at least 1 whitespace.
fn require_whitespace(i: TokenSlice) -> PResult<()> {
repeat(1.., whitespace).parse_next(i)

View File

@ -23,8 +23,8 @@ pub struct LinearPatternData {
pub repetitions: usize,
/// The distance between each repetition. This can also be referred to as spacing.
pub distance: f64,
/// The axis of the pattern. This is a 2D vector.
pub axis: [f64; 2],
/// The axis of the pattern. This is a 3D vector.
pub axis: [f64; 3],
}
/// Data for a circular pattern.
@ -36,8 +36,8 @@ pub struct CircularPatternData {
/// This excludes the original entity. For example, if `repetitions` is 1,
/// the original entity will be copied once.
pub repetitions: usize,
/// The axis around which to make the pattern. This is a 2D vector.
pub axis: [f64; 2],
/// 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.
pub center: [f64; 3],
/// The arc angle (in degrees) to place the repetitions. Must be greater than 0.
@ -50,7 +50,7 @@ pub struct CircularPatternData {
pub async fn pattern_linear(args: Args) -> Result<MemoryItem, KclError> {
let (data, geometry): (LinearPatternData, Geometry) = args.get_data_and_geometry()?;
if data.axis == [0.0, 0.0] {
if data.axis == [0.0, 0.0, 0.0] {
return Err(KclError::Semantic(KclErrorDetails {
message:
"The axis of the linear pattern cannot be the zero vector. Otherwise they will just duplicate in place."
@ -70,7 +70,7 @@ pub async fn pattern_linear(args: Args) -> Result<MemoryItem, KclError> {
pub async fn pattern_circular(args: Args) -> Result<MemoryItem, KclError> {
let (data, geometry): (CircularPatternData, Geometry) = args.get_data_and_geometry()?;
if data.axis == [0.0, 0.0] {
if data.axis == [0.0, 0.0, 0.0] {
return Err(KclError::Semantic(KclErrorDetails {
message:
"The axis of the circular pattern cannot be the zero vector. Otherwise they will just duplicate in place."
@ -97,11 +97,7 @@ async fn inner_pattern_linear(data: LinearPatternData, geometry: Geometry, args:
.send_modeling_cmd(
id,
ModelingCmd::EntityLinearPattern {
axis: kittycad::types::Point3D {
x: data.axis[0],
y: data.axis[1],
z: 0.0,
},
axis: data.axis.into(),
entity_id: geometry.id(),
num_repetitions: data.repetitions as u32,
spacing: data.distance,
@ -158,11 +154,7 @@ async fn inner_pattern_circular(
.send_modeling_cmd(
id,
ModelingCmd::EntityCircularPattern {
axis: kittycad::types::Point3D {
x: data.axis[0],
y: data.axis[1],
z: 0.0,
},
axis: data.axis.into(),
entity_id: geometry.id(),
center: data.center.into(),
num_repetitions: data.repetitions as u32,

View File

@ -53,7 +53,7 @@ fn block_comment(i: &mut Located<&str>) -> PResult<Token> {
}
fn line_comment(i: &mut Located<&str>) -> PResult<Token> {
let inner = (r#"//"#, take_till(0.., ['\n', '\r'])).recognize();
let inner = (r#"//"#, take_till(1.., ['\n', '\r'])).recognize();
let (value, range) = inner.with_span().parse_next(i)?;
Ok(Token::from_range(range, TokenType::LineComment, value.to_string()))
}

View File

@ -735,7 +735,7 @@ async fn serial_test_patterns_linear_basic() {
}
const part = circle([0,0], 2)
|> patternLinear({axis: [0,1], repetitions: 12, distance: 2}, %)
|> patternLinear({axis: [0,0,1], repetitions: 12, distance: 2}, %)
"#;
let result = execute_and_snapshot(code, kittycad::types::UnitLength::Mm)
@ -765,7 +765,7 @@ const part = startSketchOn('XY')
|> line([0, -1], %)
|> close(%)
|> extrude(1, %)
|> patternLinear({axis: [1, 0], repetitions: 3, distance: 6}, %)
|> patternLinear({axis: [1, 0,1], repetitions: 3, distance: 6}, %)
"#;
let result = execute_and_snapshot(code, kittycad::types::UnitLength::Mm)
@ -789,7 +789,7 @@ async fn serial_test_patterns_linear_basic_negative_distance() {
}
const part = circle([0,0], 2)
|> patternLinear({axis: [0,1], repetitions: 12, distance: -2}, %)
|> patternLinear({axis: [0,0,1], repetitions: 12, distance: -2}, %)
"#;
let result = execute_and_snapshot(code, kittycad::types::UnitLength::Mm)
@ -817,7 +817,7 @@ async fn serial_test_patterns_linear_basic_negative_axis() {
}
const part = circle([0,0], 2)
|> patternLinear({axis: [0,-1], repetitions: 12, distance: 2}, %)
|> patternLinear({axis: [0,0,-1], repetitions: 12, distance: 2}, %)
"#;
let result = execute_and_snapshot(code, kittycad::types::UnitLength::Mm)
@ -845,7 +845,7 @@ async fn serial_test_patterns_linear_basic_holes() {
}
const circles = circle([5, 5], 1)
|> patternLinear({axis: [1,1], repetitions: 12, distance: 3}, %)
|> patternLinear({axis: [1,1,0], repetitions: 12, distance: 3}, %)
const rectangle = startSketchOn('XY')
|> startProfileAt([0, 0], %)
@ -878,7 +878,7 @@ async fn serial_test_patterns_circular_basic_2d() {
}
const part = circle([0,0], 2)
|> patternCircular({axis: [0,1], center: [20, 20, 20], repetitions: 12, arcDegrees: 210, rotateDuplicates: true}, %)
|> patternCircular({axis: [0,0,1], center: [20, 20, 20], repetitions: 12, arcDegrees: 210, rotateDuplicates: true}, %)
"#;
let result = execute_and_snapshot(code, kittycad::types::UnitLength::Mm)
@ -908,7 +908,7 @@ const part = startSketchOn('XY')
|> line([0, -1], %)
|> close(%)
|> extrude(1, %)
|> patternCircular({axis: [0,1], center: [-20, -20, -20], repetitions: 40, arcDegrees: 360, rotateDuplicates: false}, %)
|> patternCircular({axis: [0,1,0], center: [-20, -20, -20], repetitions: 40, arcDegrees: 360, rotateDuplicates: false}, %)
"#;
let result = execute_and_snapshot(code, kittycad::types::UnitLength::Mm)
@ -938,7 +938,7 @@ const part = startSketchOn('XY')
|> line([0, -1], %)
|> close(%)
|> extrude(1, %)
|> patternCircular({axis: [1,1], center: [10, 0, 10], repetitions: 10, arcDegrees: 360, rotateDuplicates: true}, %)
|> patternCircular({axis: [1,1,-1], center: [10, 0, 10], repetitions: 10, arcDegrees: 360, rotateDuplicates: true}, %)
"#;
let result = execute_and_snapshot(code, kittycad::types::UnitLength::Mm)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 98 KiB

After

Width:  |  Height:  |  Size: 98 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 96 KiB

After

Width:  |  Height:  |  Size: 95 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 98 KiB

After

Width:  |  Height:  |  Size: 98 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 95 KiB

After

Width:  |  Height:  |  Size: 98 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 93 KiB

After

Width:  |  Height:  |  Size: 93 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 116 KiB

After

Width:  |  Height:  |  Size: 115 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 96 KiB

After

Width:  |  Height:  |  Size: 97 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 96 KiB

After

Width:  |  Height:  |  Size: 97 KiB

View File

@ -38,4 +38,4 @@ const config = defineConfig({
],
})
export default config
export default config

View File

@ -1801,10 +1801,10 @@
resolved "https://registry.yarnpkg.com/@juggle/resize-observer/-/resize-observer-3.4.0.tgz#08d6c5e20cf7e4cc02fd181c4b0c225cd31dbb60"
integrity sha512-dfLbk+PwWvFzSxwk3n5ySL0hfBog779o8h68wK/7/APo/7cgyWp5jcXockbxdk5kFRkbeXWm4Fbi9FrdN381sA==
"@kittycad/lib@^0.0.54":
version "0.0.54"
resolved "https://registry.yarnpkg.com/@kittycad/lib/-/lib-0.0.54.tgz#6744977a2048152a425809d690e986054213ceab"
integrity sha512-4fsQLo0+TDn65p4uAUa46/TpWvN55MCu5Yd5hriyF7Xt9PCrdvDsgBisn79Y5dPkh6lq5TMy16T+a1yKcdh/kg==
"@kittycad/lib@^0.0.53":
version "0.0.53"
resolved "https://registry.yarnpkg.com/@kittycad/lib/-/lib-0.0.53.tgz#32f10f63428c5f3bb6a435507dbfa72c1e7ba10d"
integrity sha512-a0WTVVGKE+J7I1bERn8pcr8cC5/X14dFi78Y7wAsu8ok/SuHVTPoPHVCZ8bUmcWzY1iWpLC/HCIIHigXjkF3ZA==
dependencies:
node-fetch "3.3.2"
openapi-types "^12.0.0"
@ -2030,6 +2030,66 @@
resolved "https://registry.yarnpkg.com/@rushstack/eslint-patch/-/eslint-patch-1.3.2.tgz#31b9c510d8cada9683549e1dbb4284cca5001faf"
integrity sha512-V+MvGwaHH03hYhY+k6Ef/xKd6RYlc4q8WBx+2ANmipHJcKuktNcI/NgEsJgdSUF6Lw32njT6OnrRsKYCdgHjYw==
"@sentry-internal/tracing@7.77.0":
version "7.77.0"
resolved "https://registry.yarnpkg.com/@sentry-internal/tracing/-/tracing-7.77.0.tgz#f3d82486f8934a955b3dd2aa54c8d29586e42a37"
integrity sha512-8HRF1rdqWwtINqGEdx8Iqs9UOP/n8E0vXUu3Nmbqj4p5sQPA7vvCfq+4Y4rTqZFc7sNdFpDsRION5iQEh8zfZw==
dependencies:
"@sentry/core" "7.77.0"
"@sentry/types" "7.77.0"
"@sentry/utils" "7.77.0"
"@sentry/browser@7.77.0":
version "7.77.0"
resolved "https://registry.yarnpkg.com/@sentry/browser/-/browser-7.77.0.tgz#155440f1a0d3a1bbd5d564c28d6b0c9853a51d72"
integrity sha512-nJ2KDZD90H8jcPx9BysQLiQW+w7k7kISCWeRjrEMJzjtge32dmHA8G4stlUTRIQugy5F+73cOayWShceFP7QJQ==
dependencies:
"@sentry-internal/tracing" "7.77.0"
"@sentry/core" "7.77.0"
"@sentry/replay" "7.77.0"
"@sentry/types" "7.77.0"
"@sentry/utils" "7.77.0"
"@sentry/core@7.77.0":
version "7.77.0"
resolved "https://registry.yarnpkg.com/@sentry/core/-/core-7.77.0.tgz#21100843132beeeff42296c8370cdcc7aa1d8510"
integrity sha512-Tj8oTYFZ/ZD+xW8IGIsU6gcFXD/gfE+FUxUaeSosd9KHwBQNOLhZSsYo/tTVf/rnQI/dQnsd4onPZLiL+27aTg==
dependencies:
"@sentry/types" "7.77.0"
"@sentry/utils" "7.77.0"
"@sentry/react@^7.77.0":
version "7.77.0"
resolved "https://registry.yarnpkg.com/@sentry/react/-/react-7.77.0.tgz#9da14e4b21eae4b5a6306d39bb7c42ef0827d2c2"
integrity sha512-Q+htKzib5em0MdaQZMmPomaswaU3xhcVqmLi2CxqQypSjbYgBPPd+DuhrXKoWYLDDkkbY2uyfe4Lp3yLRWeXYw==
dependencies:
"@sentry/browser" "7.77.0"
"@sentry/types" "7.77.0"
"@sentry/utils" "7.77.0"
hoist-non-react-statics "^3.3.2"
"@sentry/replay@7.77.0":
version "7.77.0"
resolved "https://registry.yarnpkg.com/@sentry/replay/-/replay-7.77.0.tgz#21d242c9cd70a7235237216174873fd140b6eb80"
integrity sha512-M9Ik2J5ekl+C1Och3wzLRZVaRGK33BlnBwfwf3qKjgLDwfKW+1YkwDfTHbc2b74RowkJbOVNcp4m8ptlehlSaQ==
dependencies:
"@sentry-internal/tracing" "7.77.0"
"@sentry/core" "7.77.0"
"@sentry/types" "7.77.0"
"@sentry/utils" "7.77.0"
"@sentry/types@7.77.0":
version "7.77.0"
resolved "https://registry.yarnpkg.com/@sentry/types/-/types-7.77.0.tgz#c5d00fe547b89ccde59cdea59143bf145cee3144"
integrity sha512-nfb00XRJVi0QpDHg+JkqrmEBHsqBnxJu191Ded+Cs1OJ5oPXEW6F59LVcBScGvMqe+WEk1a73eH8XezwfgrTsA==
"@sentry/utils@7.77.0":
version "7.77.0"
resolved "https://registry.yarnpkg.com/@sentry/utils/-/utils-7.77.0.tgz#1f88501f0b8777de31b371cf859d13c82ebe1379"
integrity sha512-NmM2kDOqVchrey3N5WSzdQoCsyDkQkiRxExPaNI2oKQ/jMWHs9yt0tSy7otPBcXs0AP59ihl75Bvm1tDRcsp5g==
dependencies:
"@sentry/types" "7.77.0"
"@sideway/address@^4.1.3":
version "4.1.4"
resolved "https://registry.yarnpkg.com/@sideway/address/-/address-4.1.4.tgz#03dccebc6ea47fdc226f7d3d1ad512955d4783f0"
@ -5353,7 +5413,7 @@ he@1.2.0, he@^1.2.0:
resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f"
integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==
hoist-non-react-statics@^3.3.0:
hoist-non-react-statics@^3.3.0, hoist-non-react-statics@^3.3.2:
version "3.3.2"
resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45"
integrity sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==