Compare commits
21 Commits
kurt-add-s
...
v0.15.4
Author | SHA1 | Date | |
---|---|---|---|
f3e0939057 | |||
f5e233d8a0 | |||
1cab3e628f | |||
2ca6ba52b6 | |||
f741ea2e09 | |||
9f2a7781fc | |||
990f2b4154 | |||
0af0f15281 | |||
b558548b94 | |||
29e0f9a270 | |||
9385c32cfb | |||
ce3fb5c353 | |||
f920490518 | |||
d681e667ee | |||
5c6515a60e | |||
eb8a33312d | |||
d351b3bbe4 | |||
47d40eb801 | |||
adc4b6148d | |||
27d0d4a28b | |||
fb609c19ef |
@ -3,4 +3,3 @@ 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=
|
||||
|
@ -3,4 +3,3 @@ 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=
|
||||
|
16
.github/workflows/cargo-test.yml
vendored
@ -40,6 +40,20 @@ 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
|
||||
@ -48,7 +62,7 @@ jobs:
|
||||
shell: bash
|
||||
run: |-
|
||||
cd "${{ matrix.dir }}"
|
||||
cargo nextest run --workspace --no-fail-fast -P ci
|
||||
cargo nextest run --workspace --no-fail-fast -P ci 2>&1 | tee /tmp/github-actions.log
|
||||
env:
|
||||
KITTYCAD_API_TOKEN: ${{secrets.KITTYCAD_API_TOKEN}}
|
||||
RUST_MIN_STACK: 10485760000
|
||||
|
2
.github/workflows/ci.yml
vendored
@ -336,7 +336,7 @@ jobs:
|
||||
cat last_download.json
|
||||
|
||||
- name: Authenticate to Google Cloud
|
||||
uses: 'google-github-actions/auth@v2.1.1'
|
||||
uses: 'google-github-actions/auth@v2.1.2'
|
||||
with:
|
||||
credentials_json: '${{ secrets.GOOGLE_CLOUD_DL_SA }}'
|
||||
|
||||
|
21
.github/workflows/vector.toml
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
[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"
|
2
.gitignore
vendored
@ -54,3 +54,5 @@ e2e/playwright/export-snapshots/*embedded.gltf
|
||||
|
||||
## generated files
|
||||
src/**/*.typegen.ts
|
||||
|
||||
src/wasm-lib/grackle/stdlib_cube_partial.json
|
||||
|
@ -8,10 +8,6 @@ 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
|
||||
|
@ -32506,14 +32506,14 @@
|
||||
"format": "double"
|
||||
},
|
||||
"axis": {
|
||||
"description": "The axis around which to make the pattern. This is a 3D vector.",
|
||||
"description": "The axis around which to make the pattern. This is a 2D vector.",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "number",
|
||||
"format": "double"
|
||||
},
|
||||
"maxItems": 3,
|
||||
"minItems": 3
|
||||
"maxItems": 2,
|
||||
"minItems": 2
|
||||
},
|
||||
"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 3D vector.",
|
||||
"description": "The axis of the pattern. This is a 2D vector.",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "number",
|
||||
"format": "double"
|
||||
},
|
||||
"maxItems": 3,
|
||||
"minItems": 3
|
||||
"maxItems": 2,
|
||||
"minItems": 2
|
||||
},
|
||||
"distance": {
|
||||
"description": "The distance between each repetition. This can also be referred to as spacing.",
|
||||
|
@ -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 3D vector.
|
||||
axis: [number, number, number],
|
||||
// The axis around which to make the pattern. This is a 2D vector.
|
||||
axis: [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 3D vector.
|
||||
axis: [number, number, number],
|
||||
// The axis of the pattern. This is a 2D vector.
|
||||
axis: [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.
|
||||
|
@ -3,6 +3,7 @@ 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
|
||||
@ -15,9 +16,9 @@ document.addEventListener('mousemove', (e) =>
|
||||
*/
|
||||
|
||||
const commonPoints = {
|
||||
startAt: '[26.38, -35.59]',
|
||||
num1: 26.63,
|
||||
num2: 53.01,
|
||||
startAt: '[0.93, -1.26]',
|
||||
num1: 0.95,
|
||||
num2: 1.88,
|
||||
}
|
||||
|
||||
test.beforeEach(async ({ context, page }) => {
|
||||
@ -101,13 +102,13 @@ test('Basic sketch', async ({ page }) => {
|
||||
.toHaveText(`const part001 = startSketchOn('-XZ')
|
||||
|> startProfileAt(${commonPoints.startAt}, %)
|
||||
|> line([${commonPoints.num1}, 0], %)
|
||||
|> line([0, ${commonPoints.num1}], %)`)
|
||||
|> line([0, ${commonPoints.num1 - 0.01}], %)`)
|
||||
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}], %)
|
||||
|> line([0, ${commonPoints.num1 - 0.01}], %)
|
||||
|> line([-${commonPoints.num2}, 0], %)`)
|
||||
|
||||
// deselect line tool
|
||||
@ -132,7 +133,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}], %)
|
||||
|> line([0, ${commonPoints.num1 - 0.01}], %)
|
||||
|> angledLine([180, segLen('seg01', %)], %)`)
|
||||
})
|
||||
|
||||
@ -284,10 +285,9 @@ 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([32.13, -43.34], %)`
|
||||
|> startProfileAt([1.14, -1.54], %)`
|
||||
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: 300 }, // green plane
|
||||
clickCoords: { x: 700, y: 250 }, // green plane
|
||||
})
|
||||
await TestSinglePlane({
|
||||
viewCmd: camPos,
|
||||
@ -488,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}], %)`)
|
||||
|> line([0, ${commonPoints.num1 - 0.01}], %)`)
|
||||
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}], %)
|
||||
|> line([0, ${commonPoints.num1 - 0.01}], %)
|
||||
|> line([-${commonPoints.num2}, 0], %)`)
|
||||
|
||||
// deselect line tool
|
||||
@ -765,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}], %)`)
|
||||
|> line([0, ${commonPoints.num1 - 0.01}], %)`)
|
||||
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}], %)
|
||||
|> line([0, ${commonPoints.num1 - 0.01}], %)
|
||||
|> line([-${commonPoints.num2}, 0], %)`
|
||||
await expect(page.locator('.cm-content')).toHaveText(finalCodeFirstSketch)
|
||||
|
||||
@ -793,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 = '[26.23, -35.39]'
|
||||
const startAt2 = '[0.93,-1.25]'
|
||||
await expect(
|
||||
(await page.locator('.cm-content').innerText()).replace(/\s/g, '')
|
||||
).toBe(
|
||||
@ -807,7 +807,7 @@ const part002 = startSketchOn('XY')
|
||||
await page.mouse.click(startXPx + PUR * 20, 500 - PUR * 10)
|
||||
await page.waitForTimeout(100)
|
||||
|
||||
const num2 = 26.48
|
||||
const num2 = 0.94
|
||||
await expect(
|
||||
(await page.locator('.cm-content').innerText()).replace(/\s/g, '')
|
||||
).toBe(
|
||||
@ -825,7 +825,7 @@ const part002 = startSketchOn('XY')
|
||||
const part002 = startSketchOn('XY')
|
||||
|> startProfileAt(${startAt2}, %)
|
||||
|> line([${num2}, 0], %)
|
||||
|> line([0, ${num2}], %)`.replace(/\s/g, '')
|
||||
|> line([0, ${roundOff(num2 - 0.01)}], %)`.replace(/\s/g, '')
|
||||
)
|
||||
await page.mouse.click(startXPx, 500 - PUR * 20)
|
||||
await expect(
|
||||
@ -835,8 +835,8 @@ const part002 = startSketchOn('XY')
|
||||
const part002 = startSketchOn('XY')
|
||||
|> startProfileAt(${startAt2}, %)
|
||||
|> line([${num2}, 0], %)
|
||||
|> line([0, ${num2}], %)
|
||||
|> line([-52.71, 0], %)`.replace(/\s/g, '')
|
||||
|> line([0, ${roundOff(num2 - 0.01)}], %)
|
||||
|> line([-1.87, 0], %)`.replace(/\s/g, '')
|
||||
)
|
||||
})
|
||||
|
||||
|
@ -435,7 +435,23 @@ test('extrude on each default plane should be stable', async ({
|
||||
await runSnapshotsForOtherPlanes('-YZ')
|
||||
})
|
||||
|
||||
test('Draft segments should look right', async ({ page }) => {
|
||||
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',
|
||||
})
|
||||
)
|
||||
})
|
||||
const u = getUtils(page)
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
const PUR = 400 / 37.5 //pixeltoUnitRatio
|
||||
@ -468,7 +484,7 @@ test('Draft segments should look right', async ({ page }) => {
|
||||
await page.mouse.click(startXPx + PUR * 10, 500 - PUR * 10)
|
||||
await expect(page.locator('.cm-content'))
|
||||
.toHaveText(`const part001 = startSketchOn('-XZ')
|
||||
|> startProfileAt(${commonPoints.startAt}, %)`)
|
||||
|> startProfileAt([0.93, -1.26], %)`)
|
||||
await page.waitForTimeout(100)
|
||||
|
||||
await u.closeDebugPanel()
|
||||
@ -482,8 +498,8 @@ test('Draft segments should look right', async ({ page }) => {
|
||||
|
||||
await expect(page.locator('.cm-content'))
|
||||
.toHaveText(`const part001 = startSketchOn('-XZ')
|
||||
|> startProfileAt(${commonPoints.startAt}, %)
|
||||
|> line([${commonPoints.num1}, 0], %)`)
|
||||
|> startProfileAt([0.93, -1.26], %)
|
||||
|> line([0.95, 0], %)`)
|
||||
|
||||
await page.getByRole('button', { name: 'Tangential Arc' }).click()
|
||||
|
||||
@ -493,3 +509,195 @@ test('Draft segments should look right', async ({ page }) => {
|
||||
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,
|
||||
})
|
||||
})
|
||||
|
After Width: | Height: | Size: 44 KiB |
After Width: | Height: | Size: 45 KiB |
After Width: | Height: | Size: 48 KiB |
After Width: | Height: | Size: 48 KiB |
Before Width: | Height: | Size: 40 KiB After Width: | Height: | Size: 35 KiB |
Before Width: | Height: | Size: 44 KiB After Width: | Height: | Size: 38 KiB |
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "untitled-app",
|
||||
"version": "0.15.2",
|
||||
"version": "0.15.4",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@codemirror/autocomplete": "^6.10.2",
|
||||
@ -10,12 +10,11 @@
|
||||
"@fortawesome/react-fontawesome": "^0.2.0",
|
||||
"@headlessui/react": "^1.7.17",
|
||||
"@headlessui/tailwindcss": "^0.2.0",
|
||||
"@kittycad/lib": "^0.0.53",
|
||||
"@kittycad/lib": "^0.0.54",
|
||||
"@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",
|
||||
|
18
src-tauri/Cargo.lock
generated
@ -67,9 +67,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.79"
|
||||
version = "1.0.80"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "080e9890a082662b09c1ad45f567faeeb47f22b5fb23895fbe1e651e718e25ca"
|
||||
checksum = "5ad32ce52e4161730f7098c077cd2ed6229b5804ccf99e5366be1ab72a98b4e1"
|
||||
|
||||
[[package]]
|
||||
name = "app"
|
||||
@ -3235,9 +3235,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.196"
|
||||
version = "1.0.197"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "870026e60fa08c69f064aa766c10f10b1d62db9ccd4d0abb206472bee0ce3b32"
|
||||
checksum = "3fb1c873e1b9b056a4dc4c0c198b24c3ffa059243875552b2bd0933b1aee4ce2"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
@ -3253,9 +3253,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.196"
|
||||
version = "1.0.197"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "33c85360c95e7d137454dc81d9a4ed2b8efd8fbe19cee57357b32b9771fccb67"
|
||||
checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@ -3275,9 +3275,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.113"
|
||||
version = "1.0.114"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "69801b70b1c3dac963ecb03a364ba0ceda9cf60c71cfe475e99864759c8b8a79"
|
||||
checksum = "c5f09b1bd632ef549eaa9f60a1f8de742bdbc698e6cee2095fc84dde5f549ae0"
|
||||
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#01211ff0759d578e0e9ac8c98c31fdf09077eb34"
|
||||
source = "git+https://github.com/tauri-apps/plugins-workspace?branch=v1#ed682dd96eb765e7cd3cdbc3cc64f794a0d6f9df"
|
||||
dependencies = [
|
||||
"log",
|
||||
"serde",
|
||||
|
@ -7,7 +7,7 @@
|
||||
},
|
||||
"package": {
|
||||
"productName": "zoo-modeling-app",
|
||||
"version": "0.15.2"
|
||||
"version": "0.15.4"
|
||||
},
|
||||
"tauri": {
|
||||
"allowlist": {
|
||||
|
@ -38,8 +38,6 @@ 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'
|
||||
@ -48,38 +46,6 @@ 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]
|
||||
|
@ -151,6 +151,17 @@ 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 = {
|
||||
@ -209,6 +220,7 @@ export class CameraControls {
|
||||
this.onWindowResize()
|
||||
|
||||
this.update()
|
||||
this._usePerspectiveCamera()
|
||||
}
|
||||
|
||||
private _isCamMovingCallback: (isMoving: boolean, isTween: boolean) => void =
|
||||
@ -242,6 +254,7 @@ export class CameraControls {
|
||||
onMouseDown = (event: MouseEvent) => {
|
||||
this.isDragging = true
|
||||
this.mouseDownPosition.set(event.clientX, event.clientY)
|
||||
this.handleStart()
|
||||
}
|
||||
|
||||
onMouseMove = (event: MouseEvent) => {
|
||||
@ -297,15 +310,18 @@ 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 = () => {
|
||||
@ -358,7 +374,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
|
||||
@ -374,14 +390,17 @@ 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.fov,
|
||||
fov_y:
|
||||
this.camera instanceof PerspectiveCamera ? this.camera.fov : 45,
|
||||
...calculateNearFarFromFOV(this.lastPerspectiveFov),
|
||||
},
|
||||
},
|
||||
|
@ -13,7 +13,6 @@ import {
|
||||
Quaternion,
|
||||
Scene,
|
||||
Shape,
|
||||
SphereGeometry,
|
||||
Vector2,
|
||||
Vector3,
|
||||
} from 'three'
|
||||
@ -86,7 +85,6 @@ 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.
|
||||
@ -108,9 +106,10 @@ 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)
|
||||
: perspScale(sceneInfra.camControls.camera, segment)) /
|
||||
sceneInfra._baseUnitMultiplier
|
||||
if (
|
||||
segment.userData.from &&
|
||||
segment.userData.to &&
|
||||
@ -145,9 +144,9 @@ class SceneEntities {
|
||||
? orthoFactor
|
||||
: perspScale(sceneInfra.camControls.camera, this.axisGroup)
|
||||
const x = this.axisGroup.getObjectByName(X_AXIS)
|
||||
x?.scale.set(1, factor, 1)
|
||||
x?.scale.set(1, factor / sceneInfra._baseUnitMultiplier, 1)
|
||||
const y = this.axisGroup.getObjectByName(Y_AXIS)
|
||||
y?.scale.set(factor, 1, 1)
|
||||
y?.scale.set(factor / sceneInfra._baseUnitMultiplier, 1, 1)
|
||||
}
|
||||
}
|
||||
|
||||
@ -171,6 +170,7 @@ 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,6 +210,14 @@ 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)
|
||||
@ -241,16 +249,12 @@ 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
|
||||
}) {
|
||||
if (!skipListeners) {
|
||||
sceneInfra.resetMouseListeners()
|
||||
}
|
||||
sceneInfra.resetMouseListeners()
|
||||
this.createIntersectionPlane()
|
||||
|
||||
const { truncatedAst, programMemoryOverride, variableDeclarationName } =
|
||||
@ -285,9 +289,10 @@ 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)
|
||||
: perspScale(sceneInfra.camControls.camera, dummy)) /
|
||||
sceneInfra._baseUnitMultiplier
|
||||
sketchGroup.value.forEach((segment, index) => {
|
||||
let segPathToNode = getNodePathFromSourceRange(
|
||||
draftSegment ? truncatedAst : kclManager.ast,
|
||||
@ -332,60 +337,11 @@ class SceneEntities {
|
||||
this.currentSketchQuaternion
|
||||
)
|
||||
|
||||
let addingNewSegmentStatus: 'nothing' | 'pending' | 'added' = 'nothing'
|
||||
|
||||
this.scene.add(group)
|
||||
if (!draftSegment && !skipListeners) {
|
||||
if (!draftSegment) {
|
||||
sceneInfra.setCallbacks({
|
||||
onDragEnd: async () => {
|
||||
if (addingNewSegmentStatus !== 'nothing') {
|
||||
await this.tearDownSketch({ removeAxis: false })
|
||||
this.setupSketch({ sketchPathToNode })
|
||||
}
|
||||
},
|
||||
onDrag: async (args) => {
|
||||
onDrag: (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,
|
||||
@ -446,7 +402,7 @@ class SceneEntities {
|
||||
}
|
||||
},
|
||||
})
|
||||
} else if (draftSegment && !skipListeners) {
|
||||
} else {
|
||||
sceneInfra.setCallbacks({
|
||||
onDrag: () => {},
|
||||
onClick: async (args) => {
|
||||
@ -459,7 +415,7 @@ class SceneEntities {
|
||||
const isClosingSketch = compareVec2Epsilon2(
|
||||
firstSeg.from,
|
||||
[intersection2d.x, intersection2d.y],
|
||||
1
|
||||
0.5
|
||||
)
|
||||
let modifiedAst
|
||||
if (isClosingSketch) {
|
||||
@ -555,7 +511,6 @@ class SceneEntities {
|
||||
variableDeclarationName: string
|
||||
}
|
||||
}) {
|
||||
if (object.name === STRAIGHT_SEGMENT_BODY) return
|
||||
const group = getParentGroup(object)
|
||||
if (!group) return
|
||||
const pathToNode: PathToNode = JSON.parse(
|
||||
@ -623,9 +578,10 @@ 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)
|
||||
: perspScale(sceneInfra.camControls.camera, group)) /
|
||||
sceneInfra._baseUnitMultiplier
|
||||
if (type === TANGENTIAL_ARC_TO_SEGMENT) {
|
||||
this.updateTangentialArcToSegment({
|
||||
prevSegment: sketchGroup[index - 1],
|
||||
@ -662,7 +618,9 @@ class SceneEntities {
|
||||
group.userData.from = from
|
||||
group.userData.to = to
|
||||
group.userData.prevSegment = prevSegment
|
||||
const arrowGroup = group.getObjectByName(ARROWHEAD) as Group
|
||||
const arrowGroup = group.children.find(
|
||||
(child) => child.userData.type === ARROWHEAD
|
||||
) as Group
|
||||
|
||||
arrowGroup.position.set(to[0], to[1], 0)
|
||||
|
||||
@ -737,7 +695,9 @@ 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.getObjectByName(ARROWHEAD) as Group
|
||||
const arrowGroup = group.children.find(
|
||||
(child) => child.userData.type === ARROWHEAD
|
||||
) as Group
|
||||
|
||||
arrowGroup.position.set(to[0], to[1], 0)
|
||||
|
||||
@ -750,32 +710,6 @@ 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
|
||||
|
@ -18,9 +18,8 @@ import {
|
||||
Intersection,
|
||||
Object3D,
|
||||
Object3DEventMap,
|
||||
BoxGeometry,
|
||||
} from 'three'
|
||||
import { compareVec2Epsilon2 } from 'lang/std/sketch'
|
||||
import { Coords2d, compareVec2Epsilon2 } from 'lang/std/sketch'
|
||||
import { useModelingContext } from 'hooks/useModelingContext'
|
||||
import * as TWEEN from '@tweenjs/tween.js'
|
||||
import { SourceRange } from 'lang/wasm'
|
||||
@ -88,25 +87,20 @@ class SceneInfra {
|
||||
fov = 45
|
||||
fovBeforeAnimate = 45
|
||||
isFovAnimationInProgress = false
|
||||
onDragStartCallback: (arg: OnDragCallbackArgs) => void = () => {}
|
||||
onDragEndCallback: (arg: OnDragCallbackArgs) => void = () => {}
|
||||
_baseUnit: BaseUnit = 'mm'
|
||||
_baseUnitMultiplier = 1
|
||||
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
|
||||
@ -114,10 +108,17 @@ 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: () => {},
|
||||
@ -211,7 +212,12 @@ class SceneInfra {
|
||||
const axisGroup = this.scene
|
||||
.getObjectByName(AXIS_GROUP)
|
||||
?.getObjectByName('gridHelper')
|
||||
planesGroup && planesGroup.scale.set(scale, scale, scale)
|
||||
planesGroup &&
|
||||
planesGroup.scale.set(
|
||||
scale / sceneInfra._baseUnitMultiplier,
|
||||
scale / sceneInfra._baseUnitMultiplier,
|
||||
scale / sceneInfra._baseUnitMultiplier
|
||||
)
|
||||
axisGroup?.name === 'gridHelper' && axisGroup.scale.set(scale, scale, scale)
|
||||
}
|
||||
|
||||
@ -279,8 +285,11 @@ class SceneInfra {
|
||||
}
|
||||
|
||||
return {
|
||||
intersection2d: new Vector2(transformedPoint.x, transformedPoint.y), // z should be 0
|
||||
intersectPoint,
|
||||
intersection2d: new Vector2(
|
||||
transformedPoint.x / this._baseUnitMultiplier,
|
||||
transformedPoint.y / this._baseUnitMultiplier
|
||||
), // z should be 0
|
||||
intersectPoint: intersectPoint.divideScalar(this._baseUnitMultiplier),
|
||||
intersection: planeIntersects[0],
|
||||
}
|
||||
}
|
||||
@ -429,13 +438,8 @@ class SceneInfra {
|
||||
|
||||
if (this.selected) {
|
||||
if (this.selected.hasBeenDragged) {
|
||||
// TODO do the types properly here
|
||||
this.onDragEndCallback({
|
||||
object: this.selected.object,
|
||||
event,
|
||||
intersection2d: planeIntersectPoint?.intersection2d,
|
||||
...planeIntersectPoint,
|
||||
} as any)
|
||||
// this is where we could fire a onDragEnd event
|
||||
// console.log('onDragEnd', this.selected)
|
||||
} else if (planeIntersectPoint) {
|
||||
// fire onClick event as there was no drags
|
||||
this.onClickCallback({
|
||||
@ -497,7 +501,11 @@ class SceneInfra {
|
||||
this.camControls.camera,
|
||||
this.camControls.target
|
||||
)
|
||||
planesGroup.scale.set(sceneScale, sceneScale, sceneScale)
|
||||
planesGroup.scale.set(
|
||||
sceneScale / sceneInfra._baseUnitMultiplier,
|
||||
sceneScale / sceneInfra._baseUnitMultiplier,
|
||||
sceneScale / sceneInfra._baseUnitMultiplier
|
||||
)
|
||||
this.scene.add(planesGroup)
|
||||
}
|
||||
removeDefaultPlanes() {
|
||||
|
@ -81,7 +81,6 @@ export function straightSegment({
|
||||
pathToNode,
|
||||
isSelected: false,
|
||||
}
|
||||
group.name = STRAIGHT_SEGMENT
|
||||
|
||||
const arrowGroup = createArrowhead(scale)
|
||||
arrowGroup.position.set(to[0], to[1], 0)
|
||||
@ -170,7 +169,6 @@ export function tangentialArcToSegment({
|
||||
pathToNode,
|
||||
isSelected: false,
|
||||
}
|
||||
group.name = TANGENTIAL_ARC_TO_SEGMENT
|
||||
|
||||
const arrowGroup = createArrowhead(scale)
|
||||
arrowGroup.position.set(to[0], to[1], 0)
|
||||
|
@ -18,6 +18,7 @@ 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>
|
||||
@ -97,6 +98,7 @@ 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)
|
||||
|
@ -27,6 +27,8 @@ describe('UserSidebarMenu tests', () => {
|
||||
phone: '555-555-5555',
|
||||
first_name: 'Test',
|
||||
last_name: 'User',
|
||||
can_train_on_data: false,
|
||||
is_service_account: false,
|
||||
}
|
||||
|
||||
render(
|
||||
@ -57,6 +59,8 @@ describe('UserSidebarMenu tests', () => {
|
||||
first_name: '',
|
||||
last_name: '',
|
||||
name: '',
|
||||
can_train_on_data: false,
|
||||
is_service_account: false,
|
||||
}
|
||||
|
||||
render(
|
||||
@ -84,6 +88,8 @@ describe('UserSidebarMenu tests', () => {
|
||||
first_name: 'Test',
|
||||
last_name: 'User',
|
||||
image: '',
|
||||
can_train_on_data: false,
|
||||
is_service_account: false,
|
||||
}
|
||||
|
||||
render(
|
||||
|
@ -7,6 +7,5 @@ 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
|
||||
|
@ -3,7 +3,6 @@ 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'
|
||||
|
||||
@ -290,12 +289,6 @@ 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.
|
||||
//
|
||||
@ -308,41 +301,6 @@ 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()
|
||||
|
||||
@ -393,10 +351,6 @@ 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
|
||||
@ -429,17 +383,6 @@ 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) {
|
||||
@ -522,10 +465,6 @@ class EngineConnection {
|
||||
},
|
||||
}
|
||||
|
||||
if (this.shouldTrace()) {
|
||||
dataChannelSpan.resolve?.()
|
||||
}
|
||||
|
||||
// Everything is now connected.
|
||||
this.state = { type: EngineConnectionStateType.ConnectionEstablished }
|
||||
|
||||
@ -577,27 +516,6 @@ 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) => {
|
||||
@ -786,13 +704,6 @@ 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':
|
||||
|
@ -181,10 +181,6 @@ export const line: SketchLineHelper = {
|
||||
pathToNode,
|
||||
'PipeExpression'
|
||||
)
|
||||
const { node: callExpression } = getNodeFromPath<
|
||||
PipeExpression | CallExpression
|
||||
>(_node, pathToNode, 'CallExpression')
|
||||
|
||||
const { node: varDec } = getNodeFromPath<VariableDeclarator>(
|
||||
_node,
|
||||
pathToNode,
|
||||
@ -194,38 +190,6 @@ 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(
|
||||
@ -1047,6 +1011,15 @@ 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],
|
||||
@ -1071,15 +1044,6 @@ 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,
|
||||
|
@ -20,6 +20,8 @@ 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 {
|
||||
|
131
src/wasm-lib/Cargo.lock
generated
@ -246,7 +246,7 @@ checksum = "5fd55a5ba1179988837d24ab4c7cc8ed6efdeff578ede0416b4225a5fca35bd0"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.49",
|
||||
"syn 2.0.52",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -257,7 +257,7 @@ checksum = "c980ee35e870bd1a4d2c8294d4c04d0499e67bca1e4b5cefcc693c2fa00caea9"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.49",
|
||||
"syn 2.0.52",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -574,9 +574,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.5.0"
|
||||
version = "4.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "80c21025abd42669a92efc996ef13cfb2c5c627858421ea58d5c3b331a6c134f"
|
||||
checksum = "c918d541ef2913577a0f9566e9ce27cb35b6df072075769e0b26cb5a554520da"
|
||||
dependencies = [
|
||||
"clap_builder",
|
||||
"clap_derive",
|
||||
@ -584,9 +584,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap_builder"
|
||||
version = "4.5.0"
|
||||
version = "4.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "458bf1f341769dfcf849846f65dffdf9146daa56bcd2a47cb4e1de9915567c99"
|
||||
checksum = "9f3e7391dad68afb0c2ede1bf619f579a3dc9c2ec67f089baa397123a2f3d1eb"
|
||||
dependencies = [
|
||||
"anstream",
|
||||
"anstyle",
|
||||
@ -606,7 +606,7 @@ dependencies = [
|
||||
"heck",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.49",
|
||||
"syn 2.0.52",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -856,7 +856,7 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.49",
|
||||
"syn 2.0.52",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -897,7 +897,7 @@ checksum = "377af281d8f23663862a7c84623bc5dcf7f8c44b13c7496a590bdc157f941a43"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.49",
|
||||
"syn 2.0.52",
|
||||
"synstructure 0.13.0",
|
||||
]
|
||||
|
||||
@ -949,7 +949,7 @@ dependencies = [
|
||||
"regex",
|
||||
"serde",
|
||||
"serde_tokenstream",
|
||||
"syn 2.0.49",
|
||||
"syn 2.0.52",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -965,7 +965,7 @@ dependencies = [
|
||||
"regex",
|
||||
"serde",
|
||||
"serde_tokenstream",
|
||||
"syn 2.0.49",
|
||||
"syn 2.0.52",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -977,7 +977,7 @@ dependencies = [
|
||||
"diesel_table_macro_syntax",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.49",
|
||||
"syn 2.0.52",
|
||||
]
|
||||
|
||||
[[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.49",
|
||||
"syn 2.0.52",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1036,7 +1036,7 @@ checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.49",
|
||||
"syn 2.0.52",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1118,7 +1118,7 @@ checksum = "eecf8589574ce9b895052fa12d69af7a233f99e6107f5cb8dd1044f2a17bfdcb"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.49",
|
||||
"syn 2.0.52",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1335,7 +1335,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.49",
|
||||
"syn 2.0.52",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1440,7 +1440,7 @@ dependencies = [
|
||||
"inflections",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.49",
|
||||
"syn 2.0.52",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1460,13 +1460,17 @@ 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]]
|
||||
@ -1912,7 +1916,7 @@ dependencies = [
|
||||
"itertools 0.12.1",
|
||||
"js-sys",
|
||||
"kittycad",
|
||||
"kittycad-execution-plan-macros 0.1.4 (git+https://github.com/KittyCAD/modeling-api?branch=main)",
|
||||
"kittycad-execution-plan-macros",
|
||||
"kittycad-execution-plan-traits",
|
||||
"lazy_static",
|
||||
"parse-display 0.9.0",
|
||||
@ -1943,7 +1947,7 @@ dependencies = [
|
||||
"pretty_assertions",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.49",
|
||||
"syn 2.0.52",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1986,7 +1990,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "kittycad-execution-plan"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/KittyCAD/modeling-api?branch=main#08f05d91062380fe3a69f4baa1f1301532d31977"
|
||||
source = "git+https://github.com/KittyCAD/modeling-api?branch=main#29086e1079adb82b6427639a779dc58eabcd7f78"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"insta",
|
||||
@ -2004,30 +2008,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "kittycad-execution-plan-macros"
|
||||
version = "0.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "71d31b689c944d00aadda2ef83d8422a6efff97e1be5654a61f9d95496f0c19e"
|
||||
version = "0.1.6"
|
||||
source = "git+https://github.com/KittyCAD/modeling-api?branch=main#29086e1079adb82b6427639a779dc58eabcd7f78"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"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",
|
||||
"syn 2.0.52",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "kittycad-execution-plan-traits"
|
||||
version = "0.1.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a3ec8efd57b59697eb140b63c0ffe7db44fdfe5a55f14e45513411eba2280ba5"
|
||||
version = "0.1.11"
|
||||
source = "git+https://github.com/KittyCAD/modeling-api?branch=main#29086e1079adb82b6427639a779dc58eabcd7f78"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"thiserror",
|
||||
@ -2036,8 +2028,8 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "kittycad-modeling-cmds"
|
||||
version = "0.1.18"
|
||||
source = "git+https://github.com/KittyCAD/modeling-api?branch=main#08f05d91062380fe3a69f4baa1f1301532d31977"
|
||||
version = "0.1.26"
|
||||
source = "git+https://github.com/KittyCAD/modeling-api?branch=main#29086e1079adb82b6427639a779dc58eabcd7f78"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"chrono",
|
||||
@ -2047,8 +2039,9 @@ dependencies = [
|
||||
"enum-iterator-derive",
|
||||
"euler",
|
||||
"http 0.2.9",
|
||||
"kittycad-execution-plan-macros 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"kittycad-execution-plan-macros",
|
||||
"kittycad-execution-plan-traits",
|
||||
"kittycad-modeling-cmds-macros",
|
||||
"kittycad-unit-conversion-derive",
|
||||
"measurements",
|
||||
"parse-display 0.8.2",
|
||||
@ -2061,10 +2054,20 @@ dependencies = [
|
||||
"webrtc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "kittycad-modeling-cmds-macros"
|
||||
version = "0.1.1"
|
||||
source = "git+https://github.com/KittyCAD/modeling-api?branch=main#29086e1079adb82b6427639a779dc58eabcd7f78"
|
||||
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#08f05d91062380fe3a69f4baa1f1301532d31977"
|
||||
source = "git+https://github.com/KittyCAD/modeling-api?branch=main#29086e1079adb82b6427639a779dc58eabcd7f78"
|
||||
dependencies = [
|
||||
"futures",
|
||||
"kittycad",
|
||||
@ -2496,7 +2499,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.49",
|
||||
"syn 2.0.52",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -2653,7 +2656,7 @@ dependencies = [
|
||||
"regex",
|
||||
"regex-syntax 0.7.5",
|
||||
"structmeta 0.2.0",
|
||||
"syn 2.0.49",
|
||||
"syn 2.0.52",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -2667,7 +2670,7 @@ dependencies = [
|
||||
"regex",
|
||||
"regex-syntax 0.8.2",
|
||||
"structmeta 0.3.0",
|
||||
"syn 2.0.49",
|
||||
"syn 2.0.52",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -2733,7 +2736,7 @@ checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.49",
|
||||
"syn 2.0.52",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -3601,7 +3604,7 @@ checksum = "33c85360c95e7d137454dc81d9a4ed2b8efd8fbe19cee57357b32b9771fccb67"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.49",
|
||||
"syn 2.0.52",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -3635,7 +3638,7 @@ checksum = "8725e1dfadb3a50f7e5ce0b1a540466f6ed3fe7a0fca2ac2b8b831d31316bd00"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.49",
|
||||
"syn 2.0.52",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -3656,7 +3659,7 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"serde",
|
||||
"syn 2.0.49",
|
||||
"syn 2.0.52",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -3913,7 +3916,7 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"structmeta-derive 0.2.0",
|
||||
"syn 2.0.49",
|
||||
"syn 2.0.52",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -3925,7 +3928,7 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"structmeta-derive 0.3.0",
|
||||
"syn 2.0.49",
|
||||
"syn 2.0.52",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -3936,7 +3939,7 @@ checksum = "a60bcaff7397072dca0017d1db428e30d5002e00b6847703e2e42005c95fbe00"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.49",
|
||||
"syn 2.0.52",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -3947,7 +3950,7 @@ checksum = "152a0b65a590ff6c3da95cabe2353ee04e6167c896b28e3b14478c2636c922fc"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.49",
|
||||
"syn 2.0.52",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -4019,9 +4022,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.49"
|
||||
version = "2.0.52"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "915aea9e586f80826ee59f8453c1101f9d1c4b3964cd2460185ee8e299ada496"
|
||||
checksum = "b699d15b36d1f02c3e7c69f8ffef53de37aefae075d8488d4ba1a7788d574a07"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@ -4054,7 +4057,7 @@ checksum = "285ba80e733fac80aa4270fbcdf83772a79b80aa35c97075320abfee4a915b06"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.49",
|
||||
"syn 2.0.52",
|
||||
"unicode-xid",
|
||||
]
|
||||
|
||||
@ -4184,7 +4187,7 @@ checksum = "a953cb265bef375dae3de6663da4d3804eee9682ea80d8e2542529b73c531c81"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.49",
|
||||
"syn 2.0.52",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -4291,7 +4294,7 @@ checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.49",
|
||||
"syn 2.0.52",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -4454,7 +4457,7 @@ checksum = "84fd902d4e0b9a4b27f2f440108dc034e1758628a9b702f8ec61ad66355422fa"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.49",
|
||||
"syn 2.0.52",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -4482,7 +4485,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.49",
|
||||
"syn 2.0.52",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -4566,7 +4569,7 @@ dependencies = [
|
||||
"Inflector",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.49",
|
||||
"syn 2.0.52",
|
||||
"termcolor",
|
||||
]
|
||||
|
||||
@ -4813,7 +4816,7 @@ dependencies = [
|
||||
"once_cell",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.49",
|
||||
"syn 2.0.52",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
@ -4848,7 +4851,7 @@ checksum = "642f325be6301eb8107a83d12a8ac6c1e1c54345a7ef1a9261962dfefda09e66"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.49",
|
||||
"syn 2.0.52",
|
||||
"wasm-bindgen-backend",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
@ -5466,7 +5469,7 @@ checksum = "b3c129550b3e6de3fd0ba67ba5c81818f9805e58b8d7fee80a3a59d2c9fc601a"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.49",
|
||||
"syn 2.0.52",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -5486,7 +5489,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.49",
|
||||
"syn 2.0.52",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -60,9 +60,10 @@ members = [
|
||||
[workspace.dependencies]
|
||||
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-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" }
|
||||
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-modeling-session = { git = "https://github.com/KittyCAD/modeling-api", branch = "main" }
|
||||
|
||||
[[test]]
|
||||
name = "executor"
|
||||
@ -73,6 +74,9 @@ 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-modeling-session = { path = "../../../modeling-api/modeling-session" }
|
||||
#[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" }
|
||||
|
@ -19,7 +19,7 @@ quote = "1"
|
||||
regex = "1.10"
|
||||
serde = { version = "1.0.193", features = ["derive"] }
|
||||
serde_tokenstream = "0.2"
|
||||
syn = { version = "2.0.49", features = ["full"] }
|
||||
syn = { version = "2.0.52", features = ["full"] }
|
||||
|
||||
[dev-dependencies]
|
||||
expectorate = "1.1.0"
|
||||
|
@ -7,11 +7,15 @@ 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"
|
||||
|
@ -103,7 +103,7 @@ impl BindingScope {
|
||||
("add".into(), EpBinding::from(KclFunction::Add(native_functions::Add))),
|
||||
(
|
||||
"startSketchAt".into(),
|
||||
EpBinding::from(KclFunction::StartSketchAt(native_functions::StartSketchAt)),
|
||||
EpBinding::from(KclFunction::StartSketchAt(native_functions::sketch::StartSketchAt)),
|
||||
),
|
||||
]),
|
||||
parent: None,
|
||||
|
@ -45,6 +45,12 @@ 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} but you supplied {actual}")]
|
||||
ArgWrongType {
|
||||
fn_name: &'static str,
|
||||
expected: &'static str,
|
||||
actual: String,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
|
@ -618,7 +618,7 @@ impl Eq for UserDefinedFunction {}
|
||||
#[cfg_attr(test, derive(Eq, PartialEq))]
|
||||
enum KclFunction {
|
||||
Id(native_functions::Id),
|
||||
StartSketchAt(native_functions::StartSketchAt),
|
||||
StartSketchAt(native_functions::sketch::StartSketchAt),
|
||||
Add(native_functions::Add),
|
||||
UserDefined(UserDefinedFunction),
|
||||
}
|
||||
|
@ -2,12 +2,13 @@
|
||||
//! 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, Value};
|
||||
use kittycad_execution_plan_traits::Address;
|
||||
|
||||
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))]
|
||||
@ -41,34 +42,6 @@ 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))]
|
||||
|
7
src/wasm-lib/grackle/src/native_functions/sketch.rs
Normal file
@ -0,0 +1,7 @@
|
||||
//! Native functions for sketching on the plane.
|
||||
|
||||
pub mod helpers;
|
||||
pub mod stdlib_functions;
|
||||
pub mod types;
|
||||
|
||||
pub use stdlib_functions::StartSketchAt;
|
82
src/wasm-lib/grackle/src/native_functions/sketch/helpers.rs
Normal file
@ -0,0 +1,82 @@
|
||||
use kittycad_execution_plan::{api_request::ApiRequest, 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) -> Result<Address, CompileError> {
|
||||
match b {
|
||||
EpBinding::Single(a) => Ok(a),
|
||||
EpBinding::Sequence { .. } => Err(CompileError::ArgWrongType {
|
||||
fn_name,
|
||||
expected,
|
||||
actual: "array".to_owned(),
|
||||
}),
|
||||
EpBinding::Map { .. } => Err(CompileError::ArgWrongType {
|
||||
fn_name,
|
||||
expected,
|
||||
actual: "object".to_owned(),
|
||||
}),
|
||||
EpBinding::Function(_) => Err(CompileError::ArgWrongType {
|
||||
fn_name,
|
||||
expected,
|
||||
actual: "function".to_owned(),
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn sequence_binding(
|
||||
b: EpBinding,
|
||||
fn_name: &'static str,
|
||||
expected: &'static str,
|
||||
) -> Result<Vec<EpBinding>, CompileError> {
|
||||
match b {
|
||||
EpBinding::Sequence { elements, .. } => Ok(elements),
|
||||
EpBinding::Single(_) => Err(CompileError::ArgWrongType {
|
||||
fn_name,
|
||||
expected,
|
||||
actual: "single".to_owned(),
|
||||
}),
|
||||
EpBinding::Map { .. } => Err(CompileError::ArgWrongType {
|
||||
fn_name,
|
||||
expected,
|
||||
actual: "object".to_owned(),
|
||||
}),
|
||||
EpBinding::Function(_) => Err(CompileError::ArgWrongType {
|
||||
fn_name,
|
||||
expected,
|
||||
actual: "function".to_owned(),
|
||||
}),
|
||||
}
|
||||
}
|
@ -0,0 +1,166 @@
|
||||
use kittycad_execution_plan::{api_request::ApiRequest, Instruction};
|
||||
use kittycad_execution_plan_traits::{Address, InMemory, Value};
|
||||
use kittycad_modeling_cmds::{
|
||||
shared::{Point3d, Point4d},
|
||||
ModelingCmdEndpoint,
|
||||
};
|
||||
use uuid::Uuid;
|
||||
|
||||
use super::{
|
||||
helpers::{no_arg_api_call, sequence_binding, 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 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 = {
|
||||
let expected = "2D point (array with length 2)";
|
||||
let fn_name = "startSketchAt";
|
||||
let elements = sequence_binding(start, "startSketchAt", "an array of length 2")?;
|
||||
if elements.len() != 2 {
|
||||
return Err(CompileError::ArgWrongType {
|
||||
fn_name,
|
||||
expected,
|
||||
actual: format!("array of length {}", elements.len()),
|
||||
});
|
||||
}
|
||||
// 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 (first parameter, elem 0)", "number")?,
|
||||
destination: start_x,
|
||||
},
|
||||
Instruction::Copy {
|
||||
source: single_binding(elements[1].clone(), "startSketchAt (first parameter, elem 1)", "number")?,
|
||||
destination: start_y,
|
||||
},
|
||||
Instruction::SetPrimitive {
|
||||
address: start_z,
|
||||
value: 0.0.into(),
|
||||
},
|
||||
]);
|
||||
start
|
||||
};
|
||||
let tag = match args_iter.next() {
|
||||
None => None,
|
||||
Some(b) => Some(single_binding(b, "startSketchAt", "a single string")?),
|
||||
};
|
||||
|
||||
// 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),
|
||||
})
|
||||
}
|
||||
}
|
133
src/wasm-lib/grackle/src/native_functions/sketch/types.rs
Normal file
@ -0,0 +1,133 @@
|
||||
use kittycad_execution_plan::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 {
|
||||
/// 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 {
|
||||
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: base_path_addr,
|
||||
},
|
||||
// Copy over the `to` field.
|
||||
Instruction::Copy {
|
||||
source: start_point,
|
||||
destination: base_path_addr + self.path_first.from.into_parts().len(),
|
||||
},
|
||||
];
|
||||
if let Some(tag) = tag {
|
||||
// Copy over the `name` field.
|
||||
out.push(Instruction::Copy {
|
||||
source: tag,
|
||||
destination: base_path_addr
|
||||
+ self.path_first.from.into_parts().len()
|
||||
+ self.path_first.to.into_parts().len(),
|
||||
});
|
||||
}
|
||||
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,
|
||||
}
|
@ -1,7 +1,8 @@
|
||||
use std::collections::HashMap;
|
||||
use std::{collections::HashMap, env};
|
||||
|
||||
use ep::{Destination, UnaryArithmetic};
|
||||
use ept::{ListHeader, ObjectHeader};
|
||||
use kittycad_modeling_session::SessionBuilder;
|
||||
use pretty_assertions::assert_eq;
|
||||
|
||||
use super::*;
|
||||
@ -1044,6 +1045,71 @@ fn store_object_with_array_property() {
|
||||
)
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn stdlib_cube_partial() {
|
||||
let program = r#"
|
||||
let cube = startSketchAt([22.0, 33.0])
|
||||
"#;
|
||||
let (plan, _scope) = must_plan(program);
|
||||
std::fs::write("stdlib_cube_partial.json", serde_json::to_string_pretty(&plan).unwrap()).unwrap();
|
||||
let ast = kcl_lib::parser::Parser::new(kcl_lib::token::lexer(program))
|
||||
.ast()
|
||||
.unwrap();
|
||||
let mem = crate::execute(ast, Some(test_client().await)).await.unwrap();
|
||||
dbg!(mem);
|
||||
}
|
||||
|
||||
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() {
|
||||
|
@ -15,7 +15,7 @@ databake = "0.1.7"
|
||||
kcl-lib = { path = "../kcl" }
|
||||
proc-macro2 = "1"
|
||||
quote = "1"
|
||||
syn = { version = "2.0.49", features = ["full"] }
|
||||
syn = { version = "2.0.52", features = ["full"] }
|
||||
|
||||
[dev-dependencies]
|
||||
pretty_assertions = "1.4.0"
|
||||
|
@ -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.0", features = ["cargo", "derive", "env", "unicode"], optional = true }
|
||||
clap = { version = "4.5.1", features = ["cargo", "derive", "env", "unicode"], optional = true }
|
||||
dashmap = "5.5.3"
|
||||
databake = { version = "0.1.7", features = ["derive"] }
|
||||
derive-docs = { version = "0.1.8" }
|
||||
|
@ -96,7 +96,19 @@ impl Program {
|
||||
let custom_white_space_or_comment = match self.non_code_meta.non_code_nodes.get(&index) {
|
||||
Some(noncodes) => noncodes
|
||||
.iter()
|
||||
.map(|custom_white_space_or_comment| custom_white_space_or_comment.format(&indentation))
|
||||
.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
|
||||
}
|
||||
})
|
||||
.collect::<String>(),
|
||||
None => String::new(),
|
||||
};
|
||||
@ -159,6 +171,35 @@ 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![];
|
||||
@ -431,6 +472,24 @@ 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;
|
||||
@ -736,6 +795,10 @@ 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(),
|
||||
@ -755,18 +818,27 @@ impl NonCodeNode {
|
||||
value,
|
||||
style: CommentStyle::Block,
|
||||
} => format!(" /* {} */", value),
|
||||
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::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::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 => 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())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
NonCodeValue::NewLine => "\n\n".to_string(),
|
||||
@ -863,6 +935,16 @@ 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)]
|
||||
@ -2533,7 +2615,13 @@ 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 {
|
||||
s += val.format(&indentation).trim_end_matches('\n')
|
||||
let formatted = val.format(&indentation).trim_end_matches('\n').to_string();
|
||||
if let NonCodeValue::BlockComment { .. } = val.value {
|
||||
s += "\n";
|
||||
s += &formatted;
|
||||
} else {
|
||||
s += &formatted;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -3106,6 +3194,109 @@ 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#"
|
||||
@ -3381,6 +3572,82 @@ 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_recast_negative_var() {
|
||||
let some_program_string = r#"const w = 20
|
||||
@ -3407,6 +3674,51 @@ 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], %)
|
||||
|
@ -430,6 +430,20 @@ impl LanguageServer for Backend {
|
||||
}
|
||||
|
||||
async fn completion(&self, params: CompletionParams) -> RpcResult<Option<CompletionResponse>> {
|
||||
// We want to get the position we are in.
|
||||
// Because if we are in a comment, we don't want to show completions.
|
||||
let filename = params.text_document_position.text_document.uri.to_string();
|
||||
if let Some(current_code) = self.current_code_map.get(&filename) {
|
||||
let pos = position_to_char_index(params.text_document_position.position, ¤t_code);
|
||||
// Let's iterate over the AST and find the node that contains the cursor.
|
||||
if let Some(ast) = self.ast_map.get(&filename) {
|
||||
if ast.get_non_code_meta_for_position(pos).is_some() {
|
||||
// We are in a comment, don't show completions.
|
||||
return Ok(None);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut completions = vec![CompletionItem {
|
||||
label: PIPE_OPERATOR.to_string(),
|
||||
label_details: None,
|
||||
|
@ -440,6 +440,7 @@ 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 })
|
||||
@ -977,6 +978,11 @@ 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)
|
||||
|
@ -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 3D vector.
|
||||
pub axis: [f64; 3],
|
||||
/// The axis of the pattern. This is a 2D vector.
|
||||
pub axis: [f64; 2],
|
||||
}
|
||||
|
||||
/// 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 3D vector.
|
||||
pub axis: [f64; 3],
|
||||
/// The axis around which to make the pattern. This is a 2D vector.
|
||||
pub axis: [f64; 2],
|
||||
/// 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, 0.0] {
|
||||
if data.axis == [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, 0.0] {
|
||||
if data.axis == [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,7 +97,11 @@ async fn inner_pattern_linear(data: LinearPatternData, geometry: Geometry, args:
|
||||
.send_modeling_cmd(
|
||||
id,
|
||||
ModelingCmd::EntityLinearPattern {
|
||||
axis: data.axis.into(),
|
||||
axis: kittycad::types::Point3D {
|
||||
x: data.axis[0],
|
||||
y: data.axis[1],
|
||||
z: 0.0,
|
||||
},
|
||||
entity_id: geometry.id(),
|
||||
num_repetitions: data.repetitions as u32,
|
||||
spacing: data.distance,
|
||||
@ -154,7 +158,11 @@ async fn inner_pattern_circular(
|
||||
.send_modeling_cmd(
|
||||
id,
|
||||
ModelingCmd::EntityCircularPattern {
|
||||
axis: data.axis.into(),
|
||||
axis: kittycad::types::Point3D {
|
||||
x: data.axis[0],
|
||||
y: data.axis[1],
|
||||
z: 0.0,
|
||||
},
|
||||
entity_id: geometry.id(),
|
||||
center: data.center.into(),
|
||||
num_repetitions: data.repetitions as u32,
|
||||
|
@ -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(1.., ['\n', '\r'])).recognize();
|
||||
let inner = (r#"//"#, take_till(0.., ['\n', '\r'])).recognize();
|
||||
let (value, range) = inner.with_span().parse_next(i)?;
|
||||
Ok(Token::from_range(range, TokenType::LineComment, value.to_string()))
|
||||
}
|
||||
|
@ -735,7 +735,7 @@ async fn serial_test_patterns_linear_basic() {
|
||||
}
|
||||
|
||||
const part = circle([0,0], 2)
|
||||
|> patternLinear({axis: [0,0,1], repetitions: 12, distance: 2}, %)
|
||||
|> patternLinear({axis: [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,1], repetitions: 3, distance: 6}, %)
|
||||
|> patternLinear({axis: [1, 0], 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,0,1], repetitions: 12, distance: -2}, %)
|
||||
|> patternLinear({axis: [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,0,-1], repetitions: 12, distance: 2}, %)
|
||||
|> patternLinear({axis: [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,0], repetitions: 12, distance: 3}, %)
|
||||
|> patternLinear({axis: [1,1], 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,0,1], center: [20, 20, 20], repetitions: 12, arcDegrees: 210, rotateDuplicates: true}, %)
|
||||
|> patternCircular({axis: [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,0], center: [-20, -20, -20], repetitions: 40, arcDegrees: 360, rotateDuplicates: false}, %)
|
||||
|> patternCircular({axis: [0,1], 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,-1], center: [10, 0, 10], repetitions: 10, arcDegrees: 360, rotateDuplicates: true}, %)
|
||||
|> patternCircular({axis: [1,1], center: [10, 0, 10], repetitions: 10, arcDegrees: 360, rotateDuplicates: true}, %)
|
||||
"#;
|
||||
|
||||
let result = execute_and_snapshot(code, kittycad::types::UnitLength::Mm)
|
||||
|
Before Width: | Height: | Size: 98 KiB After Width: | Height: | Size: 98 KiB |
Before Width: | Height: | Size: 95 KiB After Width: | Height: | Size: 96 KiB |
Before Width: | Height: | Size: 98 KiB After Width: | Height: | Size: 98 KiB |
Before Width: | Height: | Size: 98 KiB After Width: | Height: | Size: 95 KiB |
Before Width: | Height: | Size: 93 KiB After Width: | Height: | Size: 93 KiB |
Before Width: | Height: | Size: 115 KiB After Width: | Height: | Size: 116 KiB |
Before Width: | Height: | Size: 97 KiB After Width: | Height: | Size: 96 KiB |
Before Width: | Height: | Size: 97 KiB After Width: | Height: | Size: 96 KiB |
@ -38,4 +38,4 @@ const config = defineConfig({
|
||||
],
|
||||
})
|
||||
|
||||
export default config
|
||||
export default config
|
||||
|
70
yarn.lock
@ -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.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==
|
||||
"@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==
|
||||
dependencies:
|
||||
node-fetch "3.3.2"
|
||||
openapi-types "^12.0.0"
|
||||
@ -2030,66 +2030,6 @@
|
||||
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"
|
||||
@ -5413,7 +5353,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.2:
|
||||
hoist-non-react-statics@^3.3.0:
|
||||
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==
|
||||
|