Compare commits
13 Commits
dev
...
kurt-scale
Author | SHA1 | Date | |
---|---|---|---|
b55a10208b | |||
a3331e1e33 | |||
af18dbb148 | |||
3459ac53ba | |||
cf0e10e1f4 | |||
cdc1d790bc | |||
b8eb3266c9 | |||
d2d0af1aa6 | |||
5c2ca466e5 | |||
963d2400d0 | |||
917ce728b3 | |||
218fc12051 | |||
4bac168a20 |
@ -3,17 +3,18 @@
|
||||
NODE_ENV=development
|
||||
DEV=true
|
||||
|
||||
# App
|
||||
VITE_KC_API_WS_MODELING_URL=wss://api.dev.zoo.dev/ws/modeling/commands
|
||||
VITE_KITTYCAD_API_BASE_URL=https://api.dev.zoo.dev
|
||||
VITE_KC_API_BASE_URL=https://api.dev.zoo.dev
|
||||
VITE_KC_SITE_BASE_URL=https://dev.zoo.dev
|
||||
VITE_KC_SITE_APP_URL=https://app.dev.zoo.dev
|
||||
VITE_KC_SKIP_AUTH=false
|
||||
VITE_KC_CONNECTION_TIMEOUT_MS=5000
|
||||
#VITE_WASM_URL="optional override of Wasm URL if not on default port 3000"
|
||||
#VITE_KITTYCAD_API_TOKEN="required for testing, optional to skip auth in the app"
|
||||
FAIL_ON_CONSOLE_ERRORS=true
|
||||
#VITE_WASM_URL="optional way of overriding the wasm url, particular for unit tests which need this if you running not on the default 3000 port"
|
||||
#VITE_KC_DEV_TOKEN="optional token to skip auth in the app"
|
||||
#token="required token for playwright. TODO: clean up env vars in #3973"
|
||||
|
||||
# KCL
|
||||
RUST_BACKTRACE=1
|
||||
PYO3_PYTHON=/usr/local/bin/python3
|
||||
#KITTYCAD_API_TOKEN=$VITE_KITTYCAD_API_TOKEN
|
||||
#KITTYCAD_API_TOKEN="required token for engine testing"
|
||||
|
||||
FAIL_ON_CONSOLE_ERRORS=true
|
||||
|
@ -1,8 +1,7 @@
|
||||
NODE_ENV=production
|
||||
|
||||
# App
|
||||
VITE_KC_API_WS_MODELING_URL=wss://api.zoo.dev/ws/modeling/commands
|
||||
VITE_KITTYCAD_API_BASE_URL=https://api.zoo.dev
|
||||
VITE_KC_API_BASE_URL=https://api.zoo.dev
|
||||
VITE_KC_SITE_BASE_URL=https://zoo.dev
|
||||
VITE_KC_SITE_APP_URL=https://app.zoo.dev
|
||||
VITE_KC_SKIP_AUTH=false
|
||||
VITE_KC_CONNECTION_TIMEOUT_MS=15000
|
||||
|
8
.github/workflows/e2e-tests.yml
vendored
8
.github/workflows/e2e-tests.yml
vendored
@ -157,7 +157,7 @@ jobs:
|
||||
timeout_minutes: 5
|
||||
max_attempts: 5
|
||||
env:
|
||||
VITE_KITTYCAD_API_TOKEN: ${{ secrets.KITTYCAD_API_TOKEN_DEV }}
|
||||
token: ${{ secrets.KITTYCAD_API_TOKEN_DEV }}
|
||||
TAB_API_URL: ${{ secrets.TAB_API_URL }}
|
||||
TAB_API_KEY: ${{ secrets.TAB_API_KEY }}
|
||||
CI_COMMIT_SHA: ${{ github.event.pull_request.head.sha }}
|
||||
@ -169,7 +169,7 @@ jobs:
|
||||
if: always()
|
||||
run: npm run test:snapshots -- --last-failed --update-snapshots
|
||||
env:
|
||||
VITE_KITTYCAD_API_TOKEN: ${{ secrets.KITTYCAD_API_TOKEN_DEV }}
|
||||
token: ${{ secrets.KITTYCAD_API_TOKEN_DEV }}
|
||||
TAB_API_URL: ${{ secrets.TAB_API_URL }}
|
||||
TAB_API_KEY: ${{ secrets.TAB_API_KEY }}
|
||||
CI_COMMIT_SHA: ${{ github.event.pull_request.head.sha }}
|
||||
@ -284,7 +284,7 @@ jobs:
|
||||
timeout_minutes: 5
|
||||
max_attempts: 5
|
||||
env:
|
||||
VITE_KITTYCAD_API_TOKEN: ${{ secrets.KITTYCAD_API_TOKEN_DEV }}
|
||||
token: ${{ secrets.KITTYCAD_API_TOKEN_DEV }}
|
||||
TAB_API_URL: ${{ secrets.TAB_API_URL }}
|
||||
TAB_API_KEY: ${{ secrets.TAB_API_KEY }}
|
||||
CI_COMMIT_SHA: ${{ github.event.pull_request.head.sha }}
|
||||
@ -410,7 +410,7 @@ jobs:
|
||||
max_attempts: 9
|
||||
env:
|
||||
FAIL_ON_CONSOLE_ERRORS: true
|
||||
VITE_KITTYCAD_API_TOKEN: ${{ secrets.KITTYCAD_API_TOKEN_DEV }}
|
||||
token: ${{ secrets.KITTYCAD_API_TOKEN_DEV }}
|
||||
TAB_API_URL: ${{ secrets.TAB_API_URL }}
|
||||
TAB_API_KEY: ${{ secrets.TAB_API_KEY }}
|
||||
CI_COMMIT_SHA: ${{ github.event.pull_request.head.sha }}
|
||||
|
2
.github/workflows/unit-tests.yml
vendored
2
.github/workflows/unit-tests.yml
vendored
@ -62,7 +62,7 @@ jobs:
|
||||
if: ${{ github.event_name != 'release' && github.event_name != 'schedule' }}
|
||||
run: xvfb-run -a npm run test:unit
|
||||
env:
|
||||
VITE_KITTYCAD_API_TOKEN: ${{ secrets.KITTYCAD_API_TOKEN_DEV }}
|
||||
VITE_KC_DEV_TOKEN: ${{ secrets.KITTYCAD_API_TOKEN_DEV }}
|
||||
|
||||
- name: Check for changes
|
||||
if: ${{ github.event_name != 'release' && github.event_name != 'schedule' }}
|
||||
|
@ -65,7 +65,7 @@ If you're not a Zoo employee you won't be able to access the dev environment, yo
|
||||
|
||||
### Development environment variables
|
||||
|
||||
The Copilot LSP plugin in the editor requires a Zoo API token to run. In production, we authenticate this with a token via cookie in the browser and device auth token in the desktop environment, but this token is inaccessible in the dev browser version because the cookie is considered "cross-site" (from `localhost` to `zoo.dev`). There is an optional environment variable called `VITE_KITTYCAD_API_TOKEN` that you can populate with a dev token in a `.env.development.local` file to not check it into Git, which will use that token instead of other methods for the LSP service.
|
||||
The Copilot LSP plugin in the editor requires a Zoo API token to run. In production, we authenticate this with a token via cookie in the browser and device auth token in the desktop environment, but this token is inaccessible in the dev browser version because the cookie is considered "cross-site" (from `localhost` to `zoo.dev`). There is an optional environment variable called `VITE_KC_DEV_TOKEN` that you can populate with a dev token in a `.env.development.local` file to not check it into Git, which will use that token instead of other methods for the LSP service.
|
||||
|
||||
### Developing in Chrome
|
||||
|
||||
@ -96,7 +96,7 @@ To package the app for your platform with electron-builder, run `npm run tronb:p
|
||||
|
||||
Prepare these system dependencies:
|
||||
|
||||
- Set `$VITE_KITTYCAD_API_TOKEN` from https://zoo.dev/account/api-tokens
|
||||
- Set $token from https://zoo.dev/account/api-tokens
|
||||
|
||||
#### Snapshot tests (Google Chrome on Ubuntu only)
|
||||
|
||||
@ -259,7 +259,7 @@ If the application needs to overwrite the known file on disk use this pattern. T
|
||||
- `npm run circular-deps:overwrite`
|
||||
- `npm run url-checker:overwrite`
|
||||
|
||||
#### Diff baseline and current
|
||||
#### Diff baseline and current
|
||||
|
||||
These commands will write a /tmp/ file on disk and compare it to the known file in the repository. This command will also be used in the CI CD pipeline for automated checks
|
||||
|
||||
|
2
Makefile
2
Makefile
@ -49,7 +49,7 @@ RUST_SOURCES := $(wildcard rust/**/*.rs)
|
||||
|
||||
REACT_SOURCES := $(wildcard src/*.tsx) $(wildcard src/**/*.tsx)
|
||||
TYPESCRIPT_SOURCES := tsconfig.* $(wildcard src/*.ts) $(wildcard src/**/*.ts)
|
||||
VITE_SOURCES := $(wildcard vite.*) $(wildcard vite/**/*.tsx) .env*
|
||||
VITE_SOURCES := $(wildcard vite.*) $(wildcard vite/**/*.tsx)
|
||||
|
||||
.PHONY: build
|
||||
build: install public/kcl_wasm_lib_bg.wasm public/kcl-samples/manifest.json .vite/build/main.js
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { expect, test } from '@e2e/playwright/zoo-test'
|
||||
|
||||
// test file is for testing auth functionality
|
||||
test.describe('Authentication tests', () => {
|
||||
test(
|
||||
`The user can sign out and back in`,
|
||||
@ -12,12 +13,22 @@ test.describe('Authentication tests', () => {
|
||||
await page.setBodyDimensions({ width: 1000, height: 500 })
|
||||
await homePage.projectSection.waitFor()
|
||||
|
||||
// This is only needed as an override to test-utils' setup() for this test
|
||||
await page.addInitScript(() => {
|
||||
localStorage.setItem('TOKEN_PERSIST_KEY', '')
|
||||
})
|
||||
|
||||
await test.step('Click on sign out and expect sign in page', async () => {
|
||||
await toolbar.userSidebarButton.click()
|
||||
await toolbar.signOutButton.click()
|
||||
await expect(signInPage.signInButton).toBeVisible()
|
||||
})
|
||||
|
||||
await test.step("Refresh doesn't log the user back in", async () => {
|
||||
await page.reload()
|
||||
await expect(signInPage.signInButton).toBeVisible()
|
||||
})
|
||||
|
||||
await test.step('Click on sign in and cancel, click again and expect different code', async () => {
|
||||
await signInPage.signInButton.click()
|
||||
await expect(signInPage.userCode).toBeVisible()
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 52 KiB After Width: | Height: | Size: 52 KiB |
Binary file not shown.
Before Width: | Height: | Size: 58 KiB After Width: | Height: | Size: 58 KiB |
@ -17,7 +17,7 @@ import dotenv from 'dotenv'
|
||||
|
||||
const NODE_ENV = process.env.NODE_ENV || 'development'
|
||||
dotenv.config({ path: [`.env.${NODE_ENV}.local`, `.env.${NODE_ENV}`] })
|
||||
export const token = process.env.VITE_KITTYCAD_API_TOKEN || ''
|
||||
export const token = process.env.token || ''
|
||||
|
||||
import type { ProjectConfiguration } from '@rust/kcl-lib/bindings/ProjectConfiguration'
|
||||
|
||||
|
@ -1105,6 +1105,270 @@ part002 = startSketchOn(XZ)
|
||||
// checking the count of the overlays is a good proxy check that the client sketch scene is in a good state
|
||||
await expect(page.getByTestId('segment-overlay')).toHaveCount(3)
|
||||
})
|
||||
|
||||
test.describe('Sketch scaling on first constraint', () => {
|
||||
test('Should scale entire sketch when constraining first dimension with scale checkbox enabled', async ({
|
||||
page,
|
||||
homePage,
|
||||
scene,
|
||||
cmdBar,
|
||||
}) => {
|
||||
await page.addInitScript(async () => {
|
||||
localStorage.setItem(
|
||||
'persistCode',
|
||||
`sketch001 = startSketchOn(XZ)
|
||||
profile001 = startProfile(sketch001, at = [100, 100])
|
||||
|> line(end = [200, 0])
|
||||
|> line(end = [0, 200])
|
||||
|> line(end = [-200, 0])
|
||||
|> close()
|
||||
profile002 = startProfile(sketch001, at = [400, 400])
|
||||
|> circle(center = [0, 0], radius = 50)`
|
||||
)
|
||||
})
|
||||
|
||||
const u = await getUtils(page)
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
|
||||
await homePage.goToModelingScene()
|
||||
await scene.settled(cmdBar)
|
||||
|
||||
// Click on the first line segment to select it
|
||||
await page.getByText('line(end = [200, 0])').click()
|
||||
await page.getByRole('button', { name: 'Edit Sketch' }).click()
|
||||
|
||||
// Wait for overlays to populate
|
||||
await page.waitForTimeout(1000)
|
||||
|
||||
// Click on the first line segment in the sketch
|
||||
const line1 = await u.getSegmentBodyCoords(`[data-overlay-index="1"]`)
|
||||
await page.mouse.click(line1.x, line1.y)
|
||||
await page.waitForTimeout(100)
|
||||
|
||||
// Open constraints menu and click length constraint
|
||||
await page
|
||||
.getByRole('button', {
|
||||
name: 'constraints: open menu',
|
||||
})
|
||||
.click()
|
||||
await page.getByTestId('constraint-length').click()
|
||||
|
||||
// Verify the scale sketch checkbox is present and enabled
|
||||
const scaleCheckbox = page.getByTestId('scale-sketch-checkbox')
|
||||
await expect(scaleCheckbox).toBeVisible()
|
||||
await expect(scaleCheckbox).toBeEnabled()
|
||||
await expect(scaleCheckbox).toBeChecked() // Should be checked by default since no constraints exist
|
||||
|
||||
// Enter new value (100, which is half of original 200)
|
||||
await page
|
||||
.getByTestId('cmd-bar-arg-value')
|
||||
.getByRole('textbox')
|
||||
.fill('100')
|
||||
|
||||
// Ensure scale checkbox is still checked
|
||||
await expect(scaleCheckbox).toBeChecked()
|
||||
|
||||
// Submit the constraint
|
||||
await page
|
||||
.getByRole('button', {
|
||||
name: 'arrow right Continue',
|
||||
})
|
||||
.click()
|
||||
|
||||
// Wait for the changes to be applied
|
||||
await page.waitForTimeout(1000)
|
||||
|
||||
// Verify the constraint was applied with a variable
|
||||
await expect(page.locator('.cm-content')).toContainText('length001 = 100')
|
||||
await expect(page.locator('.cm-content')).toContainText(
|
||||
'line(end = [length001, 0])'
|
||||
)
|
||||
|
||||
// Verify scaling occurred - all dimensions should be scaled by 0.5 (100/200)
|
||||
// Original: line(end = [0, 200]) -> Scaled: line(end = [0, 100])
|
||||
await expect(page.locator('.cm-content')).toContainText(
|
||||
'line(end = [0, 100])'
|
||||
)
|
||||
// Original: line(end = [-200, 0]) -> Scaled: line(end = [-100, 0])
|
||||
await expect(page.locator('.cm-content')).toContainText(
|
||||
'line(end = [-100, 0])'
|
||||
)
|
||||
|
||||
// Original: startProfile(at = [100, 100]) -> Scaled: startProfile(at = [50, 50])
|
||||
await expect(page.locator('.cm-content')).toContainText(
|
||||
'startProfile(sketch001, at = [50, 50])'
|
||||
)
|
||||
// Original: startProfile(at = [400, 400]) -> Scaled: startProfile(at = [200, 200])
|
||||
await expect(page.locator('.cm-content')).toContainText(
|
||||
'startProfile(sketch001, at = [200, 200])'
|
||||
)
|
||||
|
||||
// Original: radius = 50 -> Scaled: radius = 25
|
||||
await expect(page.locator('.cm-content')).toContainText('radius = 25')
|
||||
})
|
||||
|
||||
test('Should not scale sketch when constraining with scale checkbox disabled', async ({
|
||||
page,
|
||||
homePage,
|
||||
scene,
|
||||
cmdBar,
|
||||
}) => {
|
||||
await page.addInitScript(async () => {
|
||||
localStorage.setItem(
|
||||
'persistCode',
|
||||
`sketch001 = startSketchOn(XZ)
|
||||
profile001 = startProfile(sketch001, at = [100, 100])
|
||||
|> line(end = [200, 0])
|
||||
|> line(end = [0, 200])
|
||||
|> close()`
|
||||
)
|
||||
})
|
||||
|
||||
const u = await getUtils(page)
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
|
||||
await homePage.goToModelingScene()
|
||||
await scene.settled(cmdBar)
|
||||
|
||||
// Click on the first line segment to select it
|
||||
await page.getByText('line(end = [200, 0])').click()
|
||||
await page.getByRole('button', { name: 'Edit Sketch' }).click()
|
||||
|
||||
// Wait for overlays to populate
|
||||
await page.waitForTimeout(1000)
|
||||
|
||||
// Click on the first line segment in the sketch
|
||||
const line1 = await u.getSegmentBodyCoords(`[data-overlay-index="1"]`)
|
||||
await page.mouse.click(line1.x, line1.y)
|
||||
await page.waitForTimeout(100)
|
||||
|
||||
// Open constraints menu and click length constraint
|
||||
await page
|
||||
.getByRole('button', {
|
||||
name: 'constraints: open menu',
|
||||
})
|
||||
.click()
|
||||
await page.getByTestId('constraint-length').click()
|
||||
|
||||
// Verify the scale sketch checkbox is present and enabled
|
||||
const scaleCheckbox = page.getByTestId('scale-sketch-checkbox')
|
||||
await expect(scaleCheckbox).toBeVisible()
|
||||
await expect(scaleCheckbox).toBeEnabled()
|
||||
await expect(scaleCheckbox).toBeChecked()
|
||||
|
||||
// Uncheck the scale checkbox
|
||||
await scaleCheckbox.click()
|
||||
await expect(scaleCheckbox).not.toBeChecked()
|
||||
|
||||
// Enter new value (100, which is half of original 200)
|
||||
await page
|
||||
.getByTestId('cmd-bar-arg-value')
|
||||
.getByRole('textbox')
|
||||
.fill('100')
|
||||
|
||||
// Submit the constraint
|
||||
await page
|
||||
.getByRole('button', {
|
||||
name: 'arrow right Continue',
|
||||
})
|
||||
.click()
|
||||
|
||||
// Wait for the changes to be applied
|
||||
await page.waitForTimeout(1000)
|
||||
|
||||
// Verify the constraint was applied with a variable
|
||||
await expect(page.locator('.cm-content')).toContainText('length001 = 100')
|
||||
await expect(page.locator('.cm-content')).toContainText(
|
||||
'line(end = [length001, 0])'
|
||||
)
|
||||
|
||||
// Verify NO scaling occurred - other dimensions should remain unchanged
|
||||
await expect(page.locator('.cm-content')).toContainText(
|
||||
'line(end = [0, 200])'
|
||||
) // Should remain 200, not 100
|
||||
await expect(page.locator('.cm-content')).toContainText(
|
||||
'startProfile(sketch001, at = [100, 100])'
|
||||
) // Should remain [100, 100]
|
||||
})
|
||||
|
||||
test('Should disable scale checkbox when sketch already has constraints', async ({
|
||||
page,
|
||||
homePage,
|
||||
scene,
|
||||
cmdBar,
|
||||
}) => {
|
||||
await page.addInitScript(async () => {
|
||||
localStorage.setItem(
|
||||
'persistCode',
|
||||
`length_var = 150
|
||||
sketch001 = startSketchOn(XZ)
|
||||
profile001 = startProfile(sketch001, at = [100, 100])
|
||||
|> line(end = [length_var, 0])
|
||||
|> line(end = [0, 200])
|
||||
|> close()`
|
||||
)
|
||||
})
|
||||
|
||||
const u = await getUtils(page)
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
|
||||
await homePage.goToModelingScene()
|
||||
await scene.settled(cmdBar)
|
||||
|
||||
// Click on the second line segment (the one without constraints)
|
||||
await page.getByText('line(end = [0, 200])').click()
|
||||
await page.getByRole('button', { name: 'Edit Sketch' }).click()
|
||||
|
||||
// Wait for overlays to populate
|
||||
await page.waitForTimeout(1000)
|
||||
|
||||
// Click on the second line segment in the sketch
|
||||
const line2 = await u.getSegmentBodyCoords(`[data-overlay-index="2"]`)
|
||||
await page.mouse.click(line2.x, line2.y)
|
||||
await page.waitForTimeout(100)
|
||||
|
||||
// Open constraints menu and click length constraint
|
||||
await page
|
||||
.getByRole('button', {
|
||||
name: 'constraints: open menu',
|
||||
})
|
||||
.click()
|
||||
await page.getByTestId('constraint-length').click()
|
||||
|
||||
// Verify the scale sketch checkbox is present but disabled/unchecked
|
||||
// because the sketch already has constraints (length_var)
|
||||
const scaleCheckbox = page.getByTestId('scale-sketch-checkbox')
|
||||
await expect(scaleCheckbox).toBeVisible()
|
||||
await expect(scaleCheckbox).not.toBeChecked() // Should be unchecked because constraints exist
|
||||
|
||||
// Enter new value
|
||||
await page
|
||||
.getByTestId('cmd-bar-arg-value')
|
||||
.getByRole('textbox')
|
||||
.fill('100')
|
||||
|
||||
// Submit the constraint
|
||||
await page
|
||||
.getByRole('button', {
|
||||
name: 'arrow right Continue',
|
||||
})
|
||||
.click()
|
||||
|
||||
// Wait for the changes to be applied
|
||||
await page.waitForTimeout(1000)
|
||||
|
||||
// Verify the constraint was applied
|
||||
await expect(page.locator('.cm-content')).toContainText('length002 = 100')
|
||||
|
||||
// Verify existing constraint and values remain unchanged
|
||||
await expect(page.locator('.cm-content')).toContainText(
|
||||
'length_var = 150'
|
||||
)
|
||||
await expect(page.locator('.cm-content')).toContainText(
|
||||
'line(end = [length_var, 0])'
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
test.describe('Electron constraint tests', () => {
|
||||
test(
|
||||
|
7
interface.d.ts
vendored
7
interface.d.ts
vendored
@ -72,13 +72,16 @@ export interface IElectronAPI {
|
||||
}
|
||||
process: {
|
||||
env: {
|
||||
BASE_URL: string
|
||||
IS_PLAYWRIGHT: string
|
||||
VITE_KITTYCAD_API_TOKEN: string
|
||||
VITE_KC_DEV_TOKEN: string
|
||||
VITE_KC_API_WS_MODELING_URL: string
|
||||
VITE_KITTYCAD_API_BASE_URL: string
|
||||
VITE_KC_API_BASE_URL: string
|
||||
VITE_KC_SITE_BASE_URL: string
|
||||
VITE_KC_SITE_APP_URL: string
|
||||
VITE_KC_SKIP_AUTH: string
|
||||
VITE_KC_CONNECTION_TIMEOUT_MS: string
|
||||
VITE_KC_DEV_TOKEN: string
|
||||
NODE_ENV: string
|
||||
PROD: string
|
||||
DEV: string
|
||||
|
234
package-lock.json
generated
234
package-lock.json
generated
@ -149,7 +149,7 @@
|
||||
"ts-node": "^10.0.0",
|
||||
"typescript": "^5.8.3",
|
||||
"typescript-eslint": "^8.30.1",
|
||||
"vite": "^5.4.19",
|
||||
"vite": "^5.4.18",
|
||||
"vite-plugin-package-version": "^1.1.0",
|
||||
"vite-plugin-top-level-await": "^1.5.0",
|
||||
"vite-tsconfig-paths": "^4.3.2",
|
||||
@ -3652,9 +3652,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/aix-ppc64": {
|
||||
"version": "0.25.4",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.4.tgz",
|
||||
"integrity": "sha512-1VCICWypeQKhVbE9oW/sJaAmjLxhVqacdkvPLEjwlttjfwENRSClS8EjBz0KzRyFSCPDIkuXW34Je/vk7zdB7Q==",
|
||||
"version": "0.25.3",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.3.tgz",
|
||||
"integrity": "sha512-W8bFfPA8DowP8l//sxjJLSLkD8iEjMc7cBVyP+u4cEv9sM7mdUCkgsj+t0n/BWPFtv7WWCN5Yzj0N6FJNUUqBQ==",
|
||||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
@ -3668,9 +3668,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/android-arm": {
|
||||
"version": "0.25.4",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.4.tgz",
|
||||
"integrity": "sha512-QNdQEps7DfFwE3hXiU4BZeOV68HHzYwGd0Nthhd3uCkkEKK7/R6MTgM0P7H7FAs5pU/DIWsviMmEGxEoxIZ+ZQ==",
|
||||
"version": "0.25.3",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.3.tgz",
|
||||
"integrity": "sha512-PuwVXbnP87Tcff5I9ngV0lmiSu40xw1At6i3GsU77U7cjDDB4s0X2cyFuBiDa1SBk9DnvWwnGvVaGBqoFWPb7A==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
@ -3684,9 +3684,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/android-arm64": {
|
||||
"version": "0.25.4",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.4.tgz",
|
||||
"integrity": "sha512-bBy69pgfhMGtCnwpC/x5QhfxAz/cBgQ9enbtwjf6V9lnPI/hMyT9iWpR1arm0l3kttTr4L0KSLpKmLp/ilKS9A==",
|
||||
"version": "0.25.3",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.3.tgz",
|
||||
"integrity": "sha512-XelR6MzjlZuBM4f5z2IQHK6LkK34Cvv6Rj2EntER3lwCBFdg6h2lKbtRjpTTsdEjD/WSe1q8UyPBXP1x3i/wYQ==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@ -3700,9 +3700,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/android-x64": {
|
||||
"version": "0.25.4",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.4.tgz",
|
||||
"integrity": "sha512-TVhdVtQIFuVpIIR282btcGC2oGQoSfZfmBdTip2anCaVYcqWlZXGcdcKIUklfX2wj0JklNYgz39OBqh2cqXvcQ==",
|
||||
"version": "0.25.3",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.3.tgz",
|
||||
"integrity": "sha512-ogtTpYHT/g1GWS/zKM0cc/tIebFjm1F9Aw1boQ2Y0eUQ+J89d0jFY//s9ei9jVIlkYi8AfOjiixcLJSGNSOAdQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@ -3716,9 +3716,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/darwin-arm64": {
|
||||
"version": "0.25.4",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.4.tgz",
|
||||
"integrity": "sha512-Y1giCfM4nlHDWEfSckMzeWNdQS31BQGs9/rouw6Ub91tkK79aIMTH3q9xHvzH8d0wDru5Ci0kWB8b3up/nl16g==",
|
||||
"version": "0.25.3",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.3.tgz",
|
||||
"integrity": "sha512-eESK5yfPNTqpAmDfFWNsOhmIOaQA59tAcF/EfYvo5/QWQCzXn5iUSOnqt3ra3UdzBv073ykTtmeLJZGt3HhA+w==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@ -3732,9 +3732,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/darwin-x64": {
|
||||
"version": "0.25.4",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.4.tgz",
|
||||
"integrity": "sha512-CJsry8ZGM5VFVeyUYB3cdKpd/H69PYez4eJh1W/t38vzutdjEjtP7hB6eLKBoOdxcAlCtEYHzQ/PJ/oU9I4u0A==",
|
||||
"version": "0.25.3",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.3.tgz",
|
||||
"integrity": "sha512-Kd8glo7sIZtwOLcPbW0yLpKmBNWMANZhrC1r6K++uDR2zyzb6AeOYtI6udbtabmQpFaxJ8uduXMAo1gs5ozz8A==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@ -3748,9 +3748,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/freebsd-arm64": {
|
||||
"version": "0.25.4",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.4.tgz",
|
||||
"integrity": "sha512-yYq+39NlTRzU2XmoPW4l5Ifpl9fqSk0nAJYM/V/WUGPEFfek1epLHJIkTQM6bBs1swApjO5nWgvr843g6TjxuQ==",
|
||||
"version": "0.25.3",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.3.tgz",
|
||||
"integrity": "sha512-EJiyS70BYybOBpJth3M0KLOus0n+RRMKTYzhYhFeMwp7e/RaajXvP+BWlmEXNk6uk+KAu46j/kaQzr6au+JcIw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@ -3764,9 +3764,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/freebsd-x64": {
|
||||
"version": "0.25.4",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.4.tgz",
|
||||
"integrity": "sha512-0FgvOJ6UUMflsHSPLzdfDnnBBVoCDtBTVyn/MrWloUNvq/5SFmh13l3dvgRPkDihRxb77Y17MbqbCAa2strMQQ==",
|
||||
"version": "0.25.3",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.3.tgz",
|
||||
"integrity": "sha512-Q+wSjaLpGxYf7zC0kL0nDlhsfuFkoN+EXrx2KSB33RhinWzejOd6AvgmP5JbkgXKmjhmpfgKZq24pneodYqE8Q==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@ -3780,9 +3780,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-arm": {
|
||||
"version": "0.25.4",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.4.tgz",
|
||||
"integrity": "sha512-kro4c0P85GMfFYqW4TWOpvmF8rFShbWGnrLqlzp4X1TNWjRY3JMYUfDCtOxPKOIY8B0WC8HN51hGP4I4hz4AaQ==",
|
||||
"version": "0.25.3",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.3.tgz",
|
||||
"integrity": "sha512-dUOVmAUzuHy2ZOKIHIKHCm58HKzFqd+puLaS424h6I85GlSDRZIA5ycBixb3mFgM0Jdh+ZOSB6KptX30DD8YOQ==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
@ -3796,9 +3796,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-arm64": {
|
||||
"version": "0.25.4",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.4.tgz",
|
||||
"integrity": "sha512-+89UsQTfXdmjIvZS6nUnOOLoXnkUTB9hR5QAeLrQdzOSWZvNSAXAtcRDHWtqAUtAmv7ZM1WPOOeSxDzzzMogiQ==",
|
||||
"version": "0.25.3",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.3.tgz",
|
||||
"integrity": "sha512-xCUgnNYhRD5bb1C1nqrDV1PfkwgbswTTBRbAd8aH5PhYzikdf/ddtsYyMXFfGSsb/6t6QaPSzxtbfAZr9uox4A==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@ -3812,9 +3812,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-ia32": {
|
||||
"version": "0.25.4",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.4.tgz",
|
||||
"integrity": "sha512-yTEjoapy8UP3rv8dB0ip3AfMpRbyhSN3+hY8mo/i4QXFeDxmiYbEKp3ZRjBKcOP862Ua4b1PDfwlvbuwY7hIGQ==",
|
||||
"version": "0.25.3",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.3.tgz",
|
||||
"integrity": "sha512-yplPOpczHOO4jTYKmuYuANI3WhvIPSVANGcNUeMlxH4twz/TeXuzEP41tGKNGWJjuMhotpGabeFYGAOU2ummBw==",
|
||||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
@ -3828,9 +3828,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-loong64": {
|
||||
"version": "0.25.4",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.4.tgz",
|
||||
"integrity": "sha512-NeqqYkrcGzFwi6CGRGNMOjWGGSYOpqwCjS9fvaUlX5s3zwOtn1qwg1s2iE2svBe4Q/YOG1q6875lcAoQK/F4VA==",
|
||||
"version": "0.25.3",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.3.tgz",
|
||||
"integrity": "sha512-P4BLP5/fjyihmXCELRGrLd793q/lBtKMQl8ARGpDxgzgIKJDRJ/u4r1A/HgpBpKpKZelGct2PGI4T+axcedf6g==",
|
||||
"cpu": [
|
||||
"loong64"
|
||||
],
|
||||
@ -3844,9 +3844,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-mips64el": {
|
||||
"version": "0.25.4",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.4.tgz",
|
||||
"integrity": "sha512-IcvTlF9dtLrfL/M8WgNI/qJYBENP3ekgsHbYUIzEzq5XJzzVEV/fXY9WFPfEEXmu3ck2qJP8LG/p3Q8f7Zc2Xg==",
|
||||
"version": "0.25.3",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.3.tgz",
|
||||
"integrity": "sha512-eRAOV2ODpu6P5divMEMa26RRqb2yUoYsuQQOuFUexUoQndm4MdpXXDBbUoKIc0iPa4aCO7gIhtnYomkn2x+bag==",
|
||||
"cpu": [
|
||||
"mips64el"
|
||||
],
|
||||
@ -3860,9 +3860,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-ppc64": {
|
||||
"version": "0.25.4",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.4.tgz",
|
||||
"integrity": "sha512-HOy0aLTJTVtoTeGZh4HSXaO6M95qu4k5lJcH4gxv56iaycfz1S8GO/5Jh6X4Y1YiI0h7cRyLi+HixMR+88swag==",
|
||||
"version": "0.25.3",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.3.tgz",
|
||||
"integrity": "sha512-ZC4jV2p7VbzTlnl8nZKLcBkfzIf4Yad1SJM4ZMKYnJqZFD4rTI+pBG65u8ev4jk3/MPwY9DvGn50wi3uhdaghg==",
|
||||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
@ -3876,9 +3876,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-riscv64": {
|
||||
"version": "0.25.4",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.4.tgz",
|
||||
"integrity": "sha512-i8JUDAufpz9jOzo4yIShCTcXzS07vEgWzyX3NH2G7LEFVgrLEhjwL3ajFE4fZI3I4ZgiM7JH3GQ7ReObROvSUA==",
|
||||
"version": "0.25.3",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.3.tgz",
|
||||
"integrity": "sha512-LDDODcFzNtECTrUUbVCs6j9/bDVqy7DDRsuIXJg6so+mFksgwG7ZVnTruYi5V+z3eE5y+BJZw7VvUadkbfg7QA==",
|
||||
"cpu": [
|
||||
"riscv64"
|
||||
],
|
||||
@ -3892,9 +3892,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-s390x": {
|
||||
"version": "0.25.4",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.4.tgz",
|
||||
"integrity": "sha512-jFnu+6UbLlzIjPQpWCNh5QtrcNfMLjgIavnwPQAfoGx4q17ocOU9MsQ2QVvFxwQoWpZT8DvTLooTvmOQXkO51g==",
|
||||
"version": "0.25.3",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.3.tgz",
|
||||
"integrity": "sha512-s+w/NOY2k0yC2p9SLen+ymflgcpRkvwwa02fqmAwhBRI3SC12uiS10edHHXlVWwfAagYSY5UpmT/zISXPMW3tQ==",
|
||||
"cpu": [
|
||||
"s390x"
|
||||
],
|
||||
@ -3908,9 +3908,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-x64": {
|
||||
"version": "0.25.4",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.4.tgz",
|
||||
"integrity": "sha512-6e0cvXwzOnVWJHq+mskP8DNSrKBr1bULBvnFLpc1KY+d+irZSgZ02TGse5FsafKS5jg2e4pbvK6TPXaF/A6+CA==",
|
||||
"version": "0.25.3",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.3.tgz",
|
||||
"integrity": "sha512-nQHDz4pXjSDC6UfOE1Fw9Q8d6GCAd9KdvMZpfVGWSJztYCarRgSDfOVBY5xwhQXseiyxapkiSJi/5/ja8mRFFA==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@ -3924,9 +3924,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/netbsd-arm64": {
|
||||
"version": "0.25.4",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.4.tgz",
|
||||
"integrity": "sha512-vUnkBYxZW4hL/ie91hSqaSNjulOnYXE1VSLusnvHg2u3jewJBz3YzB9+oCw8DABeVqZGg94t9tyZFoHma8gWZQ==",
|
||||
"version": "0.25.3",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.3.tgz",
|
||||
"integrity": "sha512-1QaLtOWq0mzK6tzzp0jRN3eccmN3hezey7mhLnzC6oNlJoUJz4nym5ZD7mDnS/LZQgkrhEbEiTn515lPeLpgWA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@ -3940,9 +3940,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/netbsd-x64": {
|
||||
"version": "0.25.4",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.4.tgz",
|
||||
"integrity": "sha512-XAg8pIQn5CzhOB8odIcAm42QsOfa98SBeKUdo4xa8OvX8LbMZqEtgeWE9P/Wxt7MlG2QqvjGths+nq48TrUiKw==",
|
||||
"version": "0.25.3",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.3.tgz",
|
||||
"integrity": "sha512-i5Hm68HXHdgv8wkrt+10Bc50zM0/eonPb/a/OFVfB6Qvpiirco5gBA5bz7S2SHuU+Y4LWn/zehzNX14Sp4r27g==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@ -3956,9 +3956,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/openbsd-arm64": {
|
||||
"version": "0.25.4",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.4.tgz",
|
||||
"integrity": "sha512-Ct2WcFEANlFDtp1nVAXSNBPDxyU+j7+tId//iHXU2f/lN5AmO4zLyhDcpR5Cz1r08mVxzt3Jpyt4PmXQ1O6+7A==",
|
||||
"version": "0.25.3",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.3.tgz",
|
||||
"integrity": "sha512-zGAVApJEYTbOC6H/3QBr2mq3upG/LBEXr85/pTtKiv2IXcgKV0RT0QA/hSXZqSvLEpXeIxah7LczB4lkiYhTAQ==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@ -3972,9 +3972,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/openbsd-x64": {
|
||||
"version": "0.25.4",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.4.tgz",
|
||||
"integrity": "sha512-xAGGhyOQ9Otm1Xu8NT1ifGLnA6M3sJxZ6ixylb+vIUVzvvd6GOALpwQrYrtlPouMqd/vSbgehz6HaVk4+7Afhw==",
|
||||
"version": "0.25.3",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.3.tgz",
|
||||
"integrity": "sha512-fpqctI45NnCIDKBH5AXQBsD0NDPbEFczK98hk/aa6HJxbl+UtLkJV2+Bvy5hLSLk3LHmqt0NTkKNso1A9y1a4w==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@ -3988,9 +3988,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/sunos-x64": {
|
||||
"version": "0.25.4",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.4.tgz",
|
||||
"integrity": "sha512-Mw+tzy4pp6wZEK0+Lwr76pWLjrtjmJyUB23tHKqEDP74R3q95luY/bXqXZeYl4NYlvwOqoRKlInQialgCKy67Q==",
|
||||
"version": "0.25.3",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.3.tgz",
|
||||
"integrity": "sha512-ROJhm7d8bk9dMCUZjkS8fgzsPAZEjtRJqCAmVgB0gMrvG7hfmPmz9k1rwO4jSiblFjYmNvbECL9uhaPzONMfgA==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@ -4004,9 +4004,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/win32-arm64": {
|
||||
"version": "0.25.4",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.4.tgz",
|
||||
"integrity": "sha512-AVUP428VQTSddguz9dO9ngb+E5aScyg7nOeJDrF1HPYu555gmza3bDGMPhmVXL8svDSoqPCsCPjb265yG/kLKQ==",
|
||||
"version": "0.25.3",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.3.tgz",
|
||||
"integrity": "sha512-YWcow8peiHpNBiIXHwaswPnAXLsLVygFwCB3A7Bh5jRkIBFWHGmNQ48AlX4xDvQNoMZlPYzjVOQDYEzWCqufMQ==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@ -4020,9 +4020,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/win32-ia32": {
|
||||
"version": "0.25.4",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.4.tgz",
|
||||
"integrity": "sha512-i1sW+1i+oWvQzSgfRcxxG2k4I9n3O9NRqy8U+uugaT2Dy7kLO9Y7wI72haOahxceMX8hZAzgGou1FhndRldxRg==",
|
||||
"version": "0.25.3",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.3.tgz",
|
||||
"integrity": "sha512-qspTZOIGoXVS4DpNqUYUs9UxVb04khS1Degaw/MnfMe7goQ3lTfQ13Vw4qY/Nj0979BGvMRpAYbs/BAxEvU8ew==",
|
||||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
@ -4036,9 +4036,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/win32-x64": {
|
||||
"version": "0.25.4",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.4.tgz",
|
||||
"integrity": "sha512-nOT2vZNw6hJ+z43oP1SPea/G/6AbN6X+bGNhNuq8NtRHy4wsMhw765IKLNmnjek7GvjWBYQ8Q5VBoYTFg9y1UQ==",
|
||||
"version": "0.25.3",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.3.tgz",
|
||||
"integrity": "sha512-ICgUR+kPimx0vvRzf+N/7L7tVSQeE3BYY+NhHRHXS1kBuPO7z2+7ea2HbhDyZdTephgvNvKrlDDKUexuCVBVvg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@ -13219,9 +13219,9 @@
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/esbuild": {
|
||||
"version": "0.25.4",
|
||||
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.4.tgz",
|
||||
"integrity": "sha512-8pgjLUcUjcgDg+2Q4NYXnPbo/vncAY4UmyaCm0jZevERqCHZIaWwdJHkf8XQtu4AxSKCdvrUbT0XUr1IdZzI8Q==",
|
||||
"version": "0.25.3",
|
||||
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.3.tgz",
|
||||
"integrity": "sha512-qKA6Pvai73+M2FtftpNKRxJ78GIjmFXFxd/1DVBqGo/qNhLSfv+G12n9pNoWdytJC8U00TrViOwpjT0zgqQS8Q==",
|
||||
"dev": true,
|
||||
"hasInstallScript": true,
|
||||
"bin": {
|
||||
@ -13231,31 +13231,31 @@
|
||||
"node": ">=18"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@esbuild/aix-ppc64": "0.25.4",
|
||||
"@esbuild/android-arm": "0.25.4",
|
||||
"@esbuild/android-arm64": "0.25.4",
|
||||
"@esbuild/android-x64": "0.25.4",
|
||||
"@esbuild/darwin-arm64": "0.25.4",
|
||||
"@esbuild/darwin-x64": "0.25.4",
|
||||
"@esbuild/freebsd-arm64": "0.25.4",
|
||||
"@esbuild/freebsd-x64": "0.25.4",
|
||||
"@esbuild/linux-arm": "0.25.4",
|
||||
"@esbuild/linux-arm64": "0.25.4",
|
||||
"@esbuild/linux-ia32": "0.25.4",
|
||||
"@esbuild/linux-loong64": "0.25.4",
|
||||
"@esbuild/linux-mips64el": "0.25.4",
|
||||
"@esbuild/linux-ppc64": "0.25.4",
|
||||
"@esbuild/linux-riscv64": "0.25.4",
|
||||
"@esbuild/linux-s390x": "0.25.4",
|
||||
"@esbuild/linux-x64": "0.25.4",
|
||||
"@esbuild/netbsd-arm64": "0.25.4",
|
||||
"@esbuild/netbsd-x64": "0.25.4",
|
||||
"@esbuild/openbsd-arm64": "0.25.4",
|
||||
"@esbuild/openbsd-x64": "0.25.4",
|
||||
"@esbuild/sunos-x64": "0.25.4",
|
||||
"@esbuild/win32-arm64": "0.25.4",
|
||||
"@esbuild/win32-ia32": "0.25.4",
|
||||
"@esbuild/win32-x64": "0.25.4"
|
||||
"@esbuild/aix-ppc64": "0.25.3",
|
||||
"@esbuild/android-arm": "0.25.3",
|
||||
"@esbuild/android-arm64": "0.25.3",
|
||||
"@esbuild/android-x64": "0.25.3",
|
||||
"@esbuild/darwin-arm64": "0.25.3",
|
||||
"@esbuild/darwin-x64": "0.25.3",
|
||||
"@esbuild/freebsd-arm64": "0.25.3",
|
||||
"@esbuild/freebsd-x64": "0.25.3",
|
||||
"@esbuild/linux-arm": "0.25.3",
|
||||
"@esbuild/linux-arm64": "0.25.3",
|
||||
"@esbuild/linux-ia32": "0.25.3",
|
||||
"@esbuild/linux-loong64": "0.25.3",
|
||||
"@esbuild/linux-mips64el": "0.25.3",
|
||||
"@esbuild/linux-ppc64": "0.25.3",
|
||||
"@esbuild/linux-riscv64": "0.25.3",
|
||||
"@esbuild/linux-s390x": "0.25.3",
|
||||
"@esbuild/linux-x64": "0.25.3",
|
||||
"@esbuild/netbsd-arm64": "0.25.3",
|
||||
"@esbuild/netbsd-x64": "0.25.3",
|
||||
"@esbuild/openbsd-arm64": "0.25.3",
|
||||
"@esbuild/openbsd-x64": "0.25.3",
|
||||
"@esbuild/sunos-x64": "0.25.3",
|
||||
"@esbuild/win32-arm64": "0.25.3",
|
||||
"@esbuild/win32-ia32": "0.25.3",
|
||||
"@esbuild/win32-x64": "0.25.3"
|
||||
}
|
||||
},
|
||||
"node_modules/escalade": {
|
||||
@ -25132,10 +25132,11 @@
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/vite": {
|
||||
"version": "5.4.19",
|
||||
"resolved": "https://registry.npmjs.org/vite/-/vite-5.4.19.tgz",
|
||||
"integrity": "sha512-qO3aKv3HoQC8QKiNSTuUM1l9o/XX3+c+VTgLHbJWHZGeTPVAg2XwazI9UWzoxjIJCGCV2zU60uqMzjeLZuULqA==",
|
||||
"version": "5.4.18",
|
||||
"resolved": "https://registry.npmjs.org/vite/-/vite-5.4.18.tgz",
|
||||
"integrity": "sha512-1oDcnEp3lVyHCuQ2YFelM4Alm2o91xNoMncRm1U7S+JdYfYOvbiGZ3/CxGttrOu2M/KcGz7cRC2DoNUA6urmMA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"esbuild": "^0.21.3",
|
||||
"postcss": "^8.4.43",
|
||||
@ -26639,25 +26640,10 @@
|
||||
"vscode-uri": "^3.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^24.0.7",
|
||||
"@types/node": "^22.14.1",
|
||||
"ts-node": "^10.9.2"
|
||||
}
|
||||
},
|
||||
"packages/codemirror-lsp-client/node_modules/@types/node": {
|
||||
"version": "24.0.10",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-24.0.10.tgz",
|
||||
"integrity": "sha512-ENHwaH+JIRTDIEEbDK6QSQntAYGtbvdDXnMXnZaZ6k13Du1dPMmprkEHIL7ok2Wl2aZevetwTAb5S+7yIF+enA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"undici-types": "~7.8.0"
|
||||
}
|
||||
},
|
||||
"packages/codemirror-lsp-client/node_modules/undici-types": {
|
||||
"version": "7.8.0",
|
||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.8.0.tgz",
|
||||
"integrity": "sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw==",
|
||||
"dev": true
|
||||
},
|
||||
"rust/kcl-language-server": {
|
||||
"version": "0.0.0",
|
||||
"license": "MIT",
|
||||
@ -26675,7 +26661,7 @@
|
||||
"@vscode/test-electron": "^2.4.1",
|
||||
"@vscode/vsce": "^3.3.2",
|
||||
"cross-env": "^7.0.3",
|
||||
"esbuild": "^0.25.4",
|
||||
"esbuild": "^0.25.3",
|
||||
"glob": "^11.0.1",
|
||||
"mocha": "^11.1.0",
|
||||
"typescript": "^5.8.3"
|
||||
|
@ -227,7 +227,7 @@
|
||||
"ts-node": "^10.0.0",
|
||||
"typescript": "^5.8.3",
|
||||
"typescript-eslint": "^8.30.1",
|
||||
"vite": "^5.4.19",
|
||||
"vite": "^5.4.18",
|
||||
"vite-plugin-package-version": "^1.1.0",
|
||||
"vite-plugin-top-level-await": "^1.5.0",
|
||||
"vite-tsconfig-paths": "^4.3.2",
|
||||
|
@ -123,7 +123,7 @@
|
||||
"@vscode/test-electron": "^2.4.1",
|
||||
"@vscode/vsce": "^3.3.2",
|
||||
"cross-env": "^7.0.3",
|
||||
"esbuild": "^0.25.4",
|
||||
"esbuild": "^0.25.3",
|
||||
"glob": "^11.0.1",
|
||||
"mocha": "^11.1.0",
|
||||
"typescript": "^5.8.3"
|
||||
|
@ -10,76 +10,71 @@ DATA;
|
||||
NAMED_UNIT(*)
|
||||
SI_UNIT($, .METRE.)
|
||||
);
|
||||
#2 = (
|
||||
NAMED_UNIT(*)
|
||||
PLANE_ANGLE_UNIT()
|
||||
SI_UNIT($, .RADIAN.)
|
||||
);
|
||||
#3 = UNCERTAINTY_MEASURE_WITH_UNIT(0.00001, #1, 'DISTANCE_ACCURACY_VALUE', $);
|
||||
#4 = (
|
||||
#2 = UNCERTAINTY_MEASURE_WITH_UNIT(0.00001, #1, 'DISTANCE_ACCURACY_VALUE', $);
|
||||
#3 = (
|
||||
GEOMETRIC_REPRESENTATION_CONTEXT(3)
|
||||
GLOBAL_UNCERTAINTY_ASSIGNED_CONTEXT((#3))
|
||||
GLOBAL_UNIT_ASSIGNED_CONTEXT((#1, #2))
|
||||
GLOBAL_UNCERTAINTY_ASSIGNED_CONTEXT((#2))
|
||||
GLOBAL_UNIT_ASSIGNED_CONTEXT((#1))
|
||||
REPRESENTATION_CONTEXT('', '3D')
|
||||
);
|
||||
#5 = CARTESIAN_POINT('NONE', (0.015, -0.01, -0.005));
|
||||
#6 = VERTEX_POINT('NONE', #5);
|
||||
#7 = CARTESIAN_POINT('NONE', (0.015, 0, -0.005));
|
||||
#8 = VERTEX_POINT('NONE', #7);
|
||||
#9 = DIRECTION('NONE', (1, 0, -0));
|
||||
#10 = DIRECTION('NONE', (0, 1, 0));
|
||||
#11 = CARTESIAN_POINT('NONE', (0.005, -0.01, -0.005));
|
||||
#12 = AXIS2_PLACEMENT_3D('NONE', #11, #10, #9);
|
||||
#13 = CIRCLE('NONE', #12, 0.01);
|
||||
#14 = DIRECTION('NONE', (0, 1, 0));
|
||||
#15 = VECTOR('NONE', #14, 1);
|
||||
#16 = CARTESIAN_POINT('NONE', (0.015, -0.01, -0.005));
|
||||
#17 = LINE('NONE', #16, #15);
|
||||
#18 = DIRECTION('NONE', (1, 0, -0));
|
||||
#19 = DIRECTION('NONE', (0, 1, 0));
|
||||
#20 = CARTESIAN_POINT('NONE', (0.005, 0, -0.005));
|
||||
#21 = AXIS2_PLACEMENT_3D('NONE', #20, #19, #18);
|
||||
#22 = CIRCLE('NONE', #21, 0.01);
|
||||
#23 = EDGE_CURVE('NONE', #6, #6, #13, .T.);
|
||||
#24 = EDGE_CURVE('NONE', #6, #8, #17, .T.);
|
||||
#25 = EDGE_CURVE('NONE', #8, #8, #22, .T.);
|
||||
#26 = CARTESIAN_POINT('NONE', (0.005, -0.005, -0.005));
|
||||
#27 = DIRECTION('NONE', (0, 1, 0));
|
||||
#28 = DIRECTION('NONE', (1, 0, -0));
|
||||
#29 = AXIS2_PLACEMENT_3D('NONE', #26, #27, #28);
|
||||
#30 = CYLINDRICAL_SURFACE('NONE', #29, 0.01);
|
||||
#31 = CARTESIAN_POINT('NONE', (0, -0.01, -0));
|
||||
#32 = DIRECTION('NONE', (0, 1, 0));
|
||||
#33 = AXIS2_PLACEMENT_3D('NONE', #31, #32, $);
|
||||
#34 = PLANE('NONE', #33);
|
||||
#35 = CARTESIAN_POINT('NONE', (0, 0, -0));
|
||||
#36 = DIRECTION('NONE', (0, 1, 0));
|
||||
#37 = AXIS2_PLACEMENT_3D('NONE', #35, #36, $);
|
||||
#38 = PLANE('NONE', #37);
|
||||
#39 = ORIENTED_EDGE('NONE', *, *, #23, .T.);
|
||||
#40 = ORIENTED_EDGE('NONE', *, *, #25, .F.);
|
||||
#41 = EDGE_LOOP('NONE', (#39));
|
||||
#42 = FACE_BOUND('NONE', #41, .T.);
|
||||
#43 = EDGE_LOOP('NONE', (#40));
|
||||
#44 = FACE_BOUND('NONE', #43, .T.);
|
||||
#45 = ADVANCED_FACE('NONE', (#42, #44), #30, .T.);
|
||||
#46 = ORIENTED_EDGE('NONE', *, *, #23, .F.);
|
||||
#47 = EDGE_LOOP('NONE', (#46));
|
||||
#48 = FACE_BOUND('NONE', #47, .T.);
|
||||
#49 = ADVANCED_FACE('NONE', (#48), #34, .F.);
|
||||
#50 = ORIENTED_EDGE('NONE', *, *, #25, .T.);
|
||||
#51 = EDGE_LOOP('NONE', (#50));
|
||||
#52 = FACE_BOUND('NONE', #51, .T.);
|
||||
#53 = ADVANCED_FACE('NONE', (#52), #38, .T.);
|
||||
#54 = CLOSED_SHELL('NONE', (#45, #49, #53));
|
||||
#55 = MANIFOLD_SOLID_BREP('NONE', #54);
|
||||
#56 = APPLICATION_CONTEXT('configuration controlled 3D design of mechanical parts and assemblies');
|
||||
#57 = PRODUCT_DEFINITION_CONTEXT('part definition', #56, 'design');
|
||||
#58 = PRODUCT('UNIDENTIFIED_PRODUCT', 'NONE', $, ());
|
||||
#59 = PRODUCT_DEFINITION_FORMATION('', $, #58);
|
||||
#60 = PRODUCT_DEFINITION('design', $, #59, #57);
|
||||
#61 = PRODUCT_DEFINITION_SHAPE('NONE', $, #60);
|
||||
#62 = ADVANCED_BREP_SHAPE_REPRESENTATION('NONE', (#55), #4);
|
||||
#63 = SHAPE_DEFINITION_REPRESENTATION(#61, #62);
|
||||
#4 = CARTESIAN_POINT('NONE', (0.015, -0.01, -0.005));
|
||||
#5 = VERTEX_POINT('NONE', #4);
|
||||
#6 = CARTESIAN_POINT('NONE', (0.015, 0, -0.005));
|
||||
#7 = VERTEX_POINT('NONE', #6);
|
||||
#8 = DIRECTION('NONE', (1, 0, -0));
|
||||
#9 = DIRECTION('NONE', (0, 1, 0));
|
||||
#10 = CARTESIAN_POINT('NONE', (0.005, -0.01, -0.005));
|
||||
#11 = AXIS2_PLACEMENT_3D('NONE', #10, #9, #8);
|
||||
#12 = CIRCLE('NONE', #11, 0.01);
|
||||
#13 = DIRECTION('NONE', (0, 1, 0));
|
||||
#14 = VECTOR('NONE', #13, 1);
|
||||
#15 = CARTESIAN_POINT('NONE', (0.015, -0.01, -0.005));
|
||||
#16 = LINE('NONE', #15, #14);
|
||||
#17 = DIRECTION('NONE', (1, 0, -0));
|
||||
#18 = DIRECTION('NONE', (0, 1, 0));
|
||||
#19 = CARTESIAN_POINT('NONE', (0.005, 0, -0.005));
|
||||
#20 = AXIS2_PLACEMENT_3D('NONE', #19, #18, #17);
|
||||
#21 = CIRCLE('NONE', #20, 0.01);
|
||||
#22 = EDGE_CURVE('NONE', #5, #5, #12, .T.);
|
||||
#23 = EDGE_CURVE('NONE', #5, #7, #16, .T.);
|
||||
#24 = EDGE_CURVE('NONE', #7, #7, #21, .T.);
|
||||
#25 = CARTESIAN_POINT('NONE', (0.005, -0.005, -0.005));
|
||||
#26 = DIRECTION('NONE', (0, 1, 0));
|
||||
#27 = DIRECTION('NONE', (1, 0, -0));
|
||||
#28 = AXIS2_PLACEMENT_3D('NONE', #25, #26, #27);
|
||||
#29 = CYLINDRICAL_SURFACE('NONE', #28, 0.01);
|
||||
#30 = CARTESIAN_POINT('NONE', (0, -0.01, -0));
|
||||
#31 = DIRECTION('NONE', (0, 1, 0));
|
||||
#32 = AXIS2_PLACEMENT_3D('NONE', #30, #31, $);
|
||||
#33 = PLANE('NONE', #32);
|
||||
#34 = CARTESIAN_POINT('NONE', (0, 0, -0));
|
||||
#35 = DIRECTION('NONE', (0, 1, 0));
|
||||
#36 = AXIS2_PLACEMENT_3D('NONE', #34, #35, $);
|
||||
#37 = PLANE('NONE', #36);
|
||||
#38 = ORIENTED_EDGE('NONE', *, *, #22, .T.);
|
||||
#39 = ORIENTED_EDGE('NONE', *, *, #24, .F.);
|
||||
#40 = EDGE_LOOP('NONE', (#38));
|
||||
#41 = FACE_BOUND('NONE', #40, .T.);
|
||||
#42 = EDGE_LOOP('NONE', (#39));
|
||||
#43 = FACE_BOUND('NONE', #42, .T.);
|
||||
#44 = ADVANCED_FACE('NONE', (#41, #43), #29, .T.);
|
||||
#45 = ORIENTED_EDGE('NONE', *, *, #22, .F.);
|
||||
#46 = EDGE_LOOP('NONE', (#45));
|
||||
#47 = FACE_BOUND('NONE', #46, .T.);
|
||||
#48 = ADVANCED_FACE('NONE', (#47), #33, .F.);
|
||||
#49 = ORIENTED_EDGE('NONE', *, *, #24, .T.);
|
||||
#50 = EDGE_LOOP('NONE', (#49));
|
||||
#51 = FACE_BOUND('NONE', #50, .T.);
|
||||
#52 = ADVANCED_FACE('NONE', (#51), #37, .T.);
|
||||
#53 = CLOSED_SHELL('NONE', (#44, #48, #52));
|
||||
#54 = MANIFOLD_SOLID_BREP('NONE', #53);
|
||||
#55 = APPLICATION_CONTEXT('configuration controlled 3D design of mechanical parts and assemblies');
|
||||
#56 = PRODUCT_DEFINITION_CONTEXT('part definition', #55, 'design');
|
||||
#57 = PRODUCT('UNIDENTIFIED_PRODUCT', 'NONE', $, ());
|
||||
#58 = PRODUCT_DEFINITION_FORMATION('', $, #57);
|
||||
#59 = PRODUCT_DEFINITION('design', $, #58, #56);
|
||||
#60 = PRODUCT_DEFINITION_SHAPE('NONE', $, #59);
|
||||
#61 = ADVANCED_BREP_SHAPE_REPRESENTATION('NONE', (#54), #3);
|
||||
#62 = SHAPE_DEFINITION_REPRESENTATION(#60, #61);
|
||||
ENDSEC;
|
||||
END-ISO-10303-21;
|
||||
|
@ -994,39 +994,6 @@ impl Node<MemberExpression> {
|
||||
|
||||
// Check the property and object match -- e.g. ints for arrays, strs for objects.
|
||||
match (object, property, self.computed) {
|
||||
(KclValue::Plane { value: plane }, Property::String(property), false) => match property.as_str() {
|
||||
"yAxis" => {
|
||||
let (p, u) = plane.info.y_axis.as_3_dims();
|
||||
Ok(KclValue::array_from_point3d(
|
||||
p,
|
||||
NumericType::Known(crate::exec::UnitType::Length(u)),
|
||||
vec![meta],
|
||||
))
|
||||
}
|
||||
"xAxis" => {
|
||||
let (p, u) = plane.info.x_axis.as_3_dims();
|
||||
Ok(KclValue::array_from_point3d(
|
||||
p,
|
||||
NumericType::Known(crate::exec::UnitType::Length(u)),
|
||||
vec![meta],
|
||||
))
|
||||
}
|
||||
"origin" => {
|
||||
let (p, u) = plane.info.origin.as_3_dims();
|
||||
Ok(KclValue::array_from_point3d(
|
||||
p,
|
||||
NumericType::Known(crate::exec::UnitType::Length(u)),
|
||||
vec![meta],
|
||||
))
|
||||
}
|
||||
other => Err(KclError::new_undefined_value(
|
||||
KclErrorDetails::new(
|
||||
format!("Property '{other}' not found in plane"),
|
||||
vec![self.clone().into()],
|
||||
),
|
||||
None,
|
||||
)),
|
||||
},
|
||||
(KclValue::Object { value: map, meta: _ }, Property::String(property), false) => {
|
||||
if let Some(value) = map.get(&property) {
|
||||
Ok(value.to_owned())
|
||||
@ -1046,22 +1013,7 @@ impl Node<MemberExpression> {
|
||||
vec![self.clone().into()],
|
||||
)))
|
||||
}
|
||||
(KclValue::Object { value: map, .. }, p @ Property::UInt(i), _) => {
|
||||
if i == 0
|
||||
&& let Some(value) = map.get("x")
|
||||
{
|
||||
return Ok(value.to_owned());
|
||||
}
|
||||
if i == 1
|
||||
&& let Some(value) = map.get("y")
|
||||
{
|
||||
return Ok(value.to_owned());
|
||||
}
|
||||
if i == 2
|
||||
&& let Some(value) = map.get("z")
|
||||
{
|
||||
return Ok(value.to_owned());
|
||||
}
|
||||
(KclValue::Object { .. }, p, _) => {
|
||||
let t = p.type_name();
|
||||
let article = article_for(t);
|
||||
Err(KclError::new_semantic(KclErrorDetails::new(
|
||||
@ -2253,12 +2205,4 @@ y = x[0mm + 1]
|
||||
"#;
|
||||
parse_execute(ast).await.unwrap_err();
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn getting_property_of_plane() {
|
||||
// let ast = include_str!("../../tests/inputs/planestuff.kcl");
|
||||
let ast = std::fs::read_to_string("tests/inputs/planestuff.kcl").unwrap();
|
||||
|
||||
parse_execute(&ast).await.unwrap();
|
||||
}
|
||||
}
|
||||
|
@ -921,12 +921,6 @@ impl Point3d {
|
||||
units: UnitLen::Unknown,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_3_dims(&self) -> ([f64; 3], UnitLen) {
|
||||
let p = [self.x, self.y, self.z];
|
||||
let u = self.units;
|
||||
(p, u)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<[TyF64; 3]> for Point3d {
|
||||
|
@ -458,31 +458,6 @@ impl KclValue {
|
||||
}
|
||||
}
|
||||
|
||||
/// Put the point into a KCL point.
|
||||
pub fn array_from_point3d(p: [f64; 3], ty: NumericType, meta: Vec<Metadata>) -> Self {
|
||||
let [x, y, z] = p;
|
||||
Self::HomArray {
|
||||
value: vec![
|
||||
Self::Number {
|
||||
value: x,
|
||||
meta: meta.clone(),
|
||||
ty,
|
||||
},
|
||||
Self::Number {
|
||||
value: y,
|
||||
meta: meta.clone(),
|
||||
ty,
|
||||
},
|
||||
Self::Number {
|
||||
value: z,
|
||||
meta: meta.clone(),
|
||||
ty,
|
||||
},
|
||||
],
|
||||
ty: ty.into(),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn as_usize(&self) -> Option<usize> {
|
||||
match self {
|
||||
KclValue::Number { value, .. } => crate::try_f64_to_usize(*value),
|
||||
|
@ -1,60 +0,0 @@
|
||||
// There are 3 ways to define a plane in KCL, according to https://zoo.dev/docs/kcl-std/types/std-types-Plane
|
||||
// - A default plane
|
||||
// - Modifying a default plane e.g. via offsetPlane
|
||||
// - Defining your own struct
|
||||
// This file tests they all work equivalently.
|
||||
|
||||
// Define a plane using struct representation.
|
||||
myPlane = {
|
||||
origin = { x = 0, y = 0, z = 0 },
|
||||
xAxis = { x = 1, y = 0, z = 0 },
|
||||
yAxis = { x = 0, y = 1, z = 0 },
|
||||
}
|
||||
|
||||
// Prove we can get its axes and origin.
|
||||
ax = myPlane.xAxis
|
||||
assert(ax[0], isEqualTo = 1)
|
||||
assert(ax[1], isEqualTo = 0)
|
||||
assert(ax[2], isEqualTo = 0)
|
||||
ay = myPlane.yAxis
|
||||
assert(ay[0], isEqualTo = 0)
|
||||
assert(ay[1], isEqualTo = 1)
|
||||
assert(ay[2], isEqualTo = 0)
|
||||
aorigin = myPlane.origin
|
||||
assert(aorigin[0], isEqualTo = 0)
|
||||
assert(aorigin[1], isEqualTo = 0)
|
||||
assert(aorigin[2], isEqualTo = 0)
|
||||
|
||||
// Define a plane using standard planes.
|
||||
myOtherPlane = XY
|
||||
|
||||
// Prove we can get its axes and origin.
|
||||
axOther = myOtherPlane.xAxis
|
||||
assert(axOther[0], isEqualTo = 1)
|
||||
assert(axOther[1], isEqualTo = 0)
|
||||
assert(axOther[2], isEqualTo = 0)
|
||||
ayOther = myOtherPlane.yAxis
|
||||
assert(ayOther[0], isEqualTo = 0)
|
||||
assert(ayOther[1], isEqualTo = 1)
|
||||
assert(ayOther[2], isEqualTo = 0)
|
||||
aoriginOther = myOtherPlane.origin
|
||||
assert(aoriginOther[0], isEqualTo = 0)
|
||||
assert(aoriginOther[1], isEqualTo = 0)
|
||||
assert(aoriginOther[2], isEqualTo = 0)
|
||||
|
||||
// Define a plane using a plane-modifying function like offsetPlane.
|
||||
myAlternatePlane = offsetPlane(XY, offset = 0)
|
||||
|
||||
// Prove we can get its axes and origin.
|
||||
axAlternate = myAlternatePlane.xAxis
|
||||
assert(axAlternate[0], isEqualTo = 1)
|
||||
assert(axAlternate[1], isEqualTo = 0)
|
||||
assert(axAlternate[2], isEqualTo = 0)
|
||||
ayAlternate = myAlternatePlane.yAxis
|
||||
assert(ayAlternate[0], isEqualTo = 0)
|
||||
assert(ayAlternate[1], isEqualTo = 1)
|
||||
assert(ayAlternate[2], isEqualTo = 0)
|
||||
aoriginAlternate = myAlternatePlane.origin
|
||||
assert(aoriginAlternate[0], isEqualTo = 0)
|
||||
assert(aoriginAlternate[1], isEqualTo = 0)
|
||||
assert(aoriginAlternate[2], isEqualTo = 0)
|
@ -4,8 +4,6 @@
|
||||
|
||||
URL STATUS
|
||||
000 https://${BASE_URL}
|
||||
405 https://api.dev.zoo.dev/oauth2/token/revoke
|
||||
401 https://api.dev.zoo.dev/users
|
||||
301 https://discord.gg/JQEpHR7Nt2
|
||||
404 https://github.com/KittyCAD/engine/issues/3528
|
||||
404 https://github.com/KittyCAD/modeling-app/commit/${ref}
|
||||
|
@ -1026,7 +1026,7 @@ export class SceneEntities {
|
||||
const sketch = sketchFromPathToNode({
|
||||
pathToNode: sketchEntryNodePath,
|
||||
variables: this.kclManager.variables,
|
||||
kclManager: this.kclManager,
|
||||
ast: this.kclManager.ast,
|
||||
})
|
||||
if (err(sketch)) return Promise.reject(sketch)
|
||||
if (!sketch) return Promise.reject(new Error('No sketch found'))
|
||||
@ -1171,12 +1171,64 @@ export class SceneEntities {
|
||||
codeManager: this.codeManager,
|
||||
})
|
||||
|
||||
// Check if we need to update sketchNodePaths due to CallExpression -> PipeExpression transformation
|
||||
let updatedSketchNodePaths = [...sketchNodePaths]
|
||||
let updatedSketchEntryNodePath = sketchEntryNodePath
|
||||
let needsSelectionUpdate = false
|
||||
|
||||
// Check if the sketch entry was converted from CallExpression to PipeExpression
|
||||
const _nodeAfter = getNodeFromPath<VariableDeclaration>(
|
||||
modifiedAst,
|
||||
sketchEntryNodePath,
|
||||
'VariableDeclaration'
|
||||
)
|
||||
if (!err(_nodeAfter)) {
|
||||
const initAfter = _nodeAfter.node?.declaration?.init
|
||||
if (initAfter?.type === 'PipeExpression') {
|
||||
const INIT_PATH_STEP_INDEX = 3
|
||||
const pathNeedingUpdatingIndex = sketchNodePaths.findIndex(
|
||||
(pathToNode) =>
|
||||
pathToNode.length === INIT_PATH_STEP_INDEX + 1 &&
|
||||
pathToNode.every(
|
||||
(step, index) =>
|
||||
step?.[0] === sketchEntryNodePath?.[index]?.[0]
|
||||
)
|
||||
)
|
||||
if (pathNeedingUpdatingIndex !== -1) {
|
||||
const updatedPath: PathToNode = [
|
||||
...sketchNodePaths[pathNeedingUpdatingIndex],
|
||||
['body', 'PipeExpression'],
|
||||
[0, 'CallExpressionKw'],
|
||||
]
|
||||
updatedSketchNodePaths[pathNeedingUpdatingIndex] = updatedPath
|
||||
updatedSketchEntryNodePath = updatedPath
|
||||
needsSelectionUpdate = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (intersectsProfileStart) {
|
||||
this.sceneInfra.modelingSend({ type: 'Close sketch' })
|
||||
} else {
|
||||
// Send selection update if paths were modified
|
||||
if (needsSelectionUpdate) {
|
||||
this.sceneInfra.modelingSend({
|
||||
type: 'Set selection',
|
||||
data: {
|
||||
selectionType: 'completeSelection',
|
||||
selection: {
|
||||
graphSelections: [],
|
||||
otherSelections: [],
|
||||
},
|
||||
updatedSketchEntryNodePath,
|
||||
updatedSketchNodePaths,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
await this.setupDraftSegment(
|
||||
sketchEntryNodePath,
|
||||
sketchNodePaths,
|
||||
updatedSketchNodePaths,
|
||||
planeNodePath,
|
||||
forward,
|
||||
up,
|
||||
@ -2530,7 +2582,7 @@ export class SceneEntities {
|
||||
const sketch = sketchFromPathToNode({
|
||||
pathToNode,
|
||||
variables: this.kclManager.variables,
|
||||
kclManager: this.kclManager,
|
||||
ast: this.kclManager.ast,
|
||||
})
|
||||
if (trap(sketch)) return
|
||||
if (!sketch) {
|
||||
@ -3784,14 +3836,14 @@ function prepareTruncatedAst(
|
||||
function sketchFromPathToNode({
|
||||
pathToNode,
|
||||
variables,
|
||||
kclManager,
|
||||
ast,
|
||||
}: {
|
||||
pathToNode: PathToNode
|
||||
variables: VariableMap
|
||||
kclManager: KclManager
|
||||
ast: Node<Program>
|
||||
}): Sketch | null | Error {
|
||||
const _varDec = getNodeFromPath<VariableDeclarator>(
|
||||
kclManager.ast,
|
||||
ast,
|
||||
pathToNode,
|
||||
'VariableDeclarator'
|
||||
)
|
||||
@ -3808,6 +3860,141 @@ function sketchFromPathToNode({
|
||||
return sg
|
||||
}
|
||||
|
||||
export function scaleProfiles({
|
||||
ast,
|
||||
pathsToProfile,
|
||||
factor,
|
||||
variables,
|
||||
}: {
|
||||
ast: Node<Program>
|
||||
pathsToProfile: PathToNode[]
|
||||
factor: number
|
||||
variables: VariableMap
|
||||
}) {
|
||||
let clonedAst = structuredClone(ast)
|
||||
for (const pathToProfile of pathsToProfile) {
|
||||
const profile = sketchFromPathToNode({
|
||||
pathToNode: pathToProfile,
|
||||
variables: variables,
|
||||
ast,
|
||||
})
|
||||
if (err(profile)) return profile
|
||||
if (!profile) return Error('Profile not found')
|
||||
|
||||
// Scale the startProfile 'at' parameter
|
||||
const scaledStartAt: [number, number] = [
|
||||
profile.start.from[0] * factor,
|
||||
profile.start.from[1] * factor,
|
||||
]
|
||||
if (
|
||||
profile.paths?.[0]?.type !== 'Circle' &&
|
||||
profile.paths?.[0]?.type !== 'CircleThreePoint'
|
||||
) {
|
||||
const startProfileResult = changeSketchArguments(
|
||||
clonedAst,
|
||||
variables,
|
||||
{
|
||||
type: 'path',
|
||||
pathToNode: pathToProfile,
|
||||
},
|
||||
{
|
||||
type: 'straight-segment',
|
||||
from: [0, 0], // not used for startProfile
|
||||
to: scaledStartAt,
|
||||
}
|
||||
)
|
||||
if (trap(startProfileResult)) return startProfileResult
|
||||
clonedAst = startProfileResult.modifiedAst
|
||||
}
|
||||
|
||||
// Scale the path segments
|
||||
for (let pathIndex = 0; pathIndex < profile.paths.length; pathIndex++) {
|
||||
const path = profile.paths[pathIndex]
|
||||
let input: Parameters<typeof changeSketchArguments>[3] | undefined =
|
||||
undefined
|
||||
const pathToSegment = getNodePathFromSourceRange(
|
||||
clonedAst,
|
||||
sourceRangeFromRust(path.__geoMeta.sourceRange)
|
||||
)
|
||||
if (!pathToSegment) {
|
||||
console.log(
|
||||
'Could not find path for segment:',
|
||||
path.type,
|
||||
'sourceRange:',
|
||||
path.__geoMeta.sourceRange
|
||||
)
|
||||
}
|
||||
const scaleTuple = (tuple: [number, number]): [number, number] => [
|
||||
tuple[0] * factor,
|
||||
tuple[1] * factor,
|
||||
]
|
||||
const previous = profile.paths[pathIndex - 1]
|
||||
if (
|
||||
path.type === 'ToPoint' ||
|
||||
path.type === 'TangentialArcTo' ||
|
||||
path.type === 'TangentialArc'
|
||||
) {
|
||||
input = {
|
||||
type: 'straight-segment',
|
||||
from: scaleTuple(path.from),
|
||||
to: scaleTuple(path.to),
|
||||
previousEndTangent: previous
|
||||
? findTangentDirectionPath(previous)
|
||||
: undefined,
|
||||
}
|
||||
} else if (
|
||||
path.type === 'ArcThreePoint' ||
|
||||
path.type === 'CircleThreePoint'
|
||||
) {
|
||||
input = {
|
||||
type: 'circle-three-point-segment',
|
||||
p1: scaleTuple(path.p1),
|
||||
p2: scaleTuple(path.p2),
|
||||
p3: scaleTuple(path.p3),
|
||||
}
|
||||
} else if (path.type === 'Circle') {
|
||||
input = {
|
||||
type: 'arc-segment',
|
||||
from: scaleTuple(path.from),
|
||||
to: scaleTuple(path.to),
|
||||
center: scaleTuple(path.center),
|
||||
radius: path.radius * factor,
|
||||
ccw: path.ccw,
|
||||
}
|
||||
} else {
|
||||
// Skip close calls - they don't have dimensions to scale
|
||||
console.log('Unhandled path type:', path.type)
|
||||
continue
|
||||
}
|
||||
if (input && pathToSegment) {
|
||||
try {
|
||||
const changeSketchResult = changeSketchArguments(
|
||||
clonedAst,
|
||||
variables,
|
||||
{
|
||||
type: 'path',
|
||||
pathToNode: pathToSegment,
|
||||
},
|
||||
input
|
||||
)
|
||||
if (!err(changeSketchResult)) {
|
||||
clonedAst = changeSketchResult.modifiedAst
|
||||
}
|
||||
} catch (error) {
|
||||
console.log('Error scaling segment:', path.type, 'error:', error)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
const pResult = parse(recast(clonedAst))
|
||||
if (trap(pResult) || !resultIsOk(pResult)) {
|
||||
return Error('Unexpected compilation error')
|
||||
}
|
||||
return {
|
||||
modifiedAst: pResult.program,
|
||||
}
|
||||
}
|
||||
|
||||
function colorSegment(object: any, color: number) {
|
||||
const segmentHead = getParentGroup(object, [ARROWHEAD, PROFILE_START])
|
||||
if (segmentHead) {
|
||||
@ -3837,7 +4024,7 @@ export function getSketchQuaternion(
|
||||
const sketch = sketchFromPathToNode({
|
||||
pathToNode: sketchPathToNode,
|
||||
variables: kclManager.variables,
|
||||
kclManager,
|
||||
ast: kclManager.ast,
|
||||
})
|
||||
if (err(sketch)) return sketch
|
||||
const zAxis =
|
||||
@ -3895,7 +4082,7 @@ function getSketchesInfo({
|
||||
const sketch = sketchFromPathToNode({
|
||||
pathToNode: path,
|
||||
variables,
|
||||
kclManager,
|
||||
ast: kclManager.ast,
|
||||
})
|
||||
if (err(sketch)) continue
|
||||
if (!sketch) continue
|
||||
@ -3974,3 +4161,44 @@ function findTangentDirection(segmentGroup: Group) {
|
||||
}
|
||||
return tangentDirection
|
||||
}
|
||||
|
||||
// implements the same as, but for a Path instead of segment Group
|
||||
function findTangentDirectionPath(path: Path): Coords2d | undefined {
|
||||
let tangentDirection: Coords2d | undefined
|
||||
|
||||
if (path.type === 'TangentialArcTo' || path.type === 'TangentialArc') {
|
||||
// For tangential arcs with center, calculate tangent at the end point
|
||||
const tangentAngle =
|
||||
deg2Rad(getAngle(path.center, path.to)) +
|
||||
(Math.PI / 2) * (path.ccw ? 1 : -1)
|
||||
tangentDirection = [Math.cos(tangentAngle), Math.sin(tangentAngle)]
|
||||
} else if (path.type === 'Arc') {
|
||||
// For regular arcs, calculate tangent at the end point
|
||||
const tangentAngle =
|
||||
deg2Rad(getAngle(path.center, path.to)) +
|
||||
(Math.PI / 2) * (path.ccw ? 1 : -1)
|
||||
tangentDirection = [Math.cos(tangentAngle), Math.sin(tangentAngle)]
|
||||
} else if (path.type === 'ArcThreePoint') {
|
||||
// For three-point arcs, we need to calculate the center first
|
||||
// This is more complex, so for now we'll skip tangent calculation
|
||||
console.warn(
|
||||
'ArcThreePoint tangent direction calculation not implemented yet'
|
||||
)
|
||||
} else if (path.type === 'ToPoint') {
|
||||
// For straight lines, the tangent is the direction from start to end
|
||||
const to = path.to as Coords2d
|
||||
const from = path.from as Coords2d
|
||||
tangentDirection = subVec(to, from)
|
||||
const normalized = normalizeVec(tangentDirection)
|
||||
if (normalized) {
|
||||
tangentDirection = normalized
|
||||
}
|
||||
} else {
|
||||
console.warn(
|
||||
'Unsupported path type for tangent direction calculation: ',
|
||||
path.type
|
||||
)
|
||||
}
|
||||
|
||||
return tangentDirection
|
||||
}
|
||||
|
@ -31,6 +31,8 @@ import { roundOff, roundOffWithUnits } from '@src/lib/utils'
|
||||
import { varMentions } from '@src/lib/varCompletionExtension'
|
||||
import { useSettings } from '@src/lib/singletons'
|
||||
import { commandBarActor, useCommandBarState } from '@src/lib/singletons'
|
||||
import { doesProfileHaveAnyConstrainedDimension } from '@src/lang/queryAst'
|
||||
import type { PathToNode } from '@src/lang/wasm'
|
||||
|
||||
import styles from './CommandBarKclInput.module.css'
|
||||
|
||||
@ -50,6 +52,7 @@ function CommandBarKclInput({
|
||||
stepBack: () => void
|
||||
onSubmit: (event: unknown) => void
|
||||
}) {
|
||||
// console.log('arg', arg)
|
||||
const commandBarState = useCommandBarState()
|
||||
const previouslySetValue = commandBarState.context.argumentsToSubmit[
|
||||
arg.name
|
||||
@ -109,6 +112,32 @@ function CommandBarKclInput({
|
||||
arg.createVariable === 'force' ||
|
||||
false
|
||||
)
|
||||
|
||||
// Check if this is the "Constrain with named value" command
|
||||
const isConstrainWithNamedValueCommand =
|
||||
commandBarState.context.selectedCommand?.name ===
|
||||
'Constrain with named value'
|
||||
const sketchDetails = argMachineContext?.sketchDetails
|
||||
|
||||
// Checkbox should be enabled (clickable) when it's the right command
|
||||
const shouldEnableScaleCheckbox = isConstrainWithNamedValueCommand
|
||||
|
||||
// Checkbox should be checked by default when ALL profiles have no constrained dimensions
|
||||
const shouldCheckScaleByDefault = useMemo(() => {
|
||||
if (!isConstrainWithNamedValueCommand || !sketchDetails?.sketchNodePaths) {
|
||||
return false
|
||||
}
|
||||
// Check if ALL profiles have no constrained dimensions (meaning we can safely scale)
|
||||
return sketchDetails.sketchNodePaths.every((pathToProfile: PathToNode) => {
|
||||
const hasConstrainedDimension = doesProfileHaveAnyConstrainedDimension(
|
||||
pathToProfile,
|
||||
kclManager.ast
|
||||
)
|
||||
return !hasConstrainedDimension // We want profiles with NO constrained dimensions
|
||||
})
|
||||
}, [isConstrainWithNamedValueCommand, sketchDetails, kclManager.ast])
|
||||
|
||||
const [scaleSketch, setScaleSketch] = useState(shouldCheckScaleByDefault)
|
||||
const [canSubmit, setCanSubmit] = useState(true)
|
||||
useHotkeys('mod + k, mod + /', () => commandBarActor.send({ type: 'Close' }))
|
||||
const editorRef = useRef<HTMLDivElement>(null)
|
||||
@ -225,6 +254,23 @@ function CommandBarKclInput({
|
||||
)
|
||||
}, [calcResult, createNewVariable, isNewVariableNameUnique, isExecuting])
|
||||
|
||||
// Update scale checkbox when the condition changes
|
||||
useEffect(() => {
|
||||
setScaleSketch(shouldCheckScaleByDefault)
|
||||
}, [shouldCheckScaleByDefault])
|
||||
|
||||
// Store scaleSketch value in command bar context for "Constrain with named value" command
|
||||
useEffect(() => {
|
||||
if (isConstrainWithNamedValueCommand) {
|
||||
commandBarActor.send({
|
||||
type: 'Set additional data',
|
||||
data: {
|
||||
scaleSketch,
|
||||
},
|
||||
})
|
||||
}
|
||||
}, [scaleSketch, isConstrainWithNamedValueCommand])
|
||||
|
||||
function handleSubmit(e?: React.FormEvent<HTMLFormElement>) {
|
||||
e?.preventDefault()
|
||||
if (!canSubmit || valueNode === null) {
|
||||
@ -237,26 +283,26 @@ function CommandBarKclInput({
|
||||
return
|
||||
}
|
||||
|
||||
onSubmit(
|
||||
createNewVariable
|
||||
? ({
|
||||
valueAst: valueNode,
|
||||
valueText: value,
|
||||
valueCalculated: calcResult,
|
||||
variableName: newVariableName,
|
||||
insertIndex: newVariableInsertIndex,
|
||||
variableIdentifierAst: createLocalName(newVariableName),
|
||||
variableDeclarationAst: createVariableDeclaration(
|
||||
newVariableName,
|
||||
valueNode
|
||||
),
|
||||
} satisfies KclCommandValue)
|
||||
: ({
|
||||
valueAst: valueNode,
|
||||
valueText: value,
|
||||
valueCalculated: calcResult,
|
||||
} satisfies KclCommandValue)
|
||||
)
|
||||
const commandValue = createNewVariable
|
||||
? ({
|
||||
valueAst: valueNode,
|
||||
valueText: value,
|
||||
valueCalculated: calcResult,
|
||||
variableName: newVariableName,
|
||||
insertIndex: newVariableInsertIndex,
|
||||
variableIdentifierAst: createLocalName(newVariableName),
|
||||
variableDeclarationAst: createVariableDeclaration(
|
||||
newVariableName,
|
||||
valueNode
|
||||
),
|
||||
} satisfies KclCommandValue)
|
||||
: ({
|
||||
valueAst: valueNode,
|
||||
valueText: value,
|
||||
valueCalculated: calcResult,
|
||||
} satisfies KclCommandValue)
|
||||
|
||||
onSubmit(commandValue)
|
||||
}
|
||||
|
||||
return (
|
||||
@ -359,6 +405,34 @@ function CommandBarKclInput({
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
{isConstrainWithNamedValueCommand && (
|
||||
<div className="flex items-baseline gap-4 mx-4">
|
||||
<input
|
||||
type="checkbox"
|
||||
id="scale-sketch-checkbox"
|
||||
data-testid="scale-sketch-checkbox"
|
||||
checked={scaleSketch}
|
||||
disabled={!shouldEnableScaleCheckbox}
|
||||
onChange={(e) => {
|
||||
setScaleSketch(e.target.checked)
|
||||
}}
|
||||
className="bg-chalkboard-10 dark:bg-chalkboard-80"
|
||||
/>
|
||||
<label
|
||||
htmlFor="scale-sketch-checkbox"
|
||||
className={`text-blue border-none bg-transparent font-sm flex gap-1 items-center pl-0 pr-1 ${
|
||||
!shouldEnableScaleCheckbox ? 'opacity-50 cursor-not-allowed' : ''
|
||||
}`}
|
||||
>
|
||||
Scale sketch
|
||||
</label>
|
||||
{!shouldEnableScaleCheckbox && (
|
||||
<span className="text-sm text-chalkboard-60 dark:text-chalkboard-50">
|
||||
(disabled - sketch has constrained dimensions)
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</form>
|
||||
)
|
||||
}
|
||||
|
@ -7,7 +7,7 @@ import {
|
||||
LanguageServerClient,
|
||||
LspWorkerEventType,
|
||||
} from '@kittycad/codemirror-lsp-client'
|
||||
import { TEST } from '@src/env'
|
||||
import { TEST, VITE_KC_API_BASE_URL } from '@src/env'
|
||||
import React, { createContext, useContext, useMemo, useState } from 'react'
|
||||
import { useNavigate } from 'react-router-dom'
|
||||
import type * as LSP from 'vscode-languageserver-protocol'
|
||||
@ -28,7 +28,6 @@ import type { FileEntry } from '@src/lib/project'
|
||||
import { codeManager } from '@src/lib/singletons'
|
||||
import { err } from '@src/lib/trap'
|
||||
import { useToken } from '@src/lib/singletons'
|
||||
import { withAPIBaseURL } from '@src/lib/withBaseURL'
|
||||
|
||||
function getWorkspaceFolders(): LSP.WorkspaceFolder[] {
|
||||
return []
|
||||
@ -86,7 +85,7 @@ export const LspProvider = ({ children }: { children: React.ReactNode }) => {
|
||||
const initEvent: KclWorkerOptions = {
|
||||
wasmUrl: wasmUrl(),
|
||||
token: token,
|
||||
apiBaseUrl: withAPIBaseURL(''),
|
||||
apiBaseUrl: VITE_KC_API_BASE_URL,
|
||||
}
|
||||
lspWorker.postMessage({
|
||||
worker: LspWorker.Kcl,
|
||||
@ -179,7 +178,7 @@ export const LspProvider = ({ children }: { children: React.ReactNode }) => {
|
||||
const initEvent: CopilotWorkerOptions = {
|
||||
wasmUrl: wasmUrl(),
|
||||
token: token,
|
||||
apiBaseUrl: withAPIBaseURL(''),
|
||||
apiBaseUrl: VITE_KC_API_BASE_URL,
|
||||
}
|
||||
lspWorker.postMessage({
|
||||
worker: LspWorker.Copilot,
|
||||
|
@ -8,14 +8,13 @@ export const NODE_ENV = env.NODE_ENV as string | undefined
|
||||
export const VITE_KC_API_WS_MODELING_URL = env.VITE_KC_API_WS_MODELING_URL as
|
||||
| string
|
||||
| undefined
|
||||
export const VITE_KITTYCAD_API_BASE_URL = env.VITE_KITTYCAD_API_BASE_URL
|
||||
export const VITE_KC_API_BASE_URL = env.VITE_KC_API_BASE_URL
|
||||
export const VITE_KC_SITE_BASE_URL = env.VITE_KC_SITE_BASE_URL
|
||||
export const VITE_KC_SITE_APP_URL = env.VITE_KC_SITE_APP_URL
|
||||
export const VITE_KC_SKIP_AUTH = env.VITE_KC_SKIP_AUTH as string | undefined
|
||||
export const VITE_KC_CONNECTION_TIMEOUT_MS =
|
||||
env.VITE_KC_CONNECTION_TIMEOUT_MS as string | undefined
|
||||
export const VITE_KITTYCAD_API_TOKEN = env.VITE_KITTYCAD_API_TOKEN as
|
||||
| string
|
||||
| undefined
|
||||
export const VITE_KC_DEV_TOKEN = env.VITE_KC_DEV_TOKEN as string | undefined
|
||||
export const PROD = env.PROD as string | undefined
|
||||
export const TEST = env.TEST as string | undefined
|
||||
export const DEV = env.DEV as string | undefined
|
||||
|
@ -18,3 +18,7 @@ export const ARG_INTERIOR_ABSOLUTE = 'interiorAbsolute'
|
||||
export const ARG_AT = 'at'
|
||||
export const ARG_LEG = 'leg'
|
||||
export const ARG_HYPOTENUSE = 'hypotenuse'
|
||||
export const ARG_P1 = 'p1'
|
||||
export const ARG_P2 = 'p2'
|
||||
export const ARG_P3 = 'p3'
|
||||
export const ARG_DIAMETER = 'diameter'
|
||||
|
@ -25,12 +25,13 @@ import type { Artifact } from '@src/lang/std/artifactGraph'
|
||||
import { codeRefFromRange } from '@src/lang/std/artifactGraph'
|
||||
import { topLevelRange } from '@src/lang/util'
|
||||
import type { Identifier, Literal, LiteralValue } from '@src/lang/wasm'
|
||||
import { assertParse, recast } from '@src/lang/wasm'
|
||||
import { assertParse, recast, parse, resultIsOk } from '@src/lang/wasm'
|
||||
import { initPromise } from '@src/lang/wasmUtils'
|
||||
import { enginelessExecutor } from '@src/lib/testHelpers'
|
||||
import { err } from '@src/lib/trap'
|
||||
import { deleteFromSelection } from '@src/lang/modifyAst/deleteFromSelection'
|
||||
import { assertNotErr } from '@src/unitTestUtils'
|
||||
import { scaleProfiles } from '@src/clientSideScene/sceneEntities'
|
||||
|
||||
beforeAll(async () => {
|
||||
await initPromise
|
||||
@ -917,3 +918,223 @@ extrude001 = extrude(part001, length = 5)
|
||||
expect(result instanceof Error).toBe(true)
|
||||
})
|
||||
})
|
||||
|
||||
describe('testing sketch scaling', () => {
|
||||
it('can scale sketch by half simple case', async () => {
|
||||
const basicSketch = `sketch002 = startSketchOn(XZ)
|
||||
profile006 = startProfile(sketch002, at = [114.64, 124.18])
|
||||
|> line(end = [173.02, 306.77])
|
||||
|> line(end = [175.14, -282.35])
|
||||
|> tangentialArc(end = [-52.01, -194.25])`
|
||||
const ast = assertParse(basicSketch)
|
||||
const result = await enginelessExecutor(ast)
|
||||
const searchSnippet = 'startProfile(sketch002, at = [114.64, 124.18])'
|
||||
const startIndex = basicSketch.indexOf(searchSnippet)
|
||||
const range = topLevelRange(startIndex, startIndex + searchSnippet.length)
|
||||
const pathToProfile = getNodePathFromSourceRange(ast, range)
|
||||
|
||||
if (err(result)) throw result
|
||||
const scaledProfile = scaleProfiles({
|
||||
ast,
|
||||
factor: 0.5,
|
||||
variables: result.variables,
|
||||
pathsToProfile: [pathToProfile],
|
||||
})
|
||||
if (err(scaledProfile)) throw scaledProfile
|
||||
const modifiedAst = scaledProfile.modifiedAst
|
||||
const newCode = recast(modifiedAst)
|
||||
if (err(newCode)) throw newCode
|
||||
|
||||
expect(newCode).toBe(`sketch002 = startSketchOn(XZ)
|
||||
profile006 = startProfile(sketch002, at = [57.32, 62.09])
|
||||
|> line(end = [86.51, 153.39])
|
||||
|> line(end = [87.57, -141.18])
|
||||
|> tangentialArc(end = [-26, -97.12])
|
||||
`)
|
||||
})
|
||||
it('can scale sketch more complex', async () => {
|
||||
let code = `sketch001 = startSketchOn(YZ)
|
||||
profile001 = startProfile(sketch001, at = [100, 101])
|
||||
|> line(end = [102, 103])
|
||||
|> line(endAbsolute = [104, 105])
|
||||
|> angledLine(angle = 206, length = 106)
|
||||
|> angledLine(angle = -208, lengthX = 107)
|
||||
|> angledLine(angle = 210, lengthY = 108)
|
||||
|> angledLine(angle = 212, endAbsoluteX = 109)
|
||||
|> angledLine(angle = 214, endAbsoluteY = 110)
|
||||
|> arc(interiorAbsolute = [111, 112], endAbsolute = [113, 114])
|
||||
|> tangentialArc(end = [115, -116])
|
||||
|> tangentialArc(endAbsolute = [117, 118])
|
||||
|> tangentialArc(angle = 224, radius = 119)
|
||||
|> tangentialArc(angle = 226, diameter = 120)
|
||||
|
||||
profile002 = startProfile(sketch001, at = [-121, 122])
|
||||
|> angledLine(angle = 130, length = 123, tag = $rectangleSegmentA001)
|
||||
|> angledLine(angle = segAng(rectangleSegmentA001) - 232, length = 124)
|
||||
|> angledLine(angle = segAng(rectangleSegmentA001), length = -segLen(rectangleSegmentA001))
|
||||
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|
||||
|> close()
|
||||
profile003 = circle(sketch001, center = [-125, -126], radius = 127)
|
||||
profile004 = circleThreePoint(
|
||||
sketch001,
|
||||
p1 = [128, 129],
|
||||
p2 = [130, 131],
|
||||
p3 = [132, 133],
|
||||
)
|
||||
profile005 = circle(sketch001, center = [-134, -135], diameter = 136)`
|
||||
let ast = assertParse(code)
|
||||
const result = await enginelessExecutor(ast)
|
||||
const searchSnippets = [
|
||||
'startProfile(sketch001, at = [100, 101])',
|
||||
'startProfile(sketch001, at = [-121, 122])',
|
||||
'circle(sketch001, center = [-125, -126], radius = 127)',
|
||||
'circleThreePoint(',
|
||||
'circle(sketch001, center = [-134, -135], diameter = 136)',
|
||||
]
|
||||
const ranges = searchSnippets.map((searchSnippet) => {
|
||||
const startIndex = code.indexOf(searchSnippet)
|
||||
return topLevelRange(startIndex, startIndex + searchSnippet.length)
|
||||
})
|
||||
const pathsToProfiles = ranges.map((range) =>
|
||||
getNodePathFromSourceRange(ast, range)
|
||||
)
|
||||
|
||||
if (err(result)) throw result
|
||||
const scaledProfile = scaleProfiles({
|
||||
ast,
|
||||
factor: 0.5,
|
||||
variables: result.variables,
|
||||
pathsToProfile: pathsToProfiles,
|
||||
})
|
||||
if (err(scaledProfile)) throw scaledProfile
|
||||
const pResult = parse(recast(scaledProfile.modifiedAst))
|
||||
if (err(pResult) || !resultIsOk(pResult)) return
|
||||
ast = pResult.program
|
||||
const newCode = recast(ast)
|
||||
if (err(newCode)) throw newCode
|
||||
|
||||
expect(newCode).toBe(`sketch001 = startSketchOn(YZ)
|
||||
profile001 = startProfile(sketch001, at = [50, 50.5])
|
||||
|> line(end = [51, 51.5])
|
||||
|> line(endAbsolute = [52, 52.5])
|
||||
|> angledLine(angle = -154, length = 53)
|
||||
|> angledLine(angle = 152, lengthX = 53.5)
|
||||
|> angledLine(angle = -150, lengthY = 54)
|
||||
|> angledLine(angle = 32, endAbsoluteX = 54.5)
|
||||
|> angledLine(angle = -146, endAbsoluteY = 55)
|
||||
|> arc(interiorAbsolute = [55.5, 56], endAbsolute = [56.5, 57])
|
||||
|> tangentialArc(end = [57.5, -58])
|
||||
|> tangentialArc(endAbsolute = [58.5, 59])
|
||||
|> tangentialArc(angle = 224deg, radius = 59.5)
|
||||
|> tangentialArc(angle = 226deg, diameter = 60)
|
||||
|
||||
profile002 = startProfile(sketch001, at = [-60.5, 61])
|
||||
|> angledLine(angle = 130, length = 61.5, tag = $rectangleSegmentA001)
|
||||
|> angledLine(angle = segAng(rectangleSegmentA001) - 232, length = 62)
|
||||
|> angledLine(angle = segAng(rectangleSegmentA001), length = -segLen(rectangleSegmentA001))
|
||||
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|
||||
|> close()
|
||||
profile003 = circle(sketch001, center = [-62.5, -63], radius = 63.5)
|
||||
profile004 = circleThreePoint(
|
||||
sketch001,
|
||||
p1 = [64, 64.5],
|
||||
p2 = [65, 65.5],
|
||||
p3 = [66, 66.5],
|
||||
)
|
||||
profile005 = circle(sketch001, center = [-67, -67.5], diameter = 68)
|
||||
`)
|
||||
})
|
||||
it("make sure it doesn't stomp constraints", async () => {
|
||||
let code = `sketch001 = startSketchOn(YZ)
|
||||
profile001 = startProfile(sketch001, at = [100 + 0, 101])
|
||||
|> line(end = [102, 103 + 0])
|
||||
|> line(endAbsolute = [104 + 0, 105])
|
||||
|> angledLine(angle = 206, length = 106 + 0)
|
||||
|> angledLine(angle = -208 + 0, lengthX = 107)
|
||||
|> angledLine(angle = 210, lengthY = 108 + 0)
|
||||
|> angledLine(angle = 212 + 0, endAbsoluteX = 109)
|
||||
|> angledLine(angle = 214, endAbsoluteY = 110 + 0)
|
||||
|> arc(interiorAbsolute = [111 + 0, 112], endAbsolute = [113, 114 + 0])
|
||||
|> tangentialArc(end = [115, -116 + 0])
|
||||
|> tangentialArc(endAbsolute = [117 + 0, 118])
|
||||
|> tangentialArc(angle = 224, radius = 119 + 0)
|
||||
|> tangentialArc(angle = 226 + 0, diameter = 120)
|
||||
|
||||
profile002 = startProfile(sketch001, at = [-121 + 0, 122])
|
||||
|> angledLine(angle = 130, length = 123 + 0, tag = $rectangleSegmentA001)
|
||||
|> angledLine(angle = segAng(rectangleSegmentA001) - 232, length = 124)
|
||||
|> angledLine(angle = segAng(rectangleSegmentA001), length = -segLen(rectangleSegmentA001))
|
||||
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|
||||
|> close()
|
||||
profile003 = circle(sketch001, center = [-125 + 0, -126], radius = 127 + 0)
|
||||
profile004 = circleThreePoint(
|
||||
sketch001,
|
||||
p1 = [128, 129 + 0],
|
||||
p2 = [130 + 0, 131],
|
||||
p3 = [133, 132],
|
||||
)
|
||||
profile005 = circle(sketch001, center = [-134, -135 + 0], diameter = 136)
|
||||
`
|
||||
let ast = assertParse(code)
|
||||
const result = await enginelessExecutor(ast)
|
||||
const searchSnippets = [
|
||||
'startProfile(sketch001, at = [100 + 0, 101])',
|
||||
'startProfile(sketch001, at = [-121 + 0, 122])',
|
||||
'circle(sketch001, center = [-125 + 0, -126], radius = 127 + 0)',
|
||||
'circleThreePoint(',
|
||||
'circle(sketch001, center = [-134, -135 + 0], diameter = 136)',
|
||||
]
|
||||
const ranges = searchSnippets.map((searchSnippet) => {
|
||||
const startIndex = code.indexOf(searchSnippet)
|
||||
return topLevelRange(startIndex, startIndex + searchSnippet.length)
|
||||
})
|
||||
const pathsToProfiles = ranges.map((range) =>
|
||||
getNodePathFromSourceRange(ast, range)
|
||||
)
|
||||
|
||||
if (err(result)) throw result
|
||||
const scaledProfile = scaleProfiles({
|
||||
ast,
|
||||
factor: 0.5,
|
||||
variables: result.variables,
|
||||
pathsToProfile: pathsToProfiles,
|
||||
})
|
||||
if (err(scaledProfile)) throw scaledProfile
|
||||
const pResult = parse(recast(scaledProfile.modifiedAst))
|
||||
if (err(pResult) || !resultIsOk(pResult)) return
|
||||
ast = pResult.program
|
||||
const newCode = recast(ast)
|
||||
if (err(newCode)) throw newCode
|
||||
|
||||
expect(newCode).toBe(`sketch001 = startSketchOn(YZ)
|
||||
profile001 = startProfile(sketch001, at = [100 + 0, 50.5])
|
||||
|> line(end = [51, 103 + 0])
|
||||
|> line(endAbsolute = [104 + 0, 52.5])
|
||||
|> angledLine(angle = -154, length = 106 + 0)
|
||||
|> angledLine(angle = -208 + 0, lengthX = 53.5)
|
||||
|> angledLine(angle = -150, lengthY = 108 + 0)
|
||||
|> angledLine(angle = 212 + 0, endAbsoluteX = 54.5)
|
||||
|> angledLine(angle = -146, endAbsoluteY = 110 + 0)
|
||||
|> arc(interiorAbsolute = [111 + 0, 56], endAbsolute = [56.5, 114 + 0])
|
||||
|> tangentialArc(end = [57.5, -116 + 0])
|
||||
|> tangentialArc(endAbsolute = [117 + 0, 59])
|
||||
|> tangentialArc(angle = 224deg, radius = 119 + 0)
|
||||
|> tangentialArc(angle = 226 + 0, diameter = 60)
|
||||
|
||||
profile002 = startProfile(sketch001, at = [-121 + 0, 61])
|
||||
|> angledLine(angle = 130, length = 123 + 0, tag = $rectangleSegmentA001)
|
||||
|> angledLine(angle = segAng(rectangleSegmentA001) - 232, length = 62)
|
||||
|> angledLine(angle = segAng(rectangleSegmentA001), length = -segLen(rectangleSegmentA001))
|
||||
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|
||||
|> close()
|
||||
profile003 = circle(sketch001, center = [-125 + 0, -63], radius = 127 + 0)
|
||||
profile004 = circleThreePoint(
|
||||
sketch001,
|
||||
p1 = [64, 129 + 0],
|
||||
p2 = [130 + 0, 65.5],
|
||||
p3 = [66.5, 66],
|
||||
)
|
||||
profile005 = circle(sketch001, center = [-67, -135 + 0], diameter = 68)
|
||||
`)
|
||||
})
|
||||
})
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { VITE_KITTYCAD_API_TOKEN } from '@src/env'
|
||||
import { VITE_KC_DEV_TOKEN } from '@src/env'
|
||||
|
||||
import { createLiteral } from '@src/lang/create'
|
||||
import type {
|
||||
@ -40,9 +40,10 @@ import { isOverlap } from '@src/lib/utils'
|
||||
beforeAll(async () => {
|
||||
await initPromise
|
||||
|
||||
// THESE TEST WILL FAIL without VITE_KC_DEV_TOKEN set in .env.development.local
|
||||
await new Promise((resolve) => {
|
||||
engineCommandManager.start({
|
||||
token: VITE_KITTYCAD_API_TOKEN,
|
||||
token: VITE_KC_DEV_TOKEN,
|
||||
width: 256,
|
||||
height: 256,
|
||||
setMediaStream: () => {},
|
||||
|
@ -4,15 +4,16 @@ import { initPromise } from '@src/lang/wasmUtils'
|
||||
import { err } from '@src/lib/trap'
|
||||
import type { Selection } from '@src/lib/selections'
|
||||
import { engineCommandManager, kclManager } from '@src/lib/singletons'
|
||||
import { VITE_KITTYCAD_API_TOKEN } from '@src/env'
|
||||
import { VITE_KC_DEV_TOKEN } from '@src/env'
|
||||
import { modifyAstWithTagsForSelection } from '@src/lang/modifyAst/tagManagement'
|
||||
|
||||
beforeAll(async () => {
|
||||
await initPromise
|
||||
|
||||
// THESE TEST WILL FAIL without VITE_KC_DEV_TOKEN set in .env.development.local
|
||||
await new Promise((resolve) => {
|
||||
engineCommandManager.start({
|
||||
token: VITE_KITTYCAD_API_TOKEN,
|
||||
token: VITE_KC_DEV_TOKEN,
|
||||
width: 256,
|
||||
height: 256,
|
||||
setMediaStream: () => {},
|
||||
|
@ -10,6 +10,7 @@ import {
|
||||
createPipeSubstitution,
|
||||
} from '@src/lang/create'
|
||||
import {
|
||||
doesProfileHaveAnyConstrainedDimension,
|
||||
doesSceneHaveExtrudedSketch,
|
||||
doesSceneHaveSweepableSketch,
|
||||
findAllPreviousVariables,
|
||||
@ -35,6 +36,95 @@ beforeAll(async () => {
|
||||
await initPromise
|
||||
})
|
||||
|
||||
describe('doesProfileHaveConstrainDimension', () => {
|
||||
const code = `sketch001 = startSketchOn(YZ)
|
||||
profile001 = startProfile(sketch001, at = [100, 101])
|
||||
|> line(end = [102, 103])
|
||||
|> line(endAbsolute = [104, 105])
|
||||
|> angledLine(angle = 206, length = 106)
|
||||
|> angledLine(angle = -208, lengthX = 107)
|
||||
|> angledLine(angle = 210, lengthY = 108)
|
||||
|> angledLine(angle = 212, endAbsoluteX = 109)
|
||||
|> angledLine(angle = 214, endAbsoluteY = 110)
|
||||
|> arc(interiorAbsolute = [111, 112], endAbsolute = [113, 114])
|
||||
|> tangentialArc(end = [115, -116])
|
||||
|> tangentialArc(endAbsolute = [117, 118])
|
||||
|> tangentialArc(angle = 224, radius = 119)
|
||||
|> tangentialArc(angle = 226, diameter = 120)
|
||||
|
||||
profile002 = startProfile(sketch001, at = [-121, 122])
|
||||
|> angledLine(angle = 130, length = 123, tag = $rectangleSegmentA001)
|
||||
|> angledLine(angle = segAng(rectangleSegmentA001) - 232, length = 124)
|
||||
|> angledLine(angle = segAng(rectangleSegmentA001), length = -segLen(rectangleSegmentA001))
|
||||
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|
||||
|> close()
|
||||
profile003 = circle(sketch001, center = [-125, -126], radius = 127)
|
||||
profile004 = circleThreePoint(
|
||||
sketch001,
|
||||
p1 = [128, 129],
|
||||
p2 = [130, 131],
|
||||
p3 = [132, 133],
|
||||
)
|
||||
profile005 = circle(sketch001, center = [-134, -135], diameter = 136)`
|
||||
const profileSearchStrings = [
|
||||
{
|
||||
profileSearchString: 'profile001 = startProfile',
|
||||
replaceCases: { start: 100, end: 120 },
|
||||
},
|
||||
{
|
||||
profileSearchString: 'profile002 = startProfile',
|
||||
replaceCases: { start: 121, end: 124 },
|
||||
},
|
||||
{
|
||||
profileSearchString: 'profile003 = circle',
|
||||
replaceCases: { start: 125, end: 127 },
|
||||
},
|
||||
{
|
||||
profileSearchString: 'profile004 = circleThreePoint',
|
||||
replaceCases: { start: 128, end: 133 },
|
||||
},
|
||||
{
|
||||
profileSearchString: 'profile005 = circle',
|
||||
replaceCases: { start: 134, end: 136 },
|
||||
},
|
||||
] as const
|
||||
it('should return false for all profiles (no constrained dimensions detected)', () => {
|
||||
const ast = assertParse(code)
|
||||
|
||||
profileSearchStrings.forEach((profile) => {
|
||||
const profileStart = code.indexOf(profile.profileSearchString)
|
||||
const profilePath = getNodePathFromSourceRange(
|
||||
ast,
|
||||
topLevelRange(profileStart, profileStart)
|
||||
)
|
||||
expect(
|
||||
doesProfileHaveAnyConstrainedDimension(profilePath, ast)
|
||||
).toBeFalsy()
|
||||
})
|
||||
})
|
||||
it('should true false when adding constraints for each Profile all profiles (no constrained dimensions detected)', () => {
|
||||
profileSearchStrings.forEach((profile) => {
|
||||
for (
|
||||
let i = profile.replaceCases.start;
|
||||
i <= profile.replaceCases.end;
|
||||
i++
|
||||
) {
|
||||
const modifiedCode = code.replaceAll(String(i), `${i} + 5`)
|
||||
const ast = assertParse(modifiedCode)
|
||||
const profileStart = modifiedCode.indexOf(profile.profileSearchString)
|
||||
const profilePath = getNodePathFromSourceRange(
|
||||
ast,
|
||||
topLevelRange(profileStart, profileStart)
|
||||
)
|
||||
expect(
|
||||
doesProfileHaveAnyConstrainedDimension(profilePath, ast)
|
||||
).toBeTruthy()
|
||||
}
|
||||
// })
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('findAllPreviousVariables', () => {
|
||||
it('should find all previous variables', async () => {
|
||||
const code = `baseThick = 1
|
||||
|
@ -55,6 +55,23 @@ import type { OpKclValue, Operation } from '@rust/kcl-lib/bindings/Operation'
|
||||
import { ARG_INDEX_FIELD, LABELED_ARG_FIELD } from '@src/lang/queryAstConstants'
|
||||
import type { KclCommandValue } from '@src/lib/commandTypes'
|
||||
import type { UnaryExpression } from 'typescript'
|
||||
import {
|
||||
ARG_END_ABSOLUTE,
|
||||
ARG_END,
|
||||
ARG_LENGTH,
|
||||
ARG_CIRCLE_CENTER,
|
||||
ARG_RADIUS,
|
||||
ARG_LENGTH_X,
|
||||
ARG_LENGTH_Y,
|
||||
ARG_END_ABSOLUTE_X,
|
||||
ARG_END_ABSOLUTE_Y,
|
||||
ARG_INTERIOR_ABSOLUTE,
|
||||
ARG_AT,
|
||||
ARG_P1,
|
||||
ARG_P2,
|
||||
ARG_P3,
|
||||
ARG_DIAMETER,
|
||||
} from '@src/lang/constants'
|
||||
import type { NumericType } from '@rust/kcl-lib/bindings/NumericType'
|
||||
|
||||
/**
|
||||
@ -1293,3 +1310,146 @@ export const getPathNormalisedForTruncatedAst = (
|
||||
Number(nodePathWithCorrectedIndexForTruncatedAst[1][0]) - minIndex
|
||||
return nodePathWithCorrectedIndexForTruncatedAst
|
||||
}
|
||||
|
||||
export function doesProfileHaveAnyConstrainedDimension(
|
||||
profilePath: PathToNode,
|
||||
ast: Node<Program>
|
||||
): boolean {
|
||||
// Get the profile node from the path
|
||||
const profileNodeResult = getNodeFromPath<Node<VariableDeclaration>>(
|
||||
ast,
|
||||
profilePath,
|
||||
'VariableDeclaration'
|
||||
)
|
||||
if (err(profileNodeResult)) return false
|
||||
|
||||
const profileNode = profileNodeResult.node
|
||||
|
||||
// Single value dimension parameters to check (excluding angle as per requirements)
|
||||
const singleValueLengthParams = new Set([
|
||||
ARG_DIAMETER,
|
||||
ARG_RADIUS,
|
||||
ARG_LENGTH,
|
||||
ARG_LENGTH_X,
|
||||
ARG_LENGTH_Y,
|
||||
ARG_END_ABSOLUTE_X,
|
||||
ARG_END_ABSOLUTE_Y,
|
||||
])
|
||||
|
||||
// Tuple value dimension parameters to check
|
||||
const tupleValueParams = new Set([
|
||||
ARG_CIRCLE_CENTER,
|
||||
ARG_P1,
|
||||
ARG_P2,
|
||||
ARG_P3,
|
||||
ARG_AT,
|
||||
ARG_END,
|
||||
ARG_END_ABSOLUTE,
|
||||
ARG_INTERIOR_ABSOLUTE,
|
||||
])
|
||||
|
||||
let hasConstrainedDimension = false
|
||||
|
||||
// Traverse the profile node to find all call expressions and their arguments
|
||||
traverse(profileNode, {
|
||||
enter: (node: any) => {
|
||||
if (node.type === 'CallExpressionKw' && node.arguments) {
|
||||
for (const arg of node.arguments) {
|
||||
if (arg.type === 'LabeledArg' && arg.label?.name) {
|
||||
const paramName = arg.label.name
|
||||
|
||||
// Check if this parameter is in our whitelist
|
||||
if (
|
||||
singleValueLengthParams.has(paramName) ||
|
||||
tupleValueParams.has(paramName)
|
||||
) {
|
||||
// Special case: endAbsolute = [profileStartX(%), profileStartY(%)]
|
||||
// This should NOT count as constrained
|
||||
if (
|
||||
paramName === ARG_END_ABSOLUTE &&
|
||||
arg.arg.type === 'ArrayExpression' &&
|
||||
arg.arg.elements.length === 2
|
||||
) {
|
||||
const [first, second] = arg.arg.elements
|
||||
if (
|
||||
first.type === 'CallExpressionKw' &&
|
||||
second.type === 'CallExpressionKw' &&
|
||||
first.callee.type === 'Name' &&
|
||||
second.callee.type === 'Name' &&
|
||||
first.callee.name.name === 'profileStartX' &&
|
||||
second.callee.name.name === 'profileStartY'
|
||||
) {
|
||||
// This is the special case - don't count as constrained
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
// Special case: angledLine length = -segLen(rectangleSegmentA001)
|
||||
// This should NOT count as constrained
|
||||
if (
|
||||
node.callee?.type === 'Name' &&
|
||||
node.callee.name.name === 'angledLine' &&
|
||||
paramName === ARG_LENGTH
|
||||
) {
|
||||
let callExpr = null
|
||||
|
||||
// Check if it's a direct call expression or unary expression with call expression
|
||||
if (arg.arg.type === 'CallExpressionKw') {
|
||||
callExpr = arg.arg
|
||||
} else if (
|
||||
arg.arg.type === 'UnaryExpression' &&
|
||||
arg.arg.argument?.type === 'CallExpressionKw'
|
||||
) {
|
||||
callExpr = arg.arg.argument
|
||||
}
|
||||
|
||||
if (
|
||||
callExpr &&
|
||||
callExpr.callee?.type === 'Name' &&
|
||||
callExpr.callee.name.name === 'segLen' &&
|
||||
callExpr.unlabeled?.type === 'Name' &&
|
||||
callExpr.unlabeled.name.name.startsWith('rectangleSegmentA')
|
||||
) {
|
||||
// This is the special case - don't count as constrained
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
// Check if the argument value is non-static
|
||||
if (!isStaticValue(arg.arg)) {
|
||||
hasConstrainedDimension = true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// If we found a constrained dimension, we can break out of the outer loop too
|
||||
if (hasConstrainedDimension) {
|
||||
return false // This stops the traversal
|
||||
}
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
return hasConstrainedDimension
|
||||
}
|
||||
|
||||
// Helper function to check if a node represents a static/constant value (literal, array of literals, or negative literal)
|
||||
function isStaticValue(node: any): boolean {
|
||||
if (node.type === 'Literal') {
|
||||
return true
|
||||
}
|
||||
|
||||
if (node.type === 'ArrayExpression') {
|
||||
// Array is literal if all elements are literals
|
||||
return node.elements.every((element: any) => isStaticValue(element))
|
||||
}
|
||||
|
||||
if (node.type === 'UnaryExpression' && node.operator === '-') {
|
||||
// Negative literal numbers
|
||||
return isStaticValue(node.argument)
|
||||
}
|
||||
|
||||
// All other node types (Name, CallExpression, BinaryExpression, etc.) are non-literal
|
||||
return false
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
import type { Models } from '@kittycad/lib'
|
||||
import { VITE_KC_API_WS_MODELING_URL, VITE_KITTYCAD_API_TOKEN } from '@src/env'
|
||||
import { VITE_KC_API_WS_MODELING_URL, VITE_KC_DEV_TOKEN } from '@src/env'
|
||||
import { jsAppSettings } from '@src/lib/settings/settingsUtils'
|
||||
import { BSON } from 'bson'
|
||||
|
||||
@ -400,7 +400,7 @@ class EngineConnection extends EventTarget {
|
||||
this.send({
|
||||
type: 'headers',
|
||||
headers: {
|
||||
Authorization: `Bearer ${VITE_KITTYCAD_API_TOKEN}`,
|
||||
Authorization: `Bearer ${VITE_KC_DEV_TOKEN}`,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
@ -24,6 +24,7 @@ import {
|
||||
ARG_TAG,
|
||||
DETERMINING_ARGS,
|
||||
ARG_INTERIOR_ABSOLUTE,
|
||||
ARG_DIAMETER,
|
||||
} from '@src/lang/constants'
|
||||
import {
|
||||
createArrayExpression,
|
||||
@ -1424,13 +1425,27 @@ export const circle: SketchLineHelperKw = {
|
||||
|
||||
const { node: callExpression } = nodeMeta
|
||||
|
||||
// All function arguments, except the tag
|
||||
const functionArguments = callExpression.arguments
|
||||
.map((arg) => arg.label?.name)
|
||||
.filter((n) => n && n !== ARG_TAG)
|
||||
|
||||
const newCenter = createArrayExpression([
|
||||
createLiteral(roundOff(center[0])),
|
||||
createLiteral(roundOff(center[1])),
|
||||
])
|
||||
mutateKwArg(ARG_CIRCLE_CENTER, callExpression, newCenter)
|
||||
const newRadius = createLiteral(roundOff(radius))
|
||||
mutateKwArg(ARG_RADIUS, callExpression, newRadius)
|
||||
|
||||
// Check if the circle uses diameter or radius
|
||||
const isDiameter = functionArguments.includes(ARG_DIAMETER)
|
||||
if (isDiameter) {
|
||||
const newDiameter = createLiteral(roundOff(radius * 2))
|
||||
mutateKwArg(ARG_DIAMETER, callExpression, newDiameter)
|
||||
} else {
|
||||
const newRadius = createLiteral(roundOff(radius))
|
||||
mutateKwArg(ARG_RADIUS, callExpression, newRadius)
|
||||
}
|
||||
|
||||
return {
|
||||
modifiedAst: _node,
|
||||
pathToNode,
|
||||
@ -4168,7 +4183,14 @@ const tangentialArcHelpers = {
|
||||
.map((arg) => arg.label?.name)
|
||||
.filter((n) => n && n !== ARG_TAG)
|
||||
|
||||
if (areArraysEqual(functionArguments, [ARG_ANGLE, ARG_RADIUS])) {
|
||||
const isDiameter = areArraysEqual(functionArguments, [
|
||||
ARG_ANGLE,
|
||||
ARG_DIAMETER,
|
||||
])
|
||||
if (
|
||||
areArraysEqual(functionArguments, [ARG_ANGLE, ARG_RADIUS]) ||
|
||||
isDiameter
|
||||
) {
|
||||
// Using length and radius -> convert "from", "to" to the matching length and radius
|
||||
const previousEndTangent = input.previousEndTangent
|
||||
if (previousEndTangent) {
|
||||
@ -4219,11 +4241,19 @@ const tangentialArcHelpers = {
|
||||
|
||||
const radius = distance2d(center, from)
|
||||
|
||||
mutateKwArg(
|
||||
ARG_RADIUS,
|
||||
callExpression,
|
||||
createLiteral(roundOff(radius, 2))
|
||||
)
|
||||
if (!isDiameter) {
|
||||
mutateKwArg(
|
||||
ARG_RADIUS,
|
||||
callExpression,
|
||||
createLiteral(roundOff(radius, 2))
|
||||
)
|
||||
} else {
|
||||
mutateKwArg(
|
||||
ARG_DIAMETER,
|
||||
callExpression,
|
||||
createLiteral(roundOff(radius * 2, 2))
|
||||
)
|
||||
}
|
||||
const angleValue = createLiteralMaybeSuffix({
|
||||
value: roundOff(angle, 2),
|
||||
suffix: 'Deg',
|
||||
|
@ -172,6 +172,7 @@ export type ModelingCommandSchema = {
|
||||
variableName: string
|
||||
}
|
||||
namedValue: KclCommandValue
|
||||
scaleSketch?: boolean
|
||||
}
|
||||
'Prompt-to-edit': {
|
||||
prompt: string
|
||||
|
@ -1,3 +1,4 @@
|
||||
import { VITE_KC_API_BASE_URL } from '@src/env'
|
||||
import { UAParser } from 'ua-parser-js'
|
||||
|
||||
import type { OsInfo } from '@rust/kcl-lib/bindings/OsInfo'
|
||||
@ -10,7 +11,6 @@ import { isDesktop } from '@src/lib/isDesktop'
|
||||
import type RustContext from '@src/lib/rustContext'
|
||||
import screenshot from '@src/lib/screenshot'
|
||||
import { APP_VERSION } from '@src/routes/utils'
|
||||
import { withAPIBaseURL } from '@src/lib/withBaseURL'
|
||||
|
||||
/* eslint-disable suggest-no-throw/suggest-no-throw --
|
||||
* All the throws in CoreDumpManager are intentional and should be caught and handled properly
|
||||
@ -35,7 +35,7 @@ export class CoreDumpManager {
|
||||
codeManager: CodeManager
|
||||
rustContext: RustContext
|
||||
token: string | undefined
|
||||
baseUrl: string = withAPIBaseURL('')
|
||||
baseUrl: string = VITE_KC_API_BASE_URL
|
||||
|
||||
constructor(
|
||||
engineCommandManager: EngineCommandManager,
|
||||
|
@ -26,7 +26,6 @@ import { err } from '@src/lib/trap'
|
||||
import type { DeepPartial } from '@src/lib/types'
|
||||
import { getInVariableCase } from '@src/lib/utils'
|
||||
import { IS_STAGING } from '@src/routes/utils'
|
||||
import { withAPIBaseURL } from '@src/lib/withBaseURL'
|
||||
|
||||
export async function renameProjectDirectory(
|
||||
projectPath: string,
|
||||
@ -698,9 +697,7 @@ export const readTokenFile = async () => {
|
||||
export const writeTokenFile = async (token: string) => {
|
||||
const tokenFilePath = await getTokenFilePath()
|
||||
if (err(token)) return Promise.reject(token)
|
||||
const result = window.electron.writeFile(tokenFilePath, token)
|
||||
console.log('token written to disk')
|
||||
return result
|
||||
return window.electron.writeFile(tokenFilePath, token)
|
||||
}
|
||||
|
||||
export const writeTelemetryFile = async (content: string) => {
|
||||
@ -725,9 +722,12 @@ export const setState = async (state: Project | undefined): Promise<void> => {
|
||||
appStateStore = state
|
||||
}
|
||||
|
||||
export const getUser = async (token: string): Promise<Models['User_type']> => {
|
||||
export const getUser = async (
|
||||
token: string,
|
||||
hostname: string
|
||||
): Promise<Models['User_type']> => {
|
||||
try {
|
||||
const user = await fetch(withAPIBaseURL('/users/me'), {
|
||||
const user = await fetch(`${hostname}/users/me`, {
|
||||
headers: new Headers({
|
||||
Authorization: `Bearer ${token}`,
|
||||
}),
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { VITE_KC_SITE_APP_URL } from '@src/env'
|
||||
import { VITE_KC_API_BASE_URL, VITE_KC_SITE_APP_URL } from '@src/env'
|
||||
import toast from 'react-hot-toast'
|
||||
|
||||
import { stringToBase64 } from '@src/lib/base64'
|
||||
@ -7,7 +7,6 @@ import {
|
||||
CREATE_FILE_URL_PARAM,
|
||||
} from '@src/lib/constants'
|
||||
import { err } from '@src/lib/trap'
|
||||
import { withAPIBaseURL } from '@src/lib/withBaseURL'
|
||||
|
||||
export interface FileLinkParams {
|
||||
code: string
|
||||
@ -97,7 +96,7 @@ export async function createShortlink(
|
||||
if (password) {
|
||||
body.password = password
|
||||
}
|
||||
const response = await fetch(withAPIBaseURL('/user/shortlinks'), {
|
||||
const response = await fetch(`${VITE_KC_API_BASE_URL}/user/shortlinks`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-type': 'application/json',
|
||||
|
@ -1,7 +1,7 @@
|
||||
import type { SelectionRange } from '@codemirror/state'
|
||||
import { EditorSelection, Transaction } from '@codemirror/state'
|
||||
import type { Models } from '@kittycad/lib'
|
||||
import { VITE_KC_SITE_BASE_URL } from '@src/env'
|
||||
import { VITE_KC_API_BASE_URL, VITE_KC_SITE_BASE_URL } from '@src/env'
|
||||
import { diffLines } from 'diff'
|
||||
import toast from 'react-hot-toast'
|
||||
import type { TextToCadMultiFileIteration_type } from '@kittycad/lib/dist/types/src/models'
|
||||
@ -28,7 +28,6 @@ import { uuidv4 } from '@src/lib/utils'
|
||||
import type { File as KittyCadLibFile } from '@kittycad/lib/dist/types/src/models'
|
||||
import type { FileMeta } from '@src/lib/types'
|
||||
import type { RequestedKCLFile } from '@src/machines/systemIO/utils'
|
||||
import { withAPIBaseURL } from '@src/lib/withBaseURL'
|
||||
|
||||
type KclFileMetaMap = {
|
||||
[execStateFileNamesIndex: number]: Extract<FileMeta, { type: 'kcl' }>
|
||||
@ -78,7 +77,7 @@ async function submitTextToCadRequest(
|
||||
})
|
||||
|
||||
const response = await fetch(
|
||||
withAPIBaseURL('/ml/text-to-cad/multi-file/iteration'),
|
||||
`${VITE_KC_API_BASE_URL}/ml/text-to-cad/multi-file/iteration`,
|
||||
{
|
||||
method: 'POST',
|
||||
headers: {
|
||||
@ -305,7 +304,7 @@ export async function getPromptToEditResult(
|
||||
id: string,
|
||||
token?: string
|
||||
): Promise<Models['TextToCadMultiFileIteration_type'] | Error> {
|
||||
const url = withAPIBaseURL(`/async/operations/${id}`)
|
||||
const url = VITE_KC_API_BASE_URL + '/async/operations/' + id
|
||||
const data: Models['TextToCadMultiFileIteration_type'] | Error =
|
||||
await crossPlatformFetch(
|
||||
url,
|
||||
@ -336,6 +335,14 @@ export async function doPromptEdit({
|
||||
const toastId = toast.loading('Submitting to Text-to-CAD API...')
|
||||
|
||||
let submitResult
|
||||
|
||||
// work around for @kittycad/lib not really being built for the browser
|
||||
;(window as any).process = {
|
||||
env: {
|
||||
ZOO_API_TOKEN: token,
|
||||
ZOO_HOST: VITE_KC_API_BASE_URL,
|
||||
},
|
||||
}
|
||||
try {
|
||||
submitResult = await submitPromptToEditToQueue({
|
||||
prompt,
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { withAPIBaseURL } from '@src/lib/withBaseURL'
|
||||
import { VITE_KC_API_BASE_URL } from '@src/env'
|
||||
|
||||
import EditorManager from '@src/editor/manager'
|
||||
import { KclManager } from '@src/lang/KclSingleton'
|
||||
@ -171,7 +171,7 @@ const appMachine = setup({
|
||||
systemId: BILLING,
|
||||
input: {
|
||||
...BILLING_CONTEXT_DEFAULTS,
|
||||
urlUserService: withAPIBaseURL(''),
|
||||
urlUserService: VITE_KC_API_BASE_URL,
|
||||
},
|
||||
}),
|
||||
],
|
||||
|
@ -1,4 +1,5 @@
|
||||
import type { Models } from '@kittycad/lib'
|
||||
import { VITE_KC_API_BASE_URL } from '@src/env'
|
||||
import toast from 'react-hot-toast'
|
||||
import type { NavigateFunction } from 'react-router-dom'
|
||||
import {
|
||||
@ -18,7 +19,6 @@ import { err, reportRejection } from '@src/lib/trap'
|
||||
import { toSync } from '@src/lib/utils'
|
||||
import { getAllSubDirectoriesAtProjectRoot } from '@src/machines/systemIO/snapshotContext'
|
||||
import { joinOSPaths } from '@src/lib/paths'
|
||||
import { withAPIBaseURL } from '@src/lib/withBaseURL'
|
||||
|
||||
export async function submitTextToCadPrompt(
|
||||
prompt: string,
|
||||
@ -32,7 +32,7 @@ export async function submitTextToCadPrompt(
|
||||
kcl_version: kclManager.kclVersion,
|
||||
}
|
||||
// Glb has a smaller footprint than gltf, should we want to render it.
|
||||
const url = withAPIBaseURL('/ai/text-to-cad/glb?kcl=true')
|
||||
const url = VITE_KC_API_BASE_URL + '/ai/text-to-cad/glb?kcl=true'
|
||||
const data: Models['TextToCad_type'] | Error = await crossPlatformFetch(
|
||||
url,
|
||||
{
|
||||
@ -58,7 +58,7 @@ export async function getTextToCadResult(
|
||||
id: string,
|
||||
token?: string
|
||||
): Promise<Models['TextToCad_type'] | Error> {
|
||||
const url = withAPIBaseURL(`/user/text-to-cad/${id}`)
|
||||
const url = VITE_KC_API_BASE_URL + '/user/text-to-cad/' + id
|
||||
const data: Models['TextToCad_type'] | Error = await crossPlatformFetch(
|
||||
url,
|
||||
{
|
||||
|
@ -1,13 +1,14 @@
|
||||
import type { Models } from '@kittycad/lib/dist/types/src'
|
||||
import { VITE_KC_API_BASE_URL } from '@src/env'
|
||||
import crossPlatformFetch from '@src/lib/crossPlatformFetch'
|
||||
import { withAPIBaseURL } from '@src/lib/withBaseURL'
|
||||
|
||||
export async function sendTelemetry(
|
||||
id: string,
|
||||
feedback: Models['MlFeedback_type'],
|
||||
token?: string
|
||||
): Promise<void> {
|
||||
const url = withAPIBaseURL(`/user/text-to-cad/${id}?feedback=${feedback}`)
|
||||
const url =
|
||||
VITE_KC_API_BASE_URL + '/user/text-to-cad/' + id + '?feedback=' + feedback
|
||||
await crossPlatformFetch(
|
||||
url,
|
||||
{
|
||||
|
@ -1,34 +0,0 @@
|
||||
import { withAPIBaseURL } from '@src/lib/withBaseURL'
|
||||
|
||||
describe('withBaseURL', () => {
|
||||
/**
|
||||
* running in the development environment
|
||||
* the .env.development should load
|
||||
*/
|
||||
describe('withAPIBaseUrl', () => {
|
||||
it('should return base url', () => {
|
||||
const expected = 'https://api.dev.zoo.dev'
|
||||
const actual = withAPIBaseURL('')
|
||||
expect(actual).toBe(expected)
|
||||
})
|
||||
it('should return base url with /users', () => {
|
||||
const expected = 'https://api.dev.zoo.dev/users'
|
||||
const actual = withAPIBaseURL('/users')
|
||||
expect(actual).toBe(expected)
|
||||
})
|
||||
it('should return a longer base url with /oauth2/token/revoke', () => {
|
||||
const expected = 'https://api.dev.zoo.dev/oauth2/token/revoke'
|
||||
const actual = withAPIBaseURL('/oauth2/token/revoke')
|
||||
expect(actual).toBe(expected)
|
||||
})
|
||||
it('should ensure base url does not have ending slash', () => {
|
||||
const expected = 'https://api.dev.zoo.dev'
|
||||
const actual = withAPIBaseURL('')
|
||||
expect(actual).toBe(expected)
|
||||
const expectedEndsWith = expected[expected.length - 1]
|
||||
const actualEndsWith = actual[actual.length - 1]
|
||||
expect(actual).toBe(expected)
|
||||
expect(actualEndsWith).toBe(expectedEndsWith)
|
||||
})
|
||||
})
|
||||
})
|
@ -1,5 +1,5 @@
|
||||
import { VITE_KITTYCAD_API_BASE_URL } from '@src/env'
|
||||
import { VITE_KC_API_BASE_URL } from '@src/env'
|
||||
|
||||
export function withAPIBaseURL(path: string): string {
|
||||
return VITE_KITTYCAD_API_BASE_URL + path
|
||||
export default function withBaseUrl(path: string): string {
|
||||
return VITE_KC_API_BASE_URL + path
|
||||
}
|
||||
|
@ -1,5 +1,10 @@
|
||||
import type { Models } from '@kittycad/lib'
|
||||
import { VITE_KITTYCAD_API_TOKEN } from '@src/env'
|
||||
import {
|
||||
DEV,
|
||||
VITE_KC_API_BASE_URL,
|
||||
VITE_KC_DEV_TOKEN,
|
||||
VITE_KC_SKIP_AUTH,
|
||||
} from '@src/env'
|
||||
import { assign, fromPromise, setup } from 'xstate'
|
||||
|
||||
import { COOKIE_NAME, OAUTH2_DEVICE_CLIENT_ID } from '@src/lib/constants'
|
||||
@ -10,9 +15,32 @@ import {
|
||||
} from '@src/lib/desktop'
|
||||
import { isDesktop } from '@src/lib/isDesktop'
|
||||
import { markOnce } from '@src/lib/performance'
|
||||
import { withAPIBaseURL } from '@src/lib/withBaseURL'
|
||||
import {
|
||||
default as withBaseURL,
|
||||
default as withBaseUrl,
|
||||
} from '@src/lib/withBaseURL'
|
||||
import { ACTOR_IDS } from '@src/machines/machineConstants'
|
||||
|
||||
const SKIP_AUTH = VITE_KC_SKIP_AUTH === 'true' && DEV
|
||||
|
||||
const LOCAL_USER: Models['User_type'] = {
|
||||
id: '8675309',
|
||||
name: 'Test User',
|
||||
email: 'kittycad.sidebar.test@example.com',
|
||||
image: 'https://placekitten.com/200/200',
|
||||
created_at: 'yesteryear',
|
||||
updated_at: 'today',
|
||||
company: 'Test Company',
|
||||
discord: 'Test User#1234',
|
||||
github: 'testuser',
|
||||
phone: '555-555-5555',
|
||||
first_name: 'Test',
|
||||
last_name: 'User',
|
||||
can_train_on_data: false,
|
||||
is_service_account: false,
|
||||
deletion_scheduled: false,
|
||||
}
|
||||
|
||||
export interface UserContext {
|
||||
user?: Models['User_type']
|
||||
token: string
|
||||
@ -28,21 +56,11 @@ export type Events =
|
||||
}
|
||||
|
||||
export const TOKEN_PERSIST_KEY = 'TOKEN_PERSIST_KEY'
|
||||
|
||||
/**
|
||||
* Determine which token do we have persisted to initialize the auth machine
|
||||
*/
|
||||
const persistedCookie = getCookie(COOKIE_NAME)
|
||||
const persistedLocalStorage = localStorage?.getItem(TOKEN_PERSIST_KEY) || ''
|
||||
const persistedDevToken = VITE_KITTYCAD_API_TOKEN
|
||||
export const persistedToken =
|
||||
persistedDevToken || persistedCookie || persistedLocalStorage
|
||||
console.log('Initial persisted token')
|
||||
console.table([
|
||||
['cookie', !!persistedCookie],
|
||||
['local storage', !!persistedLocalStorage],
|
||||
['api token', !!persistedDevToken],
|
||||
])
|
||||
VITE_KC_DEV_TOKEN ||
|
||||
getCookie(COOKIE_NAME) ||
|
||||
localStorage?.getItem(TOKEN_PERSIST_KEY) ||
|
||||
''
|
||||
|
||||
export const authMachine = setup({
|
||||
types: {} as {
|
||||
@ -139,7 +157,7 @@ export const authMachine = setup({
|
||||
|
||||
async function getUser(input: { token?: string }) {
|
||||
const token = await getAndSyncStoredToken(input)
|
||||
const url = withAPIBaseURL('/user')
|
||||
const url = withBaseURL('/user')
|
||||
const headers: { [key: string]: string } = {
|
||||
'Content-Type': 'application/json',
|
||||
}
|
||||
@ -147,8 +165,21 @@ async function getUser(input: { token?: string }) {
|
||||
if (!token && isDesktop()) return Promise.reject(new Error('No token found'))
|
||||
if (token) headers['Authorization'] = `Bearer ${token}`
|
||||
|
||||
if (SKIP_AUTH) {
|
||||
// For local tests
|
||||
if (localStorage.getItem('FORCE_NO_IMAGE')) {
|
||||
LOCAL_USER.image = ''
|
||||
}
|
||||
|
||||
markOnce('code/didAuth')
|
||||
return {
|
||||
user: LOCAL_USER,
|
||||
token,
|
||||
}
|
||||
}
|
||||
|
||||
const userPromise = isDesktop()
|
||||
? getUserDesktop(token)
|
||||
? getUserDesktop(token, VITE_KC_API_BASE_URL)
|
||||
: fetch(url, {
|
||||
method: 'GET',
|
||||
credentials: 'include',
|
||||
@ -197,28 +228,16 @@ async function getAndSyncStoredToken(input: {
|
||||
token?: string
|
||||
}): Promise<string> {
|
||||
// dev mode
|
||||
if (VITE_KITTYCAD_API_TOKEN) {
|
||||
console.log('Token used for authentication')
|
||||
console.table([['api token', !!VITE_KITTYCAD_API_TOKEN]])
|
||||
return VITE_KITTYCAD_API_TOKEN
|
||||
}
|
||||
if (VITE_KC_DEV_TOKEN) return VITE_KC_DEV_TOKEN
|
||||
|
||||
const inputToken = input.token && input.token !== '' ? input.token : ''
|
||||
const cookieToken = getCookie(COOKIE_NAME)
|
||||
const localStorageToken = localStorage?.getItem(TOKEN_PERSIST_KEY) || ''
|
||||
const token = inputToken || cookieToken || localStorageToken
|
||||
|
||||
console.log('Token used for authentication')
|
||||
console.table([
|
||||
['persisted token', !!inputToken],
|
||||
['cookie', !!cookieToken],
|
||||
['local storage', !!localStorageToken],
|
||||
['api token', !!VITE_KITTYCAD_API_TOKEN],
|
||||
])
|
||||
const token =
|
||||
input.token && input.token !== ''
|
||||
? input.token
|
||||
: getCookie(COOKIE_NAME) || localStorage?.getItem(TOKEN_PERSIST_KEY) || ''
|
||||
if (token) {
|
||||
// has just logged in, update storage
|
||||
localStorage.setItem(TOKEN_PERSIST_KEY, token)
|
||||
if (isDesktop()) {
|
||||
// has just logged in, update storage
|
||||
localStorage.setItem(TOKEN_PERSIST_KEY, token)
|
||||
await writeTokenFile(token)
|
||||
}
|
||||
return token
|
||||
@ -240,7 +259,7 @@ async function logout() {
|
||||
|
||||
if (token) {
|
||||
try {
|
||||
await fetch(withAPIBaseURL('/oauth2/token/revoke'), {
|
||||
await fetch(withBaseUrl('/oauth2/token/revoke'), {
|
||||
method: 'POST',
|
||||
credentials: 'include',
|
||||
headers: {
|
||||
@ -263,7 +282,7 @@ async function logout() {
|
||||
}
|
||||
}
|
||||
|
||||
return fetch(withAPIBaseURL('/logout'), {
|
||||
return fetch(withBaseUrl('/logout'), {
|
||||
method: 'POST',
|
||||
credentials: 'include',
|
||||
})
|
||||
|
@ -15,6 +15,7 @@ export type CommandBarContext = {
|
||||
currentArgument?: CommandArgument<unknown> & { name: string }
|
||||
argumentsToSubmit: { [x: string]: unknown }
|
||||
machineManager: MachineManager
|
||||
additionalData: { [x: string]: unknown } // For storing extra parameters like scaleSketch
|
||||
}
|
||||
|
||||
export type CommandBarMachineEvent =
|
||||
@ -77,6 +78,7 @@ export type CommandBarMachineEvent =
|
||||
data: { [x: string]: CommandArgumentWithName<unknown> }
|
||||
}
|
||||
| { type: 'Set machine manager'; data: MachineManager }
|
||||
| { type: 'Set additional data'; data: { [x: string]: unknown } }
|
||||
|
||||
export const commandBarMachine = setup({
|
||||
types: {
|
||||
@ -117,6 +119,12 @@ export const commandBarMachine = setup({
|
||||
resolvedArgs[argName] =
|
||||
typeof argValue === 'function' ? argValue(context) : argValue
|
||||
}
|
||||
|
||||
// Special handling for "Constrain with named value" command to include scaleSketch
|
||||
if (selectedCommand.name === 'Constrain with named value') {
|
||||
resolvedArgs.scaleSketch = context.additionalData.scaleSketch
|
||||
}
|
||||
|
||||
selectedCommand?.onSubmit(resolvedArgs)
|
||||
} else {
|
||||
selectedCommand?.onSubmit({ context, event })
|
||||
@ -224,6 +232,16 @@ export const commandBarMachine = setup({
|
||||
selectedCommand: undefined,
|
||||
currentArgument: undefined,
|
||||
argumentsToSubmit: {},
|
||||
additionalData: {},
|
||||
}),
|
||||
'Set additional data': assign({
|
||||
additionalData: ({ context, event }) => {
|
||||
if (event.type !== 'Set additional data') return context.additionalData
|
||||
return {
|
||||
...context.additionalData,
|
||||
...event.data,
|
||||
}
|
||||
},
|
||||
}),
|
||||
'Set selected command': assign({
|
||||
selectedCommand: ({ context, event }) =>
|
||||
@ -468,6 +486,7 @@ export const commandBarMachine = setup({
|
||||
codeBasedSelections: [],
|
||||
},
|
||||
argumentsToSubmit: {},
|
||||
additionalData: {},
|
||||
machineManager: {
|
||||
machines: [],
|
||||
machineApiIp: null,
|
||||
@ -626,6 +645,11 @@ export const commandBarMachine = setup({
|
||||
actions: 'Set machine manager',
|
||||
},
|
||||
|
||||
'Set additional data': {
|
||||
reenter: false,
|
||||
actions: 'Set additional data',
|
||||
},
|
||||
|
||||
Close: {
|
||||
target: '.Closed',
|
||||
actions: 'Clear selected command',
|
||||
|
@ -11,7 +11,7 @@ import {
|
||||
engineCommandManager,
|
||||
kclManager,
|
||||
} from '@src/lib/singletons'
|
||||
import { VITE_KITTYCAD_API_TOKEN } from '@src/env'
|
||||
import { VITE_KC_DEV_TOKEN } from '@src/env'
|
||||
import { getConstraintInfoKw } from '@src/lang/std/sketch'
|
||||
import { getNodeFromPath } from '@src/lang/queryAst'
|
||||
import type { Node } from '@rust/kcl-lib/bindings/Node'
|
||||
@ -19,6 +19,7 @@ import { err } from '@src/lib/trap'
|
||||
import {
|
||||
createIdentifier,
|
||||
createLiteral,
|
||||
createLocalName,
|
||||
createVariableDeclaration,
|
||||
} from '@src/lang/create'
|
||||
import { ARG_END_ABSOLUTE, ARG_INTERIOR_ABSOLUTE } from '@src/lang/constants'
|
||||
@ -29,9 +30,10 @@ import { removeSingleConstraintInfo } from '@src/lang/modifyAst'
|
||||
beforeAll(async () => {
|
||||
await initPromise
|
||||
|
||||
// THESE TEST WILL FAIL without VITE_KC_DEV_TOKEN set in .env.development.local
|
||||
await new Promise((resolve) => {
|
||||
engineCommandManager.start({
|
||||
token: VITE_KITTYCAD_API_TOKEN,
|
||||
token: VITE_KC_DEV_TOKEN,
|
||||
width: 256,
|
||||
height: 256,
|
||||
setMediaStream: () => {},
|
||||
@ -1156,7 +1158,7 @@ p3 = [342.51, 216.38],
|
||||
filter
|
||||
)
|
||||
const constraint = constraintInfo[constraintIndex]
|
||||
console.log('constraint', constraint)
|
||||
|
||||
if (!constraint.argPosition) {
|
||||
throw new Error(
|
||||
`Constraint at index ${constraintIndex} does not have argPosition`
|
||||
@ -1291,3 +1293,279 @@ p3 = [342.51, 216.38],
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe('testing sketch scale on first length constraint', () => {
|
||||
it('should scale sketch when constrain with named value is used with scale checkbox enabled', async () => {
|
||||
// Create a sketch with multiple segments using only literal values (no constraints)
|
||||
const code = `sketch001 = startSketchOn(XZ)
|
||||
profile001 = startProfile(sketch001, at = [100, 100])
|
||||
|> line(end = [200, 0])
|
||||
|> line(end = [0, 200])
|
||||
|> line(end = [-200, 0])
|
||||
|> close()
|
||||
profile002 = circle(sketch001, center = [400, 400], radius = 50)
|
||||
`
|
||||
|
||||
const ast = assertParse(code)
|
||||
await kclManager.executeAst({ ast })
|
||||
expect(kclManager.errors).toEqual([])
|
||||
|
||||
// Find a segment to constrain (the first line segment)
|
||||
const indexOfInterest = code.indexOf('line(end = [200, 0])')
|
||||
const artifact = [...kclManager.artifactGraph].find(
|
||||
([_, artifact]) =>
|
||||
artifact?.type === 'segment' &&
|
||||
artifact.codeRef.range[0] <= indexOfInterest &&
|
||||
indexOfInterest <= artifact.codeRef.range[1]
|
||||
)?.[1]
|
||||
|
||||
if (!artifact || !('codeRef' in artifact)) {
|
||||
throw new Error('Artifact not found or invalid artifact structure')
|
||||
}
|
||||
|
||||
// Create modeling machine actor
|
||||
const modelingActor = createActor(modelingMachine, {
|
||||
input: modelingMachineDefaultContext,
|
||||
}).start()
|
||||
|
||||
// Enter sketch mode
|
||||
modelingActor.send({
|
||||
type: 'Set selection',
|
||||
data: {
|
||||
selectionType: 'mirrorCodeMirrorSelections',
|
||||
selection: {
|
||||
graphSelections: [
|
||||
{
|
||||
artifact: artifact,
|
||||
codeRef: artifact.codeRef,
|
||||
},
|
||||
],
|
||||
otherSelections: [],
|
||||
},
|
||||
},
|
||||
})
|
||||
modelingActor.send({ type: 'Enter sketch' })
|
||||
|
||||
// Wait for sketch mode
|
||||
await waitForCondition(() => {
|
||||
const snapshot = modelingActor.getSnapshot()
|
||||
return snapshot.value !== 'animating to existing sketch'
|
||||
}, 5000)
|
||||
|
||||
expect(modelingActor.getSnapshot().value).toEqual({
|
||||
Sketch: { SketchIdle: 'scene drawn' },
|
||||
})
|
||||
|
||||
// Get constraint info for the segment
|
||||
const callExp = getNodeFromPath<Node<CallExpressionKw>>(
|
||||
kclManager.ast,
|
||||
artifact.codeRef.pathToNode,
|
||||
'CallExpressionKw'
|
||||
)
|
||||
if (err(callExp)) {
|
||||
throw new Error('Failed to get CallExpressionKw node')
|
||||
}
|
||||
|
||||
const constraintInfo = getConstraintInfoKw(
|
||||
callExp.node,
|
||||
codeManager.code,
|
||||
artifact.codeRef.pathToNode
|
||||
)
|
||||
const constraint = constraintInfo[0] // First constraint (x value)
|
||||
|
||||
// Store original code to compare scaling
|
||||
const originalCode = codeManager.code
|
||||
|
||||
// No need for command bar setup, we're testing the modeling machine directly
|
||||
|
||||
// Simulate submitting the command with scaling enabled
|
||||
// The new value will be 100 (half of original 200), so scale factor should be 0.5
|
||||
modelingActor.send({
|
||||
type: 'Constrain with named value',
|
||||
data: {
|
||||
currentValue: {
|
||||
valueText: constraint.value,
|
||||
pathToNode: constraint.pathToNode,
|
||||
variableName: 'length_var',
|
||||
},
|
||||
namedValue: {
|
||||
valueText: '100',
|
||||
variableName: 'length_var',
|
||||
insertIndex: 0,
|
||||
valueCalculated: '100',
|
||||
variableDeclarationAst: createVariableDeclaration(
|
||||
'length_var',
|
||||
createLiteral('100')
|
||||
),
|
||||
variableIdentifierAst: createLocalName('length_var'),
|
||||
valueAst: createLiteral('100'),
|
||||
},
|
||||
scaleSketch: true, // This should trigger scaling
|
||||
},
|
||||
})
|
||||
|
||||
// Wait for the constraint to be applied and sketch to be scaled
|
||||
await waitForCondition(() => {
|
||||
const snapshot = modelingActor.getSnapshot()
|
||||
return (
|
||||
JSON.stringify(snapshot.value) !==
|
||||
JSON.stringify({ Sketch: 'Converting to named value' })
|
||||
)
|
||||
}, 5000)
|
||||
|
||||
// Wait for code to be updated
|
||||
const startTime = Date.now()
|
||||
while (codeManager.code === originalCode && Date.now() - startTime < 5000) {
|
||||
await new Promise((resolve) => setTimeout(resolve, 100))
|
||||
}
|
||||
await new Promise((resolve) => setTimeout(resolve, 1000))
|
||||
console.log('code is', codeManager.code)
|
||||
|
||||
// Verify the constraint was applied
|
||||
expect(codeManager.code).toContain('length_var')
|
||||
expect(codeManager.code).toContain("length_var = '100'")
|
||||
|
||||
// Verify scaling occurred - all dimensions should be scaled by 0.5 (100/200)
|
||||
// Original values: line(end = [200, 0]), line(end = [0, 200]), line(end = [-200, 0])
|
||||
// Scaled values should be: line(end = [100, 0]), line(end = [0, 100]), line(end = [-100, 0])
|
||||
expect(codeManager.code).toContain('line(end = [length_var, 0])') // First line uses variable
|
||||
expect(codeManager.code).toContain('line(end = [0, 100])') // Second line scaled
|
||||
expect(codeManager.code).toContain('line(end = [-100, 0])') // Third line scaled
|
||||
|
||||
// Circle should also be scaled: radius = 50 -> radius = 25
|
||||
expect(codeManager.code).toContain('radius = 25')
|
||||
|
||||
// Start positions should be scaled: at = [100, 100] -> at = [50, 50], at = [400, 400] -> at = [200, 200]
|
||||
expect(codeManager.code).toContain('at = [50, 50]')
|
||||
expect(codeManager.code).toContain('center = [200, 200]')
|
||||
}, 15_000)
|
||||
|
||||
it('should not scale sketch when constrain with named value is used with scale checkbox disabled', async () => {
|
||||
// Create a sketch with multiple segments using only literal values (no constraints)
|
||||
const code = `sketch001 = startSketchOn(XZ)
|
||||
profile001 = startProfile(sketch001, at = [100, 100])
|
||||
|> line(end = [200, 0])
|
||||
|> line(end = [0, 200])
|
||||
|> close()`
|
||||
|
||||
const ast = assertParse(code)
|
||||
await kclManager.executeAst({ ast })
|
||||
expect(kclManager.errors).toEqual([])
|
||||
|
||||
// Find a segment to constrain (the first line segment)
|
||||
const indexOfInterest = code.indexOf('line(end = [200, 0])')
|
||||
const artifact = [...kclManager.artifactGraph].find(
|
||||
([_, artifact]) =>
|
||||
artifact?.type === 'segment' &&
|
||||
artifact.codeRef.range[0] <= indexOfInterest &&
|
||||
indexOfInterest <= artifact.codeRef.range[1]
|
||||
)?.[1]
|
||||
|
||||
if (!artifact || !('codeRef' in artifact)) {
|
||||
throw new Error('Artifact not found or invalid artifact structure')
|
||||
}
|
||||
|
||||
// Create modeling machine actor
|
||||
const modelingActor = createActor(modelingMachine, {
|
||||
input: modelingMachineDefaultContext,
|
||||
}).start()
|
||||
|
||||
// Enter sketch mode
|
||||
modelingActor.send({
|
||||
type: 'Set selection',
|
||||
data: {
|
||||
selectionType: 'mirrorCodeMirrorSelections',
|
||||
selection: {
|
||||
graphSelections: [
|
||||
{
|
||||
artifact: artifact,
|
||||
codeRef: artifact.codeRef,
|
||||
},
|
||||
],
|
||||
otherSelections: [],
|
||||
},
|
||||
},
|
||||
})
|
||||
modelingActor.send({ type: 'Enter sketch' })
|
||||
|
||||
// Wait for sketch mode
|
||||
await waitForCondition(() => {
|
||||
const snapshot = modelingActor.getSnapshot()
|
||||
return snapshot.value !== 'animating to existing sketch'
|
||||
}, 5000)
|
||||
|
||||
// Get constraint info for the segment
|
||||
const callExp = getNodeFromPath<Node<CallExpressionKw>>(
|
||||
kclManager.ast,
|
||||
artifact.codeRef.pathToNode,
|
||||
'CallExpressionKw'
|
||||
)
|
||||
if (err(callExp)) {
|
||||
throw new Error('Failed to get CallExpressionKw node')
|
||||
}
|
||||
|
||||
const constraintInfo = getConstraintInfoKw(
|
||||
callExp.node,
|
||||
codeManager.code,
|
||||
artifact.codeRef.pathToNode
|
||||
)
|
||||
const constraint = constraintInfo[0] // First constraint (x value)
|
||||
|
||||
// Store original code to compare
|
||||
const originalCode = codeManager.code
|
||||
|
||||
// Submit command with scaling disabled directly to modeling machine
|
||||
|
||||
modelingActor.send({
|
||||
type: 'Constrain with named value',
|
||||
data: {
|
||||
currentValue: {
|
||||
valueText: constraint.value,
|
||||
pathToNode: constraint.pathToNode,
|
||||
variableName: 'length_var',
|
||||
},
|
||||
namedValue: {
|
||||
valueText: '100',
|
||||
variableName: 'length_var',
|
||||
insertIndex: 0,
|
||||
valueCalculated: '100',
|
||||
variableDeclarationAst: createVariableDeclaration(
|
||||
'length_var',
|
||||
createLiteral('100')
|
||||
),
|
||||
variableIdentifierAst: createLocalName('length_var'),
|
||||
valueAst: createLiteral('100'),
|
||||
},
|
||||
scaleSketch: false, // Scaling disabled
|
||||
},
|
||||
})
|
||||
|
||||
// Wait for the constraint to be applied
|
||||
await waitForCondition(() => {
|
||||
const snapshot = modelingActor.getSnapshot()
|
||||
return (
|
||||
JSON.stringify(snapshot.value) !==
|
||||
JSON.stringify({ Sketch: 'Converting to named value' })
|
||||
)
|
||||
}, 5000)
|
||||
|
||||
// Wait for code to be updated
|
||||
const startTime = Date.now()
|
||||
while (codeManager.code === originalCode && Date.now() - startTime < 5000) {
|
||||
await new Promise((resolve) => setTimeout(resolve, 100))
|
||||
}
|
||||
|
||||
// Verify the constraint was applied but no scaling occurred
|
||||
expect(codeManager.code).toContain('length_var')
|
||||
expect(codeManager.code).toContain("length_var = '100'")
|
||||
expect(codeManager.code).toContain('line(end = [length_var, 0])')
|
||||
|
||||
// Other dimensions should remain unchanged (no scaling)
|
||||
expect(codeManager.code).toContain('line(end = [0, 200])') // Should remain 200, not scaled to 100
|
||||
expect(codeManager.code).toContain('at = [100, 100]') // Should remain [100, 100], not scaled
|
||||
}, 15_000)
|
||||
|
||||
// Note: The third test for checking if scale checkbox is disabled when sketch has existing constraints
|
||||
// would be better tested in the UI layer (CommandBarKclInput.tsx) or as an e2e test
|
||||
// since it's primarily a UI behavior test
|
||||
})
|
||||
|
@ -9,6 +9,7 @@ import {
|
||||
orthoScale,
|
||||
quaternionFromUpNForward,
|
||||
} from '@src/clientSideScene/helpers'
|
||||
import { scaleProfiles } from '@src/clientSideScene/sceneEntities'
|
||||
import type { Setting } from '@src/lib/settings/initialSettings'
|
||||
import type { CameraProjectionType } from '@rust/kcl-lib/bindings/CameraProjectionType'
|
||||
import { DRAFT_DASHED_LINE } from '@src/clientSideScene/sceneConstants'
|
||||
@ -2248,6 +2249,55 @@ export const modelingMachine = setup({
|
||||
return Promise.reject(new Error('Unexpected compilation error'))
|
||||
let parsed = pResult.program
|
||||
|
||||
// Apply sketch scaling if enabled
|
||||
if (data.scaleSketch && sketchDetails.sketchNodePaths) {
|
||||
const originalValue = parseFloat(data.currentValue.valueText)
|
||||
const newValue = parseFloat(
|
||||
typeof data.namedValue === 'object' &&
|
||||
'valueText' in data.namedValue
|
||||
? data.namedValue.valueText
|
||||
: String(data.namedValue)
|
||||
)
|
||||
|
||||
if (
|
||||
!Number.isNaN(originalValue) &&
|
||||
!Number.isNaN(newValue) &&
|
||||
originalValue !== 0
|
||||
) {
|
||||
const scaleFactor = newValue / originalValue
|
||||
|
||||
try {
|
||||
const scaleResult = scaleProfiles({
|
||||
ast: parsed,
|
||||
pathsToProfile: sketchDetails.sketchNodePaths,
|
||||
factor: scaleFactor,
|
||||
variables: kclManager.variables,
|
||||
})
|
||||
|
||||
if (!err(scaleResult)) {
|
||||
parsed = scaleResult.modifiedAst
|
||||
|
||||
// Reparse and recast to get fresh source ranges after scaling
|
||||
const reparseResult = parse(recast(parsed))
|
||||
if (!trap(reparseResult) && resultIsOk(reparseResult)) {
|
||||
parsed = reparseResult.program
|
||||
}
|
||||
} else {
|
||||
// Continue with constraint application even if scaling fails
|
||||
console.warn(
|
||||
'Failed to scale sketch, continuing with constraint application'
|
||||
)
|
||||
}
|
||||
} catch (error) {
|
||||
// Continue with constraint application even if scaling fails
|
||||
console.warn(
|
||||
'Error scaling sketch, continuing with constraint application:',
|
||||
error
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let result: {
|
||||
modifiedAst: Node<Program>
|
||||
pathToReplaced: PathToNode | null
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { engineCommandManager, kclManager } from '@src/lib/singletons'
|
||||
import { VITE_KITTYCAD_API_TOKEN } from '@src/env'
|
||||
import { VITE_KC_DEV_TOKEN } from '@src/env'
|
||||
import { getModuleIdByFileName, isArray } from '@src/lib/utils'
|
||||
import { vi, inject } from 'vitest'
|
||||
import { assertParse } from '@src/lang/wasm'
|
||||
@ -355,9 +355,10 @@ cases.push(
|
||||
beforeAll(async () => {
|
||||
await initPromise
|
||||
|
||||
// THESE TEST WILL FAIL without VITE_KC_DEV_TOKEN set in .env.development.local
|
||||
await new Promise((resolve) => {
|
||||
engineCommandManager.start({
|
||||
token: VITE_KITTYCAD_API_TOKEN,
|
||||
token: VITE_KC_DEV_TOKEN,
|
||||
width: 256,
|
||||
height: 256,
|
||||
setMediaStream: () => {},
|
||||
|
@ -70,10 +70,12 @@ dotenv.config({ path: [`.env.${NODE_ENV}.local`, `.env.${NODE_ENV}`] })
|
||||
|
||||
// default vite values based on mode
|
||||
process.env.NODE_ENV ??= viteEnv.MODE
|
||||
process.env.BASE_URL ??= viteEnv.VITE_KC_API_BASE_URL
|
||||
process.env.VITE_KC_API_WS_MODELING_URL ??= viteEnv.VITE_KC_API_WS_MODELING_URL
|
||||
process.env.VITE_KITTYCAD_API_BASE_URL ??= viteEnv.VITE_KITTYCAD_API_BASE_URL
|
||||
process.env.VITE_KC_API_BASE_URL ??= viteEnv.VITE_KC_API_BASE_URL
|
||||
process.env.VITE_KC_SITE_BASE_URL ??= viteEnv.VITE_KC_SITE_BASE_URL
|
||||
process.env.VITE_KC_SITE_APP_URL ??= viteEnv.VITE_KC_SITE_APP_URL
|
||||
process.env.VITE_KC_SKIP_AUTH ??= viteEnv.VITE_KC_SKIP_AUTH
|
||||
process.env.VITE_KC_CONNECTION_TIMEOUT_MS ??=
|
||||
viteEnv.VITE_KC_CONNECTION_TIMEOUT_MS
|
||||
|
||||
|
@ -289,11 +289,12 @@ contextBridge.exposeInMainWorld('electron', {
|
||||
exposeProcessEnvs([
|
||||
'NODE_ENV',
|
||||
'VITE_KC_API_WS_MODELING_URL',
|
||||
'VITE_KITTYCAD_API_BASE_URL',
|
||||
'VITE_KC_API_BASE_URL',
|
||||
'VITE_KC_SITE_BASE_URL',
|
||||
'VITE_KC_SITE_APP_URL',
|
||||
'VITE_KC_SKIP_AUTH',
|
||||
'VITE_KC_CONNECTION_TIMEOUT_MS',
|
||||
'VITE_KITTYCAD_API_TOKEN',
|
||||
'VITE_KC_DEV_TOKEN',
|
||||
|
||||
'IS_PLAYWRIGHT',
|
||||
|
||||
|
@ -6,7 +6,7 @@ import { Link } from 'react-router-dom'
|
||||
import { ActionButton } from '@src/components/ActionButton'
|
||||
import { CustomIcon } from '@src/components/CustomIcon'
|
||||
import { Logo } from '@src/components/Logo'
|
||||
import { VITE_KC_SITE_BASE_URL } from '@src/env'
|
||||
import { VITE_KC_API_BASE_URL, VITE_KC_SITE_BASE_URL } from '@src/env'
|
||||
import { APP_NAME } from '@src/lib/constants'
|
||||
import { isDesktop } from '@src/lib/isDesktop'
|
||||
import { openExternalBrowserIfDesktop } from '@src/lib/openWindow'
|
||||
@ -15,7 +15,6 @@ import { reportRejection } from '@src/lib/trap'
|
||||
import { toSync } from '@src/lib/utils'
|
||||
import { authActor, useSettings } from '@src/lib/singletons'
|
||||
import { APP_VERSION, generateSignInUrl } from '@src/routes/utils'
|
||||
import { withAPIBaseURL } from '@src/lib/withBaseURL'
|
||||
|
||||
const subtleBorder =
|
||||
'border border-solid border-chalkboard-30 dark:border-chalkboard-80'
|
||||
@ -55,7 +54,7 @@ const SignIn = () => {
|
||||
const signInDesktop = async () => {
|
||||
// We want to invoke our command to login via device auth.
|
||||
const userCodeToDisplay = await window.electron
|
||||
.startDeviceFlow(withAPIBaseURL(location.search))
|
||||
.startDeviceFlow(VITE_KC_API_BASE_URL + location.search)
|
||||
.catch(reportError)
|
||||
if (!userCodeToDisplay) {
|
||||
console.error('No user code received while trying to log in')
|
||||
|
Reference in New Issue
Block a user