Compare commits

...

17 Commits

Author SHA1 Message Date
f3e0939057 Cut release v0.15.4 (#1587) 2024-03-01 09:50:28 +11:00
f5e233d8a0 Finish removing Sentry (#1588)
Finish removing Sentry

Following Frank's PR in 8f5034f997, I'm
sending up a PR to finish pulling Sentry out of the codebase, rather
than just disabling it via configuration.

F

Signed-off-by: Paul R. Tagliamonte <paul@kittycad.io>
2024-02-29 16:41:20 -05:00
1cab3e628f client side sketch scene not respecting base-unit-scale (#1576)
* client side sketch scene not respecting base-unit-scale

* test tweak

* remove dead code

* fix test

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

* test fix up

* trigger ci

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

* trigger ci

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-03-01 06:55:49 +11:00
2ca6ba52b6 Bump serde_json from 1.0.113 to 1.0.114 in /src-tauri (#1463)
Bumps [serde_json](https://github.com/serde-rs/json) from 1.0.113 to 1.0.114.
- [Release notes](https://github.com/serde-rs/json/releases)
- [Commits](https://github.com/serde-rs/json/compare/v1.0.113...v1.0.114)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-02-29 11:35:35 -08:00
f741ea2e09 Bump serde from 1.0.196 to 1.0.197 in /src-tauri (#1462)
Bumps [serde](https://github.com/serde-rs/serde) from 1.0.196 to 1.0.197.
- [Release notes](https://github.com/serde-rs/serde/releases)
- [Commits](https://github.com/serde-rs/serde/compare/v1.0.196...v1.0.197)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-02-29 11:34:43 -08:00
9f2a7781fc Bump anyhow from 1.0.79 to 1.0.80 in /src-tauri (#1465)
Bumps [anyhow](https://github.com/dtolnay/anyhow) from 1.0.79 to 1.0.80.
- [Release notes](https://github.com/dtolnay/anyhow/releases)
- [Commits](https://github.com/dtolnay/anyhow/compare/1.0.79...1.0.80)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-02-29 11:34:30 -08:00
990f2b4154 Vector for tracking cargo tests (#1580)
* try and log to vector

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

* iupdates

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

* try and log to vector

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

* try and log to vector

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

* ud[ates

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

* ud[ates

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

* ud[ates

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

* ud[ates

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

* ud[ates

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

* ud[ates

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

* ud[ates

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

---------

Signed-off-by: Jess Frazelle <github@jessfraz.com>
2024-02-29 11:14:01 -08:00
0af0f15281 Bump clap from 4.5.0 to 4.5.1 in /src/wasm-lib (#1448)
Bumps [clap](https://github.com/clap-rs/clap) from 4.5.0 to 4.5.1.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/clap_complete-v4.5.0...clap_complete-v4.5.1)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-02-29 00:53:57 -08:00
b558548b94 Bump google-github-actions/auth from 2.1.1 to 2.1.2 (#1521)
Bumps [google-github-actions/auth](https://github.com/google-github-actions/auth) from 2.1.1 to 2.1.2.
- [Release notes](https://github.com/google-github-actions/auth/releases)
- [Changelog](https://github.com/google-github-actions/auth/blob/main/CHANGELOG.md)
- [Commits](https://github.com/google-github-actions/auth/compare/v2.1.1...v2.1.2)

---
updated-dependencies:
- dependency-name: google-github-actions/auth
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-02-29 00:52:34 -08:00
29e0f9a270 Bump kittycad-execution-plan from 9cb86ba to 29086e1 in /src/wasm-lib (#1570)
Bump kittycad-execution-plan in /src/wasm-lib

Bumps [kittycad-execution-plan](https://github.com/KittyCAD/modeling-api) from `9cb86ba` to `29086e1`.
- [Commits](9cb86ba54e...29086e1079)

---
updated-dependencies:
- dependency-name: kittycad-execution-plan
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-02-29 00:52:18 -08:00
9385c32cfb Bump kittycad-modeling-session from 9cb86ba to 29086e1 in /src/wasm-lib (#1568)
Bump kittycad-modeling-session in /src/wasm-lib

Bumps [kittycad-modeling-session](https://github.com/KittyCAD/modeling-api) from `9cb86ba` to `29086e1`.
- [Commits](9cb86ba54e...29086e1079)

---
updated-dependencies:
- dependency-name: kittycad-modeling-session
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-02-29 00:51:56 -08:00
ce3fb5c353 Bump tauri-plugin-fs-extra from 01211ff to ed682dd in /src-tauri (#1567)
Bumps [tauri-plugin-fs-extra](https://github.com/tauri-apps/plugins-workspace) from `01211ff` to `ed682dd`.
- [Release notes](https://github.com/tauri-apps/plugins-workspace/releases)
- [Commits](01211ff075...ed682dd96e)

---
updated-dependencies:
- dependency-name: tauri-plugin-fs-extra
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-02-29 00:51:24 -08:00
f920490518 Bump syn from 2.0.49 to 2.0.52 in /src/wasm-lib (#1563)
Bumps [syn](https://github.com/dtolnay/syn) from 2.0.49 to 2.0.52.
- [Release notes](https://github.com/dtolnay/syn/releases)
- [Commits](https://github.com/dtolnay/syn/compare/2.0.49...2.0.52)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-02-29 00:51:03 -08:00
d681e667ee Hide cam when moving (#1577)
hide cam when moving
2024-02-29 19:25:48 +11:00
5c6515a60e Fix autocomplete in comment (#1575) 2024-02-28 23:24:11 -08:00
eb8a33312d fix trailing comma (#1574)
Signed-off-by: Jess Frazelle <github@jessfraz.com>
2024-02-28 22:24:11 -08:00
d351b3bbe4 fix recast (#1571)
* fix recast

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

* updates

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

* fixes

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

---------

Signed-off-by: Jess Frazelle <github@jessfraz.com>
2024-02-28 21:19:10 -08:00
35 changed files with 751 additions and 310 deletions

View File

@ -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=

View File

@ -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=

View File

@ -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

View File

@ -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
View 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"

View File

@ -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, '')
)
})

View File

@ -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,
})
})

Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

After

Width:  |  Height:  |  Size: 38 KiB

View File

@ -1,6 +1,6 @@
{
"name": "untitled-app",
"version": "0.15.3",
"version": "0.15.4",
"private": true,
"dependencies": {
"@codemirror/autocomplete": "^6.10.2",
@ -15,7 +15,6 @@
"@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
View File

@ -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",

View File

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

View File

@ -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]

View File

@ -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),
},
},

View File

@ -106,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 &&
@ -143,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)
}
}
@ -169,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)
@ -208,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)
@ -279,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,
@ -404,7 +415,7 @@ class SceneEntities {
const isClosingSketch = compareVec2Epsilon2(
firstSeg.from,
[intersection2d.x, intersection2d.y],
1
0.5
)
let modifiedAst
if (isClosingSketch) {
@ -567,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],

View File

@ -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,6 +87,8 @@ class SceneInfra {
fov = 45
fovBeforeAnimate = 45
isFovAnimationInProgress = false
_baseUnit: BaseUnit = 'mm'
_baseUnitMultiplier = 1
onDragCallback: (arg: OnDragCallbackArgs) => void = () => {}
onMoveCallback: (arg: onMoveCallbackArgs) => void = () => {}
onClickCallback: (arg?: OnClickCallbackArgs) => void = () => {}
@ -107,6 +108,15 @@ 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({
onDrag: () => {},
@ -202,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)
}
@ -270,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],
}
}
@ -483,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() {

View File

@ -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)

View File

@ -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

View File

@ -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':

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

@ -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]]
@ -1947,7 +1947,7 @@ dependencies = [
"pretty_assertions",
"proc-macro2",
"quote",
"syn 2.0.49",
"syn 2.0.52",
]
[[package]]
@ -1990,7 +1990,7 @@ dependencies = [
[[package]]
name = "kittycad-execution-plan"
version = "0.1.0"
source = "git+https://github.com/KittyCAD/modeling-api?branch=main#9cb86ba54e4a60aa775fa2fd8af6f0ac9d05ebeb"
source = "git+https://github.com/KittyCAD/modeling-api?branch=main#29086e1079adb82b6427639a779dc58eabcd7f78"
dependencies = [
"bytes",
"insta",
@ -2009,17 +2009,17 @@ dependencies = [
[[package]]
name = "kittycad-execution-plan-macros"
version = "0.1.6"
source = "git+https://github.com/KittyCAD/modeling-api?branch=main#9cb86ba54e4a60aa775fa2fd8af6f0ac9d05ebeb"
source = "git+https://github.com/KittyCAD/modeling-api?branch=main#29086e1079adb82b6427639a779dc58eabcd7f78"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.49",
"syn 2.0.52",
]
[[package]]
name = "kittycad-execution-plan-traits"
version = "0.1.11"
source = "git+https://github.com/KittyCAD/modeling-api?branch=main#9cb86ba54e4a60aa775fa2fd8af6f0ac9d05ebeb"
source = "git+https://github.com/KittyCAD/modeling-api?branch=main#29086e1079adb82b6427639a779dc58eabcd7f78"
dependencies = [
"serde",
"thiserror",
@ -2028,8 +2028,8 @@ dependencies = [
[[package]]
name = "kittycad-modeling-cmds"
version = "0.1.25"
source = "git+https://github.com/KittyCAD/modeling-api?branch=main#9cb86ba54e4a60aa775fa2fd8af6f0ac9d05ebeb"
version = "0.1.26"
source = "git+https://github.com/KittyCAD/modeling-api?branch=main#29086e1079adb82b6427639a779dc58eabcd7f78"
dependencies = [
"anyhow",
"chrono",
@ -2057,17 +2057,17 @@ dependencies = [
[[package]]
name = "kittycad-modeling-cmds-macros"
version = "0.1.1"
source = "git+https://github.com/KittyCAD/modeling-api?branch=main#9cb86ba54e4a60aa775fa2fd8af6f0ac9d05ebeb"
source = "git+https://github.com/KittyCAD/modeling-api?branch=main#29086e1079adb82b6427639a779dc58eabcd7f78"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.49",
"syn 2.0.52",
]
[[package]]
name = "kittycad-modeling-session"
version = "0.1.0"
source = "git+https://github.com/KittyCAD/modeling-api?branch=main#9cb86ba54e4a60aa775fa2fd8af6f0ac9d05ebeb"
source = "git+https://github.com/KittyCAD/modeling-api?branch=main#29086e1079adb82b6427639a779dc58eabcd7f78"
dependencies = [
"futures",
"kittycad",
@ -2499,7 +2499,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.49",
"syn 2.0.52",
]
[[package]]
@ -2656,7 +2656,7 @@ dependencies = [
"regex",
"regex-syntax 0.7.5",
"structmeta 0.2.0",
"syn 2.0.49",
"syn 2.0.52",
]
[[package]]
@ -2670,7 +2670,7 @@ dependencies = [
"regex",
"regex-syntax 0.8.2",
"structmeta 0.3.0",
"syn 2.0.49",
"syn 2.0.52",
]
[[package]]
@ -2736,7 +2736,7 @@ checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.49",
"syn 2.0.52",
]
[[package]]
@ -3604,7 +3604,7 @@ checksum = "33c85360c95e7d137454dc81d9a4ed2b8efd8fbe19cee57357b32b9771fccb67"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.49",
"syn 2.0.52",
]
[[package]]
@ -3638,7 +3638,7 @@ checksum = "8725e1dfadb3a50f7e5ce0b1a540466f6ed3fe7a0fca2ac2b8b831d31316bd00"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.49",
"syn 2.0.52",
]
[[package]]
@ -3659,7 +3659,7 @@ dependencies = [
"proc-macro2",
"quote",
"serde",
"syn 2.0.49",
"syn 2.0.52",
]
[[package]]
@ -3916,7 +3916,7 @@ dependencies = [
"proc-macro2",
"quote",
"structmeta-derive 0.2.0",
"syn 2.0.49",
"syn 2.0.52",
]
[[package]]
@ -3928,7 +3928,7 @@ dependencies = [
"proc-macro2",
"quote",
"structmeta-derive 0.3.0",
"syn 2.0.49",
"syn 2.0.52",
]
[[package]]
@ -3939,7 +3939,7 @@ checksum = "a60bcaff7397072dca0017d1db428e30d5002e00b6847703e2e42005c95fbe00"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.49",
"syn 2.0.52",
]
[[package]]
@ -3950,7 +3950,7 @@ checksum = "152a0b65a590ff6c3da95cabe2353ee04e6167c896b28e3b14478c2636c922fc"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.49",
"syn 2.0.52",
]
[[package]]
@ -4022,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",
@ -4057,7 +4057,7 @@ checksum = "285ba80e733fac80aa4270fbcdf83772a79b80aa35c97075320abfee4a915b06"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.49",
"syn 2.0.52",
"unicode-xid",
]
@ -4187,7 +4187,7 @@ checksum = "a953cb265bef375dae3de6663da4d3804eee9682ea80d8e2542529b73c531c81"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.49",
"syn 2.0.52",
]
[[package]]
@ -4294,7 +4294,7 @@ checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.49",
"syn 2.0.52",
]
[[package]]
@ -4457,7 +4457,7 @@ checksum = "84fd902d4e0b9a4b27f2f440108dc034e1758628a9b702f8ec61ad66355422fa"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.49",
"syn 2.0.52",
]
[[package]]
@ -4485,7 +4485,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.49",
"syn 2.0.52",
]
[[package]]
@ -4569,7 +4569,7 @@ dependencies = [
"Inflector",
"proc-macro2",
"quote",
"syn 2.0.49",
"syn 2.0.52",
"termcolor",
]
@ -4816,7 +4816,7 @@ dependencies = [
"once_cell",
"proc-macro2",
"quote",
"syn 2.0.49",
"syn 2.0.52",
"wasm-bindgen-shared",
]
@ -4851,7 +4851,7 @@ checksum = "642f325be6301eb8107a83d12a8ac6c1e1c54345a7ef1a9261962dfefda09e66"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.49",
"syn 2.0.52",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]
@ -5469,7 +5469,7 @@ checksum = "b3c129550b3e6de3fd0ba67ba5c81818f9805e58b8d7fee80a3a59d2c9fc601a"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.49",
"syn 2.0.52",
]
[[package]]
@ -5489,7 +5489,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.49",
"syn 2.0.52",
]
[[package]]

View File

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

View File

@ -6,12 +6,11 @@ use kittycad_modeling_cmds::{
};
use uuid::Uuid;
use crate::{binding_scope::EpBinding, error::CompileError, native_functions::Callable, EvalPlan};
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))]

View File

@ -1,5 +1,4 @@
use std::collections::HashMap;
use std::env;
use std::{collections::HashMap, env};
use ep::{Destination, UnaryArithmetic};
use ept::{ListHeader, ObjectHeader};

View File

@ -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"

View File

@ -14,7 +14,7 @@ keywords = ["kcl", "KittyCAD", "CAD"]
anyhow = { version = "1.0.79", features = ["backtrace"] }
async-recursion = "1.0.5"
async-trait = "0.1.77"
clap = { version = "4.5.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" }

View File

@ -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], %)

View File

@ -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, &current_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,

View File

@ -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)

View File

@ -53,7 +53,7 @@ fn block_comment(i: &mut Located<&str>) -> PResult<Token> {
}
fn line_comment(i: &mut Located<&str>) -> PResult<Token> {
let inner = (r#"//"#, take_till(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()))
}

View File

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

View File

@ -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==