Compare commits
43 Commits
jtran/plus
...
pierremtb/
Author | SHA1 | Date | |
---|---|---|---|
3ae4b045ad | |||
cd672d52f6 | |||
30c4f79285 | |||
c3c8b727bd | |||
65168eb139 | |||
6a9870b6f3 | |||
78f885c3d1 | |||
a40ba06641 | |||
597f4a1d54 | |||
c035398ad7 | |||
1387c2fbcb | |||
c814c90a9e | |||
a9f95f7574 | |||
64832f9046 | |||
b2b7ac5bf8 | |||
172e01529c | |||
5a4a32c044 | |||
b955184191 | |||
d7914219da | |||
ead4c1286b | |||
34494f3bba | |||
a0fe33260e | |||
8955b5fcd3 | |||
df6256266c | |||
5708b8c64b | |||
5b8284e737 | |||
dd9b0ec5f0 | |||
c467568ee4 | |||
e5d082f441 | |||
d4d3e179b1 | |||
cb976ec31b | |||
cc9eb65456 | |||
298583181b | |||
a589f56e73 | |||
4f4c44e7c7 | |||
1b75020686 | |||
e668cb55f9 | |||
9942a65612 | |||
187925ff21 | |||
4c1564e2b0 | |||
50ac0244fd | |||
8d7858978f | |||
74d73ece7b |
@ -3,18 +3,17 @@
|
|||||||
NODE_ENV=development
|
NODE_ENV=development
|
||||||
DEV=true
|
DEV=true
|
||||||
|
|
||||||
|
# App
|
||||||
VITE_KC_API_WS_MODELING_URL=wss://api.dev.zoo.dev/ws/modeling/commands
|
VITE_KC_API_WS_MODELING_URL=wss://api.dev.zoo.dev/ws/modeling/commands
|
||||||
VITE_KC_API_BASE_URL=https://api.dev.zoo.dev
|
VITE_KITTYCAD_API_BASE_URL=https://api.dev.zoo.dev
|
||||||
VITE_KC_SITE_BASE_URL=https://dev.zoo.dev
|
VITE_KC_SITE_BASE_URL=https://dev.zoo.dev
|
||||||
VITE_KC_SITE_APP_URL=https://app.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_KC_CONNECTION_TIMEOUT_MS=5000
|
||||||
#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_WASM_URL="optional override of Wasm URL if not on default port 3000"
|
||||||
#VITE_KC_DEV_TOKEN="optional token to skip auth in the app"
|
#VITE_KITTYCAD_API_TOKEN="required for testing, optional to skip auth in the app"
|
||||||
#token="required token for playwright. TODO: clean up env vars in #3973"
|
FAIL_ON_CONSOLE_ERRORS=true
|
||||||
|
|
||||||
|
# KCL
|
||||||
RUST_BACKTRACE=1
|
RUST_BACKTRACE=1
|
||||||
PYO3_PYTHON=/usr/local/bin/python3
|
PYO3_PYTHON=/usr/local/bin/python3
|
||||||
#KITTYCAD_API_TOKEN="required token for engine testing"
|
#KITTYCAD_API_TOKEN=$VITE_KITTYCAD_API_TOKEN
|
||||||
|
|
||||||
FAIL_ON_CONSOLE_ERRORS=true
|
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
NODE_ENV=production
|
NODE_ENV=production
|
||||||
|
|
||||||
|
# App
|
||||||
VITE_KC_API_WS_MODELING_URL=wss://api.zoo.dev/ws/modeling/commands
|
VITE_KC_API_WS_MODELING_URL=wss://api.zoo.dev/ws/modeling/commands
|
||||||
VITE_KC_API_BASE_URL=https://api.zoo.dev
|
VITE_KITTYCAD_API_BASE_URL=https://api.zoo.dev
|
||||||
VITE_KC_SITE_BASE_URL=https://zoo.dev
|
VITE_KC_SITE_BASE_URL=https://zoo.dev
|
||||||
VITE_KC_SITE_APP_URL=https://app.zoo.dev
|
VITE_KC_SITE_APP_URL=https://app.zoo.dev
|
||||||
VITE_KC_SKIP_AUTH=false
|
|
||||||
VITE_KC_CONNECTION_TIMEOUT_MS=15000
|
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
|
timeout_minutes: 5
|
||||||
max_attempts: 5
|
max_attempts: 5
|
||||||
env:
|
env:
|
||||||
token: ${{ secrets.KITTYCAD_API_TOKEN_DEV }}
|
VITE_KITTYCAD_API_TOKEN: ${{ secrets.KITTYCAD_API_TOKEN_DEV }}
|
||||||
TAB_API_URL: ${{ secrets.TAB_API_URL }}
|
TAB_API_URL: ${{ secrets.TAB_API_URL }}
|
||||||
TAB_API_KEY: ${{ secrets.TAB_API_KEY }}
|
TAB_API_KEY: ${{ secrets.TAB_API_KEY }}
|
||||||
CI_COMMIT_SHA: ${{ github.event.pull_request.head.sha }}
|
CI_COMMIT_SHA: ${{ github.event.pull_request.head.sha }}
|
||||||
@ -169,7 +169,7 @@ jobs:
|
|||||||
if: always()
|
if: always()
|
||||||
run: npm run test:snapshots -- --last-failed --update-snapshots
|
run: npm run test:snapshots -- --last-failed --update-snapshots
|
||||||
env:
|
env:
|
||||||
token: ${{ secrets.KITTYCAD_API_TOKEN_DEV }}
|
VITE_KITTYCAD_API_TOKEN: ${{ secrets.KITTYCAD_API_TOKEN_DEV }}
|
||||||
TAB_API_URL: ${{ secrets.TAB_API_URL }}
|
TAB_API_URL: ${{ secrets.TAB_API_URL }}
|
||||||
TAB_API_KEY: ${{ secrets.TAB_API_KEY }}
|
TAB_API_KEY: ${{ secrets.TAB_API_KEY }}
|
||||||
CI_COMMIT_SHA: ${{ github.event.pull_request.head.sha }}
|
CI_COMMIT_SHA: ${{ github.event.pull_request.head.sha }}
|
||||||
@ -284,7 +284,7 @@ jobs:
|
|||||||
timeout_minutes: 5
|
timeout_minutes: 5
|
||||||
max_attempts: 5
|
max_attempts: 5
|
||||||
env:
|
env:
|
||||||
token: ${{ secrets.KITTYCAD_API_TOKEN_DEV }}
|
VITE_KITTYCAD_API_TOKEN: ${{ secrets.KITTYCAD_API_TOKEN_DEV }}
|
||||||
TAB_API_URL: ${{ secrets.TAB_API_URL }}
|
TAB_API_URL: ${{ secrets.TAB_API_URL }}
|
||||||
TAB_API_KEY: ${{ secrets.TAB_API_KEY }}
|
TAB_API_KEY: ${{ secrets.TAB_API_KEY }}
|
||||||
CI_COMMIT_SHA: ${{ github.event.pull_request.head.sha }}
|
CI_COMMIT_SHA: ${{ github.event.pull_request.head.sha }}
|
||||||
@ -410,7 +410,7 @@ jobs:
|
|||||||
max_attempts: 9
|
max_attempts: 9
|
||||||
env:
|
env:
|
||||||
FAIL_ON_CONSOLE_ERRORS: true
|
FAIL_ON_CONSOLE_ERRORS: true
|
||||||
token: ${{ secrets.KITTYCAD_API_TOKEN_DEV }}
|
VITE_KITTYCAD_API_TOKEN: ${{ secrets.KITTYCAD_API_TOKEN_DEV }}
|
||||||
TAB_API_URL: ${{ secrets.TAB_API_URL }}
|
TAB_API_URL: ${{ secrets.TAB_API_URL }}
|
||||||
TAB_API_KEY: ${{ secrets.TAB_API_KEY }}
|
TAB_API_KEY: ${{ secrets.TAB_API_KEY }}
|
||||||
CI_COMMIT_SHA: ${{ github.event.pull_request.head.sha }}
|
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' }}
|
if: ${{ github.event_name != 'release' && github.event_name != 'schedule' }}
|
||||||
run: xvfb-run -a npm run test:unit
|
run: xvfb-run -a npm run test:unit
|
||||||
env:
|
env:
|
||||||
VITE_KC_DEV_TOKEN: ${{ secrets.KITTYCAD_API_TOKEN_DEV }}
|
VITE_KITTYCAD_API_TOKEN: ${{ secrets.KITTYCAD_API_TOKEN_DEV }}
|
||||||
|
|
||||||
- name: Check for changes
|
- name: Check for changes
|
||||||
if: ${{ github.event_name != 'release' && github.event_name != 'schedule' }}
|
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
|
### 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_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.
|
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.
|
||||||
|
|
||||||
### Developing in Chrome
|
### 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:
|
Prepare these system dependencies:
|
||||||
|
|
||||||
- Set $token from https://zoo.dev/account/api-tokens
|
- Set `$VITE_KITTYCAD_API_TOKEN` from https://zoo.dev/account/api-tokens
|
||||||
|
|
||||||
#### Snapshot tests (Google Chrome on Ubuntu only)
|
#### Snapshot tests (Google Chrome on Ubuntu only)
|
||||||
|
|
||||||
|
2
Makefile
2
Makefile
@ -49,7 +49,7 @@ RUST_SOURCES := $(wildcard rust/**/*.rs)
|
|||||||
|
|
||||||
REACT_SOURCES := $(wildcard src/*.tsx) $(wildcard src/**/*.tsx)
|
REACT_SOURCES := $(wildcard src/*.tsx) $(wildcard src/**/*.tsx)
|
||||||
TYPESCRIPT_SOURCES := tsconfig.* $(wildcard src/*.ts) $(wildcard src/**/*.ts)
|
TYPESCRIPT_SOURCES := tsconfig.* $(wildcard src/*.ts) $(wildcard src/**/*.ts)
|
||||||
VITE_SOURCES := $(wildcard vite.*) $(wildcard vite/**/*.tsx)
|
VITE_SOURCES := $(wildcard vite.*) $(wildcard vite/**/*.tsx) .env*
|
||||||
|
|
||||||
.PHONY: build
|
.PHONY: build
|
||||||
build: install public/kcl_wasm_lib_bg.wasm public/kcl-samples/manifest.json .vite/build/main.js
|
build: install public/kcl_wasm_lib_bg.wasm public/kcl-samples/manifest.json .vite/build/main.js
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import { expect, test } from '@e2e/playwright/zoo-test'
|
import { expect, test } from '@e2e/playwright/zoo-test'
|
||||||
|
|
||||||
// test file is for testing auth functionality
|
|
||||||
test.describe('Authentication tests', () => {
|
test.describe('Authentication tests', () => {
|
||||||
test(
|
test(
|
||||||
`The user can sign out and back in`,
|
`The user can sign out and back in`,
|
||||||
@ -13,22 +12,12 @@ test.describe('Authentication tests', () => {
|
|||||||
await page.setBodyDimensions({ width: 1000, height: 500 })
|
await page.setBodyDimensions({ width: 1000, height: 500 })
|
||||||
await homePage.projectSection.waitFor()
|
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 test.step('Click on sign out and expect sign in page', async () => {
|
||||||
await toolbar.userSidebarButton.click()
|
await toolbar.userSidebarButton.click()
|
||||||
await toolbar.signOutButton.click()
|
await toolbar.signOutButton.click()
|
||||||
await expect(signInPage.signInButton).toBeVisible()
|
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 test.step('Click on sign in and cancel, click again and expect different code', async () => {
|
||||||
await signInPage.signInButton.click()
|
await signInPage.signInButton.click()
|
||||||
await expect(signInPage.userCode).toBeVisible()
|
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'
|
const NODE_ENV = process.env.NODE_ENV || 'development'
|
||||||
dotenv.config({ path: [`.env.${NODE_ENV}.local`, `.env.${NODE_ENV}`] })
|
dotenv.config({ path: [`.env.${NODE_ENV}.local`, `.env.${NODE_ENV}`] })
|
||||||
export const token = process.env.token || ''
|
export const token = process.env.VITE_KITTYCAD_API_TOKEN || ''
|
||||||
|
|
||||||
import type { ProjectConfiguration } from '@rust/kcl-lib/bindings/ProjectConfiguration'
|
import type { ProjectConfiguration } from '@rust/kcl-lib/bindings/ProjectConfiguration'
|
||||||
|
|
||||||
|
7
interface.d.ts
vendored
7
interface.d.ts
vendored
@ -72,16 +72,13 @@ export interface IElectronAPI {
|
|||||||
}
|
}
|
||||||
process: {
|
process: {
|
||||||
env: {
|
env: {
|
||||||
BASE_URL: string
|
|
||||||
IS_PLAYWRIGHT: string
|
IS_PLAYWRIGHT: string
|
||||||
VITE_KC_DEV_TOKEN: string
|
VITE_KITTYCAD_API_TOKEN: string
|
||||||
VITE_KC_API_WS_MODELING_URL: string
|
VITE_KC_API_WS_MODELING_URL: string
|
||||||
VITE_KC_API_BASE_URL: string
|
VITE_KITTYCAD_API_BASE_URL: string
|
||||||
VITE_KC_SITE_BASE_URL: string
|
VITE_KC_SITE_BASE_URL: string
|
||||||
VITE_KC_SITE_APP_URL: string
|
VITE_KC_SITE_APP_URL: string
|
||||||
VITE_KC_SKIP_AUTH: string
|
|
||||||
VITE_KC_CONNECTION_TIMEOUT_MS: string
|
VITE_KC_CONNECTION_TIMEOUT_MS: string
|
||||||
VITE_KC_DEV_TOKEN: string
|
|
||||||
NODE_ENV: string
|
NODE_ENV: string
|
||||||
PROD: string
|
PROD: string
|
||||||
DEV: string
|
DEV: string
|
||||||
|
234
package-lock.json
generated
234
package-lock.json
generated
@ -149,7 +149,7 @@
|
|||||||
"ts-node": "^10.0.0",
|
"ts-node": "^10.0.0",
|
||||||
"typescript": "^5.8.3",
|
"typescript": "^5.8.3",
|
||||||
"typescript-eslint": "^8.30.1",
|
"typescript-eslint": "^8.30.1",
|
||||||
"vite": "^5.4.18",
|
"vite": "^5.4.19",
|
||||||
"vite-plugin-package-version": "^1.1.0",
|
"vite-plugin-package-version": "^1.1.0",
|
||||||
"vite-plugin-top-level-await": "^1.5.0",
|
"vite-plugin-top-level-await": "^1.5.0",
|
||||||
"vite-tsconfig-paths": "^4.3.2",
|
"vite-tsconfig-paths": "^4.3.2",
|
||||||
@ -3652,9 +3652,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/aix-ppc64": {
|
"node_modules/@esbuild/aix-ppc64": {
|
||||||
"version": "0.25.3",
|
"version": "0.25.4",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.3.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.4.tgz",
|
||||||
"integrity": "sha512-W8bFfPA8DowP8l//sxjJLSLkD8iEjMc7cBVyP+u4cEv9sM7mdUCkgsj+t0n/BWPFtv7WWCN5Yzj0N6FJNUUqBQ==",
|
"integrity": "sha512-1VCICWypeQKhVbE9oW/sJaAmjLxhVqacdkvPLEjwlttjfwENRSClS8EjBz0KzRyFSCPDIkuXW34Je/vk7zdB7Q==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"ppc64"
|
"ppc64"
|
||||||
],
|
],
|
||||||
@ -3668,9 +3668,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/android-arm": {
|
"node_modules/@esbuild/android-arm": {
|
||||||
"version": "0.25.3",
|
"version": "0.25.4",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.3.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.4.tgz",
|
||||||
"integrity": "sha512-PuwVXbnP87Tcff5I9ngV0lmiSu40xw1At6i3GsU77U7cjDDB4s0X2cyFuBiDa1SBk9DnvWwnGvVaGBqoFWPb7A==",
|
"integrity": "sha512-QNdQEps7DfFwE3hXiU4BZeOV68HHzYwGd0Nthhd3uCkkEKK7/R6MTgM0P7H7FAs5pU/DIWsviMmEGxEoxIZ+ZQ==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm"
|
"arm"
|
||||||
],
|
],
|
||||||
@ -3684,9 +3684,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/android-arm64": {
|
"node_modules/@esbuild/android-arm64": {
|
||||||
"version": "0.25.3",
|
"version": "0.25.4",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.3.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.4.tgz",
|
||||||
"integrity": "sha512-XelR6MzjlZuBM4f5z2IQHK6LkK34Cvv6Rj2EntER3lwCBFdg6h2lKbtRjpTTsdEjD/WSe1q8UyPBXP1x3i/wYQ==",
|
"integrity": "sha512-bBy69pgfhMGtCnwpC/x5QhfxAz/cBgQ9enbtwjf6V9lnPI/hMyT9iWpR1arm0l3kttTr4L0KSLpKmLp/ilKS9A==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@ -3700,9 +3700,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/android-x64": {
|
"node_modules/@esbuild/android-x64": {
|
||||||
"version": "0.25.3",
|
"version": "0.25.4",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.3.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.4.tgz",
|
||||||
"integrity": "sha512-ogtTpYHT/g1GWS/zKM0cc/tIebFjm1F9Aw1boQ2Y0eUQ+J89d0jFY//s9ei9jVIlkYi8AfOjiixcLJSGNSOAdQ==",
|
"integrity": "sha512-TVhdVtQIFuVpIIR282btcGC2oGQoSfZfmBdTip2anCaVYcqWlZXGcdcKIUklfX2wj0JklNYgz39OBqh2cqXvcQ==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@ -3716,9 +3716,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/darwin-arm64": {
|
"node_modules/@esbuild/darwin-arm64": {
|
||||||
"version": "0.25.3",
|
"version": "0.25.4",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.3.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.4.tgz",
|
||||||
"integrity": "sha512-eESK5yfPNTqpAmDfFWNsOhmIOaQA59tAcF/EfYvo5/QWQCzXn5iUSOnqt3ra3UdzBv073ykTtmeLJZGt3HhA+w==",
|
"integrity": "sha512-Y1giCfM4nlHDWEfSckMzeWNdQS31BQGs9/rouw6Ub91tkK79aIMTH3q9xHvzH8d0wDru5Ci0kWB8b3up/nl16g==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@ -3732,9 +3732,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/darwin-x64": {
|
"node_modules/@esbuild/darwin-x64": {
|
||||||
"version": "0.25.3",
|
"version": "0.25.4",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.3.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.4.tgz",
|
||||||
"integrity": "sha512-Kd8glo7sIZtwOLcPbW0yLpKmBNWMANZhrC1r6K++uDR2zyzb6AeOYtI6udbtabmQpFaxJ8uduXMAo1gs5ozz8A==",
|
"integrity": "sha512-CJsry8ZGM5VFVeyUYB3cdKpd/H69PYez4eJh1W/t38vzutdjEjtP7hB6eLKBoOdxcAlCtEYHzQ/PJ/oU9I4u0A==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@ -3748,9 +3748,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/freebsd-arm64": {
|
"node_modules/@esbuild/freebsd-arm64": {
|
||||||
"version": "0.25.3",
|
"version": "0.25.4",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.3.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.4.tgz",
|
||||||
"integrity": "sha512-EJiyS70BYybOBpJth3M0KLOus0n+RRMKTYzhYhFeMwp7e/RaajXvP+BWlmEXNk6uk+KAu46j/kaQzr6au+JcIw==",
|
"integrity": "sha512-yYq+39NlTRzU2XmoPW4l5Ifpl9fqSk0nAJYM/V/WUGPEFfek1epLHJIkTQM6bBs1swApjO5nWgvr843g6TjxuQ==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@ -3764,9 +3764,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/freebsd-x64": {
|
"node_modules/@esbuild/freebsd-x64": {
|
||||||
"version": "0.25.3",
|
"version": "0.25.4",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.3.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.4.tgz",
|
||||||
"integrity": "sha512-Q+wSjaLpGxYf7zC0kL0nDlhsfuFkoN+EXrx2KSB33RhinWzejOd6AvgmP5JbkgXKmjhmpfgKZq24pneodYqE8Q==",
|
"integrity": "sha512-0FgvOJ6UUMflsHSPLzdfDnnBBVoCDtBTVyn/MrWloUNvq/5SFmh13l3dvgRPkDihRxb77Y17MbqbCAa2strMQQ==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@ -3780,9 +3780,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/linux-arm": {
|
"node_modules/@esbuild/linux-arm": {
|
||||||
"version": "0.25.3",
|
"version": "0.25.4",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.3.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.4.tgz",
|
||||||
"integrity": "sha512-dUOVmAUzuHy2ZOKIHIKHCm58HKzFqd+puLaS424h6I85GlSDRZIA5ycBixb3mFgM0Jdh+ZOSB6KptX30DD8YOQ==",
|
"integrity": "sha512-kro4c0P85GMfFYqW4TWOpvmF8rFShbWGnrLqlzp4X1TNWjRY3JMYUfDCtOxPKOIY8B0WC8HN51hGP4I4hz4AaQ==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm"
|
"arm"
|
||||||
],
|
],
|
||||||
@ -3796,9 +3796,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/linux-arm64": {
|
"node_modules/@esbuild/linux-arm64": {
|
||||||
"version": "0.25.3",
|
"version": "0.25.4",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.3.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.4.tgz",
|
||||||
"integrity": "sha512-xCUgnNYhRD5bb1C1nqrDV1PfkwgbswTTBRbAd8aH5PhYzikdf/ddtsYyMXFfGSsb/6t6QaPSzxtbfAZr9uox4A==",
|
"integrity": "sha512-+89UsQTfXdmjIvZS6nUnOOLoXnkUTB9hR5QAeLrQdzOSWZvNSAXAtcRDHWtqAUtAmv7ZM1WPOOeSxDzzzMogiQ==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@ -3812,9 +3812,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/linux-ia32": {
|
"node_modules/@esbuild/linux-ia32": {
|
||||||
"version": "0.25.3",
|
"version": "0.25.4",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.3.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.4.tgz",
|
||||||
"integrity": "sha512-yplPOpczHOO4jTYKmuYuANI3WhvIPSVANGcNUeMlxH4twz/TeXuzEP41tGKNGWJjuMhotpGabeFYGAOU2ummBw==",
|
"integrity": "sha512-yTEjoapy8UP3rv8dB0ip3AfMpRbyhSN3+hY8mo/i4QXFeDxmiYbEKp3ZRjBKcOP862Ua4b1PDfwlvbuwY7hIGQ==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"ia32"
|
"ia32"
|
||||||
],
|
],
|
||||||
@ -3828,9 +3828,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/linux-loong64": {
|
"node_modules/@esbuild/linux-loong64": {
|
||||||
"version": "0.25.3",
|
"version": "0.25.4",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.3.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.4.tgz",
|
||||||
"integrity": "sha512-P4BLP5/fjyihmXCELRGrLd793q/lBtKMQl8ARGpDxgzgIKJDRJ/u4r1A/HgpBpKpKZelGct2PGI4T+axcedf6g==",
|
"integrity": "sha512-NeqqYkrcGzFwi6CGRGNMOjWGGSYOpqwCjS9fvaUlX5s3zwOtn1qwg1s2iE2svBe4Q/YOG1q6875lcAoQK/F4VA==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"loong64"
|
"loong64"
|
||||||
],
|
],
|
||||||
@ -3844,9 +3844,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/linux-mips64el": {
|
"node_modules/@esbuild/linux-mips64el": {
|
||||||
"version": "0.25.3",
|
"version": "0.25.4",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.3.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.4.tgz",
|
||||||
"integrity": "sha512-eRAOV2ODpu6P5divMEMa26RRqb2yUoYsuQQOuFUexUoQndm4MdpXXDBbUoKIc0iPa4aCO7gIhtnYomkn2x+bag==",
|
"integrity": "sha512-IcvTlF9dtLrfL/M8WgNI/qJYBENP3ekgsHbYUIzEzq5XJzzVEV/fXY9WFPfEEXmu3ck2qJP8LG/p3Q8f7Zc2Xg==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"mips64el"
|
"mips64el"
|
||||||
],
|
],
|
||||||
@ -3860,9 +3860,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/linux-ppc64": {
|
"node_modules/@esbuild/linux-ppc64": {
|
||||||
"version": "0.25.3",
|
"version": "0.25.4",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.3.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.4.tgz",
|
||||||
"integrity": "sha512-ZC4jV2p7VbzTlnl8nZKLcBkfzIf4Yad1SJM4ZMKYnJqZFD4rTI+pBG65u8ev4jk3/MPwY9DvGn50wi3uhdaghg==",
|
"integrity": "sha512-HOy0aLTJTVtoTeGZh4HSXaO6M95qu4k5lJcH4gxv56iaycfz1S8GO/5Jh6X4Y1YiI0h7cRyLi+HixMR+88swag==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"ppc64"
|
"ppc64"
|
||||||
],
|
],
|
||||||
@ -3876,9 +3876,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/linux-riscv64": {
|
"node_modules/@esbuild/linux-riscv64": {
|
||||||
"version": "0.25.3",
|
"version": "0.25.4",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.3.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.4.tgz",
|
||||||
"integrity": "sha512-LDDODcFzNtECTrUUbVCs6j9/bDVqy7DDRsuIXJg6so+mFksgwG7ZVnTruYi5V+z3eE5y+BJZw7VvUadkbfg7QA==",
|
"integrity": "sha512-i8JUDAufpz9jOzo4yIShCTcXzS07vEgWzyX3NH2G7LEFVgrLEhjwL3ajFE4fZI3I4ZgiM7JH3GQ7ReObROvSUA==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"riscv64"
|
"riscv64"
|
||||||
],
|
],
|
||||||
@ -3892,9 +3892,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/linux-s390x": {
|
"node_modules/@esbuild/linux-s390x": {
|
||||||
"version": "0.25.3",
|
"version": "0.25.4",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.3.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.4.tgz",
|
||||||
"integrity": "sha512-s+w/NOY2k0yC2p9SLen+ymflgcpRkvwwa02fqmAwhBRI3SC12uiS10edHHXlVWwfAagYSY5UpmT/zISXPMW3tQ==",
|
"integrity": "sha512-jFnu+6UbLlzIjPQpWCNh5QtrcNfMLjgIavnwPQAfoGx4q17ocOU9MsQ2QVvFxwQoWpZT8DvTLooTvmOQXkO51g==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"s390x"
|
"s390x"
|
||||||
],
|
],
|
||||||
@ -3908,9 +3908,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/linux-x64": {
|
"node_modules/@esbuild/linux-x64": {
|
||||||
"version": "0.25.3",
|
"version": "0.25.4",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.3.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.4.tgz",
|
||||||
"integrity": "sha512-nQHDz4pXjSDC6UfOE1Fw9Q8d6GCAd9KdvMZpfVGWSJztYCarRgSDfOVBY5xwhQXseiyxapkiSJi/5/ja8mRFFA==",
|
"integrity": "sha512-6e0cvXwzOnVWJHq+mskP8DNSrKBr1bULBvnFLpc1KY+d+irZSgZ02TGse5FsafKS5jg2e4pbvK6TPXaF/A6+CA==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@ -3924,9 +3924,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/netbsd-arm64": {
|
"node_modules/@esbuild/netbsd-arm64": {
|
||||||
"version": "0.25.3",
|
"version": "0.25.4",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.3.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.4.tgz",
|
||||||
"integrity": "sha512-1QaLtOWq0mzK6tzzp0jRN3eccmN3hezey7mhLnzC6oNlJoUJz4nym5ZD7mDnS/LZQgkrhEbEiTn515lPeLpgWA==",
|
"integrity": "sha512-vUnkBYxZW4hL/ie91hSqaSNjulOnYXE1VSLusnvHg2u3jewJBz3YzB9+oCw8DABeVqZGg94t9tyZFoHma8gWZQ==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@ -3940,9 +3940,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/netbsd-x64": {
|
"node_modules/@esbuild/netbsd-x64": {
|
||||||
"version": "0.25.3",
|
"version": "0.25.4",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.3.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.4.tgz",
|
||||||
"integrity": "sha512-i5Hm68HXHdgv8wkrt+10Bc50zM0/eonPb/a/OFVfB6Qvpiirco5gBA5bz7S2SHuU+Y4LWn/zehzNX14Sp4r27g==",
|
"integrity": "sha512-XAg8pIQn5CzhOB8odIcAm42QsOfa98SBeKUdo4xa8OvX8LbMZqEtgeWE9P/Wxt7MlG2QqvjGths+nq48TrUiKw==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@ -3956,9 +3956,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/openbsd-arm64": {
|
"node_modules/@esbuild/openbsd-arm64": {
|
||||||
"version": "0.25.3",
|
"version": "0.25.4",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.3.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.4.tgz",
|
||||||
"integrity": "sha512-zGAVApJEYTbOC6H/3QBr2mq3upG/LBEXr85/pTtKiv2IXcgKV0RT0QA/hSXZqSvLEpXeIxah7LczB4lkiYhTAQ==",
|
"integrity": "sha512-Ct2WcFEANlFDtp1nVAXSNBPDxyU+j7+tId//iHXU2f/lN5AmO4zLyhDcpR5Cz1r08mVxzt3Jpyt4PmXQ1O6+7A==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@ -3972,9 +3972,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/openbsd-x64": {
|
"node_modules/@esbuild/openbsd-x64": {
|
||||||
"version": "0.25.3",
|
"version": "0.25.4",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.3.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.4.tgz",
|
||||||
"integrity": "sha512-fpqctI45NnCIDKBH5AXQBsD0NDPbEFczK98hk/aa6HJxbl+UtLkJV2+Bvy5hLSLk3LHmqt0NTkKNso1A9y1a4w==",
|
"integrity": "sha512-xAGGhyOQ9Otm1Xu8NT1ifGLnA6M3sJxZ6ixylb+vIUVzvvd6GOALpwQrYrtlPouMqd/vSbgehz6HaVk4+7Afhw==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@ -3988,9 +3988,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/sunos-x64": {
|
"node_modules/@esbuild/sunos-x64": {
|
||||||
"version": "0.25.3",
|
"version": "0.25.4",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.3.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.4.tgz",
|
||||||
"integrity": "sha512-ROJhm7d8bk9dMCUZjkS8fgzsPAZEjtRJqCAmVgB0gMrvG7hfmPmz9k1rwO4jSiblFjYmNvbECL9uhaPzONMfgA==",
|
"integrity": "sha512-Mw+tzy4pp6wZEK0+Lwr76pWLjrtjmJyUB23tHKqEDP74R3q95luY/bXqXZeYl4NYlvwOqoRKlInQialgCKy67Q==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@ -4004,9 +4004,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/win32-arm64": {
|
"node_modules/@esbuild/win32-arm64": {
|
||||||
"version": "0.25.3",
|
"version": "0.25.4",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.3.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.4.tgz",
|
||||||
"integrity": "sha512-YWcow8peiHpNBiIXHwaswPnAXLsLVygFwCB3A7Bh5jRkIBFWHGmNQ48AlX4xDvQNoMZlPYzjVOQDYEzWCqufMQ==",
|
"integrity": "sha512-AVUP428VQTSddguz9dO9ngb+E5aScyg7nOeJDrF1HPYu555gmza3bDGMPhmVXL8svDSoqPCsCPjb265yG/kLKQ==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@ -4020,9 +4020,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/win32-ia32": {
|
"node_modules/@esbuild/win32-ia32": {
|
||||||
"version": "0.25.3",
|
"version": "0.25.4",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.3.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.4.tgz",
|
||||||
"integrity": "sha512-qspTZOIGoXVS4DpNqUYUs9UxVb04khS1Degaw/MnfMe7goQ3lTfQ13Vw4qY/Nj0979BGvMRpAYbs/BAxEvU8ew==",
|
"integrity": "sha512-i1sW+1i+oWvQzSgfRcxxG2k4I9n3O9NRqy8U+uugaT2Dy7kLO9Y7wI72haOahxceMX8hZAzgGou1FhndRldxRg==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"ia32"
|
"ia32"
|
||||||
],
|
],
|
||||||
@ -4036,9 +4036,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/win32-x64": {
|
"node_modules/@esbuild/win32-x64": {
|
||||||
"version": "0.25.3",
|
"version": "0.25.4",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.3.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.4.tgz",
|
||||||
"integrity": "sha512-ICgUR+kPimx0vvRzf+N/7L7tVSQeE3BYY+NhHRHXS1kBuPO7z2+7ea2HbhDyZdTephgvNvKrlDDKUexuCVBVvg==",
|
"integrity": "sha512-nOT2vZNw6hJ+z43oP1SPea/G/6AbN6X+bGNhNuq8NtRHy4wsMhw765IKLNmnjek7GvjWBYQ8Q5VBoYTFg9y1UQ==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@ -13219,9 +13219,9 @@
|
|||||||
"optional": true
|
"optional": true
|
||||||
},
|
},
|
||||||
"node_modules/esbuild": {
|
"node_modules/esbuild": {
|
||||||
"version": "0.25.3",
|
"version": "0.25.4",
|
||||||
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.3.tgz",
|
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.4.tgz",
|
||||||
"integrity": "sha512-qKA6Pvai73+M2FtftpNKRxJ78GIjmFXFxd/1DVBqGo/qNhLSfv+G12n9pNoWdytJC8U00TrViOwpjT0zgqQS8Q==",
|
"integrity": "sha512-8pgjLUcUjcgDg+2Q4NYXnPbo/vncAY4UmyaCm0jZevERqCHZIaWwdJHkf8XQtu4AxSKCdvrUbT0XUr1IdZzI8Q==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"hasInstallScript": true,
|
"hasInstallScript": true,
|
||||||
"bin": {
|
"bin": {
|
||||||
@ -13231,31 +13231,31 @@
|
|||||||
"node": ">=18"
|
"node": ">=18"
|
||||||
},
|
},
|
||||||
"optionalDependencies": {
|
"optionalDependencies": {
|
||||||
"@esbuild/aix-ppc64": "0.25.3",
|
"@esbuild/aix-ppc64": "0.25.4",
|
||||||
"@esbuild/android-arm": "0.25.3",
|
"@esbuild/android-arm": "0.25.4",
|
||||||
"@esbuild/android-arm64": "0.25.3",
|
"@esbuild/android-arm64": "0.25.4",
|
||||||
"@esbuild/android-x64": "0.25.3",
|
"@esbuild/android-x64": "0.25.4",
|
||||||
"@esbuild/darwin-arm64": "0.25.3",
|
"@esbuild/darwin-arm64": "0.25.4",
|
||||||
"@esbuild/darwin-x64": "0.25.3",
|
"@esbuild/darwin-x64": "0.25.4",
|
||||||
"@esbuild/freebsd-arm64": "0.25.3",
|
"@esbuild/freebsd-arm64": "0.25.4",
|
||||||
"@esbuild/freebsd-x64": "0.25.3",
|
"@esbuild/freebsd-x64": "0.25.4",
|
||||||
"@esbuild/linux-arm": "0.25.3",
|
"@esbuild/linux-arm": "0.25.4",
|
||||||
"@esbuild/linux-arm64": "0.25.3",
|
"@esbuild/linux-arm64": "0.25.4",
|
||||||
"@esbuild/linux-ia32": "0.25.3",
|
"@esbuild/linux-ia32": "0.25.4",
|
||||||
"@esbuild/linux-loong64": "0.25.3",
|
"@esbuild/linux-loong64": "0.25.4",
|
||||||
"@esbuild/linux-mips64el": "0.25.3",
|
"@esbuild/linux-mips64el": "0.25.4",
|
||||||
"@esbuild/linux-ppc64": "0.25.3",
|
"@esbuild/linux-ppc64": "0.25.4",
|
||||||
"@esbuild/linux-riscv64": "0.25.3",
|
"@esbuild/linux-riscv64": "0.25.4",
|
||||||
"@esbuild/linux-s390x": "0.25.3",
|
"@esbuild/linux-s390x": "0.25.4",
|
||||||
"@esbuild/linux-x64": "0.25.3",
|
"@esbuild/linux-x64": "0.25.4",
|
||||||
"@esbuild/netbsd-arm64": "0.25.3",
|
"@esbuild/netbsd-arm64": "0.25.4",
|
||||||
"@esbuild/netbsd-x64": "0.25.3",
|
"@esbuild/netbsd-x64": "0.25.4",
|
||||||
"@esbuild/openbsd-arm64": "0.25.3",
|
"@esbuild/openbsd-arm64": "0.25.4",
|
||||||
"@esbuild/openbsd-x64": "0.25.3",
|
"@esbuild/openbsd-x64": "0.25.4",
|
||||||
"@esbuild/sunos-x64": "0.25.3",
|
"@esbuild/sunos-x64": "0.25.4",
|
||||||
"@esbuild/win32-arm64": "0.25.3",
|
"@esbuild/win32-arm64": "0.25.4",
|
||||||
"@esbuild/win32-ia32": "0.25.3",
|
"@esbuild/win32-ia32": "0.25.4",
|
||||||
"@esbuild/win32-x64": "0.25.3"
|
"@esbuild/win32-x64": "0.25.4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/escalade": {
|
"node_modules/escalade": {
|
||||||
@ -25132,11 +25132,10 @@
|
|||||||
"optional": true
|
"optional": true
|
||||||
},
|
},
|
||||||
"node_modules/vite": {
|
"node_modules/vite": {
|
||||||
"version": "5.4.18",
|
"version": "5.4.19",
|
||||||
"resolved": "https://registry.npmjs.org/vite/-/vite-5.4.18.tgz",
|
"resolved": "https://registry.npmjs.org/vite/-/vite-5.4.19.tgz",
|
||||||
"integrity": "sha512-1oDcnEp3lVyHCuQ2YFelM4Alm2o91xNoMncRm1U7S+JdYfYOvbiGZ3/CxGttrOu2M/KcGz7cRC2DoNUA6urmMA==",
|
"integrity": "sha512-qO3aKv3HoQC8QKiNSTuUM1l9o/XX3+c+VTgLHbJWHZGeTPVAg2XwazI9UWzoxjIJCGCV2zU60uqMzjeLZuULqA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"esbuild": "^0.21.3",
|
"esbuild": "^0.21.3",
|
||||||
"postcss": "^8.4.43",
|
"postcss": "^8.4.43",
|
||||||
@ -26640,10 +26639,25 @@
|
|||||||
"vscode-uri": "^3.1.0"
|
"vscode-uri": "^3.1.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "^22.14.1",
|
"@types/node": "^24.0.7",
|
||||||
"ts-node": "^10.9.2"
|
"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": {
|
"rust/kcl-language-server": {
|
||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
@ -26661,7 +26675,7 @@
|
|||||||
"@vscode/test-electron": "^2.4.1",
|
"@vscode/test-electron": "^2.4.1",
|
||||||
"@vscode/vsce": "^3.3.2",
|
"@vscode/vsce": "^3.3.2",
|
||||||
"cross-env": "^7.0.3",
|
"cross-env": "^7.0.3",
|
||||||
"esbuild": "^0.25.3",
|
"esbuild": "^0.25.4",
|
||||||
"glob": "^11.0.1",
|
"glob": "^11.0.1",
|
||||||
"mocha": "^11.1.0",
|
"mocha": "^11.1.0",
|
||||||
"typescript": "^5.8.3"
|
"typescript": "^5.8.3"
|
||||||
|
@ -227,7 +227,7 @@
|
|||||||
"ts-node": "^10.0.0",
|
"ts-node": "^10.0.0",
|
||||||
"typescript": "^5.8.3",
|
"typescript": "^5.8.3",
|
||||||
"typescript-eslint": "^8.30.1",
|
"typescript-eslint": "^8.30.1",
|
||||||
"vite": "^5.4.18",
|
"vite": "^5.4.19",
|
||||||
"vite-plugin-package-version": "^1.1.0",
|
"vite-plugin-package-version": "^1.1.0",
|
||||||
"vite-plugin-top-level-await": "^1.5.0",
|
"vite-plugin-top-level-await": "^1.5.0",
|
||||||
"vite-tsconfig-paths": "^4.3.2",
|
"vite-tsconfig-paths": "^4.3.2",
|
||||||
|
@ -123,7 +123,7 @@
|
|||||||
"@vscode/test-electron": "^2.4.1",
|
"@vscode/test-electron": "^2.4.1",
|
||||||
"@vscode/vsce": "^3.3.2",
|
"@vscode/vsce": "^3.3.2",
|
||||||
"cross-env": "^7.0.3",
|
"cross-env": "^7.0.3",
|
||||||
"esbuild": "^0.25.3",
|
"esbuild": "^0.25.4",
|
||||||
"glob": "^11.0.1",
|
"glob": "^11.0.1",
|
||||||
"mocha": "^11.1.0",
|
"mocha": "^11.1.0",
|
||||||
"typescript": "^5.8.3"
|
"typescript": "^5.8.3"
|
||||||
|
@ -10,71 +10,76 @@ DATA;
|
|||||||
NAMED_UNIT(*)
|
NAMED_UNIT(*)
|
||||||
SI_UNIT($, .METRE.)
|
SI_UNIT($, .METRE.)
|
||||||
);
|
);
|
||||||
#2 = UNCERTAINTY_MEASURE_WITH_UNIT(0.00001, #1, 'DISTANCE_ACCURACY_VALUE', $);
|
#2 = (
|
||||||
#3 = (
|
NAMED_UNIT(*)
|
||||||
|
PLANE_ANGLE_UNIT()
|
||||||
|
SI_UNIT($, .RADIAN.)
|
||||||
|
);
|
||||||
|
#3 = UNCERTAINTY_MEASURE_WITH_UNIT(0.00001, #1, 'DISTANCE_ACCURACY_VALUE', $);
|
||||||
|
#4 = (
|
||||||
GEOMETRIC_REPRESENTATION_CONTEXT(3)
|
GEOMETRIC_REPRESENTATION_CONTEXT(3)
|
||||||
GLOBAL_UNCERTAINTY_ASSIGNED_CONTEXT((#2))
|
GLOBAL_UNCERTAINTY_ASSIGNED_CONTEXT((#3))
|
||||||
GLOBAL_UNIT_ASSIGNED_CONTEXT((#1))
|
GLOBAL_UNIT_ASSIGNED_CONTEXT((#1, #2))
|
||||||
REPRESENTATION_CONTEXT('', '3D')
|
REPRESENTATION_CONTEXT('', '3D')
|
||||||
);
|
);
|
||||||
#4 = CARTESIAN_POINT('NONE', (0.015, -0.01, -0.005));
|
#5 = CARTESIAN_POINT('NONE', (0.015, -0.01, -0.005));
|
||||||
#5 = VERTEX_POINT('NONE', #4);
|
#6 = VERTEX_POINT('NONE', #5);
|
||||||
#6 = CARTESIAN_POINT('NONE', (0.015, 0, -0.005));
|
#7 = CARTESIAN_POINT('NONE', (0.015, 0, -0.005));
|
||||||
#7 = VERTEX_POINT('NONE', #6);
|
#8 = VERTEX_POINT('NONE', #7);
|
||||||
#8 = DIRECTION('NONE', (1, 0, -0));
|
#9 = DIRECTION('NONE', (1, 0, -0));
|
||||||
#9 = DIRECTION('NONE', (0, 1, 0));
|
#10 = DIRECTION('NONE', (0, 1, 0));
|
||||||
#10 = CARTESIAN_POINT('NONE', (0.005, -0.01, -0.005));
|
#11 = CARTESIAN_POINT('NONE', (0.005, -0.01, -0.005));
|
||||||
#11 = AXIS2_PLACEMENT_3D('NONE', #10, #9, #8);
|
#12 = AXIS2_PLACEMENT_3D('NONE', #11, #10, #9);
|
||||||
#12 = CIRCLE('NONE', #11, 0.01);
|
#13 = CIRCLE('NONE', #12, 0.01);
|
||||||
#13 = DIRECTION('NONE', (0, 1, 0));
|
#14 = DIRECTION('NONE', (0, 1, 0));
|
||||||
#14 = VECTOR('NONE', #13, 1);
|
#15 = VECTOR('NONE', #14, 1);
|
||||||
#15 = CARTESIAN_POINT('NONE', (0.015, -0.01, -0.005));
|
#16 = CARTESIAN_POINT('NONE', (0.015, -0.01, -0.005));
|
||||||
#16 = LINE('NONE', #15, #14);
|
#17 = LINE('NONE', #16, #15);
|
||||||
#17 = DIRECTION('NONE', (1, 0, -0));
|
#18 = DIRECTION('NONE', (1, 0, -0));
|
||||||
#18 = DIRECTION('NONE', (0, 1, 0));
|
#19 = DIRECTION('NONE', (0, 1, 0));
|
||||||
#19 = CARTESIAN_POINT('NONE', (0.005, 0, -0.005));
|
#20 = CARTESIAN_POINT('NONE', (0.005, 0, -0.005));
|
||||||
#20 = AXIS2_PLACEMENT_3D('NONE', #19, #18, #17);
|
#21 = AXIS2_PLACEMENT_3D('NONE', #20, #19, #18);
|
||||||
#21 = CIRCLE('NONE', #20, 0.01);
|
#22 = CIRCLE('NONE', #21, 0.01);
|
||||||
#22 = EDGE_CURVE('NONE', #5, #5, #12, .T.);
|
#23 = EDGE_CURVE('NONE', #6, #6, #13, .T.);
|
||||||
#23 = EDGE_CURVE('NONE', #5, #7, #16, .T.);
|
#24 = EDGE_CURVE('NONE', #6, #8, #17, .T.);
|
||||||
#24 = EDGE_CURVE('NONE', #7, #7, #21, .T.);
|
#25 = EDGE_CURVE('NONE', #8, #8, #22, .T.);
|
||||||
#25 = CARTESIAN_POINT('NONE', (0.005, -0.005, -0.005));
|
#26 = CARTESIAN_POINT('NONE', (0.005, -0.005, -0.005));
|
||||||
#26 = DIRECTION('NONE', (0, 1, 0));
|
#27 = DIRECTION('NONE', (0, 1, 0));
|
||||||
#27 = DIRECTION('NONE', (1, 0, -0));
|
#28 = DIRECTION('NONE', (1, 0, -0));
|
||||||
#28 = AXIS2_PLACEMENT_3D('NONE', #25, #26, #27);
|
#29 = AXIS2_PLACEMENT_3D('NONE', #26, #27, #28);
|
||||||
#29 = CYLINDRICAL_SURFACE('NONE', #28, 0.01);
|
#30 = CYLINDRICAL_SURFACE('NONE', #29, 0.01);
|
||||||
#30 = CARTESIAN_POINT('NONE', (0, -0.01, -0));
|
#31 = CARTESIAN_POINT('NONE', (0, -0.01, -0));
|
||||||
#31 = DIRECTION('NONE', (0, 1, 0));
|
#32 = DIRECTION('NONE', (0, 1, 0));
|
||||||
#32 = AXIS2_PLACEMENT_3D('NONE', #30, #31, $);
|
#33 = AXIS2_PLACEMENT_3D('NONE', #31, #32, $);
|
||||||
#33 = PLANE('NONE', #32);
|
#34 = PLANE('NONE', #33);
|
||||||
#34 = CARTESIAN_POINT('NONE', (0, 0, -0));
|
#35 = CARTESIAN_POINT('NONE', (0, 0, -0));
|
||||||
#35 = DIRECTION('NONE', (0, 1, 0));
|
#36 = DIRECTION('NONE', (0, 1, 0));
|
||||||
#36 = AXIS2_PLACEMENT_3D('NONE', #34, #35, $);
|
#37 = AXIS2_PLACEMENT_3D('NONE', #35, #36, $);
|
||||||
#37 = PLANE('NONE', #36);
|
#38 = PLANE('NONE', #37);
|
||||||
#38 = ORIENTED_EDGE('NONE', *, *, #22, .T.);
|
#39 = ORIENTED_EDGE('NONE', *, *, #23, .T.);
|
||||||
#39 = ORIENTED_EDGE('NONE', *, *, #24, .F.);
|
#40 = ORIENTED_EDGE('NONE', *, *, #25, .F.);
|
||||||
#40 = EDGE_LOOP('NONE', (#38));
|
#41 = EDGE_LOOP('NONE', (#39));
|
||||||
#41 = FACE_BOUND('NONE', #40, .T.);
|
#42 = FACE_BOUND('NONE', #41, .T.);
|
||||||
#42 = EDGE_LOOP('NONE', (#39));
|
#43 = EDGE_LOOP('NONE', (#40));
|
||||||
#43 = FACE_BOUND('NONE', #42, .T.);
|
#44 = FACE_BOUND('NONE', #43, .T.);
|
||||||
#44 = ADVANCED_FACE('NONE', (#41, #43), #29, .T.);
|
#45 = ADVANCED_FACE('NONE', (#42, #44), #30, .T.);
|
||||||
#45 = ORIENTED_EDGE('NONE', *, *, #22, .F.);
|
#46 = ORIENTED_EDGE('NONE', *, *, #23, .F.);
|
||||||
#46 = EDGE_LOOP('NONE', (#45));
|
#47 = EDGE_LOOP('NONE', (#46));
|
||||||
#47 = FACE_BOUND('NONE', #46, .T.);
|
#48 = FACE_BOUND('NONE', #47, .T.);
|
||||||
#48 = ADVANCED_FACE('NONE', (#47), #33, .F.);
|
#49 = ADVANCED_FACE('NONE', (#48), #34, .F.);
|
||||||
#49 = ORIENTED_EDGE('NONE', *, *, #24, .T.);
|
#50 = ORIENTED_EDGE('NONE', *, *, #25, .T.);
|
||||||
#50 = EDGE_LOOP('NONE', (#49));
|
#51 = EDGE_LOOP('NONE', (#50));
|
||||||
#51 = FACE_BOUND('NONE', #50, .T.);
|
#52 = FACE_BOUND('NONE', #51, .T.);
|
||||||
#52 = ADVANCED_FACE('NONE', (#51), #37, .T.);
|
#53 = ADVANCED_FACE('NONE', (#52), #38, .T.);
|
||||||
#53 = CLOSED_SHELL('NONE', (#44, #48, #52));
|
#54 = CLOSED_SHELL('NONE', (#45, #49, #53));
|
||||||
#54 = MANIFOLD_SOLID_BREP('NONE', #53);
|
#55 = MANIFOLD_SOLID_BREP('NONE', #54);
|
||||||
#55 = APPLICATION_CONTEXT('configuration controlled 3D design of mechanical parts and assemblies');
|
#56 = APPLICATION_CONTEXT('configuration controlled 3D design of mechanical parts and assemblies');
|
||||||
#56 = PRODUCT_DEFINITION_CONTEXT('part definition', #55, 'design');
|
#57 = PRODUCT_DEFINITION_CONTEXT('part definition', #56, 'design');
|
||||||
#57 = PRODUCT('UNIDENTIFIED_PRODUCT', 'NONE', $, ());
|
#58 = PRODUCT('UNIDENTIFIED_PRODUCT', 'NONE', $, ());
|
||||||
#58 = PRODUCT_DEFINITION_FORMATION('', $, #57);
|
#59 = PRODUCT_DEFINITION_FORMATION('', $, #58);
|
||||||
#59 = PRODUCT_DEFINITION('design', $, #58, #56);
|
#60 = PRODUCT_DEFINITION('design', $, #59, #57);
|
||||||
#60 = PRODUCT_DEFINITION_SHAPE('NONE', $, #59);
|
#61 = PRODUCT_DEFINITION_SHAPE('NONE', $, #60);
|
||||||
#61 = ADVANCED_BREP_SHAPE_REPRESENTATION('NONE', (#54), #3);
|
#62 = ADVANCED_BREP_SHAPE_REPRESENTATION('NONE', (#55), #4);
|
||||||
#62 = SHAPE_DEFINITION_REPRESENTATION(#60, #61);
|
#63 = SHAPE_DEFINITION_REPRESENTATION(#61, #62);
|
||||||
ENDSEC;
|
ENDSEC;
|
||||||
END-ISO-10303-21;
|
END-ISO-10303-21;
|
||||||
|
@ -994,6 +994,39 @@ impl Node<MemberExpression> {
|
|||||||
|
|
||||||
// Check the property and object match -- e.g. ints for arrays, strs for objects.
|
// Check the property and object match -- e.g. ints for arrays, strs for objects.
|
||||||
match (object, property, self.computed) {
|
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) => {
|
(KclValue::Object { value: map, meta: _ }, Property::String(property), false) => {
|
||||||
if let Some(value) = map.get(&property) {
|
if let Some(value) = map.get(&property) {
|
||||||
Ok(value.to_owned())
|
Ok(value.to_owned())
|
||||||
@ -1013,7 +1046,22 @@ impl Node<MemberExpression> {
|
|||||||
vec![self.clone().into()],
|
vec![self.clone().into()],
|
||||||
)))
|
)))
|
||||||
}
|
}
|
||||||
(KclValue::Object { .. }, p, _) => {
|
(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());
|
||||||
|
}
|
||||||
let t = p.type_name();
|
let t = p.type_name();
|
||||||
let article = article_for(t);
|
let article = article_for(t);
|
||||||
Err(KclError::new_semantic(KclErrorDetails::new(
|
Err(KclError::new_semantic(KclErrorDetails::new(
|
||||||
@ -2205,4 +2253,12 @@ y = x[0mm + 1]
|
|||||||
"#;
|
"#;
|
||||||
parse_execute(ast).await.unwrap_err();
|
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,6 +921,12 @@ impl Point3d {
|
|||||||
units: UnitLen::Unknown,
|
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 {
|
impl From<[TyF64; 3]> for Point3d {
|
||||||
|
@ -458,6 +458,31 @@ 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> {
|
pub(crate) fn as_usize(&self) -> Option<usize> {
|
||||||
match self {
|
match self {
|
||||||
KclValue::Number { value, .. } => crate::try_f64_to_usize(*value),
|
KclValue::Number { value, .. } => crate::try_f64_to_usize(*value),
|
||||||
|
60
rust/kcl-lib/tests/inputs/planestuff.kcl
Normal file
60
rust/kcl-lib/tests/inputs/planestuff.kcl
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
// 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,6 +4,8 @@
|
|||||||
|
|
||||||
URL STATUS
|
URL STATUS
|
||||||
000 https://${BASE_URL}
|
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
|
301 https://discord.gg/JQEpHR7Nt2
|
||||||
404 https://github.com/KittyCAD/engine/issues/3528
|
404 https://github.com/KittyCAD/engine/issues/3528
|
||||||
404 https://github.com/KittyCAD/modeling-app/commit/${ref}
|
404 https://github.com/KittyCAD/modeling-app/commit/${ref}
|
||||||
|
@ -7,7 +7,7 @@ import {
|
|||||||
LanguageServerClient,
|
LanguageServerClient,
|
||||||
LspWorkerEventType,
|
LspWorkerEventType,
|
||||||
} from '@kittycad/codemirror-lsp-client'
|
} from '@kittycad/codemirror-lsp-client'
|
||||||
import { TEST, VITE_KC_API_BASE_URL } from '@src/env'
|
import { TEST } from '@src/env'
|
||||||
import React, { createContext, useContext, useMemo, useState } from 'react'
|
import React, { createContext, useContext, useMemo, useState } from 'react'
|
||||||
import { useNavigate } from 'react-router-dom'
|
import { useNavigate } from 'react-router-dom'
|
||||||
import type * as LSP from 'vscode-languageserver-protocol'
|
import type * as LSP from 'vscode-languageserver-protocol'
|
||||||
@ -28,6 +28,7 @@ import type { FileEntry } from '@src/lib/project'
|
|||||||
import { codeManager } from '@src/lib/singletons'
|
import { codeManager } from '@src/lib/singletons'
|
||||||
import { err } from '@src/lib/trap'
|
import { err } from '@src/lib/trap'
|
||||||
import { useToken } from '@src/lib/singletons'
|
import { useToken } from '@src/lib/singletons'
|
||||||
|
import { withAPIBaseURL } from '@src/lib/withBaseURL'
|
||||||
|
|
||||||
function getWorkspaceFolders(): LSP.WorkspaceFolder[] {
|
function getWorkspaceFolders(): LSP.WorkspaceFolder[] {
|
||||||
return []
|
return []
|
||||||
@ -85,7 +86,7 @@ export const LspProvider = ({ children }: { children: React.ReactNode }) => {
|
|||||||
const initEvent: KclWorkerOptions = {
|
const initEvent: KclWorkerOptions = {
|
||||||
wasmUrl: wasmUrl(),
|
wasmUrl: wasmUrl(),
|
||||||
token: token,
|
token: token,
|
||||||
apiBaseUrl: VITE_KC_API_BASE_URL,
|
apiBaseUrl: withAPIBaseURL(''),
|
||||||
}
|
}
|
||||||
lspWorker.postMessage({
|
lspWorker.postMessage({
|
||||||
worker: LspWorker.Kcl,
|
worker: LspWorker.Kcl,
|
||||||
@ -178,7 +179,7 @@ export const LspProvider = ({ children }: { children: React.ReactNode }) => {
|
|||||||
const initEvent: CopilotWorkerOptions = {
|
const initEvent: CopilotWorkerOptions = {
|
||||||
wasmUrl: wasmUrl(),
|
wasmUrl: wasmUrl(),
|
||||||
token: token,
|
token: token,
|
||||||
apiBaseUrl: VITE_KC_API_BASE_URL,
|
apiBaseUrl: withAPIBaseURL(''),
|
||||||
}
|
}
|
||||||
lspWorker.postMessage({
|
lspWorker.postMessage({
|
||||||
worker: LspWorker.Copilot,
|
worker: LspWorker.Copilot,
|
||||||
|
@ -24,7 +24,12 @@ import {
|
|||||||
getOperationVariableName,
|
getOperationVariableName,
|
||||||
stdLibMap,
|
stdLibMap,
|
||||||
} from '@src/lib/operations'
|
} from '@src/lib/operations'
|
||||||
import { editorManager, kclManager, rustContext } from '@src/lib/singletons'
|
import {
|
||||||
|
commandBarActor,
|
||||||
|
editorManager,
|
||||||
|
kclManager,
|
||||||
|
rustContext,
|
||||||
|
} from '@src/lib/singletons'
|
||||||
import {
|
import {
|
||||||
featureTreeMachine,
|
featureTreeMachine,
|
||||||
featureTreeMachineDefaultContext,
|
featureTreeMachineDefaultContext,
|
||||||
@ -59,6 +64,30 @@ export const FeatureTreePane = () => {
|
|||||||
scrollToError: () => {
|
scrollToError: () => {
|
||||||
editorManager.scrollToFirstErrorDiagnosticIfExists()
|
editorManager.scrollToFirstErrorDiagnosticIfExists()
|
||||||
},
|
},
|
||||||
|
sendTranslateCommand: () => {
|
||||||
|
commandBarActor.send({
|
||||||
|
type: 'Find and select command',
|
||||||
|
data: { name: 'Translate', groupId: 'modeling' },
|
||||||
|
})
|
||||||
|
},
|
||||||
|
sendRotateCommand: () => {
|
||||||
|
commandBarActor.send({
|
||||||
|
type: 'Find and select command',
|
||||||
|
data: { name: 'Rotate', groupId: 'modeling' },
|
||||||
|
})
|
||||||
|
},
|
||||||
|
sendScaleCommand: () => {
|
||||||
|
commandBarActor.send({
|
||||||
|
type: 'Find and select command',
|
||||||
|
data: { name: 'Scale', groupId: 'modeling' },
|
||||||
|
})
|
||||||
|
},
|
||||||
|
sendCloneCommand: () => {
|
||||||
|
commandBarActor.send({
|
||||||
|
type: 'Find and select command',
|
||||||
|
data: { name: 'Clone', groupId: 'modeling' },
|
||||||
|
})
|
||||||
|
},
|
||||||
sendSelectionEvent: ({ context }) => {
|
sendSelectionEvent: ({ context }) => {
|
||||||
if (!context.targetSourceRange) {
|
if (!context.targetSourceRange) {
|
||||||
return
|
return
|
||||||
@ -354,10 +383,6 @@ const OperationItem = (props: {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* For now we can only enter the "edit" flow for the startSketchOn operation.
|
|
||||||
* TODO: https://github.com/KittyCAD/modeling-app/issues/4442
|
|
||||||
*/
|
|
||||||
function enterEditFlow() {
|
function enterEditFlow() {
|
||||||
if (
|
if (
|
||||||
props.item.type === 'StdLibCall' ||
|
props.item.type === 'StdLibCall' ||
|
||||||
@ -409,6 +434,18 @@ const OperationItem = (props: {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function enterScaleFlow() {
|
||||||
|
if (props.item.type === 'StdLibCall' || props.item.type === 'GroupBegin') {
|
||||||
|
props.send({
|
||||||
|
type: 'enterScaleFlow',
|
||||||
|
data: {
|
||||||
|
targetSourceRange: sourceRangeFromRust(props.item.sourceRange),
|
||||||
|
currentOperation: props.item,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function enterCloneFlow() {
|
function enterCloneFlow() {
|
||||||
if (props.item.type === 'StdLibCall' || props.item.type === 'GroupBegin') {
|
if (props.item.type === 'StdLibCall' || props.item.type === 'GroupBegin') {
|
||||||
props.send({
|
props.send({
|
||||||
@ -515,7 +552,7 @@ const OperationItem = (props: {
|
|||||||
!stdLibMap[props.item.name]?.supportsTransform
|
!stdLibMap[props.item.name]?.supportsTransform
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
Set translate
|
Translate
|
||||||
</ContextMenuItem>,
|
</ContextMenuItem>,
|
||||||
<ContextMenuItem
|
<ContextMenuItem
|
||||||
onClick={enterRotateFlow}
|
onClick={enterRotateFlow}
|
||||||
@ -525,7 +562,17 @@ const OperationItem = (props: {
|
|||||||
!stdLibMap[props.item.name]?.supportsTransform
|
!stdLibMap[props.item.name]?.supportsTransform
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
Set rotate
|
Rotate
|
||||||
|
</ContextMenuItem>,
|
||||||
|
<ContextMenuItem
|
||||||
|
onClick={enterScaleFlow}
|
||||||
|
data-testid="context-menu-set-scale"
|
||||||
|
disabled={
|
||||||
|
props.item.type !== 'GroupBegin' &&
|
||||||
|
!stdLibMap[props.item.name]?.supportsTransform
|
||||||
|
}
|
||||||
|
>
|
||||||
|
Scale
|
||||||
</ContextMenuItem>,
|
</ContextMenuItem>,
|
||||||
<ContextMenuItem
|
<ContextMenuItem
|
||||||
onClick={enterCloneFlow}
|
onClick={enterCloneFlow}
|
||||||
|
@ -8,13 +8,14 @@ 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
|
export const VITE_KC_API_WS_MODELING_URL = env.VITE_KC_API_WS_MODELING_URL as
|
||||||
| string
|
| string
|
||||||
| undefined
|
| undefined
|
||||||
export const VITE_KC_API_BASE_URL = env.VITE_KC_API_BASE_URL
|
export const VITE_KITTYCAD_API_BASE_URL = env.VITE_KITTYCAD_API_BASE_URL
|
||||||
export const VITE_KC_SITE_BASE_URL = env.VITE_KC_SITE_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_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 =
|
export const VITE_KC_CONNECTION_TIMEOUT_MS =
|
||||||
env.VITE_KC_CONNECTION_TIMEOUT_MS as string | undefined
|
env.VITE_KC_CONNECTION_TIMEOUT_MS as string | undefined
|
||||||
export const VITE_KC_DEV_TOKEN = env.VITE_KC_DEV_TOKEN as string | undefined
|
export const VITE_KITTYCAD_API_TOKEN = env.VITE_KITTYCAD_API_TOKEN as
|
||||||
|
| string
|
||||||
|
| undefined
|
||||||
export const PROD = env.PROD as string | undefined
|
export const PROD = env.PROD as string | undefined
|
||||||
export const TEST = env.TEST as string | undefined
|
export const TEST = env.TEST as string | undefined
|
||||||
export const DEV = env.DEV as string | undefined
|
export const DEV = env.DEV as string | undefined
|
||||||
|
@ -2,9 +2,12 @@ import type { Node } from '@rust/kcl-lib/bindings/Node'
|
|||||||
|
|
||||||
import {
|
import {
|
||||||
createArrayExpression,
|
createArrayExpression,
|
||||||
|
createCallExpressionStdLibKw,
|
||||||
createIdentifier,
|
createIdentifier,
|
||||||
|
createLabeledArg,
|
||||||
createLiteral,
|
createLiteral,
|
||||||
createLiteralMaybeSuffix,
|
createLiteralMaybeSuffix,
|
||||||
|
createLocalName,
|
||||||
createObjectExpression,
|
createObjectExpression,
|
||||||
createPipeExpression,
|
createPipeExpression,
|
||||||
createPipeSubstitution,
|
createPipeSubstitution,
|
||||||
@ -14,12 +17,19 @@ import {
|
|||||||
} from '@src/lang/create'
|
} from '@src/lang/create'
|
||||||
import {
|
import {
|
||||||
addSketchTo,
|
addSketchTo,
|
||||||
|
createPathToNodeForLastVariable,
|
||||||
|
createVariableExpressionsArray,
|
||||||
deleteSegmentFromPipeExpression,
|
deleteSegmentFromPipeExpression,
|
||||||
moveValueIntoNewVariable,
|
moveValueIntoNewVariable,
|
||||||
|
setCallInAst,
|
||||||
sketchOnExtrudedFace,
|
sketchOnExtrudedFace,
|
||||||
splitPipedProfile,
|
splitPipedProfile,
|
||||||
} from '@src/lang/modifyAst'
|
} from '@src/lang/modifyAst'
|
||||||
import { findUsesOfTagInPipe } from '@src/lang/queryAst'
|
import {
|
||||||
|
findUsesOfTagInPipe,
|
||||||
|
getNodeFromPath,
|
||||||
|
getVariableExprsFromSelection,
|
||||||
|
} from '@src/lang/queryAst'
|
||||||
import { getNodePathFromSourceRange } from '@src/lang/queryAstNodePathUtils'
|
import { getNodePathFromSourceRange } from '@src/lang/queryAstNodePathUtils'
|
||||||
import type { Artifact } from '@src/lang/std/artifactGraph'
|
import type { Artifact } from '@src/lang/std/artifactGraph'
|
||||||
import { codeRefFromRange } from '@src/lang/std/artifactGraph'
|
import { codeRefFromRange } from '@src/lang/std/artifactGraph'
|
||||||
@ -31,6 +41,7 @@ import { enginelessExecutor } from '@src/lib/testHelpers'
|
|||||||
import { err } from '@src/lib/trap'
|
import { err } from '@src/lib/trap'
|
||||||
import { deleteFromSelection } from '@src/lang/modifyAst/deleteFromSelection'
|
import { deleteFromSelection } from '@src/lang/modifyAst/deleteFromSelection'
|
||||||
import { assertNotErr } from '@src/unitTestUtils'
|
import { assertNotErr } from '@src/unitTestUtils'
|
||||||
|
import type { Selections } from '@src/lib/selections'
|
||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
await initPromise
|
await initPromise
|
||||||
@ -917,3 +928,212 @@ extrude001 = extrude(part001, length = 5)
|
|||||||
expect(result instanceof Error).toBe(true)
|
expect(result instanceof Error).toBe(true)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe('Testing createVariableExpressionsArray', () => {
|
||||||
|
it('should return null for any number of pipe substitutions', () => {
|
||||||
|
const onePipe = [createPipeSubstitution()]
|
||||||
|
const twoPipes = [createPipeSubstitution(), createPipeSubstitution()]
|
||||||
|
const threePipes = [
|
||||||
|
createPipeSubstitution(),
|
||||||
|
createPipeSubstitution(),
|
||||||
|
createPipeSubstitution(),
|
||||||
|
]
|
||||||
|
expect(createVariableExpressionsArray(onePipe)).toBeNull()
|
||||||
|
expect(createVariableExpressionsArray(twoPipes)).toBeNull()
|
||||||
|
expect(createVariableExpressionsArray(threePipes)).toBeNull()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should create a variable expressions for one variable', () => {
|
||||||
|
const oneVariableName = [createLocalName('var1')]
|
||||||
|
const expr = createVariableExpressionsArray(oneVariableName)
|
||||||
|
if (expr?.type !== 'Name') {
|
||||||
|
throw new Error(`Expected Literal type, got ${expr?.type}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(expr.name.name).toBe('var1')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should create an array of variable expressions for two variables', () => {
|
||||||
|
const twoVariableNames = [createLocalName('var1'), createLocalName('var2')]
|
||||||
|
const exprs = createVariableExpressionsArray(twoVariableNames)
|
||||||
|
if (exprs?.type !== 'ArrayExpression') {
|
||||||
|
throw new Error('Expected ArrayExpression type')
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(exprs.elements).toHaveLength(2)
|
||||||
|
if (
|
||||||
|
exprs.elements[0].type !== 'Name' ||
|
||||||
|
exprs.elements[1].type !== 'Name'
|
||||||
|
) {
|
||||||
|
throw new Error(
|
||||||
|
`Expected elements to be of type Name, got ${exprs.elements[0].type} and ${exprs.elements[1].type}`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
expect(exprs.elements[0].name.name).toBe('var1')
|
||||||
|
expect(exprs.elements[1].name.name).toBe('var2')
|
||||||
|
})
|
||||||
|
|
||||||
|
// This would catch the issue at https://github.com/KittyCAD/modeling-app/issues/7669
|
||||||
|
// TODO: fix function to get this test to pass
|
||||||
|
// it('should create one expr if the array of variable names are the same', () => {
|
||||||
|
// const twoVariableNames = [createLocalName('var1'), createLocalName('var1')]
|
||||||
|
// const expr = createVariableExpressionsArray(twoVariableNames)
|
||||||
|
// if (expr?.type !== 'Name') {
|
||||||
|
// throw new Error(`Expected Literal type, got ${expr?.type}`)
|
||||||
|
// }
|
||||||
|
|
||||||
|
// expect(expr.name.name).toBe('var1')
|
||||||
|
// })
|
||||||
|
|
||||||
|
it('should create an array of variable expressions for one variable and a pipe', () => {
|
||||||
|
const oneVarOnePipe = [createPipeSubstitution(), createLocalName('var1')]
|
||||||
|
const exprs = createVariableExpressionsArray(oneVarOnePipe)
|
||||||
|
if (exprs?.type !== 'ArrayExpression') {
|
||||||
|
throw new Error('Expected ArrayExpression type')
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(exprs.elements).toHaveLength(2)
|
||||||
|
expect(exprs.elements[0].type).toBe('PipeSubstitution')
|
||||||
|
if (exprs.elements[1].type !== 'Name') {
|
||||||
|
throw new Error(
|
||||||
|
`Expected elements[1] to be of type Name, got ${exprs.elements[1].type}`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(exprs.elements[1].name.name).toBe('var1')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('Testing createPathToNodeForLastVariable', () => {
|
||||||
|
it('should create a path to the last variable in the array', () => {
|
||||||
|
const circleProfileInVar = `sketch001 = startSketchOn(XY)
|
||||||
|
profile001 = circle(sketch001, center = [0, 0], radius = 1)
|
||||||
|
extrude001 = extrude(profile001, length = 5)
|
||||||
|
`
|
||||||
|
const ast = assertParse(circleProfileInVar)
|
||||||
|
const path = createPathToNodeForLastVariable(ast, false)
|
||||||
|
expect(path.length).toEqual(4)
|
||||||
|
|
||||||
|
// Verify we can get the right node
|
||||||
|
const node = getNodeFromPath<any>(ast, path)
|
||||||
|
if (err(node)) {
|
||||||
|
throw node
|
||||||
|
}
|
||||||
|
// With the expected range
|
||||||
|
const startOfExtrudeIndex = circleProfileInVar.indexOf('extrude(')
|
||||||
|
expect(node.node.start).toEqual(startOfExtrudeIndex)
|
||||||
|
expect(node.node.end).toEqual(circleProfileInVar.length - 1)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should create a path to the first kwarg in the last expression', () => {
|
||||||
|
const circleProfileInVar = `sketch001 = startSketchOn(XY)
|
||||||
|
profile001 = circle(sketch001, center = [0, 0], radius = 1)
|
||||||
|
extrude001 = extrude(profile001, length = 123)
|
||||||
|
`
|
||||||
|
const ast = assertParse(circleProfileInVar)
|
||||||
|
const path = createPathToNodeForLastVariable(ast, true)
|
||||||
|
expect(path.length).toEqual(7)
|
||||||
|
|
||||||
|
// Verify we can get the right node
|
||||||
|
const node = getNodeFromPath<any>(ast, path)
|
||||||
|
if (err(node)) {
|
||||||
|
throw node
|
||||||
|
}
|
||||||
|
// With the expected range
|
||||||
|
const startOfKwargIndex = circleProfileInVar.indexOf('123')
|
||||||
|
expect(node.node.start).toEqual(startOfKwargIndex)
|
||||||
|
expect(node.node.end).toEqual(startOfKwargIndex + 3)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('Testing setCallInAst', () => {
|
||||||
|
it('should push an extrude call with variable on variable profile', () => {
|
||||||
|
const code = `sketch001 = startSketchOn(XY)
|
||||||
|
profile001 = circle(sketch001, center = [0, 0], radius = 1)
|
||||||
|
`
|
||||||
|
const ast = assertParse(code)
|
||||||
|
const exprs = createVariableExpressionsArray([
|
||||||
|
createLocalName('profile001'),
|
||||||
|
])
|
||||||
|
const call = createCallExpressionStdLibKw('extrude', exprs, [
|
||||||
|
createLabeledArg('length', createLiteral(5)),
|
||||||
|
])
|
||||||
|
const pathToNode = setCallInAst(ast, call)
|
||||||
|
if (err(pathToNode)) {
|
||||||
|
throw pathToNode
|
||||||
|
}
|
||||||
|
const newCode = recast(ast)
|
||||||
|
expect(newCode).toContain(code)
|
||||||
|
expect(newCode).toContain(`extrude001 = extrude(profile001, length = 5)`)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should push an extrude call in pipe is selection was in variable-less pipe', async () => {
|
||||||
|
const code = `startSketchOn(XY)
|
||||||
|
|> circle(center = [0, 0], radius = 1)
|
||||||
|
`
|
||||||
|
const ast = assertParse(code)
|
||||||
|
const { artifactGraph } = await enginelessExecutor(ast)
|
||||||
|
const artifact = artifactGraph.values().find((a) => a.type === 'path')
|
||||||
|
if (!artifact) {
|
||||||
|
throw new Error('Artifact not found in the graph')
|
||||||
|
}
|
||||||
|
const selections: Selections = {
|
||||||
|
graphSelections: [
|
||||||
|
{
|
||||||
|
codeRef: artifact.codeRef,
|
||||||
|
artifact,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
otherSelections: [],
|
||||||
|
}
|
||||||
|
const variableExprs = getVariableExprsFromSelection(selections, ast)
|
||||||
|
if (err(variableExprs)) throw variableExprs
|
||||||
|
const exprs = createVariableExpressionsArray(variableExprs.exprs)
|
||||||
|
const call = createCallExpressionStdLibKw('extrude', exprs, [
|
||||||
|
createLabeledArg('length', createLiteral(5)),
|
||||||
|
])
|
||||||
|
const lastPathToNode = variableExprs.paths.pop()
|
||||||
|
const pathToNode = setCallInAst(ast, call, undefined, lastPathToNode)
|
||||||
|
if (err(pathToNode)) {
|
||||||
|
throw pathToNode
|
||||||
|
}
|
||||||
|
const newCode = recast(ast)
|
||||||
|
expect(newCode).toContain(code)
|
||||||
|
expect(newCode).toContain(`|> extrude(length = 5)`)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should push an extrude call with variable if selection was in variable pipe', async () => {
|
||||||
|
const code = `profile001 = startSketchOn(XY)
|
||||||
|
|> circle(center = [0, 0], radius = 1)
|
||||||
|
`
|
||||||
|
const ast = assertParse(code)
|
||||||
|
const { artifactGraph } = await enginelessExecutor(ast)
|
||||||
|
const artifact = artifactGraph.values().find((a) => a.type === 'path')
|
||||||
|
if (!artifact) {
|
||||||
|
throw new Error('Artifact not found in the graph')
|
||||||
|
}
|
||||||
|
const selections: Selections = {
|
||||||
|
graphSelections: [
|
||||||
|
{
|
||||||
|
codeRef: artifact.codeRef,
|
||||||
|
artifact,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
otherSelections: [],
|
||||||
|
}
|
||||||
|
const variableExprs = getVariableExprsFromSelection(selections, ast)
|
||||||
|
if (err(variableExprs)) throw variableExprs
|
||||||
|
const exprs = createVariableExpressionsArray(variableExprs.exprs)
|
||||||
|
const call = createCallExpressionStdLibKw('extrude', exprs, [
|
||||||
|
createLabeledArg('length', createLiteral(5)),
|
||||||
|
])
|
||||||
|
const lastPathToNode = variableExprs.paths.pop()
|
||||||
|
const pathToNode = setCallInAst(ast, call, undefined, lastPathToNode)
|
||||||
|
if (err(pathToNode)) {
|
||||||
|
throw pathToNode
|
||||||
|
}
|
||||||
|
const newCode = recast(ast)
|
||||||
|
expect(newCode).toContain(code)
|
||||||
|
expect(newCode).toContain(`extrude001 = extrude(profile001, length = 5)`)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
@ -655,39 +655,6 @@ export function addHelix({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Add clone statement
|
|
||||||
*/
|
|
||||||
export function addClone({
|
|
||||||
ast,
|
|
||||||
geometryName,
|
|
||||||
variableName,
|
|
||||||
}: {
|
|
||||||
ast: Node<Program>
|
|
||||||
geometryName: string
|
|
||||||
variableName: string
|
|
||||||
}): { modifiedAst: Node<Program>; pathToNode: PathToNode } {
|
|
||||||
const modifiedAst = structuredClone(ast)
|
|
||||||
const variable = createVariableDeclaration(
|
|
||||||
variableName,
|
|
||||||
createCallExpressionStdLibKw('clone', createLocalName(geometryName), [])
|
|
||||||
)
|
|
||||||
|
|
||||||
modifiedAst.body.push(variable)
|
|
||||||
const insertAt = modifiedAst.body.length - 1
|
|
||||||
const pathToNode: PathToNode = [
|
|
||||||
['body', ''],
|
|
||||||
[insertAt, 'index'],
|
|
||||||
['declaration', 'VariableDeclaration'],
|
|
||||||
['init', 'VariableDeclarator'],
|
|
||||||
]
|
|
||||||
|
|
||||||
return {
|
|
||||||
modifiedAst,
|
|
||||||
pathToNode,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return a modified clone of an AST with a named constant inserted into the body
|
* Return a modified clone of an AST with a named constant inserted into the body
|
||||||
*/
|
*/
|
||||||
@ -1209,3 +1176,84 @@ export function insertVariableAndOffsetPathToNode(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Create an array expression for variables,
|
||||||
|
// or keep it null if all are PipeSubstitutions
|
||||||
|
export function createVariableExpressionsArray(sketches: Expr[]): Expr | null {
|
||||||
|
let exprs: Expr | null = null
|
||||||
|
if (sketches.every((s) => s.type === 'PipeSubstitution')) {
|
||||||
|
// Keeping null so we don't even put it the % sign
|
||||||
|
} else if (sketches.length === 1) {
|
||||||
|
exprs = sketches[0]
|
||||||
|
} else {
|
||||||
|
exprs = createArrayExpression(sketches)
|
||||||
|
}
|
||||||
|
return exprs
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a path to node to the last variable declaroator of an ast
|
||||||
|
// Optionally, can point to the first kwarg of the CallExpressionKw
|
||||||
|
export function createPathToNodeForLastVariable(
|
||||||
|
ast: Node<Program>,
|
||||||
|
toFirstKwarg = true
|
||||||
|
): PathToNode {
|
||||||
|
const argIndex = 0 // first kwarg for all sweeps here
|
||||||
|
const pathToCall: PathToNode = [
|
||||||
|
['body', ''],
|
||||||
|
[ast.body.length - 1, 'index'],
|
||||||
|
['declaration', 'VariableDeclaration'],
|
||||||
|
['init', 'VariableDeclarator'],
|
||||||
|
]
|
||||||
|
if (toFirstKwarg) {
|
||||||
|
pathToCall.push(
|
||||||
|
['arguments', 'CallExpressionKw'],
|
||||||
|
[argIndex, ARG_INDEX_FIELD],
|
||||||
|
['arg', LABELED_ARG_FIELD]
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return pathToCall
|
||||||
|
}
|
||||||
|
|
||||||
|
export function setCallInAst(
|
||||||
|
ast: Node<Program>,
|
||||||
|
call: Node<CallExpressionKw>,
|
||||||
|
nodeToEdit?: PathToNode,
|
||||||
|
lastPathToNode?: PathToNode,
|
||||||
|
toFirstKwarg?: boolean
|
||||||
|
): Error | PathToNode {
|
||||||
|
let pathToNode: PathToNode | undefined
|
||||||
|
if (nodeToEdit) {
|
||||||
|
const result = getNodeFromPath<CallExpressionKw>(
|
||||||
|
ast,
|
||||||
|
nodeToEdit,
|
||||||
|
'CallExpressionKw'
|
||||||
|
)
|
||||||
|
if (err(result)) {
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
Object.assign(result.node, call)
|
||||||
|
pathToNode = nodeToEdit
|
||||||
|
} else {
|
||||||
|
if (!call.unlabeled && lastPathToNode) {
|
||||||
|
const pipe = getNodeFromPath<PipeExpression>(
|
||||||
|
ast,
|
||||||
|
lastPathToNode,
|
||||||
|
'PipeExpression'
|
||||||
|
)
|
||||||
|
if (err(pipe)) {
|
||||||
|
return pipe
|
||||||
|
}
|
||||||
|
pipe.node.body.push(call)
|
||||||
|
pathToNode = lastPathToNode
|
||||||
|
} else {
|
||||||
|
const name = findUniqueName(ast, call.callee.name.name)
|
||||||
|
const declaration = createVariableDeclaration(name, call)
|
||||||
|
ast.body.push(declaration)
|
||||||
|
pathToNode = createPathToNodeForLastVariable(ast, toFirstKwarg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return pathToNode
|
||||||
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { VITE_KC_DEV_TOKEN } from '@src/env'
|
import { VITE_KITTYCAD_API_TOKEN } from '@src/env'
|
||||||
|
|
||||||
import { createLiteral } from '@src/lang/create'
|
import { createLiteral } from '@src/lang/create'
|
||||||
import type {
|
import type {
|
||||||
@ -40,10 +40,9 @@ import { isOverlap } from '@src/lib/utils'
|
|||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
await initPromise
|
await initPromise
|
||||||
|
|
||||||
// THESE TEST WILL FAIL without VITE_KC_DEV_TOKEN set in .env.development.local
|
|
||||||
await new Promise((resolve) => {
|
await new Promise((resolve) => {
|
||||||
engineCommandManager.start({
|
engineCommandManager.start({
|
||||||
token: VITE_KC_DEV_TOKEN,
|
token: VITE_KITTYCAD_API_TOKEN,
|
||||||
width: 256,
|
width: 256,
|
||||||
height: 256,
|
height: 256,
|
||||||
setMediaStream: () => {},
|
setMediaStream: () => {},
|
||||||
|
@ -1,192 +0,0 @@
|
|||||||
import type { Node } from '@rust/kcl-lib/bindings/Node'
|
|
||||||
|
|
||||||
import {
|
|
||||||
createCallExpressionStdLibKw,
|
|
||||||
createExpressionStatement,
|
|
||||||
createLabeledArg,
|
|
||||||
createLocalName,
|
|
||||||
createPipeExpression,
|
|
||||||
} from '@src/lang/create'
|
|
||||||
import { getNodeFromPath } from '@src/lang/queryAst'
|
|
||||||
import type {
|
|
||||||
ArtifactGraph,
|
|
||||||
CallExpressionKw,
|
|
||||||
Expr,
|
|
||||||
ExpressionStatement,
|
|
||||||
PathToNode,
|
|
||||||
PipeExpression,
|
|
||||||
Program,
|
|
||||||
VariableDeclarator,
|
|
||||||
} from '@src/lang/wasm'
|
|
||||||
import { err } from '@src/lib/trap'
|
|
||||||
import {
|
|
||||||
findAllChildrenAndOrderByPlaceInCode,
|
|
||||||
getLastVariable,
|
|
||||||
} from '@src/lang/modifyAst/boolean'
|
|
||||||
import type { Selections } from '@src/lib/selections'
|
|
||||||
|
|
||||||
export function setTranslate({
|
|
||||||
modifiedAst,
|
|
||||||
pathToNode,
|
|
||||||
x,
|
|
||||||
y,
|
|
||||||
z,
|
|
||||||
}: {
|
|
||||||
modifiedAst: Node<Program>
|
|
||||||
pathToNode: PathToNode
|
|
||||||
x: Expr
|
|
||||||
y: Expr
|
|
||||||
z: Expr
|
|
||||||
}): Error | { modifiedAst: Node<Program>; pathToNode: PathToNode } {
|
|
||||||
const noPercentSign = null
|
|
||||||
const call = createCallExpressionStdLibKw('translate', noPercentSign, [
|
|
||||||
createLabeledArg('x', x),
|
|
||||||
createLabeledArg('y', y),
|
|
||||||
createLabeledArg('z', z),
|
|
||||||
])
|
|
||||||
|
|
||||||
const potentialPipe = getNodeFromPath<PipeExpression>(
|
|
||||||
modifiedAst,
|
|
||||||
pathToNode,
|
|
||||||
['PipeExpression']
|
|
||||||
)
|
|
||||||
if (!err(potentialPipe) && potentialPipe.node.type === 'PipeExpression') {
|
|
||||||
setTransformInPipe(potentialPipe.node, call)
|
|
||||||
} else {
|
|
||||||
const error = createPipeWithTransform(modifiedAst, pathToNode, call)
|
|
||||||
if (err(error)) {
|
|
||||||
return error
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
modifiedAst,
|
|
||||||
pathToNode,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function setRotate({
|
|
||||||
modifiedAst,
|
|
||||||
pathToNode,
|
|
||||||
roll,
|
|
||||||
pitch,
|
|
||||||
yaw,
|
|
||||||
}: {
|
|
||||||
modifiedAst: Node<Program>
|
|
||||||
pathToNode: PathToNode
|
|
||||||
roll: Expr
|
|
||||||
pitch: Expr
|
|
||||||
yaw: Expr
|
|
||||||
}): Error | { modifiedAst: Node<Program>; pathToNode: PathToNode } {
|
|
||||||
const noPercentSign = null
|
|
||||||
const call = createCallExpressionStdLibKw('rotate', noPercentSign, [
|
|
||||||
createLabeledArg('roll', roll),
|
|
||||||
createLabeledArg('pitch', pitch),
|
|
||||||
createLabeledArg('yaw', yaw),
|
|
||||||
])
|
|
||||||
|
|
||||||
const potentialPipe = getNodeFromPath<PipeExpression>(
|
|
||||||
modifiedAst,
|
|
||||||
pathToNode,
|
|
||||||
['PipeExpression']
|
|
||||||
)
|
|
||||||
if (!err(potentialPipe) && potentialPipe.node.type === 'PipeExpression') {
|
|
||||||
setTransformInPipe(potentialPipe.node, call)
|
|
||||||
} else {
|
|
||||||
const error = createPipeWithTransform(modifiedAst, pathToNode, call)
|
|
||||||
if (err(error)) {
|
|
||||||
return error
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
modifiedAst,
|
|
||||||
pathToNode,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function setTransformInPipe(
|
|
||||||
expression: PipeExpression,
|
|
||||||
call: Node<CallExpressionKw>
|
|
||||||
) {
|
|
||||||
const existingIndex = expression.body.findIndex(
|
|
||||||
(v) =>
|
|
||||||
v.type === 'CallExpressionKw' &&
|
|
||||||
v.callee.type === 'Name' &&
|
|
||||||
v.callee.name.name === call.callee.name.name
|
|
||||||
)
|
|
||||||
if (existingIndex > -1) {
|
|
||||||
expression.body[existingIndex] = call
|
|
||||||
} else {
|
|
||||||
expression.body.push(call)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function createPipeWithTransform(
|
|
||||||
modifiedAst: Node<Program>,
|
|
||||||
pathToNode: PathToNode,
|
|
||||||
call: Node<CallExpressionKw>
|
|
||||||
) {
|
|
||||||
const existingCall = getNodeFromPath<
|
|
||||||
VariableDeclarator | ExpressionStatement
|
|
||||||
>(modifiedAst, pathToNode, ['VariableDeclarator', 'ExpressionStatement'])
|
|
||||||
if (err(existingCall)) {
|
|
||||||
return new Error('Unsupported operation type.')
|
|
||||||
}
|
|
||||||
|
|
||||||
if (existingCall.node.type === 'ExpressionStatement') {
|
|
||||||
existingCall.node.expression = createPipeExpression([
|
|
||||||
existingCall.node.expression,
|
|
||||||
call,
|
|
||||||
])
|
|
||||||
} else if (existingCall.node.type === 'VariableDeclarator') {
|
|
||||||
existingCall.node.init = createPipeExpression([
|
|
||||||
existingCall.node.init,
|
|
||||||
call,
|
|
||||||
])
|
|
||||||
} else {
|
|
||||||
return new Error('Unsupported operation type.')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function insertExpressionNode(ast: Node<Program>, alias: string) {
|
|
||||||
const expression = createExpressionStatement(createLocalName(alias))
|
|
||||||
ast.body.push(expression)
|
|
||||||
const pathToNode: PathToNode = [
|
|
||||||
['body', ''],
|
|
||||||
[ast.body.length - 1, 'index'],
|
|
||||||
['expression', 'Name'],
|
|
||||||
]
|
|
||||||
return pathToNode
|
|
||||||
}
|
|
||||||
|
|
||||||
export function retrievePathToNodeFromTransformSelection(
|
|
||||||
selection: Selections,
|
|
||||||
artifactGraph: ArtifactGraph,
|
|
||||||
ast: Node<Program>
|
|
||||||
): PathToNode | Error {
|
|
||||||
const error = new Error(
|
|
||||||
"Couldn't retrieve selection. If you're trying to transform an import, use the feature tree."
|
|
||||||
)
|
|
||||||
const hasPathToNode = !!selection.graphSelections[0].codeRef.pathToNode.length
|
|
||||||
const artifact = selection.graphSelections[0].artifact
|
|
||||||
let pathToNode: PathToNode | undefined
|
|
||||||
if (hasPathToNode && artifact) {
|
|
||||||
const children = findAllChildrenAndOrderByPlaceInCode(
|
|
||||||
artifact,
|
|
||||||
artifactGraph
|
|
||||||
)
|
|
||||||
const variable = getLastVariable(children, ast)
|
|
||||||
if (!variable) {
|
|
||||||
return error
|
|
||||||
}
|
|
||||||
|
|
||||||
pathToNode = variable.pathToNode
|
|
||||||
} else if (hasPathToNode) {
|
|
||||||
pathToNode = selection.graphSelections[0].codeRef.pathToNode
|
|
||||||
} else {
|
|
||||||
return error
|
|
||||||
}
|
|
||||||
|
|
||||||
return pathToNode
|
|
||||||
}
|
|
252
src/lang/modifyAst/sweeps.test.ts
Normal file
252
src/lang/modifyAst/sweeps.test.ts
Normal file
@ -0,0 +1,252 @@
|
|||||||
|
import {
|
||||||
|
type Artifact,
|
||||||
|
assertParse,
|
||||||
|
type CodeRef,
|
||||||
|
type Program,
|
||||||
|
recast,
|
||||||
|
} from '@src/lang/wasm'
|
||||||
|
import type { Selection, Selections } from '@src/lib/selections'
|
||||||
|
import { enginelessExecutor } from '@src/lib/testHelpers'
|
||||||
|
import { err } from '@src/lib/trap'
|
||||||
|
import {
|
||||||
|
addExtrude,
|
||||||
|
addLoft,
|
||||||
|
addRevolve,
|
||||||
|
addSweep,
|
||||||
|
} from '@src/lang/modifyAst/sweeps'
|
||||||
|
import { stringToKclExpression } from '@src/lib/kclHelpers'
|
||||||
|
import type { Node } from '@rust/kcl-lib/bindings/Node'
|
||||||
|
|
||||||
|
async function getAstAndArtifactGraph(code: string) {
|
||||||
|
const ast = assertParse(code)
|
||||||
|
if (err(ast)) throw ast
|
||||||
|
|
||||||
|
const { artifactGraph } = await enginelessExecutor(ast)
|
||||||
|
return { ast, artifactGraph }
|
||||||
|
}
|
||||||
|
|
||||||
|
function createSelectionFromPathArtifact(
|
||||||
|
artifacts: (Artifact & { codeRef: CodeRef })[]
|
||||||
|
): Selections {
|
||||||
|
const graphSelections = artifacts.map(
|
||||||
|
(artifact) =>
|
||||||
|
({
|
||||||
|
codeRef: artifact.codeRef,
|
||||||
|
artifact,
|
||||||
|
}) as Selection
|
||||||
|
)
|
||||||
|
return {
|
||||||
|
graphSelections,
|
||||||
|
otherSelections: [],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getAstAndSketchSelections(code: string) {
|
||||||
|
const { ast, artifactGraph } = await getAstAndArtifactGraph(code)
|
||||||
|
const artifacts = [...artifactGraph.values()].filter((a) => a.type === 'path')
|
||||||
|
if (artifacts.length === 0) {
|
||||||
|
throw new Error('Artifact not found in the graph')
|
||||||
|
}
|
||||||
|
const sketches = createSelectionFromPathArtifact(artifacts)
|
||||||
|
return { ast, sketches }
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getKclCommandValue(value: string) {
|
||||||
|
const result = await stringToKclExpression(value)
|
||||||
|
if (err(result) || 'errors' in result) {
|
||||||
|
throw new Error(`Couldn't create kcl expression`)
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
async function runNewAstAndCheckForSweep(ast: Node<Program>) {
|
||||||
|
const { artifactGraph } = await enginelessExecutor(ast)
|
||||||
|
console.log('artifactGraph', artifactGraph)
|
||||||
|
const sweepArtifact = artifactGraph.values().find((a) => a.type === 'sweep')
|
||||||
|
expect(sweepArtifact).toBeDefined()
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('Testing addExtrude', () => {
|
||||||
|
it('should push a call in pipe if selection was in variable-less pipe', async () => {
|
||||||
|
const code = `startSketchOn(XY)
|
||||||
|
|> circle(center = [0, 0], radius = 1)
|
||||||
|
`
|
||||||
|
const { ast, sketches } = await getAstAndSketchSelections(code)
|
||||||
|
const length = await getKclCommandValue('1')
|
||||||
|
const result = addExtrude({ ast, sketches, length })
|
||||||
|
if (err(result)) throw result
|
||||||
|
const newCode = recast(result.modifiedAst)
|
||||||
|
expect(newCode).toContain(code)
|
||||||
|
expect(newCode).toContain(`|> extrude(length = 1)`)
|
||||||
|
await runNewAstAndCheckForSweep(result.modifiedAst)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should push a call with variable if selection was in variable profile', async () => {
|
||||||
|
const code = `sketch001 = startSketchOn(XY)
|
||||||
|
profile001 = circle(sketch001, center = [0, 0], radius = 1)
|
||||||
|
`
|
||||||
|
const { ast, sketches } = await getAstAndSketchSelections(code)
|
||||||
|
const length = await getKclCommandValue('2')
|
||||||
|
const result = addExtrude({ ast, sketches, length })
|
||||||
|
if (err(result)) throw result
|
||||||
|
const newCode = recast(result.modifiedAst)
|
||||||
|
expect(newCode).toContain(code)
|
||||||
|
expect(newCode).toContain(`extrude001 = extrude(profile001, length = 2)`)
|
||||||
|
await runNewAstAndCheckForSweep(result.modifiedAst)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should push a call with variable if selection was in variable pipe', async () => {
|
||||||
|
const code = `profile001 = startSketchOn(XY)
|
||||||
|
|> circle(center = [0, 0], radius = 1)
|
||||||
|
`
|
||||||
|
const { ast, sketches } = await getAstAndSketchSelections(code)
|
||||||
|
const length = await getKclCommandValue('3')
|
||||||
|
const result = addExtrude({ ast, sketches, length })
|
||||||
|
if (err(result)) throw result
|
||||||
|
await runNewAstAndCheckForSweep(result.modifiedAst)
|
||||||
|
const newCode = recast(result.modifiedAst)
|
||||||
|
expect(newCode).toContain(code)
|
||||||
|
expect(newCode).toContain(`extrude001 = extrude(profile001, length = 3)`)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should push a call with many compatible optional args if asked', async () => {
|
||||||
|
const code = `sketch001 = startSketchOn(XY)
|
||||||
|
profile001 = circle(sketch001, center = [0, 0], radius = 1)
|
||||||
|
`
|
||||||
|
const { ast, sketches } = await getAstAndSketchSelections(code)
|
||||||
|
const length = await getKclCommandValue('10')
|
||||||
|
const bidirectionalLength = await getKclCommandValue('20')
|
||||||
|
const twistAngle = await getKclCommandValue('30')
|
||||||
|
const result = addExtrude({
|
||||||
|
ast,
|
||||||
|
sketches,
|
||||||
|
length,
|
||||||
|
bidirectionalLength,
|
||||||
|
twistAngle,
|
||||||
|
})
|
||||||
|
if (err(result)) throw result
|
||||||
|
await runNewAstAndCheckForSweep(result.modifiedAst)
|
||||||
|
const newCode = recast(result.modifiedAst)
|
||||||
|
expect(newCode).toContain(code)
|
||||||
|
expect(newCode).toContain(`extrude001 = extrude(
|
||||||
|
profile001,
|
||||||
|
length = 10,
|
||||||
|
bidirectionalLength = 20,
|
||||||
|
twistAngle = 30,
|
||||||
|
)`)
|
||||||
|
})
|
||||||
|
|
||||||
|
// TODO: missing edit flow test
|
||||||
|
|
||||||
|
// TODO: missing multi-profile test
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('Testing addSweep', () => {
|
||||||
|
it('should push a call with variable and all compatible optional args', async () => {
|
||||||
|
const code = `sketch001 = startSketchOn(XY)
|
||||||
|
profile001 = circle(sketch001, center = [0, 0], radius = 1)
|
||||||
|
sketch002 = startSketchOn(XZ)
|
||||||
|
profile002 = startProfile(sketch002, at = [0, 0])
|
||||||
|
|> xLine(length = -5)
|
||||||
|
|> tangentialArc(endAbsolute = [-20, 5])
|
||||||
|
`
|
||||||
|
const { ast, artifactGraph } = await getAstAndArtifactGraph(code)
|
||||||
|
const artifact1 = artifactGraph.values().find((a) => a.type === 'path')
|
||||||
|
const artifact2 = [...artifactGraph.values()].findLast(
|
||||||
|
(a) => a.type === 'path'
|
||||||
|
)
|
||||||
|
if (!artifact1 || !artifact2) {
|
||||||
|
throw new Error('Artifact not found in the graph')
|
||||||
|
}
|
||||||
|
|
||||||
|
const sketches = createSelectionFromPathArtifact([artifact1])
|
||||||
|
const path = createSelectionFromPathArtifact([artifact2])
|
||||||
|
const sectional = true
|
||||||
|
const relativeTo = 'sketchPlane'
|
||||||
|
const result = addSweep({
|
||||||
|
ast,
|
||||||
|
sketches,
|
||||||
|
path,
|
||||||
|
sectional,
|
||||||
|
relativeTo,
|
||||||
|
})
|
||||||
|
if (err(result)) throw result
|
||||||
|
await runNewAstAndCheckForSweep(result.modifiedAst)
|
||||||
|
const newCode = recast(result.modifiedAst)
|
||||||
|
expect(newCode).toContain(code)
|
||||||
|
expect(newCode).toContain(`sweep001 = sweep(
|
||||||
|
profile001,
|
||||||
|
path = profile002,
|
||||||
|
sectional = true,
|
||||||
|
relativeTo = 'sketchPlane',
|
||||||
|
)`)
|
||||||
|
})
|
||||||
|
|
||||||
|
// TODO: missing edit flow test
|
||||||
|
|
||||||
|
// TODO: missing multi-profile test
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('Testing addLoft', () => {
|
||||||
|
it('should push a call with variable and all optional args if asked', async () => {
|
||||||
|
const code = `sketch001 = startSketchOn(XZ)
|
||||||
|
profile001 = circle(sketch001, center = [0, 0], radius = 30)
|
||||||
|
plane001 = offsetPlane(XZ, offset = 50)
|
||||||
|
sketch002 = startSketchOn(plane001)
|
||||||
|
profile002 = circle(sketch002, center = [0, 0], radius = 20)
|
||||||
|
`
|
||||||
|
const { ast, sketches } = await getAstAndSketchSelections(code)
|
||||||
|
expect(sketches.graphSelections).toHaveLength(2)
|
||||||
|
const vDegree = await getKclCommandValue('3')
|
||||||
|
const result = addLoft({
|
||||||
|
ast,
|
||||||
|
sketches,
|
||||||
|
vDegree,
|
||||||
|
})
|
||||||
|
if (err(result)) throw result
|
||||||
|
const newCode = recast(result.modifiedAst)
|
||||||
|
expect(newCode).toContain(code)
|
||||||
|
expect(newCode).toContain(
|
||||||
|
`loft001 = loft([profile001, profile002], vDegree = 3)`
|
||||||
|
)
|
||||||
|
// Don't think we can find the artifact here for loft?
|
||||||
|
})
|
||||||
|
|
||||||
|
// TODO: missing edit flow test
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('Testing addRevolve', () => {
|
||||||
|
it('should push a call with variable and compatible optional args if asked', async () => {
|
||||||
|
const code = `sketch001 = startSketchOn(XZ)
|
||||||
|
profile001 = circle(sketch001, center = [3, 0], radius = 1)
|
||||||
|
`
|
||||||
|
const { ast, sketches } = await getAstAndSketchSelections(code)
|
||||||
|
expect(sketches.graphSelections).toHaveLength(1)
|
||||||
|
const result = addRevolve({
|
||||||
|
ast,
|
||||||
|
sketches,
|
||||||
|
angle: await getKclCommandValue('1'),
|
||||||
|
axisOrEdge: 'Axis',
|
||||||
|
axis: 'X',
|
||||||
|
edge: undefined,
|
||||||
|
symmetric: false,
|
||||||
|
bidirectionalAngle: await getKclCommandValue('2'),
|
||||||
|
})
|
||||||
|
if (err(result)) throw result
|
||||||
|
await runNewAstAndCheckForSweep(result.modifiedAst)
|
||||||
|
const newCode = recast(result.modifiedAst)
|
||||||
|
console.log(newCode)
|
||||||
|
expect(newCode).toContain(code)
|
||||||
|
expect(newCode).toContain(`revolve001 = revolve(
|
||||||
|
profile001,
|
||||||
|
angle = 1,
|
||||||
|
axis = X,
|
||||||
|
bidirectionalAngle = 2,
|
||||||
|
)`)
|
||||||
|
})
|
||||||
|
|
||||||
|
// TODO: missing edit flow test
|
||||||
|
|
||||||
|
// TODO: missing multi-profile test
|
||||||
|
})
|
@ -1,35 +1,28 @@
|
|||||||
import type { Node } from '@rust/kcl-lib/bindings/Node'
|
import type { Node } from '@rust/kcl-lib/bindings/Node'
|
||||||
|
|
||||||
import {
|
import {
|
||||||
createArrayExpression,
|
|
||||||
createCallExpressionStdLibKw,
|
createCallExpressionStdLibKw,
|
||||||
createLabeledArg,
|
createLabeledArg,
|
||||||
createLiteral,
|
createLiteral,
|
||||||
createLocalName,
|
createLocalName,
|
||||||
createVariableDeclaration,
|
|
||||||
findUniqueName,
|
|
||||||
} from '@src/lang/create'
|
} from '@src/lang/create'
|
||||||
import { insertVariableAndOffsetPathToNode } from '@src/lang/modifyAst'
|
import {
|
||||||
|
createVariableExpressionsArray,
|
||||||
|
insertVariableAndOffsetPathToNode,
|
||||||
|
setCallInAst,
|
||||||
|
} from '@src/lang/modifyAst'
|
||||||
import {
|
import {
|
||||||
getEdgeTagCall,
|
getEdgeTagCall,
|
||||||
mutateAstWithTagForSketchSegment,
|
mutateAstWithTagForSketchSegment,
|
||||||
} from '@src/lang/modifyAst/addEdgeTreatment'
|
} from '@src/lang/modifyAst/addEdgeTreatment'
|
||||||
import {
|
import {
|
||||||
getNodeFromPath,
|
getNodeFromPath,
|
||||||
getSketchExprsFromSelection,
|
getVariableExprsFromSelection,
|
||||||
valueOrVariable,
|
valueOrVariable,
|
||||||
} from '@src/lang/queryAst'
|
} from '@src/lang/queryAst'
|
||||||
import { ARG_INDEX_FIELD, LABELED_ARG_FIELD } from '@src/lang/queryAstConstants'
|
|
||||||
import { getNodePathFromSourceRange } from '@src/lang/queryAstNodePathUtils'
|
import { getNodePathFromSourceRange } from '@src/lang/queryAstNodePathUtils'
|
||||||
import type {
|
import type { PathToNode, Program, VariableDeclaration } from '@src/lang/wasm'
|
||||||
CallExpressionKw,
|
|
||||||
Expr,
|
|
||||||
PathToNode,
|
|
||||||
Program,
|
|
||||||
VariableDeclaration,
|
|
||||||
} from '@src/lang/wasm'
|
|
||||||
import type { KclCommandValue } from '@src/lib/commandTypes'
|
import type { KclCommandValue } from '@src/lib/commandTypes'
|
||||||
import { KCL_DEFAULT_CONSTANT_PREFIXES } from '@src/lib/constants'
|
|
||||||
import type { Selections } from '@src/lib/selections'
|
import type { Selections } from '@src/lib/selections'
|
||||||
import { err } from '@src/lib/trap'
|
import { err } from '@src/lib/trap'
|
||||||
|
|
||||||
@ -60,13 +53,13 @@ export function addExtrude({
|
|||||||
|
|
||||||
// 2. Prepare unlabeled and labeled arguments
|
// 2. Prepare unlabeled and labeled arguments
|
||||||
// Map the sketches selection into a list of kcl expressions to be passed as unlabelled argument
|
// Map the sketches selection into a list of kcl expressions to be passed as unlabelled argument
|
||||||
const sketchesExprList = getSketchExprsFromSelection(
|
const variableExpressions = getVariableExprsFromSelection(
|
||||||
sketches,
|
sketches,
|
||||||
modifiedAst,
|
modifiedAst,
|
||||||
nodeToEdit
|
nodeToEdit
|
||||||
)
|
)
|
||||||
if (err(sketchesExprList)) {
|
if (err(variableExpressions)) {
|
||||||
return sketchesExprList
|
return variableExpressions
|
||||||
}
|
}
|
||||||
|
|
||||||
// Extra labeled args expressions
|
// Extra labeled args expressions
|
||||||
@ -85,7 +78,7 @@ export function addExtrude({
|
|||||||
? [createLabeledArg('twistAngle', valueOrVariable(twistAngle))]
|
? [createLabeledArg('twistAngle', valueOrVariable(twistAngle))]
|
||||||
: []
|
: []
|
||||||
|
|
||||||
const sketchesExpr = createSketchExpression(sketchesExprList)
|
const sketchesExpr = createVariableExpressionsArray(variableExpressions.exprs)
|
||||||
const call = createCallExpressionStdLibKw('extrude', sketchesExpr, [
|
const call = createCallExpressionStdLibKw('extrude', sketchesExpr, [
|
||||||
createLabeledArg('length', valueOrVariable(length)),
|
createLabeledArg('length', valueOrVariable(length)),
|
||||||
...symmetricExpr,
|
...symmetricExpr,
|
||||||
@ -114,27 +107,10 @@ export function addExtrude({
|
|||||||
|
|
||||||
// 3. If edit, we assign the new function call declaration to the existing node,
|
// 3. If edit, we assign the new function call declaration to the existing node,
|
||||||
// otherwise just push to the end
|
// otherwise just push to the end
|
||||||
let pathToNode: PathToNode | undefined
|
const lastPath = variableExpressions.paths.pop() // TODO: check if this is correct
|
||||||
if (nodeToEdit) {
|
const pathToNode = setCallInAst(modifiedAst, call, nodeToEdit, lastPath)
|
||||||
const result = getNodeFromPath<CallExpressionKw>(
|
if (err(pathToNode)) {
|
||||||
modifiedAst,
|
return pathToNode
|
||||||
nodeToEdit,
|
|
||||||
'CallExpressionKw'
|
|
||||||
)
|
|
||||||
if (err(result)) {
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
Object.assign(result.node, call)
|
|
||||||
pathToNode = nodeToEdit
|
|
||||||
} else {
|
|
||||||
const name = findUniqueName(
|
|
||||||
modifiedAst,
|
|
||||||
KCL_DEFAULT_CONSTANT_PREFIXES.EXTRUDE
|
|
||||||
)
|
|
||||||
const declaration = createVariableDeclaration(name, call)
|
|
||||||
modifiedAst.body.push(declaration)
|
|
||||||
pathToNode = createPathToNode(modifiedAst)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@ -168,13 +144,13 @@ export function addSweep({
|
|||||||
|
|
||||||
// 2. Prepare unlabeled and labeled arguments
|
// 2. Prepare unlabeled and labeled arguments
|
||||||
// Map the sketches selection into a list of kcl expressions to be passed as unlabelled argument
|
// Map the sketches selection into a list of kcl expressions to be passed as unlabelled argument
|
||||||
const sketchesExprList = getSketchExprsFromSelection(
|
const variableExprs = getVariableExprsFromSelection(
|
||||||
sketches,
|
sketches,
|
||||||
modifiedAst,
|
modifiedAst,
|
||||||
nodeToEdit
|
nodeToEdit
|
||||||
)
|
)
|
||||||
if (err(sketchesExprList)) {
|
if (err(variableExprs)) {
|
||||||
return sketchesExprList
|
return variableExprs
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find the path declaration for the labeled argument
|
// Find the path declaration for the labeled argument
|
||||||
@ -196,7 +172,7 @@ export function addSweep({
|
|||||||
? [createLabeledArg('relativeTo', createLiteral(relativeTo))]
|
? [createLabeledArg('relativeTo', createLiteral(relativeTo))]
|
||||||
: []
|
: []
|
||||||
|
|
||||||
const sketchesExpr = createSketchExpression(sketchesExprList)
|
const sketchesExpr = createVariableExpressionsArray(variableExprs.exprs)
|
||||||
const call = createCallExpressionStdLibKw('sweep', sketchesExpr, [
|
const call = createCallExpressionStdLibKw('sweep', sketchesExpr, [
|
||||||
createLabeledArg('path', pathExpr),
|
createLabeledArg('path', pathExpr),
|
||||||
...sectionalExpr,
|
...sectionalExpr,
|
||||||
@ -205,27 +181,10 @@ export function addSweep({
|
|||||||
|
|
||||||
// 3. If edit, we assign the new function call declaration to the existing node,
|
// 3. If edit, we assign the new function call declaration to the existing node,
|
||||||
// otherwise just push to the end
|
// otherwise just push to the end
|
||||||
let pathToNode: PathToNode | undefined
|
const lastPath = variableExprs.paths.pop() // TODO: check if this is correct
|
||||||
if (nodeToEdit) {
|
const pathToNode = setCallInAst(modifiedAst, call, nodeToEdit, lastPath)
|
||||||
const result = getNodeFromPath<CallExpressionKw>(
|
if (err(pathToNode)) {
|
||||||
modifiedAst,
|
return pathToNode
|
||||||
nodeToEdit,
|
|
||||||
'CallExpressionKw'
|
|
||||||
)
|
|
||||||
if (err(result)) {
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
Object.assign(result.node, call)
|
|
||||||
pathToNode = nodeToEdit
|
|
||||||
} else {
|
|
||||||
const name = findUniqueName(
|
|
||||||
modifiedAst,
|
|
||||||
KCL_DEFAULT_CONSTANT_PREFIXES.SWEEP
|
|
||||||
)
|
|
||||||
const declaration = createVariableDeclaration(name, call)
|
|
||||||
modifiedAst.body.push(declaration)
|
|
||||||
pathToNode = createPathToNode(modifiedAst)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@ -255,13 +214,13 @@ export function addLoft({
|
|||||||
|
|
||||||
// 2. Prepare unlabeled and labeled arguments
|
// 2. Prepare unlabeled and labeled arguments
|
||||||
// Map the sketches selection into a list of kcl expressions to be passed as unlabelled argument
|
// Map the sketches selection into a list of kcl expressions to be passed as unlabelled argument
|
||||||
const sketchesExprList = getSketchExprsFromSelection(
|
const variableExprs = getVariableExprsFromSelection(
|
||||||
sketches,
|
sketches,
|
||||||
modifiedAst,
|
modifiedAst,
|
||||||
nodeToEdit
|
nodeToEdit
|
||||||
)
|
)
|
||||||
if (err(sketchesExprList)) {
|
if (err(variableExprs)) {
|
||||||
return sketchesExprList
|
return variableExprs
|
||||||
}
|
}
|
||||||
|
|
||||||
// Extra labeled args expressions
|
// Extra labeled args expressions
|
||||||
@ -269,7 +228,7 @@ export function addLoft({
|
|||||||
? [createLabeledArg('vDegree', valueOrVariable(vDegree))]
|
? [createLabeledArg('vDegree', valueOrVariable(vDegree))]
|
||||||
: []
|
: []
|
||||||
|
|
||||||
const sketchesExpr = createSketchExpression(sketchesExprList)
|
const sketchesExpr = createVariableExpressionsArray(variableExprs.exprs)
|
||||||
const call = createCallExpressionStdLibKw('loft', sketchesExpr, [
|
const call = createCallExpressionStdLibKw('loft', sketchesExpr, [
|
||||||
...vDegreeExpr,
|
...vDegreeExpr,
|
||||||
])
|
])
|
||||||
@ -281,25 +240,10 @@ export function addLoft({
|
|||||||
|
|
||||||
// 3. If edit, we assign the new function call declaration to the existing node,
|
// 3. If edit, we assign the new function call declaration to the existing node,
|
||||||
// otherwise just push to the end
|
// otherwise just push to the end
|
||||||
let pathToNode: PathToNode | undefined
|
const lastPath = variableExprs.paths.pop() // TODO: check if this is correct
|
||||||
if (nodeToEdit) {
|
const pathToNode = setCallInAst(modifiedAst, call, nodeToEdit, lastPath)
|
||||||
const result = getNodeFromPath<CallExpressionKw>(
|
if (err(pathToNode)) {
|
||||||
modifiedAst,
|
return pathToNode
|
||||||
nodeToEdit,
|
|
||||||
'CallExpressionKw'
|
|
||||||
)
|
|
||||||
if (err(result)) {
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
Object.assign(result.node, call)
|
|
||||||
pathToNode = nodeToEdit
|
|
||||||
} else {
|
|
||||||
const name = findUniqueName(modifiedAst, KCL_DEFAULT_CONSTANT_PREFIXES.LOFT)
|
|
||||||
const declaration = createVariableDeclaration(name, call)
|
|
||||||
modifiedAst.body.push(declaration)
|
|
||||||
const toFirstKwarg = !!vDegree
|
|
||||||
pathToNode = createPathToNode(modifiedAst, toFirstKwarg)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@ -339,13 +283,13 @@ export function addRevolve({
|
|||||||
|
|
||||||
// 2. Prepare unlabeled and labeled arguments
|
// 2. Prepare unlabeled and labeled arguments
|
||||||
// Map the sketches selection into a list of kcl expressions to be passed as unlabelled argument
|
// Map the sketches selection into a list of kcl expressions to be passed as unlabelled argument
|
||||||
const sketchesExprList = getSketchExprsFromSelection(
|
const variableExprs = getVariableExprsFromSelection(
|
||||||
sketches,
|
sketches,
|
||||||
modifiedAst,
|
modifiedAst,
|
||||||
nodeToEdit
|
nodeToEdit
|
||||||
)
|
)
|
||||||
if (err(sketchesExprList)) {
|
if (err(variableExprs)) {
|
||||||
return sketchesExprList
|
return variableExprs
|
||||||
}
|
}
|
||||||
|
|
||||||
// Retrieve axis expression depending on mode
|
// Retrieve axis expression depending on mode
|
||||||
@ -372,7 +316,7 @@ export function addRevolve({
|
|||||||
]
|
]
|
||||||
: []
|
: []
|
||||||
|
|
||||||
const sketchesExpr = createSketchExpression(sketchesExprList)
|
const sketchesExpr = createVariableExpressionsArray(variableExprs.exprs)
|
||||||
const call = createCallExpressionStdLibKw('revolve', sketchesExpr, [
|
const call = createCallExpressionStdLibKw('revolve', sketchesExpr, [
|
||||||
createLabeledArg('angle', valueOrVariable(angle)),
|
createLabeledArg('angle', valueOrVariable(angle)),
|
||||||
createLabeledArg('axis', getAxisResult.generatedAxis),
|
createLabeledArg('axis', getAxisResult.generatedAxis),
|
||||||
@ -399,27 +343,10 @@ export function addRevolve({
|
|||||||
|
|
||||||
// 3. If edit, we assign the new function call declaration to the existing node,
|
// 3. If edit, we assign the new function call declaration to the existing node,
|
||||||
// otherwise just push to the end
|
// otherwise just push to the end
|
||||||
let pathToNode: PathToNode | undefined
|
const lastPath = variableExprs.paths.pop() // TODO: check if this is correct
|
||||||
if (nodeToEdit) {
|
const pathToNode = setCallInAst(modifiedAst, call, nodeToEdit, lastPath)
|
||||||
const result = getNodeFromPath<CallExpressionKw>(
|
if (err(pathToNode)) {
|
||||||
modifiedAst,
|
return pathToNode
|
||||||
nodeToEdit,
|
|
||||||
'CallExpressionKw'
|
|
||||||
)
|
|
||||||
if (err(result)) {
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
Object.assign(result.node, call)
|
|
||||||
pathToNode = nodeToEdit
|
|
||||||
} else {
|
|
||||||
const name = findUniqueName(
|
|
||||||
modifiedAst,
|
|
||||||
KCL_DEFAULT_CONSTANT_PREFIXES.REVOLVE
|
|
||||||
)
|
|
||||||
const declaration = createVariableDeclaration(name, call)
|
|
||||||
modifiedAst.body.push(declaration)
|
|
||||||
pathToNode = createPathToNode(modifiedAst)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@ -430,40 +357,6 @@ export function addRevolve({
|
|||||||
|
|
||||||
// Utilities
|
// Utilities
|
||||||
|
|
||||||
function createSketchExpression(sketches: Expr[]) {
|
|
||||||
let sketchesExpr: Expr | null = null
|
|
||||||
if (sketches.every((s) => s.type === 'PipeSubstitution')) {
|
|
||||||
// Keeping null so we don't even put it the % sign
|
|
||||||
} else if (sketches.length === 1) {
|
|
||||||
sketchesExpr = sketches[0]
|
|
||||||
} else {
|
|
||||||
sketchesExpr = createArrayExpression(sketches)
|
|
||||||
}
|
|
||||||
return sketchesExpr
|
|
||||||
}
|
|
||||||
|
|
||||||
function createPathToNode(
|
|
||||||
modifiedAst: Node<Program>,
|
|
||||||
toFirstKwarg = true
|
|
||||||
): PathToNode {
|
|
||||||
const argIndex = 0 // first kwarg for all sweeps here
|
|
||||||
const pathToCall: PathToNode = [
|
|
||||||
['body', ''],
|
|
||||||
[modifiedAst.body.length - 1, 'index'],
|
|
||||||
['declaration', 'VariableDeclaration'],
|
|
||||||
['init', 'VariableDeclarator'],
|
|
||||||
]
|
|
||||||
if (toFirstKwarg) {
|
|
||||||
pathToCall.push(
|
|
||||||
['arguments', 'CallExpressionKw'],
|
|
||||||
[argIndex, ARG_INDEX_FIELD],
|
|
||||||
['arg', LABELED_ARG_FIELD]
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
return pathToCall
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getAxisExpressionAndIndex(
|
export function getAxisExpressionAndIndex(
|
||||||
axisOrEdge: 'Axis' | 'Edge',
|
axisOrEdge: 'Axis' | 'Edge',
|
||||||
axis: string | undefined,
|
axis: string | undefined,
|
@ -4,16 +4,15 @@ import { initPromise } from '@src/lang/wasmUtils'
|
|||||||
import { err } from '@src/lib/trap'
|
import { err } from '@src/lib/trap'
|
||||||
import type { Selection } from '@src/lib/selections'
|
import type { Selection } from '@src/lib/selections'
|
||||||
import { engineCommandManager, kclManager } from '@src/lib/singletons'
|
import { engineCommandManager, kclManager } from '@src/lib/singletons'
|
||||||
import { VITE_KC_DEV_TOKEN } from '@src/env'
|
import { VITE_KITTYCAD_API_TOKEN } from '@src/env'
|
||||||
import { modifyAstWithTagsForSelection } from '@src/lang/modifyAst/tagManagement'
|
import { modifyAstWithTagsForSelection } from '@src/lang/modifyAst/tagManagement'
|
||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
await initPromise
|
await initPromise
|
||||||
|
|
||||||
// THESE TEST WILL FAIL without VITE_KC_DEV_TOKEN set in .env.development.local
|
|
||||||
await new Promise((resolve) => {
|
await new Promise((resolve) => {
|
||||||
engineCommandManager.start({
|
engineCommandManager.start({
|
||||||
token: VITE_KC_DEV_TOKEN,
|
token: VITE_KITTYCAD_API_TOKEN,
|
||||||
width: 256,
|
width: 256,
|
||||||
height: 256,
|
height: 256,
|
||||||
setMediaStream: () => {},
|
setMediaStream: () => {},
|
||||||
|
127
src/lang/modifyAst/transforms.test.ts
Normal file
127
src/lang/modifyAst/transforms.test.ts
Normal file
@ -0,0 +1,127 @@
|
|||||||
|
import {
|
||||||
|
type Artifact,
|
||||||
|
assertParse,
|
||||||
|
type CodeRef,
|
||||||
|
type Program,
|
||||||
|
recast,
|
||||||
|
} from '@src/lang/wasm'
|
||||||
|
import type { Selection, Selections } from '@src/lib/selections'
|
||||||
|
import { enginelessExecutor } from '@src/lib/testHelpers'
|
||||||
|
import { err } from '@src/lib/trap'
|
||||||
|
import { stringToKclExpression } from '@src/lib/kclHelpers'
|
||||||
|
import type { Node } from '@rust/kcl-lib/bindings/Node'
|
||||||
|
import { addTranslate } from '@src/lang/modifyAst/transforms'
|
||||||
|
|
||||||
|
async function getAstAndArtifactGraph(code: string) {
|
||||||
|
const ast = assertParse(code)
|
||||||
|
if (err(ast)) throw ast
|
||||||
|
|
||||||
|
const { artifactGraph } = await enginelessExecutor(ast)
|
||||||
|
return { ast, artifactGraph }
|
||||||
|
}
|
||||||
|
|
||||||
|
function createSelectionFromPathArtifact(
|
||||||
|
artifacts: (Artifact & { codeRef: CodeRef })[]
|
||||||
|
): Selections {
|
||||||
|
const graphSelections = artifacts.map(
|
||||||
|
(artifact) =>
|
||||||
|
({
|
||||||
|
codeRef: artifact.codeRef,
|
||||||
|
artifact,
|
||||||
|
}) as Selection
|
||||||
|
)
|
||||||
|
return {
|
||||||
|
graphSelections,
|
||||||
|
otherSelections: [],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getAstAndSketchSelections(code: string) {
|
||||||
|
const { ast, artifactGraph } = await getAstAndArtifactGraph(code)
|
||||||
|
const artifacts = [...artifactGraph.values()].filter((a) => a.type === 'path')
|
||||||
|
if (artifacts.length === 0) {
|
||||||
|
throw new Error('Artifact not found in the graph')
|
||||||
|
}
|
||||||
|
const sketches = createSelectionFromPathArtifact(artifacts)
|
||||||
|
return { artifactGraph, ast, sketches }
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getKclCommandValue(value: string) {
|
||||||
|
const result = await stringToKclExpression(value)
|
||||||
|
if (err(result) || 'errors' in result) {
|
||||||
|
throw new Error(`Couldn't create kcl expression`)
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
async function runNewAstAndCheckForSweep(ast: Node<Program>) {
|
||||||
|
const { artifactGraph } = await enginelessExecutor(ast)
|
||||||
|
console.log('artifactGraph', artifactGraph)
|
||||||
|
const sweepArtifact = artifactGraph.values().find((a) => a.type === 'sweep')
|
||||||
|
expect(sweepArtifact).toBeDefined()
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('Testing addTranslate', () => {
|
||||||
|
it('should push a call with variable if selection was a variable sweep', async () => {
|
||||||
|
const code = `sketch001 = startSketchOn(XY)
|
||||||
|
profile001 = circle(sketch001, center = [0, 0], radius = 1)
|
||||||
|
extrude001 = extrude(profile001, length = 1)
|
||||||
|
`
|
||||||
|
const {
|
||||||
|
artifactGraph,
|
||||||
|
ast,
|
||||||
|
sketches: objects,
|
||||||
|
} = await getAstAndSketchSelections(code)
|
||||||
|
const result = addTranslate({
|
||||||
|
ast,
|
||||||
|
artifactGraph,
|
||||||
|
objects,
|
||||||
|
x: await getKclCommandValue('1'),
|
||||||
|
y: await getKclCommandValue('2'),
|
||||||
|
z: await getKclCommandValue('3'),
|
||||||
|
global: true,
|
||||||
|
})
|
||||||
|
if (err(result)) throw result
|
||||||
|
const newCode = recast(result.modifiedAst)
|
||||||
|
expect(newCode).toContain(code)
|
||||||
|
// TODO: this should be extrude001, I don't understand why
|
||||||
|
expect(newCode).toContain(`translate001 = translate(
|
||||||
|
profile001,
|
||||||
|
x = 1,
|
||||||
|
y = 2,
|
||||||
|
z = 3,
|
||||||
|
global = true,
|
||||||
|
)`)
|
||||||
|
await runNewAstAndCheckForSweep(result.modifiedAst)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should push a call in pipe if selection was in variable-less pipe', async () => {
|
||||||
|
const code = `startSketchOn(XY)
|
||||||
|
|> circle(center = [0, 0], radius = 1)
|
||||||
|
|> extrude(length = 1)
|
||||||
|
`
|
||||||
|
const {
|
||||||
|
artifactGraph,
|
||||||
|
ast,
|
||||||
|
sketches: objects,
|
||||||
|
} = await getAstAndSketchSelections(code)
|
||||||
|
const result = addTranslate({
|
||||||
|
ast,
|
||||||
|
artifactGraph,
|
||||||
|
objects,
|
||||||
|
x: await getKclCommandValue('1'),
|
||||||
|
y: await getKclCommandValue('2'),
|
||||||
|
z: await getKclCommandValue('3'),
|
||||||
|
})
|
||||||
|
if (err(result)) throw result
|
||||||
|
const newCode = recast(result.modifiedAst)
|
||||||
|
expect(newCode).toContain(code)
|
||||||
|
expect(newCode).toContain(`|> translate(x = 1, y = 2, z = 3)`)
|
||||||
|
await runNewAstAndCheckForSweep(result.modifiedAst)
|
||||||
|
})
|
||||||
|
|
||||||
|
// TODO: missing edit flow test
|
||||||
|
|
||||||
|
// TODO: missing multi-objects test
|
||||||
|
})
|
259
src/lang/modifyAst/transforms.ts
Normal file
259
src/lang/modifyAst/transforms.ts
Normal file
@ -0,0 +1,259 @@
|
|||||||
|
import type { Node } from '@rust/kcl-lib/bindings/Node'
|
||||||
|
|
||||||
|
import {
|
||||||
|
createCallExpressionStdLibKw,
|
||||||
|
createLabeledArg,
|
||||||
|
createLiteral,
|
||||||
|
} from '@src/lang/create'
|
||||||
|
import {
|
||||||
|
getVariableExprsFromSelection,
|
||||||
|
valueOrVariable,
|
||||||
|
} from '@src/lang/queryAst'
|
||||||
|
import type { ArtifactGraph, PathToNode, Program } from '@src/lang/wasm'
|
||||||
|
import { err } from '@src/lib/trap'
|
||||||
|
import type { Selections } from '@src/lib/selections'
|
||||||
|
import type { KclCommandValue } from '@src/lib/commandTypes'
|
||||||
|
import {
|
||||||
|
createVariableExpressionsArray,
|
||||||
|
insertVariableAndOffsetPathToNode,
|
||||||
|
setCallInAst,
|
||||||
|
} from '@src/lang/modifyAst'
|
||||||
|
|
||||||
|
export function addTranslate({
|
||||||
|
ast,
|
||||||
|
artifactGraph,
|
||||||
|
objects,
|
||||||
|
x,
|
||||||
|
y,
|
||||||
|
z,
|
||||||
|
global,
|
||||||
|
nodeToEdit,
|
||||||
|
callName,
|
||||||
|
}: {
|
||||||
|
ast: Node<Program>
|
||||||
|
artifactGraph: ArtifactGraph
|
||||||
|
objects: Selections
|
||||||
|
x?: KclCommandValue
|
||||||
|
y?: KclCommandValue
|
||||||
|
z?: KclCommandValue
|
||||||
|
global?: boolean
|
||||||
|
nodeToEdit?: PathToNode
|
||||||
|
callName?: string
|
||||||
|
}): Error | { modifiedAst: Node<Program>; pathToNode: PathToNode } {
|
||||||
|
// 1. Clone the ast so we can edit it
|
||||||
|
const modifiedAst = structuredClone(ast)
|
||||||
|
|
||||||
|
// 2. Prepare unlabeled and labeled arguments
|
||||||
|
// Map the sketches selection into a list of kcl expressions to be passed as unlabelled argument
|
||||||
|
const lastChildLookup = true
|
||||||
|
const variableExpressions = getVariableExprsFromSelection(
|
||||||
|
objects,
|
||||||
|
modifiedAst,
|
||||||
|
nodeToEdit,
|
||||||
|
lastChildLookup,
|
||||||
|
artifactGraph
|
||||||
|
)
|
||||||
|
if (err(variableExpressions)) {
|
||||||
|
return variableExpressions
|
||||||
|
}
|
||||||
|
|
||||||
|
const xExpr = x ? [createLabeledArg('x', valueOrVariable(x))] : []
|
||||||
|
const yExpr = y ? [createLabeledArg('y', valueOrVariable(y))] : []
|
||||||
|
const zExpr = z ? [createLabeledArg('z', valueOrVariable(z))] : []
|
||||||
|
const globalExpr = global
|
||||||
|
? [createLabeledArg('global', createLiteral(global))]
|
||||||
|
: []
|
||||||
|
|
||||||
|
const objectsExpr = createVariableExpressionsArray(variableExpressions.exprs)
|
||||||
|
const call = createCallExpressionStdLibKw(
|
||||||
|
callName ?? 'translate',
|
||||||
|
objectsExpr,
|
||||||
|
[...xExpr, ...yExpr, ...zExpr, ...globalExpr]
|
||||||
|
)
|
||||||
|
|
||||||
|
// Insert variables for labeled arguments if provided
|
||||||
|
if (x && 'variableName' in x && x.variableName) {
|
||||||
|
insertVariableAndOffsetPathToNode(x, modifiedAst, nodeToEdit)
|
||||||
|
}
|
||||||
|
if (y && 'variableName' in y && y.variableName) {
|
||||||
|
insertVariableAndOffsetPathToNode(y, modifiedAst, nodeToEdit)
|
||||||
|
}
|
||||||
|
if (z && 'variableName' in z && z.variableName) {
|
||||||
|
insertVariableAndOffsetPathToNode(z, modifiedAst, nodeToEdit)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. If edit, we assign the new function call declaration to the existing node,
|
||||||
|
// otherwise just push to the end
|
||||||
|
const lastPath = variableExpressions.paths.pop() // TODO: check if this is correct
|
||||||
|
const pathToNode = setCallInAst(modifiedAst, call, nodeToEdit, lastPath)
|
||||||
|
if (err(pathToNode)) {
|
||||||
|
return pathToNode
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
modifiedAst,
|
||||||
|
pathToNode,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function addRotate({
|
||||||
|
ast,
|
||||||
|
artifactGraph,
|
||||||
|
objects,
|
||||||
|
roll,
|
||||||
|
pitch,
|
||||||
|
yaw,
|
||||||
|
global,
|
||||||
|
nodeToEdit,
|
||||||
|
}: {
|
||||||
|
ast: Node<Program>
|
||||||
|
artifactGraph: ArtifactGraph
|
||||||
|
objects: Selections
|
||||||
|
roll?: KclCommandValue
|
||||||
|
pitch?: KclCommandValue
|
||||||
|
yaw?: KclCommandValue
|
||||||
|
global?: boolean
|
||||||
|
nodeToEdit?: PathToNode
|
||||||
|
}): Error | { modifiedAst: Node<Program>; pathToNode: PathToNode } {
|
||||||
|
// 1. Clone the ast so we can edit it
|
||||||
|
const modifiedAst = structuredClone(ast)
|
||||||
|
|
||||||
|
// 2. Prepare unlabeled and labeled arguments
|
||||||
|
// Map the sketches selection into a list of kcl expressions to be passed as unlabelled argument
|
||||||
|
const lastChildLookup = true
|
||||||
|
const variableExpressions = getVariableExprsFromSelection(
|
||||||
|
objects,
|
||||||
|
modifiedAst,
|
||||||
|
nodeToEdit,
|
||||||
|
lastChildLookup,
|
||||||
|
artifactGraph
|
||||||
|
)
|
||||||
|
if (err(variableExpressions)) {
|
||||||
|
return variableExpressions
|
||||||
|
}
|
||||||
|
|
||||||
|
const rollExpr = roll ? [createLabeledArg('roll', valueOrVariable(roll))] : []
|
||||||
|
const pitchExpr = pitch
|
||||||
|
? [createLabeledArg('pitch', valueOrVariable(pitch))]
|
||||||
|
: []
|
||||||
|
const yawExpr = yaw ? [createLabeledArg('yaw', valueOrVariable(yaw))] : []
|
||||||
|
const globalExpr = global
|
||||||
|
? [createLabeledArg('global', createLiteral(global))]
|
||||||
|
: []
|
||||||
|
|
||||||
|
const objectsExpr = createVariableExpressionsArray(variableExpressions.exprs)
|
||||||
|
const call = createCallExpressionStdLibKw('rotate', objectsExpr, [
|
||||||
|
...rollExpr,
|
||||||
|
...pitchExpr,
|
||||||
|
...yawExpr,
|
||||||
|
...globalExpr,
|
||||||
|
])
|
||||||
|
|
||||||
|
// Insert variables for labeled arguments if provided
|
||||||
|
if (roll && 'variableName' in roll && roll.variableName) {
|
||||||
|
insertVariableAndOffsetPathToNode(roll, modifiedAst, nodeToEdit)
|
||||||
|
}
|
||||||
|
if (pitch && 'variableName' in pitch && pitch.variableName) {
|
||||||
|
insertVariableAndOffsetPathToNode(pitch, modifiedAst, nodeToEdit)
|
||||||
|
}
|
||||||
|
if (yaw && 'variableName' in yaw && yaw.variableName) {
|
||||||
|
insertVariableAndOffsetPathToNode(yaw, modifiedAst, nodeToEdit)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. If edit, we assign the new function call declaration to the existing node,
|
||||||
|
// otherwise just push to the end
|
||||||
|
const lastPath = variableExpressions.paths.pop() // TODO: check if this is correct
|
||||||
|
const pathToNode = setCallInAst(modifiedAst, call, nodeToEdit, lastPath)
|
||||||
|
if (err(pathToNode)) {
|
||||||
|
return pathToNode
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
modifiedAst,
|
||||||
|
pathToNode,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function addScale({
|
||||||
|
ast,
|
||||||
|
artifactGraph,
|
||||||
|
objects,
|
||||||
|
x,
|
||||||
|
y,
|
||||||
|
z,
|
||||||
|
global,
|
||||||
|
nodeToEdit,
|
||||||
|
}: {
|
||||||
|
ast: Node<Program>
|
||||||
|
artifactGraph: ArtifactGraph
|
||||||
|
objects: Selections
|
||||||
|
x?: KclCommandValue
|
||||||
|
y?: KclCommandValue
|
||||||
|
z?: KclCommandValue
|
||||||
|
global?: boolean
|
||||||
|
nodeToEdit?: PathToNode
|
||||||
|
}): Error | { modifiedAst: Node<Program>; pathToNode: PathToNode } {
|
||||||
|
return addTranslate({
|
||||||
|
ast,
|
||||||
|
artifactGraph,
|
||||||
|
objects,
|
||||||
|
x,
|
||||||
|
y,
|
||||||
|
z,
|
||||||
|
global,
|
||||||
|
nodeToEdit,
|
||||||
|
callName: 'scale',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function addClone({
|
||||||
|
ast,
|
||||||
|
artifactGraph,
|
||||||
|
objects,
|
||||||
|
nodeToEdit,
|
||||||
|
}: {
|
||||||
|
ast: Node<Program>
|
||||||
|
artifactGraph: ArtifactGraph
|
||||||
|
objects: Selections
|
||||||
|
nodeToEdit?: PathToNode
|
||||||
|
}): Error | { modifiedAst: Node<Program>; pathToNode: PathToNode } {
|
||||||
|
// 1. Clone the ast so we can edit it
|
||||||
|
const modifiedAst = structuredClone(ast)
|
||||||
|
|
||||||
|
// 2. Prepare unlabeled arguments
|
||||||
|
// Map the sketches selection into a list of kcl expressions to be passed as unlabelled argument
|
||||||
|
const lastChildLookup = true
|
||||||
|
const variableExpressions = getVariableExprsFromSelection(
|
||||||
|
objects,
|
||||||
|
modifiedAst,
|
||||||
|
nodeToEdit,
|
||||||
|
lastChildLookup,
|
||||||
|
artifactGraph
|
||||||
|
)
|
||||||
|
if (err(variableExpressions)) {
|
||||||
|
return variableExpressions
|
||||||
|
}
|
||||||
|
|
||||||
|
const objectsExpr = createVariableExpressionsArray(variableExpressions.exprs)
|
||||||
|
const call = createCallExpressionStdLibKw('clone', objectsExpr, [])
|
||||||
|
|
||||||
|
// 3. If edit, we assign the new function call declaration to the existing node,
|
||||||
|
// otherwise just push to the end
|
||||||
|
const lastPath = variableExpressions.paths.pop() // TODO: check if this is correct
|
||||||
|
const toFirstKwarg = false
|
||||||
|
const pathToNode = setCallInAst(
|
||||||
|
modifiedAst,
|
||||||
|
call,
|
||||||
|
nodeToEdit,
|
||||||
|
lastPath,
|
||||||
|
toFirstKwarg
|
||||||
|
)
|
||||||
|
if (err(pathToNode)) {
|
||||||
|
return pathToNode
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
modifiedAst,
|
||||||
|
pathToNode,
|
||||||
|
}
|
||||||
|
}
|
@ -15,6 +15,7 @@ import {
|
|||||||
findAllPreviousVariables,
|
findAllPreviousVariables,
|
||||||
findUsesOfTagInPipe,
|
findUsesOfTagInPipe,
|
||||||
getNodeFromPath,
|
getNodeFromPath,
|
||||||
|
getVariableExprsFromSelection,
|
||||||
hasSketchPipeBeenExtruded,
|
hasSketchPipeBeenExtruded,
|
||||||
isCursorInFunctionDefinition,
|
isCursorInFunctionDefinition,
|
||||||
isNodeSafeToReplace,
|
isNodeSafeToReplace,
|
||||||
@ -27,7 +28,7 @@ import { topLevelRange } from '@src/lang/util'
|
|||||||
import type { Identifier, PathToNode } from '@src/lang/wasm'
|
import type { Identifier, PathToNode } from '@src/lang/wasm'
|
||||||
import { assertParse, recast } from '@src/lang/wasm'
|
import { assertParse, recast } from '@src/lang/wasm'
|
||||||
import { initPromise } from '@src/lang/wasmUtils'
|
import { initPromise } from '@src/lang/wasmUtils'
|
||||||
import { type Selection } from '@src/lib/selections'
|
import type { Selections, Selection } from '@src/lib/selections'
|
||||||
import { enginelessExecutor } from '@src/lib/testHelpers'
|
import { enginelessExecutor } from '@src/lib/testHelpers'
|
||||||
import { err } from '@src/lib/trap'
|
import { err } from '@src/lib/trap'
|
||||||
|
|
||||||
@ -778,3 +779,184 @@ describe('Testing specific sketch getNodeFromPath workflow', () => {
|
|||||||
expect(result).toEqual(false)
|
expect(result).toEqual(false)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe('Testing getVariableExprsFromSelection', () => {
|
||||||
|
it('should find the variable expr in a simple profile selection', async () => {
|
||||||
|
const circleProfileInVar = `sketch001 = startSketchOn(XY)
|
||||||
|
profile001 = circle(sketch001, center = [0, 0], radius = 1)
|
||||||
|
`
|
||||||
|
const ast = assertParse(circleProfileInVar)
|
||||||
|
const { artifactGraph } = await enginelessExecutor(ast)
|
||||||
|
const artifact = artifactGraph.values().find((a) => a.type === 'path')
|
||||||
|
if (!artifact) {
|
||||||
|
throw new Error('Artifact not found in the graph')
|
||||||
|
}
|
||||||
|
const selections: Selections = {
|
||||||
|
graphSelections: [
|
||||||
|
{
|
||||||
|
codeRef: artifact.codeRef,
|
||||||
|
artifact,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
otherSelections: [],
|
||||||
|
}
|
||||||
|
const variableExprs = getVariableExprsFromSelection(selections, ast)
|
||||||
|
if (err(variableExprs)) throw variableExprs
|
||||||
|
|
||||||
|
expect(variableExprs.exprs).toHaveLength(1)
|
||||||
|
if (variableExprs.exprs[0].type !== 'Name') {
|
||||||
|
throw new Error(`Expected Name, got ${variableExprs.exprs[0].type}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(variableExprs.exprs[0].name.name).toEqual('profile001')
|
||||||
|
|
||||||
|
expect(variableExprs.paths).toHaveLength(1)
|
||||||
|
expect(variableExprs.paths[0]).toEqual([
|
||||||
|
['body', ''],
|
||||||
|
[1, 'index'],
|
||||||
|
['declaration', 'VariableDeclaration'],
|
||||||
|
['init', ''],
|
||||||
|
])
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should return the pipe substitution symbol in a variable-less simple profile selection', async () => {
|
||||||
|
const circleProfileInVar = `startSketchOn(XY)
|
||||||
|
|> circle(center = [0, 0], radius = 1)
|
||||||
|
`
|
||||||
|
const ast = assertParse(circleProfileInVar)
|
||||||
|
const { artifactGraph } = await enginelessExecutor(ast)
|
||||||
|
const artifact = artifactGraph.values().find((a) => a.type === 'path')
|
||||||
|
if (!artifact) {
|
||||||
|
throw new Error('Artifact not found in the graph')
|
||||||
|
}
|
||||||
|
const selections: Selections = {
|
||||||
|
graphSelections: [
|
||||||
|
{
|
||||||
|
codeRef: artifact.codeRef,
|
||||||
|
artifact,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
otherSelections: [],
|
||||||
|
}
|
||||||
|
const variableExprs = getVariableExprsFromSelection(selections, ast)
|
||||||
|
if (err(variableExprs)) throw variableExprs
|
||||||
|
|
||||||
|
expect(variableExprs.exprs).toHaveLength(1)
|
||||||
|
expect(variableExprs.exprs[0].type).toEqual('PipeSubstitution')
|
||||||
|
|
||||||
|
expect(variableExprs.paths).toHaveLength(1)
|
||||||
|
expect(variableExprs.paths[0]).toEqual([
|
||||||
|
['body', ''],
|
||||||
|
[0, 'index'],
|
||||||
|
['expression', 'ExpressionStatement'],
|
||||||
|
['body', 'PipeExpression'],
|
||||||
|
[1, 'index'],
|
||||||
|
])
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should find the variable exprs in a multi profile selection ', async () => {
|
||||||
|
const circleProfileInVar = `sketch001 = startSketchOn(XY)
|
||||||
|
profile001 = circle(sketch001, center = [0, 0], radius = 1)
|
||||||
|
profile002 = circle(sketch001, center = [2, 2], radius = 1)
|
||||||
|
`
|
||||||
|
const ast = assertParse(circleProfileInVar)
|
||||||
|
const { artifactGraph } = await enginelessExecutor(ast)
|
||||||
|
const artifacts = [...artifactGraph.values()].filter(
|
||||||
|
(a) => a.type === 'path'
|
||||||
|
)
|
||||||
|
if (!artifacts || artifacts.length !== 2) {
|
||||||
|
throw new Error('Artifact not found in the graph')
|
||||||
|
}
|
||||||
|
const selections: Selections = {
|
||||||
|
graphSelections: artifacts.map((artifact) => {
|
||||||
|
return {
|
||||||
|
codeRef: artifact.codeRef,
|
||||||
|
artifact,
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
otherSelections: [],
|
||||||
|
}
|
||||||
|
const variableExprs = getVariableExprsFromSelection(selections, ast)
|
||||||
|
if (err(variableExprs)) throw variableExprs
|
||||||
|
|
||||||
|
expect(variableExprs.exprs).toHaveLength(2)
|
||||||
|
if (variableExprs.exprs[0].type !== 'Name') {
|
||||||
|
throw new Error(`Expected Name, got ${variableExprs.exprs[0].type}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (variableExprs.exprs[1].type !== 'Name') {
|
||||||
|
throw new Error(`Expected Name, got ${variableExprs.exprs[1].type}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(variableExprs.exprs[0].name.name).toEqual('profile001')
|
||||||
|
expect(variableExprs.exprs[1].name.name).toEqual('profile002')
|
||||||
|
|
||||||
|
expect(variableExprs.paths).toHaveLength(2)
|
||||||
|
expect(variableExprs.paths[0]).toEqual([
|
||||||
|
['body', ''],
|
||||||
|
[1, 'index'],
|
||||||
|
['declaration', 'VariableDeclaration'],
|
||||||
|
['init', ''],
|
||||||
|
])
|
||||||
|
expect(variableExprs.paths[1]).toEqual([
|
||||||
|
['body', ''],
|
||||||
|
[2, 'index'],
|
||||||
|
['declaration', 'VariableDeclaration'],
|
||||||
|
['init', ''],
|
||||||
|
])
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should return the pipe substitution symbol and a variable name in a complex multi profile selection', async () => {
|
||||||
|
const circleProfileInVar = `startSketchOn(XY)
|
||||||
|
|> circle(center = [0, 0], radius = 1)
|
||||||
|
profile002 = circle(sketch001, center = [2, 2], radius = 1)
|
||||||
|
`
|
||||||
|
const ast = assertParse(circleProfileInVar)
|
||||||
|
const { artifactGraph } = await enginelessExecutor(ast)
|
||||||
|
const artifacts = [...artifactGraph.values()].filter(
|
||||||
|
(a) => a.type === 'path'
|
||||||
|
)
|
||||||
|
if (!artifacts || artifacts.length !== 2) {
|
||||||
|
throw new Error('Artifact not found in the graph')
|
||||||
|
}
|
||||||
|
const selections: Selections = {
|
||||||
|
graphSelections: artifacts.map((artifact) => {
|
||||||
|
return {
|
||||||
|
codeRef: artifact.codeRef,
|
||||||
|
artifact,
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
otherSelections: [],
|
||||||
|
}
|
||||||
|
const variableExprs = getVariableExprsFromSelection(selections, ast)
|
||||||
|
if (err(variableExprs)) throw variableExprs
|
||||||
|
|
||||||
|
expect(variableExprs.exprs).toHaveLength(2)
|
||||||
|
if (variableExprs.exprs[0].type !== 'PipeSubstitution') {
|
||||||
|
throw new Error(
|
||||||
|
`Expected PipeSubstitution, got ${variableExprs.exprs[0].type}`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (variableExprs.exprs[1].type !== 'Name') {
|
||||||
|
throw new Error(`Expected Name, got ${variableExprs.exprs[1].type}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(variableExprs.exprs[1].name.name).toEqual('profile002')
|
||||||
|
|
||||||
|
expect(variableExprs.paths).toHaveLength(2)
|
||||||
|
expect(variableExprs.paths[0]).toEqual([
|
||||||
|
['body', ''],
|
||||||
|
[0, 'index'],
|
||||||
|
['expression', 'ExpressionStatement'],
|
||||||
|
['body', 'PipeExpression'],
|
||||||
|
[1, 'index'],
|
||||||
|
])
|
||||||
|
expect(variableExprs.paths[1]).toEqual([
|
||||||
|
['body', ''],
|
||||||
|
[1, 'index'],
|
||||||
|
['declaration', 'VariableDeclaration'],
|
||||||
|
['init', ''],
|
||||||
|
])
|
||||||
|
})
|
||||||
|
})
|
||||||
|
@ -9,6 +9,7 @@ import { getNodePathFromSourceRange } from '@src/lang/queryAstNodePathUtils'
|
|||||||
import {
|
import {
|
||||||
codeRefFromRange,
|
codeRefFromRange,
|
||||||
getArtifactOfTypes,
|
getArtifactOfTypes,
|
||||||
|
getCodeRefsByArtifactId,
|
||||||
} from '@src/lang/std/artifactGraph'
|
} from '@src/lang/std/artifactGraph'
|
||||||
import { getArgForEnd } from '@src/lang/std/sketch'
|
import { getArgForEnd } from '@src/lang/std/sketch'
|
||||||
import { getSketchSegmentFromSourceRange } from '@src/lang/std/sketchConstraints'
|
import { getSketchSegmentFromSourceRange } from '@src/lang/std/sketchConstraints'
|
||||||
@ -56,6 +57,10 @@ import { ARG_INDEX_FIELD, LABELED_ARG_FIELD } from '@src/lang/queryAstConstants'
|
|||||||
import type { KclCommandValue } from '@src/lib/commandTypes'
|
import type { KclCommandValue } from '@src/lib/commandTypes'
|
||||||
import type { UnaryExpression } from 'typescript'
|
import type { UnaryExpression } from 'typescript'
|
||||||
import type { NumericType } from '@rust/kcl-lib/bindings/NumericType'
|
import type { NumericType } from '@rust/kcl-lib/bindings/NumericType'
|
||||||
|
import {
|
||||||
|
findAllChildrenAndOrderByPlaceInCode,
|
||||||
|
getLastVariable,
|
||||||
|
} from '@src/lang/modifyAst/boolean'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves a node from a given path within a Program node structure, optionally stopping at a specified node type.
|
* Retrieves a node from a given path within a Program node structure, optionally stopping at a specified node type.
|
||||||
@ -1042,51 +1047,91 @@ export const valueOrVariable = (variable: KclCommandValue) => {
|
|||||||
|
|
||||||
// Go from a selection of sketches to a list of KCL expressions that
|
// Go from a selection of sketches to a list of KCL expressions that
|
||||||
// can be used to create KCL sweep call declarations.
|
// can be used to create KCL sweep call declarations.
|
||||||
export function getSketchExprsFromSelection(
|
export function getVariableExprsFromSelection(
|
||||||
selection: Selections,
|
selection: Selections,
|
||||||
ast: Node<Program>,
|
ast: Node<Program>,
|
||||||
nodeToEdit?: PathToNode
|
nodeToEdit?: PathToNode,
|
||||||
): Error | Expr[] {
|
lastChildLookup = false,
|
||||||
const sketches: Expr[] = selection.graphSelections.flatMap((s) => {
|
artifactGraph?: ArtifactGraph
|
||||||
const sketchVariable = getNodeFromPath<VariableDeclarator>(
|
): Error | { exprs: Expr[]; paths: PathToNode[] } {
|
||||||
ast,
|
const paths: PathToNode[] = []
|
||||||
s?.codeRef.pathToNode,
|
const exprs: Expr[] = []
|
||||||
'VariableDeclarator'
|
for (const s of selection.graphSelections) {
|
||||||
)
|
let variable:
|
||||||
if (err(sketchVariable)) {
|
| {
|
||||||
return []
|
node: VariableDeclaration
|
||||||
|
shallowPath: PathToNode
|
||||||
|
deepPath: PathToNode
|
||||||
|
}
|
||||||
|
| undefined
|
||||||
|
if (lastChildLookup && s.artifact && artifactGraph) {
|
||||||
|
console.log('we are looking for last child variable', s.artifact)
|
||||||
|
console.log('artifactGraph', artifactGraph)
|
||||||
|
const children = findAllChildrenAndOrderByPlaceInCode(
|
||||||
|
s.artifact,
|
||||||
|
artifactGraph
|
||||||
|
)
|
||||||
|
console.log('found children', children)
|
||||||
|
const lastChildVariable = getLastVariable(children, ast)
|
||||||
|
if (!lastChildVariable) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
variable = lastChildVariable.variableDeclaration
|
||||||
|
} else {
|
||||||
|
const directLookup = getNodeFromPath<VariableDeclaration>(
|
||||||
|
ast,
|
||||||
|
s.codeRef.pathToNode,
|
||||||
|
'VariableDeclaration'
|
||||||
|
)
|
||||||
|
if (err(directLookup)) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
variable = directLookup
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sketchVariable.node.id) {
|
if (variable.node.declaration?.id) {
|
||||||
const name = sketchVariable.node?.id.name
|
const name = variable.node.declaration.id.name
|
||||||
if (nodeToEdit) {
|
if (nodeToEdit) {
|
||||||
const result = getNodeFromPath<VariableDeclarator>(
|
const result = getNodeFromPath<VariableDeclaration>(
|
||||||
ast,
|
ast,
|
||||||
nodeToEdit,
|
nodeToEdit,
|
||||||
'VariableDeclarator'
|
'VariableDeclaration'
|
||||||
)
|
)
|
||||||
if (
|
if (
|
||||||
!err(result) &&
|
!err(result) &&
|
||||||
result.node.type === 'VariableDeclarator' &&
|
result.node.type === 'VariableDeclaration' &&
|
||||||
name === result.node.id.name
|
name === result.node.declaration.id.name
|
||||||
) {
|
) {
|
||||||
// Pointing to same variable case
|
// Pointing to same variable case
|
||||||
return createPipeSubstitution()
|
paths.push(nodeToEdit)
|
||||||
|
exprs.push(createPipeSubstitution())
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Pointing to different variable case
|
// Pointing to different variable case
|
||||||
return createLocalName(name)
|
paths.push(variable.deepPath)
|
||||||
} else {
|
exprs.push(createLocalName(name))
|
||||||
// No variable case
|
continue
|
||||||
return createPipeSubstitution()
|
|
||||||
}
|
}
|
||||||
})
|
|
||||||
|
|
||||||
if (sketches.length === 0) {
|
// import case
|
||||||
|
const importNodeAndAlias = findImportNodeAndAlias(ast, s.codeRef.pathToNode)
|
||||||
|
if (importNodeAndAlias) {
|
||||||
|
paths.push(s.codeRef.pathToNode)
|
||||||
|
exprs.push(createLocalName(importNodeAndAlias.alias))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// No variable case
|
||||||
|
paths.push(variable.deepPath)
|
||||||
|
exprs.push(createPipeSubstitution())
|
||||||
|
}
|
||||||
|
|
||||||
|
if (exprs.length === 0) {
|
||||||
return new Error("Couldn't map selections to program references")
|
return new Error("Couldn't map selections to program references")
|
||||||
}
|
}
|
||||||
|
|
||||||
return sketches
|
return { exprs, paths }
|
||||||
}
|
}
|
||||||
|
|
||||||
// Go from the sketches argument in a KCL sweep call declaration
|
// Go from the sketches argument in a KCL sweep call declaration
|
||||||
@ -1158,6 +1203,61 @@ export function getSketchSelectionsFromOperation(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getObjectSelectionsFromOperation(
|
||||||
|
operation: Operation,
|
||||||
|
artifactGraph: ArtifactGraph
|
||||||
|
): Error | Selections {
|
||||||
|
const error = new Error("Couldn't retrieve sketches from operation")
|
||||||
|
if (operation.type !== 'StdLibCall' || !operation.unlabeledArg) {
|
||||||
|
return error
|
||||||
|
}
|
||||||
|
|
||||||
|
let artifactIds: string[] = []
|
||||||
|
if (
|
||||||
|
operation.unlabeledArg.value.type === 'Solid' ||
|
||||||
|
operation.unlabeledArg.value.type === 'Sketch'
|
||||||
|
) {
|
||||||
|
artifactIds = [operation.unlabeledArg.value.value.artifactId]
|
||||||
|
} else if (operation.unlabeledArg.value.type === 'ImportedGeometry') {
|
||||||
|
artifactIds = [operation.unlabeledArg.value.artifact_id]
|
||||||
|
} else if (operation.unlabeledArg.value.type === 'Array') {
|
||||||
|
artifactIds = operation.unlabeledArg.value.value
|
||||||
|
.filter((v) => v.type === 'Solid' || v.type === 'Sketch')
|
||||||
|
.map((v) => v.value.artifactId)
|
||||||
|
} else {
|
||||||
|
return error
|
||||||
|
}
|
||||||
|
|
||||||
|
const graphSelections: Selection[] = []
|
||||||
|
for (const artifactId of artifactIds) {
|
||||||
|
const artifact = artifactGraph.get(artifactId)
|
||||||
|
if (!artifact) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
const codeRefs = getCodeRefsByArtifactId(artifactId, artifactGraph)
|
||||||
|
if (!codeRefs || codeRefs.length === 0) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
graphSelections.push({
|
||||||
|
artifact,
|
||||||
|
codeRef: codeRefs[0], // TODO: figure out why two codeRefs could be possible?
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if (graphSelections.length === 0) {
|
||||||
|
return error
|
||||||
|
}
|
||||||
|
|
||||||
|
const selections: Selections = {
|
||||||
|
graphSelections,
|
||||||
|
otherSelections: [],
|
||||||
|
}
|
||||||
|
console.log('massaged selections', selections)
|
||||||
|
return selections
|
||||||
|
}
|
||||||
|
|
||||||
export function locateVariableWithCallOrPipe(
|
export function locateVariableWithCallOrPipe(
|
||||||
ast: Program,
|
ast: Program,
|
||||||
pathToNode: PathToNode
|
pathToNode: PathToNode
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import type { Models } from '@kittycad/lib'
|
import type { Models } from '@kittycad/lib'
|
||||||
import { VITE_KC_API_WS_MODELING_URL, VITE_KC_DEV_TOKEN } from '@src/env'
|
import { VITE_KC_API_WS_MODELING_URL, VITE_KITTYCAD_API_TOKEN } from '@src/env'
|
||||||
import { jsAppSettings } from '@src/lib/settings/settingsUtils'
|
import { jsAppSettings } from '@src/lib/settings/settingsUtils'
|
||||||
import { BSON } from 'bson'
|
import { BSON } from 'bson'
|
||||||
|
|
||||||
@ -400,7 +400,7 @@ class EngineConnection extends EventTarget {
|
|||||||
this.send({
|
this.send({
|
||||||
type: 'headers',
|
type: 'headers',
|
||||||
headers: {
|
headers: {
|
||||||
Authorization: `Bearer ${VITE_KC_DEV_TOKEN}`,
|
Authorization: `Bearer ${VITE_KITTYCAD_API_TOKEN}`,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
import type { Models } from '@kittycad/lib'
|
import type { Models } from '@kittycad/lib'
|
||||||
|
|
||||||
import { angleLengthInfo } from '@src/components/Toolbar/angleLengthInfo'
|
import { angleLengthInfo } from '@src/components/Toolbar/angleLengthInfo'
|
||||||
import { findUniqueName } from '@src/lang/create'
|
|
||||||
import { getNodeFromPath } from '@src/lang/queryAst'
|
import { getNodeFromPath } from '@src/lang/queryAst'
|
||||||
import { getVariableDeclaration } from '@src/lang/queryAst/getVariableDeclaration'
|
import { getVariableDeclaration } from '@src/lang/queryAst/getVariableDeclaration'
|
||||||
import { getNodePathFromSourceRange } from '@src/lang/queryAstNodePathUtils'
|
import { getNodePathFromSourceRange } from '@src/lang/queryAstNodePathUtils'
|
||||||
@ -19,7 +18,6 @@ import type {
|
|||||||
} from '@src/lib/commandTypes'
|
} from '@src/lib/commandTypes'
|
||||||
import {
|
import {
|
||||||
IS_ML_EXPERIMENTAL,
|
IS_ML_EXPERIMENTAL,
|
||||||
KCL_DEFAULT_CONSTANT_PREFIXES,
|
|
||||||
KCL_DEFAULT_DEGREE,
|
KCL_DEFAULT_DEGREE,
|
||||||
KCL_DEFAULT_LENGTH,
|
KCL_DEFAULT_LENGTH,
|
||||||
KCL_DEFAULT_TRANSFORM,
|
KCL_DEFAULT_TRANSFORM,
|
||||||
@ -186,22 +184,31 @@ export type ModelingCommandSchema = {
|
|||||||
}
|
}
|
||||||
Translate: {
|
Translate: {
|
||||||
nodeToEdit?: PathToNode
|
nodeToEdit?: PathToNode
|
||||||
selection: Selections
|
objects: Selections
|
||||||
x: KclCommandValue
|
x?: KclCommandValue
|
||||||
y: KclCommandValue
|
y?: KclCommandValue
|
||||||
z: KclCommandValue
|
z?: KclCommandValue
|
||||||
|
global?: boolean
|
||||||
}
|
}
|
||||||
Rotate: {
|
Rotate: {
|
||||||
nodeToEdit?: PathToNode
|
nodeToEdit?: PathToNode
|
||||||
selection: Selections
|
objects: Selections
|
||||||
roll: KclCommandValue
|
roll?: KclCommandValue
|
||||||
pitch: KclCommandValue
|
pitch?: KclCommandValue
|
||||||
yaw: KclCommandValue
|
yaw?: KclCommandValue
|
||||||
|
global?: boolean
|
||||||
|
}
|
||||||
|
Scale: {
|
||||||
|
nodeToEdit?: PathToNode
|
||||||
|
objects: Selections
|
||||||
|
x?: KclCommandValue
|
||||||
|
y?: KclCommandValue
|
||||||
|
z?: KclCommandValue
|
||||||
|
global?: boolean
|
||||||
}
|
}
|
||||||
Clone: {
|
Clone: {
|
||||||
nodeToEdit?: PathToNode
|
nodeToEdit?: PathToNode
|
||||||
selection: Selections
|
objects: Selections
|
||||||
variableName: string
|
|
||||||
}
|
}
|
||||||
'Boolean Subtract': {
|
'Boolean Subtract': {
|
||||||
solids: Selections
|
solids: Selections
|
||||||
@ -750,14 +757,8 @@ export const modelingMachineCommandConfig: StateMachineCommandSetConfig<
|
|||||||
required: false,
|
required: false,
|
||||||
displayName: 'CounterClockWise',
|
displayName: 'CounterClockWise',
|
||||||
options: [
|
options: [
|
||||||
{
|
{ name: 'False', value: false },
|
||||||
name: 'False',
|
{ name: 'True', value: true },
|
||||||
value: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'True',
|
|
||||||
value: true,
|
|
||||||
},
|
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -1079,14 +1080,13 @@ export const modelingMachineCommandConfig: StateMachineCommandSetConfig<
|
|||||||
nodeToEdit: {
|
nodeToEdit: {
|
||||||
...nodeToEditProps,
|
...nodeToEditProps,
|
||||||
},
|
},
|
||||||
selection: {
|
objects: {
|
||||||
// selectionMixed allows for feature tree selection of module imports
|
// selectionMixed allows for feature tree selection of module imports
|
||||||
inputType: 'selectionMixed',
|
inputType: 'selectionMixed',
|
||||||
multiple: false,
|
selectionTypes: ['path', 'sweep'],
|
||||||
required: true,
|
|
||||||
skip: true,
|
|
||||||
selectionTypes: ['path'],
|
|
||||||
selectionFilter: ['object'],
|
selectionFilter: ['object'],
|
||||||
|
multiple: true,
|
||||||
|
required: true,
|
||||||
hidden: (context) => Boolean(context.argumentsToSubmit.nodeToEdit),
|
hidden: (context) => Boolean(context.argumentsToSubmit.nodeToEdit),
|
||||||
},
|
},
|
||||||
x: {
|
x: {
|
||||||
@ -1104,6 +1104,14 @@ export const modelingMachineCommandConfig: StateMachineCommandSetConfig<
|
|||||||
defaultValue: KCL_DEFAULT_TRANSFORM,
|
defaultValue: KCL_DEFAULT_TRANSFORM,
|
||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
|
global: {
|
||||||
|
inputType: 'options',
|
||||||
|
required: false,
|
||||||
|
options: [
|
||||||
|
{ name: 'False', value: false },
|
||||||
|
{ name: 'True', value: true },
|
||||||
|
],
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Rotate: {
|
Rotate: {
|
||||||
@ -1114,7 +1122,7 @@ export const modelingMachineCommandConfig: StateMachineCommandSetConfig<
|
|||||||
nodeToEdit: {
|
nodeToEdit: {
|
||||||
...nodeToEditProps,
|
...nodeToEditProps,
|
||||||
},
|
},
|
||||||
selection: {
|
objects: {
|
||||||
// selectionMixed allows for feature tree selection of module imports
|
// selectionMixed allows for feature tree selection of module imports
|
||||||
inputType: 'selectionMixed',
|
inputType: 'selectionMixed',
|
||||||
multiple: false,
|
multiple: false,
|
||||||
@ -1139,6 +1147,57 @@ export const modelingMachineCommandConfig: StateMachineCommandSetConfig<
|
|||||||
defaultValue: KCL_DEFAULT_TRANSFORM,
|
defaultValue: KCL_DEFAULT_TRANSFORM,
|
||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
|
global: {
|
||||||
|
inputType: 'options',
|
||||||
|
required: false,
|
||||||
|
options: [
|
||||||
|
{ name: 'False', value: false },
|
||||||
|
{ name: 'True', value: true },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Scale: {
|
||||||
|
description: 'Set scale on solid or sketch.',
|
||||||
|
icon: 'scale',
|
||||||
|
needsReview: true,
|
||||||
|
args: {
|
||||||
|
nodeToEdit: {
|
||||||
|
...nodeToEditProps,
|
||||||
|
},
|
||||||
|
objects: {
|
||||||
|
// selectionMixed allows for feature tree selection of module imports
|
||||||
|
inputType: 'selectionMixed',
|
||||||
|
multiple: false,
|
||||||
|
required: true,
|
||||||
|
skip: true,
|
||||||
|
selectionTypes: ['path'],
|
||||||
|
selectionFilter: ['object'],
|
||||||
|
hidden: (context) => Boolean(context.argumentsToSubmit.nodeToEdit),
|
||||||
|
},
|
||||||
|
x: {
|
||||||
|
inputType: 'kcl',
|
||||||
|
defaultValue: KCL_DEFAULT_TRANSFORM,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
y: {
|
||||||
|
inputType: 'kcl',
|
||||||
|
defaultValue: KCL_DEFAULT_TRANSFORM,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
z: {
|
||||||
|
inputType: 'kcl',
|
||||||
|
defaultValue: KCL_DEFAULT_TRANSFORM,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
global: {
|
||||||
|
inputType: 'options',
|
||||||
|
required: false,
|
||||||
|
options: [
|
||||||
|
{ name: 'False', value: false },
|
||||||
|
{ name: 'True', value: true },
|
||||||
|
],
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Clone: {
|
Clone: {
|
||||||
@ -1149,39 +1208,14 @@ export const modelingMachineCommandConfig: StateMachineCommandSetConfig<
|
|||||||
nodeToEdit: {
|
nodeToEdit: {
|
||||||
...nodeToEditProps,
|
...nodeToEditProps,
|
||||||
},
|
},
|
||||||
selection: {
|
objects: {
|
||||||
// selectionMixed allows for feature tree selection of module imports
|
// selectionMixed allows for feature tree selection of module imports
|
||||||
inputType: 'selectionMixed',
|
inputType: 'selectionMixed',
|
||||||
multiple: false,
|
selectionTypes: ['path', 'sweep'],
|
||||||
required: true,
|
|
||||||
skip: true,
|
|
||||||
selectionTypes: ['path'],
|
|
||||||
selectionFilter: ['object'],
|
selectionFilter: ['object'],
|
||||||
hidden: (context) => Boolean(context.argumentsToSubmit.nodeToEdit),
|
multiple: true,
|
||||||
},
|
|
||||||
variableName: {
|
|
||||||
inputType: 'string',
|
|
||||||
required: true,
|
required: true,
|
||||||
defaultValue: () => {
|
hidden: (context) => Boolean(context.argumentsToSubmit.nodeToEdit),
|
||||||
return findUniqueName(
|
|
||||||
kclManager.ast,
|
|
||||||
KCL_DEFAULT_CONSTANT_PREFIXES.CLONE
|
|
||||||
)
|
|
||||||
},
|
|
||||||
validation: async ({
|
|
||||||
data,
|
|
||||||
}: {
|
|
||||||
data: string
|
|
||||||
}) => {
|
|
||||||
// Be conservative and error out if there is an item or module with the same name.
|
|
||||||
const variableExists =
|
|
||||||
kclManager.variables[data] || kclManager.variables['__mod_' + data]
|
|
||||||
if (variableExists) {
|
|
||||||
return 'This variable name is already in use.'
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
import { VITE_KC_API_BASE_URL } from '@src/env'
|
|
||||||
import { UAParser } from 'ua-parser-js'
|
import { UAParser } from 'ua-parser-js'
|
||||||
|
|
||||||
import type { OsInfo } from '@rust/kcl-lib/bindings/OsInfo'
|
import type { OsInfo } from '@rust/kcl-lib/bindings/OsInfo'
|
||||||
@ -11,6 +10,7 @@ import { isDesktop } from '@src/lib/isDesktop'
|
|||||||
import type RustContext from '@src/lib/rustContext'
|
import type RustContext from '@src/lib/rustContext'
|
||||||
import screenshot from '@src/lib/screenshot'
|
import screenshot from '@src/lib/screenshot'
|
||||||
import { APP_VERSION } from '@src/routes/utils'
|
import { APP_VERSION } from '@src/routes/utils'
|
||||||
|
import { withAPIBaseURL } from '@src/lib/withBaseURL'
|
||||||
|
|
||||||
/* eslint-disable suggest-no-throw/suggest-no-throw --
|
/* eslint-disable suggest-no-throw/suggest-no-throw --
|
||||||
* All the throws in CoreDumpManager are intentional and should be caught and handled properly
|
* All the throws in CoreDumpManager are intentional and should be caught and handled properly
|
||||||
@ -35,7 +35,7 @@ export class CoreDumpManager {
|
|||||||
codeManager: CodeManager
|
codeManager: CodeManager
|
||||||
rustContext: RustContext
|
rustContext: RustContext
|
||||||
token: string | undefined
|
token: string | undefined
|
||||||
baseUrl: string = VITE_KC_API_BASE_URL
|
baseUrl: string = withAPIBaseURL('')
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
engineCommandManager: EngineCommandManager,
|
engineCommandManager: EngineCommandManager,
|
||||||
|
@ -26,6 +26,7 @@ import { err } from '@src/lib/trap'
|
|||||||
import type { DeepPartial } from '@src/lib/types'
|
import type { DeepPartial } from '@src/lib/types'
|
||||||
import { getInVariableCase } from '@src/lib/utils'
|
import { getInVariableCase } from '@src/lib/utils'
|
||||||
import { IS_STAGING } from '@src/routes/utils'
|
import { IS_STAGING } from '@src/routes/utils'
|
||||||
|
import { withAPIBaseURL } from '@src/lib/withBaseURL'
|
||||||
|
|
||||||
export async function renameProjectDirectory(
|
export async function renameProjectDirectory(
|
||||||
projectPath: string,
|
projectPath: string,
|
||||||
@ -697,7 +698,9 @@ export const readTokenFile = async () => {
|
|||||||
export const writeTokenFile = async (token: string) => {
|
export const writeTokenFile = async (token: string) => {
|
||||||
const tokenFilePath = await getTokenFilePath()
|
const tokenFilePath = await getTokenFilePath()
|
||||||
if (err(token)) return Promise.reject(token)
|
if (err(token)) return Promise.reject(token)
|
||||||
return window.electron.writeFile(tokenFilePath, token)
|
const result = window.electron.writeFile(tokenFilePath, token)
|
||||||
|
console.log('token written to disk')
|
||||||
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
export const writeTelemetryFile = async (content: string) => {
|
export const writeTelemetryFile = async (content: string) => {
|
||||||
@ -722,12 +725,9 @@ export const setState = async (state: Project | undefined): Promise<void> => {
|
|||||||
appStateStore = state
|
appStateStore = state
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getUser = async (
|
export const getUser = async (token: string): Promise<Models['User_type']> => {
|
||||||
token: string,
|
|
||||||
hostname: string
|
|
||||||
): Promise<Models['User_type']> => {
|
|
||||||
try {
|
try {
|
||||||
const user = await fetch(`${hostname}/users/me`, {
|
const user = await fetch(withAPIBaseURL('/users/me'), {
|
||||||
headers: new Headers({
|
headers: new Headers({
|
||||||
Authorization: `Bearer ${token}`,
|
Authorization: `Bearer ${token}`,
|
||||||
}),
|
}),
|
||||||
|
@ -1,11 +1,6 @@
|
|||||||
import { executeAstMock } from '@src/lang/langHelpers'
|
import { executeAstMock } from '@src/lang/langHelpers'
|
||||||
import {
|
import { formatNumberValue, parse, resultIsOk } from '@src/lang/wasm'
|
||||||
type CallExpressionKw,
|
import type { KclExpression } from '@src/lib/commandTypes'
|
||||||
formatNumberValue,
|
|
||||||
parse,
|
|
||||||
resultIsOk,
|
|
||||||
} from '@src/lang/wasm'
|
|
||||||
import type { KclCommandValue, KclExpression } from '@src/lib/commandTypes'
|
|
||||||
import { rustContext } from '@src/lib/singletons'
|
import { rustContext } from '@src/lib/singletons'
|
||||||
import { err } from '@src/lib/trap'
|
import { err } from '@src/lib/trap'
|
||||||
|
|
||||||
@ -74,23 +69,3 @@ export async function stringToKclExpression(value: string) {
|
|||||||
valueText: value,
|
valueText: value,
|
||||||
} satisfies KclExpression
|
} satisfies KclExpression
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function retrieveArgFromPipedCallExpression(
|
|
||||||
callExpression: CallExpressionKw,
|
|
||||||
name: string
|
|
||||||
): Promise<KclCommandValue | undefined> {
|
|
||||||
const arg = callExpression.arguments.find(
|
|
||||||
(a) => a.label?.type === 'Identifier' && a.label?.name === name
|
|
||||||
)
|
|
||||||
if (
|
|
||||||
arg?.type === 'LabeledArg' &&
|
|
||||||
(arg.arg.type === 'Name' || arg.arg.type === 'Literal')
|
|
||||||
) {
|
|
||||||
const value = arg.arg.type === 'Name' ? arg.arg.name.name : arg.arg.raw
|
|
||||||
const result = await stringToKclExpression(value)
|
|
||||||
if (!(err(result) || 'errors' in result)) {
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return undefined
|
|
||||||
}
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { VITE_KC_API_BASE_URL, VITE_KC_SITE_APP_URL } from '@src/env'
|
import { VITE_KC_SITE_APP_URL } from '@src/env'
|
||||||
import toast from 'react-hot-toast'
|
import toast from 'react-hot-toast'
|
||||||
|
|
||||||
import { stringToBase64 } from '@src/lib/base64'
|
import { stringToBase64 } from '@src/lib/base64'
|
||||||
@ -7,6 +7,7 @@ import {
|
|||||||
CREATE_FILE_URL_PARAM,
|
CREATE_FILE_URL_PARAM,
|
||||||
} from '@src/lib/constants'
|
} from '@src/lib/constants'
|
||||||
import { err } from '@src/lib/trap'
|
import { err } from '@src/lib/trap'
|
||||||
|
import { withAPIBaseURL } from '@src/lib/withBaseURL'
|
||||||
|
|
||||||
export interface FileLinkParams {
|
export interface FileLinkParams {
|
||||||
code: string
|
code: string
|
||||||
@ -96,7 +97,7 @@ export async function createShortlink(
|
|||||||
if (password) {
|
if (password) {
|
||||||
body.password = password
|
body.password = password
|
||||||
}
|
}
|
||||||
const response = await fetch(`${VITE_KC_API_BASE_URL}/user/shortlinks`, {
|
const response = await fetch(withAPIBaseURL('/user/shortlinks'), {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
'Content-type': 'application/json',
|
'Content-type': 'application/json',
|
||||||
|
@ -3,8 +3,8 @@ import type { OpKclValue, Operation } from '@rust/kcl-lib/bindings/Operation'
|
|||||||
import type { CustomIconName } from '@src/components/CustomIcon'
|
import type { CustomIconName } from '@src/components/CustomIcon'
|
||||||
import {
|
import {
|
||||||
getNodeFromPath,
|
getNodeFromPath,
|
||||||
findPipesWithImportAlias,
|
|
||||||
getSketchSelectionsFromOperation,
|
getSketchSelectionsFromOperation,
|
||||||
|
getObjectSelectionsFromOperation,
|
||||||
} from '@src/lang/queryAst'
|
} from '@src/lang/queryAst'
|
||||||
import type { Artifact } from '@src/lang/std/artifactGraph'
|
import type { Artifact } from '@src/lang/std/artifactGraph'
|
||||||
import {
|
import {
|
||||||
@ -26,10 +26,7 @@ import type {
|
|||||||
ModelingCommandSchema,
|
ModelingCommandSchema,
|
||||||
} from '@src/lib/commandBarConfigs/modelingCommandConfig'
|
} from '@src/lib/commandBarConfigs/modelingCommandConfig'
|
||||||
import type { KclCommandValue, KclExpression } from '@src/lib/commandTypes'
|
import type { KclCommandValue, KclExpression } from '@src/lib/commandTypes'
|
||||||
import {
|
import { stringToKclExpression } from '@src/lib/kclHelpers'
|
||||||
stringToKclExpression,
|
|
||||||
retrieveArgFromPipedCallExpression,
|
|
||||||
} from '@src/lib/kclHelpers'
|
|
||||||
import { isDefaultPlaneStr } from '@src/lib/planes'
|
import { isDefaultPlaneStr } from '@src/lib/planes'
|
||||||
import type { Selection, Selections } from '@src/lib/selections'
|
import type { Selection, Selections } from '@src/lib/selections'
|
||||||
import { codeManager, kclManager, rustContext } from '@src/lib/singletons'
|
import { codeManager, kclManager, rustContext } from '@src/lib/singletons'
|
||||||
@ -1179,6 +1176,8 @@ export const stdLibMap: Record<string, StdLibCallInfo> = {
|
|||||||
scale: {
|
scale: {
|
||||||
label: 'Scale',
|
label: 'Scale',
|
||||||
icon: 'scale',
|
icon: 'scale',
|
||||||
|
prepareToEdit: prepareToEditScale,
|
||||||
|
supportsTransform: true,
|
||||||
},
|
},
|
||||||
shell: {
|
shell: {
|
||||||
label: 'Shell',
|
label: 'Shell',
|
||||||
@ -1526,69 +1525,182 @@ async function prepareToEditTranslate({ operation }: EnterEditFlowProps) {
|
|||||||
name: 'Translate',
|
name: 'Translate',
|
||||||
groupId: 'modeling',
|
groupId: 'modeling',
|
||||||
}
|
}
|
||||||
const isModuleImport = operation.type === 'GroupBegin'
|
|
||||||
const isSupportedStdLibCall =
|
const isSupportedStdLibCall =
|
||||||
operation.type === 'StdLibCall' &&
|
operation.type === 'StdLibCall' &&
|
||||||
stdLibMap[operation.name]?.supportsTransform
|
stdLibMap[operation.name]?.supportsTransform
|
||||||
if (!isModuleImport && !isSupportedStdLibCall) {
|
if (!isSupportedStdLibCall) {
|
||||||
return {
|
return {
|
||||||
reason: 'Unsupported operation type. Please edit in the code editor.',
|
reason: 'Unsupported operation type. Please edit in the code editor.',
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const nodeToEdit = pathToNodeFromRustNodePath(operation.nodePath)
|
// 1. Map the unlabeled arguments to selections
|
||||||
let x: KclExpression | undefined = undefined
|
const objects = getObjectSelectionsFromOperation(
|
||||||
let y: KclExpression | undefined = undefined
|
operation,
|
||||||
let z: KclExpression | undefined = undefined
|
kclManager.artifactGraph
|
||||||
const pipeLookupFromOperation = getNodeFromPath<PipeExpression>(
|
|
||||||
kclManager.ast,
|
|
||||||
nodeToEdit,
|
|
||||||
'PipeExpression'
|
|
||||||
)
|
)
|
||||||
let pipe: PipeExpression | undefined
|
if (err(objects)) {
|
||||||
const ast = kclManager.ast
|
return { reason: "Couldn't retrieve objects" }
|
||||||
if (
|
|
||||||
err(pipeLookupFromOperation) ||
|
|
||||||
pipeLookupFromOperation.node.type !== 'PipeExpression'
|
|
||||||
) {
|
|
||||||
// Look for the last pipe with the import alias and a call to translate
|
|
||||||
const pipes = findPipesWithImportAlias(ast, nodeToEdit, 'translate')
|
|
||||||
pipe = pipes.at(-1)?.expression
|
|
||||||
} else {
|
|
||||||
pipe = pipeLookupFromOperation.node
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pipe) {
|
// 2. Convert the x y z arguments from a string to a KCL expression
|
||||||
const translate = pipe.body.find(
|
let x: KclCommandValue | undefined = undefined
|
||||||
(n) => n.type === 'CallExpressionKw' && n.callee.name.name === 'translate'
|
let y: KclCommandValue | undefined = undefined
|
||||||
|
let z: KclCommandValue | undefined = undefined
|
||||||
|
let global: boolean | undefined
|
||||||
|
if (operation.labeledArgs.x) {
|
||||||
|
const result = await stringToKclExpression(
|
||||||
|
codeManager.code.slice(
|
||||||
|
operation.labeledArgs.x.sourceRange[0],
|
||||||
|
operation.labeledArgs.x.sourceRange[1]
|
||||||
|
)
|
||||||
)
|
)
|
||||||
if (translate?.type === 'CallExpressionKw') {
|
if (err(result) || 'errors' in result) {
|
||||||
x = await retrieveArgFromPipedCallExpression(translate, 'x')
|
return { reason: "Couldn't retrieve x argument" }
|
||||||
y = await retrieveArgFromPipedCallExpression(translate, 'y')
|
|
||||||
z = await retrieveArgFromPipedCallExpression(translate, 'z')
|
|
||||||
}
|
}
|
||||||
|
x = result
|
||||||
}
|
}
|
||||||
|
|
||||||
// Won't be used since we provide nodeToEdit
|
if (operation.labeledArgs.y) {
|
||||||
const selection: Selections = { graphSelections: [], otherSelections: [] }
|
const result = await stringToKclExpression(
|
||||||
const argDefaultValues = { nodeToEdit, selection, x, y, z }
|
codeManager.code.slice(
|
||||||
|
operation.labeledArgs.y.sourceRange[0],
|
||||||
|
operation.labeledArgs.y.sourceRange[1]
|
||||||
|
)
|
||||||
|
)
|
||||||
|
if (err(result) || 'errors' in result) {
|
||||||
|
return { reason: "Couldn't retrieve y argument" }
|
||||||
|
}
|
||||||
|
y = result
|
||||||
|
}
|
||||||
|
|
||||||
|
if (operation.labeledArgs.z) {
|
||||||
|
const result = await stringToKclExpression(
|
||||||
|
codeManager.code.slice(
|
||||||
|
operation.labeledArgs.z.sourceRange[0],
|
||||||
|
operation.labeledArgs.z.sourceRange[1]
|
||||||
|
)
|
||||||
|
)
|
||||||
|
if (err(result) || 'errors' in result) {
|
||||||
|
return { reason: "Couldn't retrieve z argument" }
|
||||||
|
}
|
||||||
|
z = result
|
||||||
|
}
|
||||||
|
|
||||||
|
if (operation.labeledArgs.global) {
|
||||||
|
global =
|
||||||
|
codeManager.code.slice(
|
||||||
|
operation.labeledArgs.global.sourceRange[0],
|
||||||
|
operation.labeledArgs.global.sourceRange[1]
|
||||||
|
) === 'true'
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Assemble the default argument values for the command,
|
||||||
|
// with `nodeToEdit` set, which will let the actor know
|
||||||
|
// to edit the node that corresponds to the StdLibCall.
|
||||||
|
const argDefaultValues: ModelingCommandSchema['Translate'] = {
|
||||||
|
objects,
|
||||||
|
x,
|
||||||
|
y,
|
||||||
|
z,
|
||||||
|
global,
|
||||||
|
nodeToEdit: pathToNodeFromRustNodePath(operation.nodePath),
|
||||||
|
}
|
||||||
return {
|
return {
|
||||||
...baseCommand,
|
...baseCommand,
|
||||||
argDefaultValues,
|
argDefaultValues,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function enterTranslateFlow({
|
async function prepareToEditScale({ operation }: EnterEditFlowProps) {
|
||||||
operation,
|
const baseCommand = {
|
||||||
}: EnterEditFlowProps): Promise<Error | CommandBarMachineEvent> {
|
name: 'Scale',
|
||||||
const data = await prepareToEditTranslate({ operation })
|
groupId: 'modeling',
|
||||||
if ('reason' in data) {
|
}
|
||||||
return new Error(data.reason)
|
const isSupportedStdLibCall =
|
||||||
|
operation.type === 'StdLibCall' &&
|
||||||
|
stdLibMap[operation.name]?.supportsTransform
|
||||||
|
if (!isSupportedStdLibCall) {
|
||||||
|
return {
|
||||||
|
reason: 'Unsupported operation type. Please edit in the code editor.',
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 1. Map the unlabeled arguments to selections
|
||||||
|
const objects = getObjectSelectionsFromOperation(
|
||||||
|
operation,
|
||||||
|
kclManager.artifactGraph
|
||||||
|
)
|
||||||
|
if (err(objects)) {
|
||||||
|
return { reason: "Couldn't retrieve objects" }
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Convert the x y z arguments from a string to a KCL expression
|
||||||
|
let x: KclCommandValue | undefined = undefined
|
||||||
|
let y: KclCommandValue | undefined = undefined
|
||||||
|
let z: KclCommandValue | undefined = undefined
|
||||||
|
let global: boolean | undefined
|
||||||
|
if (operation.labeledArgs.x) {
|
||||||
|
const result = await stringToKclExpression(
|
||||||
|
codeManager.code.slice(
|
||||||
|
operation.labeledArgs.x.sourceRange[0],
|
||||||
|
operation.labeledArgs.x.sourceRange[1]
|
||||||
|
)
|
||||||
|
)
|
||||||
|
if (err(result) || 'errors' in result) {
|
||||||
|
return { reason: "Couldn't retrieve x argument" }
|
||||||
|
}
|
||||||
|
x = result
|
||||||
|
}
|
||||||
|
|
||||||
|
if (operation.labeledArgs.y) {
|
||||||
|
const result = await stringToKclExpression(
|
||||||
|
codeManager.code.slice(
|
||||||
|
operation.labeledArgs.y.sourceRange[0],
|
||||||
|
operation.labeledArgs.y.sourceRange[1]
|
||||||
|
)
|
||||||
|
)
|
||||||
|
if (err(result) || 'errors' in result) {
|
||||||
|
return { reason: "Couldn't retrieve y argument" }
|
||||||
|
}
|
||||||
|
y = result
|
||||||
|
}
|
||||||
|
|
||||||
|
if (operation.labeledArgs.z) {
|
||||||
|
const result = await stringToKclExpression(
|
||||||
|
codeManager.code.slice(
|
||||||
|
operation.labeledArgs.z.sourceRange[0],
|
||||||
|
operation.labeledArgs.z.sourceRange[1]
|
||||||
|
)
|
||||||
|
)
|
||||||
|
if (err(result) || 'errors' in result) {
|
||||||
|
return { reason: "Couldn't retrieve z argument" }
|
||||||
|
}
|
||||||
|
z = result
|
||||||
|
}
|
||||||
|
|
||||||
|
if (operation.labeledArgs.global) {
|
||||||
|
global =
|
||||||
|
codeManager.code.slice(
|
||||||
|
operation.labeledArgs.global.sourceRange[0],
|
||||||
|
operation.labeledArgs.global.sourceRange[1]
|
||||||
|
) === 'true'
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Assemble the default argument values for the command,
|
||||||
|
// with `nodeToEdit` set, which will let the actor know
|
||||||
|
// to edit the node that corresponds to the StdLibCall.
|
||||||
|
const argDefaultValues: ModelingCommandSchema['Scale'] = {
|
||||||
|
objects,
|
||||||
|
x,
|
||||||
|
y,
|
||||||
|
z,
|
||||||
|
global,
|
||||||
|
nodeToEdit: pathToNodeFromRustNodePath(operation.nodePath),
|
||||||
|
}
|
||||||
return {
|
return {
|
||||||
type: 'Find and select command',
|
...baseCommand,
|
||||||
data,
|
argDefaultValues,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1597,96 +1709,89 @@ async function prepareToEditRotate({ operation }: EnterEditFlowProps) {
|
|||||||
name: 'Rotate',
|
name: 'Rotate',
|
||||||
groupId: 'modeling',
|
groupId: 'modeling',
|
||||||
}
|
}
|
||||||
const isModuleImport = operation.type === 'GroupBegin'
|
|
||||||
const isSupportedStdLibCall =
|
const isSupportedStdLibCall =
|
||||||
operation.type === 'StdLibCall' &&
|
operation.type === 'StdLibCall' &&
|
||||||
stdLibMap[operation.name]?.supportsTransform
|
stdLibMap[operation.name]?.supportsTransform
|
||||||
if (!isModuleImport && !isSupportedStdLibCall) {
|
if (!isSupportedStdLibCall) {
|
||||||
return {
|
return {
|
||||||
reason: 'Unsupported operation type. Please edit in the code editor.',
|
reason: 'Unsupported operation type. Please edit in the code editor.',
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const nodeToEdit = pathToNodeFromRustNodePath(operation.nodePath)
|
// 1. Map the unlabeled arguments to selections
|
||||||
let roll: KclExpression | undefined = undefined
|
const objects = getObjectSelectionsFromOperation(
|
||||||
let pitch: KclExpression | undefined = undefined
|
operation,
|
||||||
let yaw: KclExpression | undefined = undefined
|
kclManager.artifactGraph
|
||||||
const pipeLookupFromOperation = getNodeFromPath<PipeExpression>(
|
|
||||||
kclManager.ast,
|
|
||||||
nodeToEdit,
|
|
||||||
'PipeExpression'
|
|
||||||
)
|
)
|
||||||
let pipe: PipeExpression | undefined
|
if (err(objects)) {
|
||||||
const ast = kclManager.ast
|
return { reason: "Couldn't retrieve objects" }
|
||||||
if (
|
|
||||||
err(pipeLookupFromOperation) ||
|
|
||||||
pipeLookupFromOperation.node.type !== 'PipeExpression'
|
|
||||||
) {
|
|
||||||
// Look for the last pipe with the import alias and a call to rotate
|
|
||||||
const pipes = findPipesWithImportAlias(ast, nodeToEdit, 'rotate')
|
|
||||||
pipe = pipes.at(-1)?.expression
|
|
||||||
} else {
|
|
||||||
pipe = pipeLookupFromOperation.node
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pipe) {
|
// 2. Convert the x y z arguments from a string to a KCL expression
|
||||||
const rotate = pipe.body.find(
|
let roll: KclCommandValue | undefined = undefined
|
||||||
(n) => n.type === 'CallExpressionKw' && n.callee.name.name === 'rotate'
|
let pitch: KclCommandValue | undefined = undefined
|
||||||
|
let yaw: KclCommandValue | undefined = undefined
|
||||||
|
let global: boolean | undefined
|
||||||
|
if (operation.labeledArgs.roll) {
|
||||||
|
const result = await stringToKclExpression(
|
||||||
|
codeManager.code.slice(
|
||||||
|
operation.labeledArgs.roll.sourceRange[0],
|
||||||
|
operation.labeledArgs.roll.sourceRange[1]
|
||||||
|
)
|
||||||
)
|
)
|
||||||
if (rotate?.type === 'CallExpressionKw') {
|
if (err(result) || 'errors' in result) {
|
||||||
roll = await retrieveArgFromPipedCallExpression(rotate, 'roll')
|
return { reason: "Couldn't retrieve roll argument" }
|
||||||
pitch = await retrieveArgFromPipedCallExpression(rotate, 'pitch')
|
|
||||||
yaw = await retrieveArgFromPipedCallExpression(rotate, 'yaw')
|
|
||||||
}
|
}
|
||||||
|
roll = result
|
||||||
}
|
}
|
||||||
|
|
||||||
// Won't be used since we provide nodeToEdit
|
if (operation.labeledArgs.pitch) {
|
||||||
const selection: Selections = { graphSelections: [], otherSelections: [] }
|
const result = await stringToKclExpression(
|
||||||
const argDefaultValues = { nodeToEdit, selection, roll, pitch, yaw }
|
codeManager.code.slice(
|
||||||
|
operation.labeledArgs.pitch.sourceRange[0],
|
||||||
|
operation.labeledArgs.pitch.sourceRange[1]
|
||||||
|
)
|
||||||
|
)
|
||||||
|
if (err(result) || 'errors' in result) {
|
||||||
|
return { reason: "Couldn't retrieve pitch argument" }
|
||||||
|
}
|
||||||
|
pitch = result
|
||||||
|
}
|
||||||
|
|
||||||
|
if (operation.labeledArgs.yaw) {
|
||||||
|
const result = await stringToKclExpression(
|
||||||
|
codeManager.code.slice(
|
||||||
|
operation.labeledArgs.yaw.sourceRange[0],
|
||||||
|
operation.labeledArgs.yaw.sourceRange[1]
|
||||||
|
)
|
||||||
|
)
|
||||||
|
if (err(result) || 'errors' in result) {
|
||||||
|
return { reason: "Couldn't retrieve yaw argument" }
|
||||||
|
}
|
||||||
|
yaw = result
|
||||||
|
}
|
||||||
|
|
||||||
|
if (operation.labeledArgs.global) {
|
||||||
|
global =
|
||||||
|
codeManager.code.slice(
|
||||||
|
operation.labeledArgs.global.sourceRange[0],
|
||||||
|
operation.labeledArgs.global.sourceRange[1]
|
||||||
|
) === 'true'
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Assemble the default argument values for the command,
|
||||||
|
// with `nodeToEdit` set, which will let the actor know
|
||||||
|
// to edit the node that corresponds to the StdLibCall.
|
||||||
|
const argDefaultValues: ModelingCommandSchema['Rotate'] = {
|
||||||
|
objects,
|
||||||
|
roll,
|
||||||
|
pitch,
|
||||||
|
yaw,
|
||||||
|
global,
|
||||||
|
nodeToEdit: pathToNodeFromRustNodePath(operation.nodePath),
|
||||||
|
}
|
||||||
return {
|
return {
|
||||||
...baseCommand,
|
...baseCommand,
|
||||||
argDefaultValues,
|
argDefaultValues,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function enterRotateFlow({
|
|
||||||
operation,
|
|
||||||
}: EnterEditFlowProps): Promise<Error | CommandBarMachineEvent> {
|
|
||||||
const data = await prepareToEditRotate({ operation })
|
|
||||||
if ('reason' in data) {
|
|
||||||
return new Error(data.reason)
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
type: 'Find and select command',
|
|
||||||
data,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function enterCloneFlow({
|
|
||||||
operation,
|
|
||||||
}: EnterEditFlowProps): Promise<Error | CommandBarMachineEvent> {
|
|
||||||
const isModuleImport = operation.type === 'GroupBegin'
|
|
||||||
const isSupportedStdLibCall =
|
|
||||||
operation.type === 'StdLibCall' &&
|
|
||||||
stdLibMap[operation.name]?.supportsTransform
|
|
||||||
if (!isModuleImport && !isSupportedStdLibCall) {
|
|
||||||
return new Error(
|
|
||||||
'Unsupported operation type. Please edit in the code editor.'
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
const nodeToEdit = pathToNodeFromRustNodePath(operation.nodePath)
|
|
||||||
|
|
||||||
// Won't be used since we provide nodeToEdit
|
|
||||||
const selection: Selections = { graphSelections: [], otherSelections: [] }
|
|
||||||
const argDefaultValues = { nodeToEdit, selection }
|
|
||||||
return {
|
|
||||||
type: 'Find and select command',
|
|
||||||
data: {
|
|
||||||
name: 'Clone',
|
|
||||||
groupId: 'modeling',
|
|
||||||
argDefaultValues,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import type { SelectionRange } from '@codemirror/state'
|
import type { SelectionRange } from '@codemirror/state'
|
||||||
import { EditorSelection, Transaction } from '@codemirror/state'
|
import { EditorSelection, Transaction } from '@codemirror/state'
|
||||||
import type { Models } from '@kittycad/lib'
|
import type { Models } from '@kittycad/lib'
|
||||||
import { VITE_KC_API_BASE_URL, VITE_KC_SITE_BASE_URL } from '@src/env'
|
import { VITE_KC_SITE_BASE_URL } from '@src/env'
|
||||||
import { diffLines } from 'diff'
|
import { diffLines } from 'diff'
|
||||||
import toast from 'react-hot-toast'
|
import toast from 'react-hot-toast'
|
||||||
import type { TextToCadMultiFileIteration_type } from '@kittycad/lib/dist/types/src/models'
|
import type { TextToCadMultiFileIteration_type } from '@kittycad/lib/dist/types/src/models'
|
||||||
@ -28,6 +28,7 @@ import { uuidv4 } from '@src/lib/utils'
|
|||||||
import type { File as KittyCadLibFile } from '@kittycad/lib/dist/types/src/models'
|
import type { File as KittyCadLibFile } from '@kittycad/lib/dist/types/src/models'
|
||||||
import type { FileMeta } from '@src/lib/types'
|
import type { FileMeta } from '@src/lib/types'
|
||||||
import type { RequestedKCLFile } from '@src/machines/systemIO/utils'
|
import type { RequestedKCLFile } from '@src/machines/systemIO/utils'
|
||||||
|
import { withAPIBaseURL } from '@src/lib/withBaseURL'
|
||||||
|
|
||||||
type KclFileMetaMap = {
|
type KclFileMetaMap = {
|
||||||
[execStateFileNamesIndex: number]: Extract<FileMeta, { type: 'kcl' }>
|
[execStateFileNamesIndex: number]: Extract<FileMeta, { type: 'kcl' }>
|
||||||
@ -77,7 +78,7 @@ async function submitTextToCadRequest(
|
|||||||
})
|
})
|
||||||
|
|
||||||
const response = await fetch(
|
const response = await fetch(
|
||||||
`${VITE_KC_API_BASE_URL}/ml/text-to-cad/multi-file/iteration`,
|
withAPIBaseURL('/ml/text-to-cad/multi-file/iteration'),
|
||||||
{
|
{
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
@ -304,7 +305,7 @@ export async function getPromptToEditResult(
|
|||||||
id: string,
|
id: string,
|
||||||
token?: string
|
token?: string
|
||||||
): Promise<Models['TextToCadMultiFileIteration_type'] | Error> {
|
): Promise<Models['TextToCadMultiFileIteration_type'] | Error> {
|
||||||
const url = VITE_KC_API_BASE_URL + '/async/operations/' + id
|
const url = withAPIBaseURL(`/async/operations/${id}`)
|
||||||
const data: Models['TextToCadMultiFileIteration_type'] | Error =
|
const data: Models['TextToCadMultiFileIteration_type'] | Error =
|
||||||
await crossPlatformFetch(
|
await crossPlatformFetch(
|
||||||
url,
|
url,
|
||||||
@ -340,7 +341,7 @@ export async function doPromptEdit({
|
|||||||
;(window as any).process = {
|
;(window as any).process = {
|
||||||
env: {
|
env: {
|
||||||
ZOO_API_TOKEN: token,
|
ZOO_API_TOKEN: token,
|
||||||
ZOO_HOST: VITE_KC_API_BASE_URL,
|
ZOO_HOST: withAPIBaseURL(''),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { VITE_KC_API_BASE_URL } from '@src/env'
|
import { withAPIBaseURL } from '@src/lib/withBaseURL'
|
||||||
|
|
||||||
import EditorManager from '@src/editor/manager'
|
import EditorManager from '@src/editor/manager'
|
||||||
import { KclManager } from '@src/lang/KclSingleton'
|
import { KclManager } from '@src/lang/KclSingleton'
|
||||||
@ -171,7 +171,7 @@ const appMachine = setup({
|
|||||||
systemId: BILLING,
|
systemId: BILLING,
|
||||||
input: {
|
input: {
|
||||||
...BILLING_CONTEXT_DEFAULTS,
|
...BILLING_CONTEXT_DEFAULTS,
|
||||||
urlUserService: VITE_KC_API_BASE_URL,
|
urlUserService: withAPIBaseURL(''),
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import type { Models } from '@kittycad/lib'
|
import type { Models } from '@kittycad/lib'
|
||||||
import { VITE_KC_API_BASE_URL } from '@src/env'
|
|
||||||
import toast from 'react-hot-toast'
|
import toast from 'react-hot-toast'
|
||||||
import type { NavigateFunction } from 'react-router-dom'
|
import type { NavigateFunction } from 'react-router-dom'
|
||||||
import {
|
import {
|
||||||
@ -19,6 +18,7 @@ import { err, reportRejection } from '@src/lib/trap'
|
|||||||
import { toSync } from '@src/lib/utils'
|
import { toSync } from '@src/lib/utils'
|
||||||
import { getAllSubDirectoriesAtProjectRoot } from '@src/machines/systemIO/snapshotContext'
|
import { getAllSubDirectoriesAtProjectRoot } from '@src/machines/systemIO/snapshotContext'
|
||||||
import { joinOSPaths } from '@src/lib/paths'
|
import { joinOSPaths } from '@src/lib/paths'
|
||||||
|
import { withAPIBaseURL } from '@src/lib/withBaseURL'
|
||||||
|
|
||||||
export async function submitTextToCadPrompt(
|
export async function submitTextToCadPrompt(
|
||||||
prompt: string,
|
prompt: string,
|
||||||
@ -32,7 +32,7 @@ export async function submitTextToCadPrompt(
|
|||||||
kcl_version: kclManager.kclVersion,
|
kcl_version: kclManager.kclVersion,
|
||||||
}
|
}
|
||||||
// Glb has a smaller footprint than gltf, should we want to render it.
|
// Glb has a smaller footprint than gltf, should we want to render it.
|
||||||
const url = VITE_KC_API_BASE_URL + '/ai/text-to-cad/glb?kcl=true'
|
const url = withAPIBaseURL('/ai/text-to-cad/glb?kcl=true')
|
||||||
const data: Models['TextToCad_type'] | Error = await crossPlatformFetch(
|
const data: Models['TextToCad_type'] | Error = await crossPlatformFetch(
|
||||||
url,
|
url,
|
||||||
{
|
{
|
||||||
@ -58,7 +58,7 @@ export async function getTextToCadResult(
|
|||||||
id: string,
|
id: string,
|
||||||
token?: string
|
token?: string
|
||||||
): Promise<Models['TextToCad_type'] | Error> {
|
): Promise<Models['TextToCad_type'] | Error> {
|
||||||
const url = VITE_KC_API_BASE_URL + '/user/text-to-cad/' + id
|
const url = withAPIBaseURL(`/user/text-to-cad/${id}`)
|
||||||
const data: Models['TextToCad_type'] | Error = await crossPlatformFetch(
|
const data: Models['TextToCad_type'] | Error = await crossPlatformFetch(
|
||||||
url,
|
url,
|
||||||
{
|
{
|
||||||
|
@ -1,14 +1,13 @@
|
|||||||
import type { Models } from '@kittycad/lib/dist/types/src'
|
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 crossPlatformFetch from '@src/lib/crossPlatformFetch'
|
||||||
|
import { withAPIBaseURL } from '@src/lib/withBaseURL'
|
||||||
|
|
||||||
export async function sendTelemetry(
|
export async function sendTelemetry(
|
||||||
id: string,
|
id: string,
|
||||||
feedback: Models['MlFeedback_type'],
|
feedback: Models['MlFeedback_type'],
|
||||||
token?: string
|
token?: string
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const url =
|
const url = withAPIBaseURL(`/user/text-to-cad/${id}?feedback=${feedback}`)
|
||||||
VITE_KC_API_BASE_URL + '/user/text-to-cad/' + id + '?feedback=' + feedback
|
|
||||||
await crossPlatformFetch(
|
await crossPlatformFetch(
|
||||||
url,
|
url,
|
||||||
{
|
{
|
||||||
|
@ -432,6 +432,24 @@ export const toolbarConfig: Record<ToolbarModeName, ToolbarMode> = {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
id: 'scale',
|
||||||
|
onClick: () =>
|
||||||
|
commandBarActor.send({
|
||||||
|
type: 'Find and select command',
|
||||||
|
data: { name: 'Scale', groupId: 'modeling' },
|
||||||
|
}),
|
||||||
|
icon: 'scale',
|
||||||
|
status: 'available',
|
||||||
|
title: 'Scale',
|
||||||
|
description: 'Apply scaling to a solid or sketch.',
|
||||||
|
links: [
|
||||||
|
{
|
||||||
|
label: 'API docs',
|
||||||
|
url: 'https://zoo.dev/docs/kcl-std/functions/std-transform-scale',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
{
|
{
|
||||||
id: 'clone',
|
id: 'clone',
|
||||||
onClick: () =>
|
onClick: () =>
|
||||||
|
34
src/lib/withBaseURL.test.ts
Normal file
34
src/lib/withBaseURL.test.ts
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
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_KC_API_BASE_URL } from '@src/env'
|
import { VITE_KITTYCAD_API_BASE_URL } from '@src/env'
|
||||||
|
|
||||||
export default function withBaseUrl(path: string): string {
|
export function withAPIBaseURL(path: string): string {
|
||||||
return VITE_KC_API_BASE_URL + path
|
return VITE_KITTYCAD_API_BASE_URL + path
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,5 @@
|
|||||||
import type { Models } from '@kittycad/lib'
|
import type { Models } from '@kittycad/lib'
|
||||||
import {
|
import { VITE_KITTYCAD_API_TOKEN } from '@src/env'
|
||||||
DEV,
|
|
||||||
VITE_KC_API_BASE_URL,
|
|
||||||
VITE_KC_DEV_TOKEN,
|
|
||||||
VITE_KC_SKIP_AUTH,
|
|
||||||
} from '@src/env'
|
|
||||||
import { assign, fromPromise, setup } from 'xstate'
|
import { assign, fromPromise, setup } from 'xstate'
|
||||||
|
|
||||||
import { COOKIE_NAME, OAUTH2_DEVICE_CLIENT_ID } from '@src/lib/constants'
|
import { COOKIE_NAME, OAUTH2_DEVICE_CLIENT_ID } from '@src/lib/constants'
|
||||||
@ -15,32 +10,9 @@ import {
|
|||||||
} from '@src/lib/desktop'
|
} from '@src/lib/desktop'
|
||||||
import { isDesktop } from '@src/lib/isDesktop'
|
import { isDesktop } from '@src/lib/isDesktop'
|
||||||
import { markOnce } from '@src/lib/performance'
|
import { markOnce } from '@src/lib/performance'
|
||||||
import {
|
import { withAPIBaseURL } from '@src/lib/withBaseURL'
|
||||||
default as withBaseURL,
|
|
||||||
default as withBaseUrl,
|
|
||||||
} from '@src/lib/withBaseURL'
|
|
||||||
import { ACTOR_IDS } from '@src/machines/machineConstants'
|
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 {
|
export interface UserContext {
|
||||||
user?: Models['User_type']
|
user?: Models['User_type']
|
||||||
token: string
|
token: string
|
||||||
@ -56,11 +28,21 @@ export type Events =
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const TOKEN_PERSIST_KEY = 'TOKEN_PERSIST_KEY'
|
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 =
|
export const persistedToken =
|
||||||
VITE_KC_DEV_TOKEN ||
|
persistedDevToken || persistedCookie || persistedLocalStorage
|
||||||
getCookie(COOKIE_NAME) ||
|
console.log('Initial persisted token')
|
||||||
localStorage?.getItem(TOKEN_PERSIST_KEY) ||
|
console.table([
|
||||||
''
|
['cookie', !!persistedCookie],
|
||||||
|
['local storage', !!persistedLocalStorage],
|
||||||
|
['api token', !!persistedDevToken],
|
||||||
|
])
|
||||||
|
|
||||||
export const authMachine = setup({
|
export const authMachine = setup({
|
||||||
types: {} as {
|
types: {} as {
|
||||||
@ -157,7 +139,7 @@ export const authMachine = setup({
|
|||||||
|
|
||||||
async function getUser(input: { token?: string }) {
|
async function getUser(input: { token?: string }) {
|
||||||
const token = await getAndSyncStoredToken(input)
|
const token = await getAndSyncStoredToken(input)
|
||||||
const url = withBaseURL('/user')
|
const url = withAPIBaseURL('/user')
|
||||||
const headers: { [key: string]: string } = {
|
const headers: { [key: string]: string } = {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
}
|
}
|
||||||
@ -165,21 +147,8 @@ async function getUser(input: { token?: string }) {
|
|||||||
if (!token && isDesktop()) return Promise.reject(new Error('No token found'))
|
if (!token && isDesktop()) return Promise.reject(new Error('No token found'))
|
||||||
if (token) headers['Authorization'] = `Bearer ${token}`
|
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()
|
const userPromise = isDesktop()
|
||||||
? getUserDesktop(token, VITE_KC_API_BASE_URL)
|
? getUserDesktop(token)
|
||||||
: fetch(url, {
|
: fetch(url, {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
credentials: 'include',
|
credentials: 'include',
|
||||||
@ -228,12 +197,24 @@ async function getAndSyncStoredToken(input: {
|
|||||||
token?: string
|
token?: string
|
||||||
}): Promise<string> {
|
}): Promise<string> {
|
||||||
// dev mode
|
// dev mode
|
||||||
if (VITE_KC_DEV_TOKEN) return VITE_KC_DEV_TOKEN
|
if (VITE_KITTYCAD_API_TOKEN) {
|
||||||
|
console.log('Token used for authentication')
|
||||||
|
console.table([['api token', !!VITE_KITTYCAD_API_TOKEN]])
|
||||||
|
return VITE_KITTYCAD_API_TOKEN
|
||||||
|
}
|
||||||
|
|
||||||
const token =
|
const inputToken = input.token && input.token !== '' ? input.token : ''
|
||||||
input.token && input.token !== ''
|
const cookieToken = getCookie(COOKIE_NAME)
|
||||||
? input.token
|
const localStorageToken = localStorage?.getItem(TOKEN_PERSIST_KEY) || ''
|
||||||
: getCookie(COOKIE_NAME) || 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],
|
||||||
|
])
|
||||||
if (token) {
|
if (token) {
|
||||||
// has just logged in, update storage
|
// has just logged in, update storage
|
||||||
localStorage.setItem(TOKEN_PERSIST_KEY, token)
|
localStorage.setItem(TOKEN_PERSIST_KEY, token)
|
||||||
@ -259,7 +240,7 @@ async function logout() {
|
|||||||
|
|
||||||
if (token) {
|
if (token) {
|
||||||
try {
|
try {
|
||||||
await fetch(withBaseUrl('/oauth2/token/revoke'), {
|
await fetch(withAPIBaseURL('/oauth2/token/revoke'), {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
credentials: 'include',
|
credentials: 'include',
|
||||||
headers: {
|
headers: {
|
||||||
@ -282,7 +263,7 @@ async function logout() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return fetch(withBaseUrl('/logout'), {
|
return fetch(withAPIBaseURL('/logout'), {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
credentials: 'include',
|
credentials: 'include',
|
||||||
})
|
})
|
||||||
|
@ -12,13 +12,7 @@ import type { Artifact } from '@src/lang/std/artifactGraph'
|
|||||||
import { getArtifactFromRange } from '@src/lang/std/artifactGraph'
|
import { getArtifactFromRange } from '@src/lang/std/artifactGraph'
|
||||||
import type { SourceRange } from '@src/lang/wasm'
|
import type { SourceRange } from '@src/lang/wasm'
|
||||||
import type { EnterEditFlowProps } from '@src/lib/operations'
|
import type { EnterEditFlowProps } from '@src/lib/operations'
|
||||||
import {
|
import { enterAppearanceFlow, enterEditFlow } from '@src/lib/operations'
|
||||||
enterAppearanceFlow,
|
|
||||||
enterCloneFlow,
|
|
||||||
enterEditFlow,
|
|
||||||
enterTranslateFlow,
|
|
||||||
enterRotateFlow,
|
|
||||||
} from '@src/lib/operations'
|
|
||||||
import { kclManager } from '@src/lib/singletons'
|
import { kclManager } from '@src/lib/singletons'
|
||||||
import { err } from '@src/lib/trap'
|
import { err } from '@src/lib/trap'
|
||||||
import { commandBarActor } from '@src/lib/singletons'
|
import { commandBarActor } from '@src/lib/singletons'
|
||||||
@ -52,6 +46,10 @@ type FeatureTreeEvent =
|
|||||||
type: 'enterRotateFlow'
|
type: 'enterRotateFlow'
|
||||||
data: { targetSourceRange: SourceRange; currentOperation: Operation }
|
data: { targetSourceRange: SourceRange; currentOperation: Operation }
|
||||||
}
|
}
|
||||||
|
| {
|
||||||
|
type: 'enterScaleFlow'
|
||||||
|
data: { targetSourceRange: SourceRange; currentOperation: Operation }
|
||||||
|
}
|
||||||
| {
|
| {
|
||||||
type: 'enterCloneFlow'
|
type: 'enterCloneFlow'
|
||||||
data: { targetSourceRange: SourceRange; currentOperation: Operation }
|
data: { targetSourceRange: SourceRange; currentOperation: Operation }
|
||||||
@ -126,75 +124,6 @@ export const featureTreeMachine = setup({
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
prepareTranslateCommand: fromPromise(
|
|
||||||
({
|
|
||||||
input,
|
|
||||||
}: {
|
|
||||||
input: EnterEditFlowProps & {
|
|
||||||
commandBarSend: (typeof commandBarActor)['send']
|
|
||||||
}
|
|
||||||
}) => {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
const { commandBarSend, ...editFlowProps } = input
|
|
||||||
enterTranslateFlow(editFlowProps)
|
|
||||||
.then((result) => {
|
|
||||||
if (err(result)) {
|
|
||||||
reject(result)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
input.commandBarSend(result)
|
|
||||||
resolve(result)
|
|
||||||
})
|
|
||||||
.catch(reject)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
),
|
|
||||||
prepareRotateCommand: fromPromise(
|
|
||||||
({
|
|
||||||
input,
|
|
||||||
}: {
|
|
||||||
input: EnterEditFlowProps & {
|
|
||||||
commandBarSend: (typeof commandBarActor)['send']
|
|
||||||
}
|
|
||||||
}) => {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
const { commandBarSend, ...editFlowProps } = input
|
|
||||||
enterRotateFlow(editFlowProps)
|
|
||||||
.then((result) => {
|
|
||||||
if (err(result)) {
|
|
||||||
reject(result)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
input.commandBarSend(result)
|
|
||||||
resolve(result)
|
|
||||||
})
|
|
||||||
.catch(reject)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
),
|
|
||||||
prepareCloneCommand: fromPromise(
|
|
||||||
({
|
|
||||||
input,
|
|
||||||
}: {
|
|
||||||
input: EnterEditFlowProps & {
|
|
||||||
commandBarSend: (typeof commandBarActor)['send']
|
|
||||||
}
|
|
||||||
}) => {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
const { commandBarSend, ...editFlowProps } = input
|
|
||||||
enterCloneFlow(editFlowProps)
|
|
||||||
.then((result) => {
|
|
||||||
if (err(result)) {
|
|
||||||
reject(result)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
input.commandBarSend(result)
|
|
||||||
resolve(result)
|
|
||||||
})
|
|
||||||
.catch(reject)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
),
|
|
||||||
sendDeleteCommand: fromPromise(
|
sendDeleteCommand: fromPromise(
|
||||||
({
|
({
|
||||||
input,
|
input,
|
||||||
@ -252,6 +181,10 @@ export const featureTreeMachine = setup({
|
|||||||
targetSourceRange: undefined,
|
targetSourceRange: undefined,
|
||||||
}),
|
}),
|
||||||
sendSelectionEvent: () => {},
|
sendSelectionEvent: () => {},
|
||||||
|
sendTranslateCommand: () => {},
|
||||||
|
sendRotateCommand: () => {},
|
||||||
|
sendScaleCommand: () => {},
|
||||||
|
sendCloneCommand: () => {},
|
||||||
openCodePane: () => {},
|
openCodePane: () => {},
|
||||||
scrollToError: () => {},
|
scrollToError: () => {},
|
||||||
},
|
},
|
||||||
@ -285,17 +218,38 @@ export const featureTreeMachine = setup({
|
|||||||
|
|
||||||
enterTranslateFlow: {
|
enterTranslateFlow: {
|
||||||
target: 'enteringTranslateFlow',
|
target: 'enteringTranslateFlow',
|
||||||
actions: ['saveTargetSourceRange', 'saveCurrentOperation'],
|
actions: [
|
||||||
|
'saveTargetSourceRange',
|
||||||
|
'saveCurrentOperation',
|
||||||
|
'sendSelectionEvent',
|
||||||
|
],
|
||||||
},
|
},
|
||||||
|
|
||||||
enterRotateFlow: {
|
enterRotateFlow: {
|
||||||
target: 'enteringRotateFlow',
|
target: 'enteringRotateFlow',
|
||||||
actions: ['saveTargetSourceRange', 'saveCurrentOperation'],
|
actions: [
|
||||||
|
'saveTargetSourceRange',
|
||||||
|
'saveCurrentOperation',
|
||||||
|
'sendSelectionEvent',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
|
||||||
|
enterScaleFlow: {
|
||||||
|
target: 'enteringScaleFlow',
|
||||||
|
actions: [
|
||||||
|
'saveTargetSourceRange',
|
||||||
|
'saveCurrentOperation',
|
||||||
|
'sendSelectionEvent',
|
||||||
|
],
|
||||||
},
|
},
|
||||||
|
|
||||||
enterCloneFlow: {
|
enterCloneFlow: {
|
||||||
target: 'enteringCloneFlow',
|
target: 'enteringCloneFlow',
|
||||||
actions: ['saveTargetSourceRange', 'saveCurrentOperation'],
|
actions: [
|
||||||
|
'saveTargetSourceRange',
|
||||||
|
'saveCurrentOperation',
|
||||||
|
'sendSelectionEvent',
|
||||||
|
],
|
||||||
},
|
},
|
||||||
|
|
||||||
deleteOperation: {
|
deleteOperation: {
|
||||||
@ -355,6 +309,82 @@ export const featureTreeMachine = setup({
|
|||||||
initial: 'selecting',
|
initial: 'selecting',
|
||||||
},
|
},
|
||||||
|
|
||||||
|
enteringTranslateFlow: {
|
||||||
|
states: {
|
||||||
|
enteringTranslateFlow: {
|
||||||
|
on: {
|
||||||
|
selected: 'done',
|
||||||
|
},
|
||||||
|
|
||||||
|
entry: 'sendTranslateCommand',
|
||||||
|
},
|
||||||
|
|
||||||
|
done: {
|
||||||
|
always: '#featureTree.idle',
|
||||||
|
entry: 'clearContext',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
initial: 'enteringTranslateFlow',
|
||||||
|
},
|
||||||
|
|
||||||
|
enteringRotateFlow: {
|
||||||
|
states: {
|
||||||
|
enteringRotateFlow: {
|
||||||
|
on: {
|
||||||
|
selected: 'done',
|
||||||
|
},
|
||||||
|
|
||||||
|
entry: 'sendRotateCommand',
|
||||||
|
},
|
||||||
|
|
||||||
|
done: {
|
||||||
|
always: '#featureTree.idle',
|
||||||
|
entry: 'clearContext',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
initial: 'enteringRotateFlow',
|
||||||
|
},
|
||||||
|
|
||||||
|
enteringScaleFlow: {
|
||||||
|
states: {
|
||||||
|
enteringScaleFlow: {
|
||||||
|
on: {
|
||||||
|
selected: 'done',
|
||||||
|
},
|
||||||
|
|
||||||
|
entry: 'sendScaleCommand',
|
||||||
|
},
|
||||||
|
|
||||||
|
done: {
|
||||||
|
always: '#featureTree.idle',
|
||||||
|
entry: 'clearContext',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
initial: 'enteringScaleFlow',
|
||||||
|
},
|
||||||
|
|
||||||
|
enteringCloneFlow: {
|
||||||
|
states: {
|
||||||
|
enteringCloneFlow: {
|
||||||
|
on: {
|
||||||
|
selected: 'done',
|
||||||
|
},
|
||||||
|
|
||||||
|
entry: 'sendCloneCommand',
|
||||||
|
},
|
||||||
|
|
||||||
|
done: {
|
||||||
|
always: '#featureTree.idle',
|
||||||
|
entry: 'clearContext',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
initial: 'enteringCloneFlow',
|
||||||
|
},
|
||||||
|
|
||||||
enteringEditFlow: {
|
enteringEditFlow: {
|
||||||
states: {
|
states: {
|
||||||
selecting: {
|
selecting: {
|
||||||
@ -463,168 +493,6 @@ export const featureTreeMachine = setup({
|
|||||||
exit: ['clearContext'],
|
exit: ['clearContext'],
|
||||||
},
|
},
|
||||||
|
|
||||||
enteringTranslateFlow: {
|
|
||||||
states: {
|
|
||||||
selecting: {
|
|
||||||
on: {
|
|
||||||
selected: {
|
|
||||||
target: 'prepareTranslateCommand',
|
|
||||||
reenter: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
done: {
|
|
||||||
always: '#featureTree.idle',
|
|
||||||
},
|
|
||||||
|
|
||||||
prepareTranslateCommand: {
|
|
||||||
invoke: {
|
|
||||||
src: 'prepareTranslateCommand',
|
|
||||||
input: ({ context }) => {
|
|
||||||
const artifact = context.targetSourceRange
|
|
||||||
? (getArtifactFromRange(
|
|
||||||
context.targetSourceRange,
|
|
||||||
kclManager.artifactGraph
|
|
||||||
) ?? undefined)
|
|
||||||
: undefined
|
|
||||||
return {
|
|
||||||
// currentOperation is guaranteed to be defined here
|
|
||||||
operation: context.currentOperation!,
|
|
||||||
artifact,
|
|
||||||
commandBarSend: commandBarActor.send,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onDone: {
|
|
||||||
target: 'done',
|
|
||||||
reenter: true,
|
|
||||||
},
|
|
||||||
onError: {
|
|
||||||
target: 'done',
|
|
||||||
reenter: true,
|
|
||||||
actions: ({ event }) => {
|
|
||||||
if ('error' in event && err(event.error)) {
|
|
||||||
toast.error(event.error.message)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
initial: 'selecting',
|
|
||||||
entry: 'sendSelectionEvent',
|
|
||||||
exit: ['clearContext'],
|
|
||||||
},
|
|
||||||
|
|
||||||
enteringRotateFlow: {
|
|
||||||
states: {
|
|
||||||
selecting: {
|
|
||||||
on: {
|
|
||||||
selected: {
|
|
||||||
target: 'prepareRotateCommand',
|
|
||||||
reenter: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
done: {
|
|
||||||
always: '#featureTree.idle',
|
|
||||||
},
|
|
||||||
|
|
||||||
prepareRotateCommand: {
|
|
||||||
invoke: {
|
|
||||||
src: 'prepareRotateCommand',
|
|
||||||
input: ({ context }) => {
|
|
||||||
const artifact = context.targetSourceRange
|
|
||||||
? (getArtifactFromRange(
|
|
||||||
context.targetSourceRange,
|
|
||||||
kclManager.artifactGraph
|
|
||||||
) ?? undefined)
|
|
||||||
: undefined
|
|
||||||
return {
|
|
||||||
// currentOperation is guaranteed to be defined here
|
|
||||||
operation: context.currentOperation!,
|
|
||||||
artifact,
|
|
||||||
commandBarSend: commandBarActor.send,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onDone: {
|
|
||||||
target: 'done',
|
|
||||||
reenter: true,
|
|
||||||
},
|
|
||||||
onError: {
|
|
||||||
target: 'done',
|
|
||||||
reenter: true,
|
|
||||||
actions: ({ event }) => {
|
|
||||||
if ('error' in event && err(event.error)) {
|
|
||||||
toast.error(event.error.message)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
initial: 'selecting',
|
|
||||||
entry: 'sendSelectionEvent',
|
|
||||||
exit: ['clearContext'],
|
|
||||||
},
|
|
||||||
|
|
||||||
enteringCloneFlow: {
|
|
||||||
states: {
|
|
||||||
selecting: {
|
|
||||||
on: {
|
|
||||||
selected: {
|
|
||||||
target: 'prepareCloneCommand',
|
|
||||||
reenter: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
done: {
|
|
||||||
always: '#featureTree.idle',
|
|
||||||
},
|
|
||||||
|
|
||||||
prepareCloneCommand: {
|
|
||||||
invoke: {
|
|
||||||
src: 'prepareCloneCommand',
|
|
||||||
input: ({ context }) => {
|
|
||||||
const artifact = context.targetSourceRange
|
|
||||||
? (getArtifactFromRange(
|
|
||||||
context.targetSourceRange,
|
|
||||||
kclManager.artifactGraph
|
|
||||||
) ?? undefined)
|
|
||||||
: undefined
|
|
||||||
return {
|
|
||||||
// currentOperation is guaranteed to be defined here
|
|
||||||
operation: context.currentOperation!,
|
|
||||||
artifact,
|
|
||||||
commandBarSend: commandBarActor.send,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onDone: {
|
|
||||||
target: 'done',
|
|
||||||
reenter: true,
|
|
||||||
},
|
|
||||||
onError: {
|
|
||||||
target: 'done',
|
|
||||||
reenter: true,
|
|
||||||
actions: ({ event }) => {
|
|
||||||
if ('error' in event && err(event.error)) {
|
|
||||||
toast.error(event.error.message)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
initial: 'selecting',
|
|
||||||
entry: 'sendSelectionEvent',
|
|
||||||
exit: ['clearContext'],
|
|
||||||
},
|
|
||||||
|
|
||||||
deletingOperation: {
|
deletingOperation: {
|
||||||
states: {
|
states: {
|
||||||
selecting: {
|
selecting: {
|
||||||
|
@ -11,7 +11,7 @@ import {
|
|||||||
engineCommandManager,
|
engineCommandManager,
|
||||||
kclManager,
|
kclManager,
|
||||||
} from '@src/lib/singletons'
|
} from '@src/lib/singletons'
|
||||||
import { VITE_KC_DEV_TOKEN } from '@src/env'
|
import { VITE_KITTYCAD_API_TOKEN } from '@src/env'
|
||||||
import { getConstraintInfoKw } from '@src/lang/std/sketch'
|
import { getConstraintInfoKw } from '@src/lang/std/sketch'
|
||||||
import { getNodeFromPath } from '@src/lang/queryAst'
|
import { getNodeFromPath } from '@src/lang/queryAst'
|
||||||
import type { Node } from '@rust/kcl-lib/bindings/Node'
|
import type { Node } from '@rust/kcl-lib/bindings/Node'
|
||||||
@ -29,10 +29,9 @@ import { removeSingleConstraintInfo } from '@src/lang/modifyAst'
|
|||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
await initPromise
|
await initPromise
|
||||||
|
|
||||||
// THESE TEST WILL FAIL without VITE_KC_DEV_TOKEN set in .env.development.local
|
|
||||||
await new Promise((resolve) => {
|
await new Promise((resolve) => {
|
||||||
engineCommandManager.start({
|
engineCommandManager.start({
|
||||||
token: VITE_KC_DEV_TOKEN,
|
token: VITE_KITTYCAD_API_TOKEN,
|
||||||
width: 256,
|
width: 256,
|
||||||
height: 256,
|
height: 256,
|
||||||
setMediaStream: () => {},
|
setMediaStream: () => {},
|
||||||
|
@ -47,12 +47,10 @@ import { angleLengthInfo } from '@src/components/Toolbar/angleLengthInfo'
|
|||||||
import { createLiteral, createLocalName } from '@src/lang/create'
|
import { createLiteral, createLocalName } from '@src/lang/create'
|
||||||
import { updateModelingState } from '@src/lang/modelingWorkflows'
|
import { updateModelingState } from '@src/lang/modelingWorkflows'
|
||||||
import {
|
import {
|
||||||
addClone,
|
|
||||||
addHelix,
|
addHelix,
|
||||||
addOffsetPlane,
|
addOffsetPlane,
|
||||||
addShell,
|
addShell,
|
||||||
insertNamedConstant,
|
insertNamedConstant,
|
||||||
insertVariableAndOffsetPathToNode,
|
|
||||||
replaceValueAtNodePath,
|
replaceValueAtNodePath,
|
||||||
} from '@src/lang/modifyAst'
|
} from '@src/lang/modifyAst'
|
||||||
import type {
|
import type {
|
||||||
@ -72,7 +70,7 @@ import {
|
|||||||
addRevolve,
|
addRevolve,
|
||||||
addSweep,
|
addSweep,
|
||||||
getAxisExpressionAndIndex,
|
getAxisExpressionAndIndex,
|
||||||
} from '@src/lang/modifyAst/addSweep'
|
} from '@src/lang/modifyAst/sweeps'
|
||||||
import {
|
import {
|
||||||
applyIntersectFromTargetOperatorSelections,
|
applyIntersectFromTargetOperatorSelections,
|
||||||
applySubtractFromTargetOperatorSelections,
|
applySubtractFromTargetOperatorSelections,
|
||||||
@ -84,15 +82,13 @@ import {
|
|||||||
} from '@src/lang/modifyAst/deleteSelection'
|
} from '@src/lang/modifyAst/deleteSelection'
|
||||||
import { setAppearance } from '@src/lang/modifyAst/setAppearance'
|
import { setAppearance } from '@src/lang/modifyAst/setAppearance'
|
||||||
import {
|
import {
|
||||||
setTranslate,
|
addTranslate,
|
||||||
setRotate,
|
addRotate,
|
||||||
insertExpressionNode,
|
addScale,
|
||||||
retrievePathToNodeFromTransformSelection,
|
addClone,
|
||||||
} from '@src/lang/modifyAst/setTransform'
|
} from '@src/lang/modifyAst/transforms'
|
||||||
import {
|
import {
|
||||||
getNodeFromPath,
|
getNodeFromPath,
|
||||||
findPipesWithImportAlias,
|
|
||||||
findImportNodeAndAlias,
|
|
||||||
isNodeSafeToReplacePath,
|
isNodeSafeToReplacePath,
|
||||||
stringifyPathToNode,
|
stringifyPathToNode,
|
||||||
updatePathToNodesAfterEdit,
|
updatePathToNodesAfterEdit,
|
||||||
@ -115,7 +111,6 @@ import type {
|
|||||||
Literal,
|
Literal,
|
||||||
Name,
|
Name,
|
||||||
PathToNode,
|
PathToNode,
|
||||||
PipeExpression,
|
|
||||||
Program,
|
Program,
|
||||||
VariableDeclaration,
|
VariableDeclaration,
|
||||||
VariableDeclarator,
|
VariableDeclarator,
|
||||||
@ -143,7 +138,6 @@ import {
|
|||||||
import type { ToolbarModeName } from '@src/lib/toolbar'
|
import type { ToolbarModeName } from '@src/lib/toolbar'
|
||||||
import { err, reportRejection, trap } from '@src/lib/trap'
|
import { err, reportRejection, trap } from '@src/lib/trap'
|
||||||
import { uuidv4 } from '@src/lib/utils'
|
import { uuidv4 } from '@src/lib/utils'
|
||||||
import type { ImportStatement } from '@rust/kcl-lib/bindings/ImportStatement'
|
|
||||||
import { isDesktop } from '@src/lib/isDesktop'
|
import { isDesktop } from '@src/lib/isDesktop'
|
||||||
import {
|
import {
|
||||||
crossProduct,
|
crossProduct,
|
||||||
@ -403,6 +397,7 @@ export type ModelingMachineEvent =
|
|||||||
| { type: 'Appearance'; data: ModelingCommandSchema['Appearance'] }
|
| { type: 'Appearance'; data: ModelingCommandSchema['Appearance'] }
|
||||||
| { type: 'Translate'; data: ModelingCommandSchema['Translate'] }
|
| { type: 'Translate'; data: ModelingCommandSchema['Translate'] }
|
||||||
| { type: 'Rotate'; data: ModelingCommandSchema['Rotate'] }
|
| { type: 'Rotate'; data: ModelingCommandSchema['Rotate'] }
|
||||||
|
| { type: 'Scale'; data: ModelingCommandSchema['Scale'] }
|
||||||
| { type: 'Clone'; data: ModelingCommandSchema['Clone'] }
|
| { type: 'Clone'; data: ModelingCommandSchema['Clone'] }
|
||||||
| {
|
| {
|
||||||
type:
|
type:
|
||||||
@ -3317,57 +3312,11 @@ export const modelingMachine = setup({
|
|||||||
}
|
}
|
||||||
|
|
||||||
const ast = kclManager.ast
|
const ast = kclManager.ast
|
||||||
const modifiedAst = structuredClone(ast)
|
const artifactGraph = kclManager.artifactGraph
|
||||||
const { x, y, z, nodeToEdit, selection } = input
|
const result = addTranslate({
|
||||||
let pathToNode = nodeToEdit
|
...input,
|
||||||
if (!(pathToNode && typeof pathToNode[1][0] === 'number')) {
|
ast,
|
||||||
const result = retrievePathToNodeFromTransformSelection(
|
artifactGraph,
|
||||||
selection,
|
|
||||||
kclManager.artifactGraph,
|
|
||||||
ast
|
|
||||||
)
|
|
||||||
if (err(result)) {
|
|
||||||
return Promise.reject(result)
|
|
||||||
}
|
|
||||||
|
|
||||||
pathToNode = result
|
|
||||||
}
|
|
||||||
|
|
||||||
// Look for the last pipe with the import alias and a call to translate, with a fallback to rotate.
|
|
||||||
// Otherwise create one
|
|
||||||
const importNodeAndAlias = findImportNodeAndAlias(ast, pathToNode)
|
|
||||||
if (importNodeAndAlias) {
|
|
||||||
const pipes = findPipesWithImportAlias(ast, pathToNode, 'translate')
|
|
||||||
const lastPipe = pipes.at(-1)
|
|
||||||
if (lastPipe && lastPipe.pathToNode) {
|
|
||||||
pathToNode = lastPipe.pathToNode
|
|
||||||
} else {
|
|
||||||
const otherRelevantPipes = findPipesWithImportAlias(
|
|
||||||
ast,
|
|
||||||
pathToNode,
|
|
||||||
'rotate'
|
|
||||||
)
|
|
||||||
const lastRelevantPipe = otherRelevantPipes.at(-1)
|
|
||||||
if (lastRelevantPipe && lastRelevantPipe.pathToNode) {
|
|
||||||
pathToNode = lastRelevantPipe.pathToNode
|
|
||||||
} else {
|
|
||||||
pathToNode = insertExpressionNode(
|
|
||||||
modifiedAst,
|
|
||||||
importNodeAndAlias.alias
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
insertVariableAndOffsetPathToNode(x, modifiedAst, pathToNode)
|
|
||||||
insertVariableAndOffsetPathToNode(y, modifiedAst, pathToNode)
|
|
||||||
insertVariableAndOffsetPathToNode(z, modifiedAst, pathToNode)
|
|
||||||
const result = setTranslate({
|
|
||||||
pathToNode,
|
|
||||||
modifiedAst,
|
|
||||||
x: valueOrVariable(x),
|
|
||||||
y: valueOrVariable(y),
|
|
||||||
z: valueOrVariable(z),
|
|
||||||
})
|
})
|
||||||
if (err(result)) {
|
if (err(result)) {
|
||||||
return Promise.reject(result)
|
return Promise.reject(result)
|
||||||
@ -3398,57 +3347,46 @@ export const modelingMachine = setup({
|
|||||||
}
|
}
|
||||||
|
|
||||||
const ast = kclManager.ast
|
const ast = kclManager.ast
|
||||||
const modifiedAst = structuredClone(ast)
|
const artifactGraph = kclManager.artifactGraph
|
||||||
const { roll, pitch, yaw, nodeToEdit, selection } = input
|
const result = addRotate({
|
||||||
let pathToNode = nodeToEdit
|
...input,
|
||||||
if (!(pathToNode && typeof pathToNode[1][0] === 'number')) {
|
ast,
|
||||||
const result = retrievePathToNodeFromTransformSelection(
|
artifactGraph,
|
||||||
selection,
|
})
|
||||||
kclManager.artifactGraph,
|
if (err(result)) {
|
||||||
ast
|
return Promise.reject(result)
|
||||||
)
|
|
||||||
if (err(result)) {
|
|
||||||
return Promise.reject(result)
|
|
||||||
}
|
|
||||||
|
|
||||||
pathToNode = result
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Look for the last pipe with the import alias and a call to rotate, with a fallback to translate.
|
await updateModelingState(
|
||||||
// Otherwise create one
|
result.modifiedAst,
|
||||||
const importNodeAndAlias = findImportNodeAndAlias(ast, pathToNode)
|
EXECUTION_TYPE_REAL,
|
||||||
if (importNodeAndAlias) {
|
{
|
||||||
const pipes = findPipesWithImportAlias(ast, pathToNode, 'rotate')
|
kclManager,
|
||||||
const lastPipe = pipes.at(-1)
|
editorManager,
|
||||||
if (lastPipe && lastPipe.pathToNode) {
|
codeManager,
|
||||||
pathToNode = lastPipe.pathToNode
|
},
|
||||||
} else {
|
{
|
||||||
const otherRelevantPipes = findPipesWithImportAlias(
|
focusPath: [result.pathToNode],
|
||||||
ast,
|
|
||||||
pathToNode,
|
|
||||||
'translate'
|
|
||||||
)
|
|
||||||
const lastRelevantPipe = otherRelevantPipes.at(-1)
|
|
||||||
if (lastRelevantPipe && lastRelevantPipe.pathToNode) {
|
|
||||||
pathToNode = lastRelevantPipe.pathToNode
|
|
||||||
} else {
|
|
||||||
pathToNode = insertExpressionNode(
|
|
||||||
modifiedAst,
|
|
||||||
importNodeAndAlias.alias
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
),
|
||||||
|
scaleAstMod: fromPromise(
|
||||||
|
async ({
|
||||||
|
input,
|
||||||
|
}: {
|
||||||
|
input: ModelingCommandSchema['Scale'] | undefined
|
||||||
|
}) => {
|
||||||
|
if (!input) {
|
||||||
|
return Promise.reject(new Error(NO_INPUT_PROVIDED_MESSAGE))
|
||||||
}
|
}
|
||||||
|
|
||||||
insertVariableAndOffsetPathToNode(roll, modifiedAst, pathToNode)
|
const ast = kclManager.ast
|
||||||
insertVariableAndOffsetPathToNode(pitch, modifiedAst, pathToNode)
|
const artifactGraph = kclManager.artifactGraph
|
||||||
insertVariableAndOffsetPathToNode(yaw, modifiedAst, pathToNode)
|
const result = addScale({
|
||||||
const result = setRotate({
|
...input,
|
||||||
pathToNode,
|
ast,
|
||||||
modifiedAst,
|
artifactGraph,
|
||||||
roll: valueOrVariable(roll),
|
|
||||||
pitch: valueOrVariable(pitch),
|
|
||||||
yaw: valueOrVariable(yaw),
|
|
||||||
})
|
})
|
||||||
if (err(result)) {
|
if (err(result)) {
|
||||||
return Promise.reject(result)
|
return Promise.reject(result)
|
||||||
@ -3479,58 +3417,14 @@ export const modelingMachine = setup({
|
|||||||
}
|
}
|
||||||
|
|
||||||
const ast = kclManager.ast
|
const ast = kclManager.ast
|
||||||
const { nodeToEdit, selection, variableName } = input
|
const artifactGraph = kclManager.artifactGraph
|
||||||
let pathToNode = nodeToEdit
|
|
||||||
if (!(pathToNode && typeof pathToNode[1][0] === 'number')) {
|
|
||||||
const result = retrievePathToNodeFromTransformSelection(
|
|
||||||
selection,
|
|
||||||
kclManager.artifactGraph,
|
|
||||||
ast
|
|
||||||
)
|
|
||||||
if (err(result)) {
|
|
||||||
return Promise.reject(result)
|
|
||||||
}
|
|
||||||
|
|
||||||
pathToNode = result
|
|
||||||
}
|
|
||||||
|
|
||||||
const returnEarly = true
|
|
||||||
const geometryNode = getNodeFromPath<
|
|
||||||
VariableDeclaration | ImportStatement | PipeExpression
|
|
||||||
>(
|
|
||||||
ast,
|
|
||||||
pathToNode,
|
|
||||||
['VariableDeclaration', 'ImportStatement', 'PipeExpression'],
|
|
||||||
returnEarly
|
|
||||||
)
|
|
||||||
if (err(geometryNode)) {
|
|
||||||
return Promise.reject(
|
|
||||||
new Error("Couldn't find corresponding path to node")
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
let geometryName: string | undefined
|
|
||||||
if (geometryNode.node.type === 'VariableDeclaration') {
|
|
||||||
geometryName = geometryNode.node.declaration.id.name
|
|
||||||
} else if (
|
|
||||||
geometryNode.node.type === 'ImportStatement' &&
|
|
||||||
geometryNode.node.selector.type === 'None' &&
|
|
||||||
geometryNode.node.selector.alias
|
|
||||||
) {
|
|
||||||
geometryName = geometryNode.node.selector.alias?.name
|
|
||||||
} else {
|
|
||||||
return Promise.reject(
|
|
||||||
new Error("Couldn't find corresponding geometry")
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
const result = addClone({
|
const result = addClone({
|
||||||
|
...input,
|
||||||
ast,
|
ast,
|
||||||
geometryName,
|
artifactGraph,
|
||||||
variableName,
|
|
||||||
})
|
})
|
||||||
if (err(result)) {
|
if (err(result)) {
|
||||||
return Promise.reject(err(result))
|
return Promise.reject(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
await updateModelingState(
|
await updateModelingState(
|
||||||
@ -3863,6 +3757,12 @@ export const modelingMachine = setup({
|
|||||||
guard: 'no kcl errors',
|
guard: 'no kcl errors',
|
||||||
},
|
},
|
||||||
|
|
||||||
|
Scale: {
|
||||||
|
target: 'Applying scale',
|
||||||
|
reenter: true,
|
||||||
|
guard: 'no kcl errors',
|
||||||
|
},
|
||||||
|
|
||||||
Clone: {
|
Clone: {
|
||||||
target: 'Applying clone',
|
target: 'Applying clone',
|
||||||
reenter: true,
|
reenter: true,
|
||||||
@ -5345,6 +5245,22 @@ export const modelingMachine = setup({
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
'Applying scale': {
|
||||||
|
invoke: {
|
||||||
|
src: 'scaleAstMod',
|
||||||
|
id: 'scaleAstMod',
|
||||||
|
input: ({ event }) => {
|
||||||
|
if (event.type !== 'Scale') return undefined
|
||||||
|
return event.data
|
||||||
|
},
|
||||||
|
onDone: ['idle'],
|
||||||
|
onError: {
|
||||||
|
target: 'idle',
|
||||||
|
actions: 'toastError',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
'Applying clone': {
|
'Applying clone': {
|
||||||
invoke: {
|
invoke: {
|
||||||
src: 'cloneAstMod',
|
src: 'cloneAstMod',
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { engineCommandManager, kclManager } from '@src/lib/singletons'
|
import { engineCommandManager, kclManager } from '@src/lib/singletons'
|
||||||
import { VITE_KC_DEV_TOKEN } from '@src/env'
|
import { VITE_KITTYCAD_API_TOKEN } from '@src/env'
|
||||||
import { getModuleIdByFileName, isArray } from '@src/lib/utils'
|
import { getModuleIdByFileName, isArray } from '@src/lib/utils'
|
||||||
import { vi, inject } from 'vitest'
|
import { vi, inject } from 'vitest'
|
||||||
import { assertParse } from '@src/lang/wasm'
|
import { assertParse } from '@src/lang/wasm'
|
||||||
@ -355,10 +355,9 @@ cases.push(
|
|||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
await initPromise
|
await initPromise
|
||||||
|
|
||||||
// THESE TEST WILL FAIL without VITE_KC_DEV_TOKEN set in .env.development.local
|
|
||||||
await new Promise((resolve) => {
|
await new Promise((resolve) => {
|
||||||
engineCommandManager.start({
|
engineCommandManager.start({
|
||||||
token: VITE_KC_DEV_TOKEN,
|
token: VITE_KITTYCAD_API_TOKEN,
|
||||||
width: 256,
|
width: 256,
|
||||||
height: 256,
|
height: 256,
|
||||||
setMediaStream: () => {},
|
setMediaStream: () => {},
|
||||||
|
@ -70,12 +70,10 @@ dotenv.config({ path: [`.env.${NODE_ENV}.local`, `.env.${NODE_ENV}`] })
|
|||||||
|
|
||||||
// default vite values based on mode
|
// default vite values based on mode
|
||||||
process.env.NODE_ENV ??= viteEnv.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_KC_API_WS_MODELING_URL ??= viteEnv.VITE_KC_API_WS_MODELING_URL
|
||||||
process.env.VITE_KC_API_BASE_URL ??= viteEnv.VITE_KC_API_BASE_URL
|
process.env.VITE_KITTYCAD_API_BASE_URL ??= viteEnv.VITE_KITTYCAD_API_BASE_URL
|
||||||
process.env.VITE_KC_SITE_BASE_URL ??= viteEnv.VITE_KC_SITE_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_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 ??=
|
process.env.VITE_KC_CONNECTION_TIMEOUT_MS ??=
|
||||||
viteEnv.VITE_KC_CONNECTION_TIMEOUT_MS
|
viteEnv.VITE_KC_CONNECTION_TIMEOUT_MS
|
||||||
|
|
||||||
|
@ -289,12 +289,11 @@ contextBridge.exposeInMainWorld('electron', {
|
|||||||
exposeProcessEnvs([
|
exposeProcessEnvs([
|
||||||
'NODE_ENV',
|
'NODE_ENV',
|
||||||
'VITE_KC_API_WS_MODELING_URL',
|
'VITE_KC_API_WS_MODELING_URL',
|
||||||
'VITE_KC_API_BASE_URL',
|
'VITE_KITTYCAD_API_BASE_URL',
|
||||||
'VITE_KC_SITE_BASE_URL',
|
'VITE_KC_SITE_BASE_URL',
|
||||||
'VITE_KC_SITE_APP_URL',
|
'VITE_KC_SITE_APP_URL',
|
||||||
'VITE_KC_SKIP_AUTH',
|
|
||||||
'VITE_KC_CONNECTION_TIMEOUT_MS',
|
'VITE_KC_CONNECTION_TIMEOUT_MS',
|
||||||
'VITE_KC_DEV_TOKEN',
|
'VITE_KITTYCAD_API_TOKEN',
|
||||||
|
|
||||||
'IS_PLAYWRIGHT',
|
'IS_PLAYWRIGHT',
|
||||||
|
|
||||||
|
@ -6,7 +6,7 @@ import { Link } from 'react-router-dom'
|
|||||||
import { ActionButton } from '@src/components/ActionButton'
|
import { ActionButton } from '@src/components/ActionButton'
|
||||||
import { CustomIcon } from '@src/components/CustomIcon'
|
import { CustomIcon } from '@src/components/CustomIcon'
|
||||||
import { Logo } from '@src/components/Logo'
|
import { Logo } from '@src/components/Logo'
|
||||||
import { VITE_KC_API_BASE_URL, VITE_KC_SITE_BASE_URL } from '@src/env'
|
import { VITE_KC_SITE_BASE_URL } from '@src/env'
|
||||||
import { APP_NAME } from '@src/lib/constants'
|
import { APP_NAME } from '@src/lib/constants'
|
||||||
import { isDesktop } from '@src/lib/isDesktop'
|
import { isDesktop } from '@src/lib/isDesktop'
|
||||||
import { openExternalBrowserIfDesktop } from '@src/lib/openWindow'
|
import { openExternalBrowserIfDesktop } from '@src/lib/openWindow'
|
||||||
@ -15,6 +15,7 @@ import { reportRejection } from '@src/lib/trap'
|
|||||||
import { toSync } from '@src/lib/utils'
|
import { toSync } from '@src/lib/utils'
|
||||||
import { authActor, useSettings } from '@src/lib/singletons'
|
import { authActor, useSettings } from '@src/lib/singletons'
|
||||||
import { APP_VERSION, generateSignInUrl } from '@src/routes/utils'
|
import { APP_VERSION, generateSignInUrl } from '@src/routes/utils'
|
||||||
|
import { withAPIBaseURL } from '@src/lib/withBaseURL'
|
||||||
|
|
||||||
const subtleBorder =
|
const subtleBorder =
|
||||||
'border border-solid border-chalkboard-30 dark:border-chalkboard-80'
|
'border border-solid border-chalkboard-30 dark:border-chalkboard-80'
|
||||||
@ -54,7 +55,7 @@ const SignIn = () => {
|
|||||||
const signInDesktop = async () => {
|
const signInDesktop = async () => {
|
||||||
// We want to invoke our command to login via device auth.
|
// We want to invoke our command to login via device auth.
|
||||||
const userCodeToDisplay = await window.electron
|
const userCodeToDisplay = await window.electron
|
||||||
.startDeviceFlow(VITE_KC_API_BASE_URL + location.search)
|
.startDeviceFlow(withAPIBaseURL(location.search))
|
||||||
.catch(reportError)
|
.catch(reportError)
|
||||||
if (!userCodeToDisplay) {
|
if (!userCodeToDisplay) {
|
||||||
console.error('No user code received while trying to log in')
|
console.error('No user code received while trying to log in')
|
||||||
|
Reference in New Issue
Block a user