Compare commits
15 Commits
v0.40.0
...
pierremtb/
| Author | SHA1 | Date | |
|---|---|---|---|
| 82d1fe5436 | |||
| 3e8ee3ffc4 | |||
| a44516bc7e | |||
| ce62fe67cf | |||
| 763a1b6628 | |||
| 3281e62e6b | |||
| f1a458f124 | |||
| 229433126d | |||
| b962b5fcb3 | |||
| 428d125139 | |||
| cffeb52b4b | |||
| e0ef10e7bb | |||
| 7095ce2377 | |||
| bfb0bd6997 | |||
| 7aa6e58121 |
@ -2,8 +2,8 @@ NODE_ENV=development
|
|||||||
DEV=true
|
DEV=true
|
||||||
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_KC_API_BASE_URL=https://api.dev.zoo.dev
|
||||||
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_SKIP_AUTH=false
|
VITE_KC_SKIP_AUTH=false
|
||||||
VITE_KC_CONNECTION_TIMEOUT_MS=5000
|
VITE_KC_CONNECTION_TIMEOUT_MS=5000
|
||||||
# ONLY add your token in .env.development.local if you want to skip auth, otherwise this token takes precedence!
|
# ONLY add your token in .env.development.local if you want to skip auth, otherwise this token takes precedence!
|
||||||
|
|||||||
@ -1,5 +1,8 @@
|
|||||||
|
NODE_ENV=production
|
||||||
|
DEV=false
|
||||||
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_KC_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_SKIP_AUTH=false
|
VITE_KC_SKIP_AUTH=false
|
||||||
VITE_KC_CONNECTION_TIMEOUT_MS=15000
|
VITE_KC_CONNECTION_TIMEOUT_MS=15000
|
||||||
|
|||||||
16
.github/workflows/build-apps.yml
vendored
@ -134,8 +134,6 @@ jobs:
|
|||||||
max_attempts: 3
|
max_attempts: 3
|
||||||
command: yarn install
|
command: yarn install
|
||||||
|
|
||||||
- run: yarn tronb:vite
|
|
||||||
|
|
||||||
- name: Prepare certificate and variables (Windows only)
|
- name: Prepare certificate and variables (Windows only)
|
||||||
if: ${{ (env.IS_RELEASE == 'true' || env.IS_NIGHTLY == 'true') && matrix.os == 'windows-2022' }}
|
if: ${{ (env.IS_RELEASE == 'true' || env.IS_NIGHTLY == 'true') && matrix.os == 'windows-2022' }}
|
||||||
run: |
|
run: |
|
||||||
@ -165,8 +163,8 @@ jobs:
|
|||||||
- name: Build the app (debug)
|
- name: Build the app (debug)
|
||||||
if: ${{ env.IS_RELEASE == 'false' && env.IS_NIGHTLY == 'false' }}
|
if: ${{ env.IS_RELEASE == 'false' && env.IS_NIGHTLY == 'false' }}
|
||||||
# electron-builder doesn't have a concept of release vs debug,
|
# electron-builder doesn't have a concept of release vs debug,
|
||||||
# this is just not doing any codesign or release yml generation
|
# this is just not doing any codesign or release yml generation, and points to dev infra
|
||||||
run: yarn electron-builder --config
|
run: yarn tronb:package:dev
|
||||||
|
|
||||||
- name: Build the app (release)
|
- name: Build the app (release)
|
||||||
if: ${{ env.IS_RELEASE == 'true' || env.IS_NIGHTLY == 'true' }}
|
if: ${{ env.IS_RELEASE == 'true' || env.IS_NIGHTLY == 'true' }}
|
||||||
@ -185,7 +183,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
timeout_minutes: 10
|
timeout_minutes: 10
|
||||||
max_attempts: 3
|
max_attempts: 3
|
||||||
command: yarn electron-builder --config --publish always
|
command: yarn tronb:package:prod
|
||||||
|
|
||||||
- name: List artifacts in out/
|
- name: List artifacts in out/
|
||||||
run: ls -R out
|
run: ls -R out
|
||||||
@ -246,7 +244,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
timeout_minutes: 10
|
timeout_minutes: 10
|
||||||
max_attempts: 3
|
max_attempts: 3
|
||||||
command: yarn electron-builder --config --publish always
|
command: yarn tronb:package:prod
|
||||||
|
|
||||||
- uses: actions/upload-artifact@v4
|
- uses: actions/upload-artifact@v4
|
||||||
if: ${{ env.IS_RELEASE == 'true' }}
|
if: ${{ env.IS_RELEASE == 'true' }}
|
||||||
@ -390,19 +388,19 @@ jobs:
|
|||||||
|
|
||||||
- name: Authenticate to Google Cloud
|
- name: Authenticate to Google Cloud
|
||||||
if: ${{ env.IS_NIGHTLY == 'true' }}
|
if: ${{ env.IS_NIGHTLY == 'true' }}
|
||||||
uses: 'google-github-actions/auth@v2.1.7'
|
uses: 'google-github-actions/auth@v2.1.8'
|
||||||
with:
|
with:
|
||||||
credentials_json: '${{ secrets.GOOGLE_CLOUD_DL_SA }}'
|
credentials_json: '${{ secrets.GOOGLE_CLOUD_DL_SA }}'
|
||||||
|
|
||||||
- name: Set up Google Cloud SDK
|
- name: Set up Google Cloud SDK
|
||||||
if: ${{ env.IS_NIGHTLY == 'true' }}
|
if: ${{ env.IS_NIGHTLY == 'true' }}
|
||||||
uses: google-github-actions/setup-gcloud@v2.1.2
|
uses: google-github-actions/setup-gcloud@v2.1.4
|
||||||
with:
|
with:
|
||||||
project_id: ${{ env.GOOGLE_CLOUD_PROJECT_ID }}
|
project_id: ${{ env.GOOGLE_CLOUD_PROJECT_ID }}
|
||||||
|
|
||||||
- name: Upload nightly files to public bucket
|
- name: Upload nightly files to public bucket
|
||||||
if: ${{ env.IS_NIGHTLY == 'true' }}
|
if: ${{ env.IS_NIGHTLY == 'true' }}
|
||||||
uses: google-github-actions/upload-cloud-storage@v2.2.1
|
uses: google-github-actions/upload-cloud-storage@v2.2.2
|
||||||
with:
|
with:
|
||||||
path: out
|
path: out
|
||||||
glob: '*'
|
glob: '*'
|
||||||
|
|||||||
4
.github/workflows/e2e-tests.yml
vendored
@ -123,9 +123,9 @@ jobs:
|
|||||||
if: steps.download-wasm.outcome == 'failure'
|
if: steps.download-wasm.outcome == 'failure'
|
||||||
shell: bash
|
shell: bash
|
||||||
run: yarn build:wasm
|
run: yarn build:wasm
|
||||||
- name: build electron
|
- name: build web
|
||||||
shell: bash
|
shell: bash
|
||||||
run: yarn tron:package
|
run: yarn tronb:vite:dev
|
||||||
# - name: Run ubuntu/chrome snapshots
|
# - name: Run ubuntu/chrome snapshots
|
||||||
# if: ${{ matrix.os == 'namespace-profile-ubuntu-8-cores' && matrix.shardIndex == 1 }}
|
# if: ${{ matrix.os == 'namespace-profile-ubuntu-8-cores' && matrix.shardIndex == 1 }}
|
||||||
# shell: bash
|
# shell: bash
|
||||||
|
|||||||
6
.github/workflows/publish-apps-release.yml
vendored
@ -108,17 +108,17 @@ jobs:
|
|||||||
run: yarn files:set-notes
|
run: yarn files:set-notes
|
||||||
|
|
||||||
- name: Authenticate to Google Cloud
|
- name: Authenticate to Google Cloud
|
||||||
uses: 'google-github-actions/auth@v2.1.7'
|
uses: 'google-github-actions/auth@v2.1.8'
|
||||||
with:
|
with:
|
||||||
credentials_json: '${{ secrets.GOOGLE_CLOUD_DL_SA }}'
|
credentials_json: '${{ secrets.GOOGLE_CLOUD_DL_SA }}'
|
||||||
|
|
||||||
- name: Set up Google Cloud SDK
|
- name: Set up Google Cloud SDK
|
||||||
uses: google-github-actions/setup-gcloud@v2.1.2
|
uses: google-github-actions/setup-gcloud@v2.1.4
|
||||||
with:
|
with:
|
||||||
project_id: ${{ env.GOOGLE_CLOUD_PROJECT_ID }}
|
project_id: ${{ env.GOOGLE_CLOUD_PROJECT_ID }}
|
||||||
|
|
||||||
- name: Upload release files to public bucket
|
- name: Upload release files to public bucket
|
||||||
uses: google-github-actions/upload-cloud-storage@v2.2.1
|
uses: google-github-actions/upload-cloud-storage@v2.2.2
|
||||||
with:
|
with:
|
||||||
path: out
|
path: out
|
||||||
glob: '*'
|
glob: '*'
|
||||||
|
|||||||
@ -101,7 +101,7 @@ This will start the application and hot-reload on changes.
|
|||||||
|
|
||||||
Devtools can be opened with the usual Cmd-Opt-I (Mac) or Ctrl-Shift-I (Linux and Windows).
|
Devtools can be opened with the usual Cmd-Opt-I (Mac) or Ctrl-Shift-I (Linux and Windows).
|
||||||
|
|
||||||
To build, run `yarn tron:package`.
|
To build with electron-builder, run `yarn tronb:package:dev` (or `yarn tronb:package:prod` to point to the .env.production variables)
|
||||||
|
|
||||||
## Checking out commits / Bisecting
|
## Checking out commits / Bisecting
|
||||||
|
|
||||||
|
|||||||
@ -18,6 +18,7 @@ export class ToolbarFixture {
|
|||||||
filletButton!: Locator
|
filletButton!: Locator
|
||||||
chamferButton!: Locator
|
chamferButton!: Locator
|
||||||
shellButton!: Locator
|
shellButton!: Locator
|
||||||
|
revolveButton!: Locator
|
||||||
offsetPlaneButton!: Locator
|
offsetPlaneButton!: Locator
|
||||||
startSketchBtn!: Locator
|
startSketchBtn!: Locator
|
||||||
lineBtn!: Locator
|
lineBtn!: Locator
|
||||||
@ -47,6 +48,7 @@ export class ToolbarFixture {
|
|||||||
this.filletButton = page.getByTestId('fillet3d')
|
this.filletButton = page.getByTestId('fillet3d')
|
||||||
this.chamferButton = page.getByTestId('chamfer3d')
|
this.chamferButton = page.getByTestId('chamfer3d')
|
||||||
this.shellButton = page.getByTestId('shell')
|
this.shellButton = page.getByTestId('shell')
|
||||||
|
this.revolveButton = page.getByTestId('revolve')
|
||||||
this.offsetPlaneButton = page.getByTestId('plane-offset')
|
this.offsetPlaneButton = page.getByTestId('plane-offset')
|
||||||
this.startSketchBtn = page.getByTestId('sketch')
|
this.startSketchBtn = page.getByTestId('sketch')
|
||||||
this.lineBtn = page.getByTestId('line')
|
this.lineBtn = page.getByTestId('line')
|
||||||
|
|||||||
@ -1183,7 +1183,7 @@ extrude001 = extrude(-12, sketch001)
|
|||||||
currentArgKey: 'radius',
|
currentArgKey: 'radius',
|
||||||
currentArgValue: '5',
|
currentArgValue: '5',
|
||||||
headerArguments: {
|
headerArguments: {
|
||||||
Selection: '1 face',
|
Selection: '1 segment',
|
||||||
Radius: '',
|
Radius: '',
|
||||||
},
|
},
|
||||||
stage: 'arguments',
|
stage: 'arguments',
|
||||||
@ -1192,7 +1192,7 @@ extrude001 = extrude(-12, sketch001)
|
|||||||
await cmdBar.expectState({
|
await cmdBar.expectState({
|
||||||
commandName: 'Fillet',
|
commandName: 'Fillet',
|
||||||
headerArguments: {
|
headerArguments: {
|
||||||
Selection: '1 face',
|
Selection: '1 segment',
|
||||||
Radius: '5',
|
Radius: '5',
|
||||||
},
|
},
|
||||||
stage: 'review',
|
stage: 'review',
|
||||||
@ -1296,6 +1296,167 @@ extrude001 = extrude(-12, sketch001)
|
|||||||
lowTolerance
|
lowTolerance
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Test 3: Delete fillets
|
||||||
|
await test.step('Delete fillet via feature tree selection', async () => {
|
||||||
|
await test.step('Open Feature Tree Pane', async () => {
|
||||||
|
await toolbar.openPane('feature-tree')
|
||||||
|
await page.waitForTimeout(500)
|
||||||
|
})
|
||||||
|
await test.step('Delete fillet via feature tree selection', async () => {
|
||||||
|
await editor.expectEditor.toContain(secondFilletDeclaration)
|
||||||
|
const operationButton = await toolbar.getFeatureTreeOperation('Fillet', 1)
|
||||||
|
await operationButton.click({ button: 'left' })
|
||||||
|
await page.keyboard.press('Backspace')
|
||||||
|
await page.waitForTimeout(500)
|
||||||
|
await scene.expectPixelColor(edgeColorWhite, secondEdgeLocation, 15) // deleted
|
||||||
|
await editor.expectEditor.not.toContain(secondFilletDeclaration)
|
||||||
|
await scene.expectPixelColor(filletColor, firstEdgeLocation, 15) // stayed
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
test(`Fillet point-and-click delete`, async ({
|
||||||
|
context,
|
||||||
|
page,
|
||||||
|
homePage,
|
||||||
|
scene,
|
||||||
|
editor,
|
||||||
|
toolbar,
|
||||||
|
}) => {
|
||||||
|
// Code samples
|
||||||
|
const initialCode = `sketch001 = startSketchOn('XY')
|
||||||
|
|> startProfileAt([-12, -6], %)
|
||||||
|
|> line([0, 12], %)
|
||||||
|
|> line([24, 0], %, $seg02)
|
||||||
|
|> line([0, -12], %)
|
||||||
|
|> lineTo([profileStartX(%), profileStartY(%)], %, $seg01)
|
||||||
|
|> close(%)
|
||||||
|
extrude001 = extrude(-12, sketch001)
|
||||||
|
|> fillet({ radius = 5, tags = [seg01] }, %) // fillet01
|
||||||
|
|> fillet({ radius = 5, tags = [seg02] }, %) // fillet02
|
||||||
|
fillet03 = fillet({ radius = 5, tags = [getOppositeEdge(seg01)]}, extrude001)
|
||||||
|
fillet04 = fillet({ radius = 5, tags = [getOppositeEdge(seg02)]}, extrude001)
|
||||||
|
`
|
||||||
|
const pipedFilletDeclaration = 'fillet({ radius = 5, tags = [seg01] }, %)'
|
||||||
|
const secondPipedFilletDeclaration =
|
||||||
|
'fillet({ radius = 5, tags = [seg02] }, %)'
|
||||||
|
const standaloneFilletDeclaration =
|
||||||
|
'fillet03 = fillet({ radius = 5, tags = [getOppositeEdge(seg01)]}, extrude001)'
|
||||||
|
const secondStandaloneFilletDeclaration =
|
||||||
|
'fillet04 = fillet({ radius = 5, tags = [getOppositeEdge(seg02)]}, extrude001)'
|
||||||
|
|
||||||
|
// Locators
|
||||||
|
const pipedFilletEdgeLocation = { x: 600, y: 193 }
|
||||||
|
const standaloneFilletEdgeLocation = { x: 600, y: 383 }
|
||||||
|
const bodyLocation = { x: 630, y: 290 }
|
||||||
|
|
||||||
|
// Colors
|
||||||
|
const edgeColorWhite: [number, number, number] = [248, 248, 248]
|
||||||
|
const bodyColor: [number, number, number] = [155, 155, 155]
|
||||||
|
const filletColor: [number, number, number] = [127, 127, 127]
|
||||||
|
const backgroundColor: [number, number, number] = [30, 30, 30]
|
||||||
|
const lowTolerance = 20
|
||||||
|
const highTolerance = 40
|
||||||
|
|
||||||
|
// Setup
|
||||||
|
await test.step(`Initial test setup`, async () => {
|
||||||
|
await context.addInitScript((initialCode) => {
|
||||||
|
localStorage.setItem('persistCode', initialCode)
|
||||||
|
}, initialCode)
|
||||||
|
await page.setBodyDimensions({ width: 1000, height: 500 })
|
||||||
|
await homePage.goToModelingScene()
|
||||||
|
|
||||||
|
// verify modeling scene is loaded
|
||||||
|
await scene.expectPixelColor(
|
||||||
|
backgroundColor,
|
||||||
|
standaloneFilletEdgeLocation,
|
||||||
|
lowTolerance
|
||||||
|
)
|
||||||
|
|
||||||
|
// wait for stream to load
|
||||||
|
await scene.expectPixelColor(bodyColor, bodyLocation, highTolerance)
|
||||||
|
})
|
||||||
|
|
||||||
|
// Test
|
||||||
|
await test.step('Delete fillet via feature tree selection', async () => {
|
||||||
|
await test.step('Open Feature Tree Pane', async () => {
|
||||||
|
await toolbar.openPane('feature-tree')
|
||||||
|
await page.waitForTimeout(500)
|
||||||
|
})
|
||||||
|
|
||||||
|
await test.step('Delete piped fillet via feature tree selection', async () => {
|
||||||
|
await test.step('Verify all fillets are present in the editor', async () => {
|
||||||
|
await editor.expectEditor.toContain(pipedFilletDeclaration)
|
||||||
|
await editor.expectEditor.toContain(secondPipedFilletDeclaration)
|
||||||
|
await editor.expectEditor.toContain(standaloneFilletDeclaration)
|
||||||
|
await editor.expectEditor.toContain(secondStandaloneFilletDeclaration)
|
||||||
|
})
|
||||||
|
await test.step('Verify test fillets are present in the scene', async () => {
|
||||||
|
await scene.expectPixelColor(
|
||||||
|
filletColor,
|
||||||
|
pipedFilletEdgeLocation,
|
||||||
|
lowTolerance
|
||||||
|
)
|
||||||
|
await scene.expectPixelColor(
|
||||||
|
backgroundColor,
|
||||||
|
standaloneFilletEdgeLocation,
|
||||||
|
lowTolerance
|
||||||
|
)
|
||||||
|
})
|
||||||
|
await test.step('Delete piped fillet', async () => {
|
||||||
|
const operationButton = await toolbar.getFeatureTreeOperation(
|
||||||
|
'Fillet',
|
||||||
|
0
|
||||||
|
)
|
||||||
|
await operationButton.click({ button: 'left' })
|
||||||
|
await page.keyboard.press('Backspace')
|
||||||
|
await page.waitForTimeout(500)
|
||||||
|
})
|
||||||
|
await test.step('Verify piped fillet is deleted but other fillets are not (in the editor)', async () => {
|
||||||
|
await editor.expectEditor.not.toContain(pipedFilletDeclaration)
|
||||||
|
await editor.expectEditor.toContain(secondPipedFilletDeclaration)
|
||||||
|
await editor.expectEditor.toContain(standaloneFilletDeclaration)
|
||||||
|
await editor.expectEditor.toContain(secondStandaloneFilletDeclaration)
|
||||||
|
})
|
||||||
|
await test.step('Verify piped fillet is deleted but non-piped is not (in the scene)', async () => {
|
||||||
|
await scene.expectPixelColor(
|
||||||
|
edgeColorWhite, // you see edge because fillet is deleted
|
||||||
|
pipedFilletEdgeLocation,
|
||||||
|
lowTolerance
|
||||||
|
)
|
||||||
|
await scene.expectPixelColor(
|
||||||
|
backgroundColor, // you see background because fillet is not deleted
|
||||||
|
standaloneFilletEdgeLocation,
|
||||||
|
lowTolerance
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
await test.step('Delete non-piped fillet via feature tree selection', async () => {
|
||||||
|
await test.step('Delete non-piped fillet', async () => {
|
||||||
|
const operationButton = await toolbar.getFeatureTreeOperation(
|
||||||
|
'Fillet',
|
||||||
|
1
|
||||||
|
)
|
||||||
|
await operationButton.click({ button: 'left' })
|
||||||
|
await page.keyboard.press('Backspace')
|
||||||
|
await page.waitForTimeout(500)
|
||||||
|
})
|
||||||
|
await test.step('Verify non-piped fillet is deleted but other two fillets are not (in the editor)', async () => {
|
||||||
|
await editor.expectEditor.toContain(secondPipedFilletDeclaration)
|
||||||
|
await editor.expectEditor.not.toContain(standaloneFilletDeclaration)
|
||||||
|
await editor.expectEditor.toContain(secondStandaloneFilletDeclaration)
|
||||||
|
})
|
||||||
|
await test.step('Verify non-piped fillet is deleted but piped is not (in the scene)', async () => {
|
||||||
|
await scene.expectPixelColor(
|
||||||
|
edgeColorWhite,
|
||||||
|
standaloneFilletEdgeLocation,
|
||||||
|
lowTolerance
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
test(`Chamfer point-and-click`, async ({
|
test(`Chamfer point-and-click`, async ({
|
||||||
@ -1398,7 +1559,7 @@ extrude001 = extrude(-12, sketch001)
|
|||||||
currentArgKey: 'length',
|
currentArgKey: 'length',
|
||||||
currentArgValue: '5',
|
currentArgValue: '5',
|
||||||
headerArguments: {
|
headerArguments: {
|
||||||
Selection: '1 face',
|
Selection: '1 segment',
|
||||||
Length: '',
|
Length: '',
|
||||||
},
|
},
|
||||||
stage: 'arguments',
|
stage: 'arguments',
|
||||||
@ -1407,7 +1568,7 @@ extrude001 = extrude(-12, sketch001)
|
|||||||
await cmdBar.expectState({
|
await cmdBar.expectState({
|
||||||
commandName: 'Chamfer',
|
commandName: 'Chamfer',
|
||||||
headerArguments: {
|
headerArguments: {
|
||||||
Selection: '1 face',
|
Selection: '1 segment',
|
||||||
Length: '5',
|
Length: '5',
|
||||||
},
|
},
|
||||||
stage: 'review',
|
stage: 'review',
|
||||||
@ -1511,6 +1672,163 @@ extrude001 = extrude(-12, sketch001)
|
|||||||
lowTolerance
|
lowTolerance
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Test 3: Delete chamfer via feature tree selection
|
||||||
|
await test.step('Open Feature Tree Pane', async () => {
|
||||||
|
await toolbar.openPane('feature-tree')
|
||||||
|
await page.waitForTimeout(500)
|
||||||
|
})
|
||||||
|
await test.step('Delete chamfer via feature tree selection', async () => {
|
||||||
|
const operationButton = await toolbar.getFeatureTreeOperation('Chamfer', 1)
|
||||||
|
await operationButton.click({ button: 'left' })
|
||||||
|
await page.keyboard.press('Backspace')
|
||||||
|
await page.waitForTimeout(500)
|
||||||
|
await scene.expectPixelColor(edgeColorWhite, secondEdgeLocation, 15) // deleted
|
||||||
|
await scene.expectPixelColor(chamferColor, firstEdgeLocation, 15) // stayed
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
test(`Chamfer point-and-click delete`, async ({
|
||||||
|
context,
|
||||||
|
page,
|
||||||
|
homePage,
|
||||||
|
scene,
|
||||||
|
editor,
|
||||||
|
toolbar,
|
||||||
|
}) => {
|
||||||
|
// Code samples
|
||||||
|
const initialCode = `sketch001 = startSketchOn('XY')
|
||||||
|
|> startProfileAt([-12, -6], %)
|
||||||
|
|> line([0, 12], %)
|
||||||
|
|> line([24, 0], %, $seg02)
|
||||||
|
|> line([0, -12], %)
|
||||||
|
|> lineTo([profileStartX(%), profileStartY(%)], %, $seg01)
|
||||||
|
|> close(%)
|
||||||
|
extrude001 = extrude(-12, sketch001)
|
||||||
|
|> chamfer({ length = 5, tags = [seg01] }, %) // chamfer01
|
||||||
|
|> chamfer({ length = 5, tags = [seg02] }, %) // chamfer02
|
||||||
|
chamfer03 = chamfer({ length = 5, tags = [getOppositeEdge(seg01)]}, extrude001)
|
||||||
|
chamfer04 = chamfer({ length = 5, tags = [getOppositeEdge(seg02)]}, extrude001)
|
||||||
|
`
|
||||||
|
const pipedChamferDeclaration = 'chamfer({ length = 5, tags = [seg01] }, %)'
|
||||||
|
const secondPipedChamferDeclaration =
|
||||||
|
'chamfer({ length = 5, tags = [seg02] }, %)'
|
||||||
|
const standaloneChamferDeclaration =
|
||||||
|
'chamfer03 = chamfer({ length = 5, tags = [getOppositeEdge(seg01)]}, extrude001)'
|
||||||
|
const secondStandaloneChamferDeclaration =
|
||||||
|
'chamfer04 = chamfer({ length = 5, tags = [getOppositeEdge(seg02)]}, extrude001)'
|
||||||
|
|
||||||
|
// Locators
|
||||||
|
const pipedChamferEdgeLocation = { x: 600, y: 193 }
|
||||||
|
const standaloneChamferEdgeLocation = { x: 600, y: 383 }
|
||||||
|
const bodyLocation = { x: 630, y: 290 }
|
||||||
|
|
||||||
|
// Colors
|
||||||
|
const edgeColorWhite: [number, number, number] = [248, 248, 248]
|
||||||
|
const bodyColor: [number, number, number] = [155, 155, 155]
|
||||||
|
const chamferColor: [number, number, number] = [168, 168, 168]
|
||||||
|
const backgroundColor: [number, number, number] = [30, 30, 30]
|
||||||
|
const lowTolerance = 20
|
||||||
|
const highTolerance = 40
|
||||||
|
|
||||||
|
// Setup
|
||||||
|
await test.step(`Initial test setup`, async () => {
|
||||||
|
await context.addInitScript((initialCode) => {
|
||||||
|
localStorage.setItem('persistCode', initialCode)
|
||||||
|
}, initialCode)
|
||||||
|
await page.setBodyDimensions({ width: 1000, height: 500 })
|
||||||
|
await homePage.goToModelingScene()
|
||||||
|
|
||||||
|
// verify modeling scene is loaded
|
||||||
|
await scene.expectPixelColor(
|
||||||
|
backgroundColor,
|
||||||
|
standaloneChamferEdgeLocation,
|
||||||
|
lowTolerance
|
||||||
|
)
|
||||||
|
|
||||||
|
// wait for stream to load
|
||||||
|
await scene.expectPixelColor(bodyColor, bodyLocation, highTolerance)
|
||||||
|
})
|
||||||
|
|
||||||
|
// Test
|
||||||
|
await test.step('Delete chamfer via feature tree selection', async () => {
|
||||||
|
await test.step('Open Feature Tree Pane', async () => {
|
||||||
|
await toolbar.openPane('feature-tree')
|
||||||
|
await page.waitForTimeout(500)
|
||||||
|
})
|
||||||
|
|
||||||
|
await test.step('Delete piped chamfer via feature tree selection', async () => {
|
||||||
|
await test.step('Verify all chamfers are present in the editor', async () => {
|
||||||
|
await editor.expectEditor.toContain(pipedChamferDeclaration)
|
||||||
|
await editor.expectEditor.toContain(secondPipedChamferDeclaration)
|
||||||
|
await editor.expectEditor.toContain(standaloneChamferDeclaration)
|
||||||
|
await editor.expectEditor.toContain(secondStandaloneChamferDeclaration)
|
||||||
|
})
|
||||||
|
await test.step('Verify test chamfers are present in the scene', async () => {
|
||||||
|
await scene.expectPixelColor(
|
||||||
|
chamferColor,
|
||||||
|
pipedChamferEdgeLocation,
|
||||||
|
lowTolerance
|
||||||
|
)
|
||||||
|
await scene.expectPixelColor(
|
||||||
|
backgroundColor,
|
||||||
|
standaloneChamferEdgeLocation,
|
||||||
|
lowTolerance
|
||||||
|
)
|
||||||
|
})
|
||||||
|
await test.step('Delete piped chamfer', async () => {
|
||||||
|
const operationButton = await toolbar.getFeatureTreeOperation(
|
||||||
|
'Chamfer',
|
||||||
|
0
|
||||||
|
)
|
||||||
|
await operationButton.click({ button: 'left' })
|
||||||
|
await page.keyboard.press('Backspace')
|
||||||
|
await page.waitForTimeout(500)
|
||||||
|
})
|
||||||
|
await test.step('Verify piped chamfer is deleted but other chamfers are not (in the editor)', async () => {
|
||||||
|
await editor.expectEditor.not.toContain(pipedChamferDeclaration)
|
||||||
|
await editor.expectEditor.toContain(secondPipedChamferDeclaration)
|
||||||
|
await editor.expectEditor.toContain(standaloneChamferDeclaration)
|
||||||
|
await editor.expectEditor.toContain(secondStandaloneChamferDeclaration)
|
||||||
|
})
|
||||||
|
await test.step('Verify piped chamfer is deleted but non-piped is not (in the scene)', async () => {
|
||||||
|
await scene.expectPixelColor(
|
||||||
|
edgeColorWhite, // you see edge color because chamfer is deleted
|
||||||
|
pipedChamferEdgeLocation,
|
||||||
|
lowTolerance
|
||||||
|
)
|
||||||
|
await scene.expectPixelColor(
|
||||||
|
backgroundColor, // you see background color instead of edge because it's chamfered
|
||||||
|
standaloneChamferEdgeLocation,
|
||||||
|
lowTolerance
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
await test.step('Delete non-piped chamfer via feature tree selection', async () => {
|
||||||
|
await test.step('Delete non-piped chamfer', async () => {
|
||||||
|
const operationButton = await toolbar.getFeatureTreeOperation(
|
||||||
|
'Chamfer',
|
||||||
|
1
|
||||||
|
)
|
||||||
|
await operationButton.click({ button: 'left' })
|
||||||
|
await page.keyboard.press('Backspace')
|
||||||
|
await page.waitForTimeout(500)
|
||||||
|
})
|
||||||
|
await test.step('Verify non-piped chamfer is deleted but other two chamfers are not (in the editor)', async () => {
|
||||||
|
await editor.expectEditor.toContain(secondPipedChamferDeclaration)
|
||||||
|
await editor.expectEditor.not.toContain(standaloneChamferDeclaration)
|
||||||
|
await editor.expectEditor.toContain(secondStandaloneChamferDeclaration)
|
||||||
|
})
|
||||||
|
await test.step('Verify non-piped chamfer is deleted but piped is not (in the scene)', async () => {
|
||||||
|
await scene.expectPixelColor(
|
||||||
|
edgeColorWhite,
|
||||||
|
standaloneChamferEdgeLocation,
|
||||||
|
lowTolerance
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
const shellPointAndClickCapCases = [
|
const shellPointAndClickCapCases = [
|
||||||
@ -1851,3 +2169,171 @@ sweep001 = sweep({ path = sketch002 }, sketch001)
|
|||||||
await page.waitForTimeout(1000)
|
await page.waitForTimeout(1000)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test.describe('Revolve point and click workflows', () => {
|
||||||
|
test('Base case workflow, auto spam continue in command bar', async ({
|
||||||
|
context,
|
||||||
|
page,
|
||||||
|
homePage,
|
||||||
|
scene,
|
||||||
|
editor,
|
||||||
|
toolbar,
|
||||||
|
cmdBar,
|
||||||
|
}) => {
|
||||||
|
const initialCode = `
|
||||||
|
sketch001 = startSketchOn('XZ')
|
||||||
|
|> startProfileAt([-100.0, 100.0], %)
|
||||||
|
|> angledLine([0, 200.0], %, $rectangleSegmentA001)
|
||||||
|
|> angledLine([segAng(rectangleSegmentA001) - 90, 200], %, $rectangleSegmentB001)
|
||||||
|
|> angledLine([
|
||||||
|
segAng(rectangleSegmentA001),
|
||||||
|
-segLen(rectangleSegmentA001)
|
||||||
|
], %, $rectangleSegmentC001)
|
||||||
|
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||||
|
|> close(%)
|
||||||
|
extrude001 = extrude(200, sketch001)
|
||||||
|
sketch002 = startSketchOn(extrude001, rectangleSegmentA001)
|
||||||
|
|> startProfileAt([-66.77, 84.81], %)
|
||||||
|
|> angledLine([180, 27.08], %, $rectangleSegmentA002)
|
||||||
|
|> angledLine([
|
||||||
|
segAng(rectangleSegmentA002) - 90,
|
||||||
|
27.8
|
||||||
|
], %, $rectangleSegmentB002)
|
||||||
|
|> angledLine([
|
||||||
|
segAng(rectangleSegmentA002),
|
||||||
|
-segLen(rectangleSegmentA002)
|
||||||
|
], %, $rectangleSegmentC002)
|
||||||
|
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||||
|
|> close(%)
|
||||||
|
`
|
||||||
|
|
||||||
|
await context.addInitScript((initialCode) => {
|
||||||
|
localStorage.setItem('persistCode', initialCode)
|
||||||
|
}, initialCode)
|
||||||
|
await page.setBodyDimensions({ width: 1000, height: 500 })
|
||||||
|
await homePage.goToModelingScene()
|
||||||
|
await scene.waitForExecutionDone()
|
||||||
|
|
||||||
|
// select line of code
|
||||||
|
const codeToSelecton = `segAng(rectangleSegmentA002) - 90,`
|
||||||
|
// revolve
|
||||||
|
await page.getByText(codeToSelecton).click()
|
||||||
|
await toolbar.revolveButton.click()
|
||||||
|
await cmdBar.progressCmdBar()
|
||||||
|
await cmdBar.progressCmdBar()
|
||||||
|
await cmdBar.progressCmdBar()
|
||||||
|
await cmdBar.progressCmdBar()
|
||||||
|
|
||||||
|
const newCodeToFind = `revolve001 = revolve({ angle = 360, axis = 'X' }, sketch002)`
|
||||||
|
expect(editor.expectEditor.toContain(newCodeToFind)).toBeTruthy()
|
||||||
|
})
|
||||||
|
test('revolve surface around edge from an extruded solid2d', async ({
|
||||||
|
context,
|
||||||
|
page,
|
||||||
|
homePage,
|
||||||
|
scene,
|
||||||
|
editor,
|
||||||
|
toolbar,
|
||||||
|
cmdBar,
|
||||||
|
}) => {
|
||||||
|
const initialCode = `
|
||||||
|
sketch001 = startSketchOn('XZ')
|
||||||
|
|> startProfileAt([-102.57, 101.72], %)
|
||||||
|
|> angledLine([0, 202.6], %, $rectangleSegmentA001)
|
||||||
|
|> angledLine([
|
||||||
|
segAng(rectangleSegmentA001) - 90,
|
||||||
|
202.6
|
||||||
|
], %, $rectangleSegmentB001)
|
||||||
|
|> angledLine([
|
||||||
|
segAng(rectangleSegmentA001),
|
||||||
|
-segLen(rectangleSegmentA001)
|
||||||
|
], %, $rectangleSegmentC001)
|
||||||
|
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||||
|
|> close(%)
|
||||||
|
extrude001 = extrude(50, sketch001)
|
||||||
|
sketch002 = startSketchOn(extrude001, rectangleSegmentA001)
|
||||||
|
|> circle({
|
||||||
|
center = [-11.34, 10.0],
|
||||||
|
radius = 8.69
|
||||||
|
}, %)
|
||||||
|
`
|
||||||
|
await context.addInitScript((initialCode) => {
|
||||||
|
localStorage.setItem('persistCode', initialCode)
|
||||||
|
}, initialCode)
|
||||||
|
await page.setBodyDimensions({ width: 1000, height: 500 })
|
||||||
|
await homePage.goToModelingScene()
|
||||||
|
await scene.waitForExecutionDone()
|
||||||
|
|
||||||
|
// select line of code
|
||||||
|
const codeToSelecton = `center = [-11.34, 10.0]`
|
||||||
|
// revolve
|
||||||
|
await page.getByText(codeToSelecton).click()
|
||||||
|
await toolbar.revolveButton.click()
|
||||||
|
await page.getByText('Edge', { exact: true }).click()
|
||||||
|
const lineCodeToSelection = `|> angledLine([0, 202.6], %, $rectangleSegmentA001)`
|
||||||
|
await page.getByText(lineCodeToSelection).click()
|
||||||
|
await cmdBar.progressCmdBar()
|
||||||
|
await cmdBar.progressCmdBar()
|
||||||
|
await cmdBar.progressCmdBar()
|
||||||
|
await cmdBar.progressCmdBar()
|
||||||
|
await cmdBar.progressCmdBar()
|
||||||
|
|
||||||
|
const newCodeToFind = `revolve001 = revolve({angle = 360, axis = getOppositeEdge(rectangleSegmentA001)}, sketch002) `
|
||||||
|
expect(editor.expectEditor.toContain(newCodeToFind)).toBeTruthy()
|
||||||
|
})
|
||||||
|
test('revolve sketch circle around line segment from startProfileAt sketch', async ({
|
||||||
|
context,
|
||||||
|
page,
|
||||||
|
homePage,
|
||||||
|
scene,
|
||||||
|
editor,
|
||||||
|
toolbar,
|
||||||
|
cmdBar,
|
||||||
|
}) => {
|
||||||
|
const initialCode = `
|
||||||
|
sketch002 = startSketchOn('XY')
|
||||||
|
|> startProfileAt([-2.02, 1.79], %)
|
||||||
|
|> xLine(2.6, %)
|
||||||
|
sketch001 = startSketchOn('-XY')
|
||||||
|
|> startProfileAt([-0.48, 1.25], %)
|
||||||
|
|> angledLine([0, 2.38], %, $rectangleSegmentA001)
|
||||||
|
|> angledLine([segAng(rectangleSegmentA001) - 90, 2.4], %, $rectangleSegmentB001)
|
||||||
|
|> angledLine([
|
||||||
|
segAng(rectangleSegmentA001),
|
||||||
|
-segLen(rectangleSegmentA001)
|
||||||
|
], %, $rectangleSegmentC001)
|
||||||
|
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||||
|
|> close(%)
|
||||||
|
extrude001 = extrude(5, sketch001)
|
||||||
|
sketch003 = startSketchOn(extrude001, 'START')
|
||||||
|
|> circle({
|
||||||
|
center = [-0.69, 0.56],
|
||||||
|
radius = 0.28
|
||||||
|
}, %)
|
||||||
|
`
|
||||||
|
|
||||||
|
await context.addInitScript((initialCode) => {
|
||||||
|
localStorage.setItem('persistCode', initialCode)
|
||||||
|
}, initialCode)
|
||||||
|
await page.setBodyDimensions({ width: 1000, height: 500 })
|
||||||
|
await homePage.goToModelingScene()
|
||||||
|
await scene.waitForExecutionDone()
|
||||||
|
|
||||||
|
// select line of code
|
||||||
|
const codeToSelecton = `center = [-0.69, 0.56]`
|
||||||
|
// revolve
|
||||||
|
await page.getByText(codeToSelecton).click()
|
||||||
|
await toolbar.revolveButton.click()
|
||||||
|
await page.getByText('Edge', { exact: true }).click()
|
||||||
|
const lineCodeToSelection = `|> xLine(2.6, %)`
|
||||||
|
await page.getByText(lineCodeToSelection).click()
|
||||||
|
await cmdBar.progressCmdBar()
|
||||||
|
await cmdBar.progressCmdBar()
|
||||||
|
await cmdBar.progressCmdBar()
|
||||||
|
await cmdBar.progressCmdBar()
|
||||||
|
await cmdBar.progressCmdBar()
|
||||||
|
|
||||||
|
const newCodeToFind = `revolve001 = revolve({ angle = 360, axis = seg01 }, sketch003)`
|
||||||
|
expect(editor.expectEditor.toContain(newCodeToFind)).toBeTruthy()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|||||||
@ -572,7 +572,7 @@ test(
|
|||||||
fs.utimesSync(`${dir}/lego/main.kcl`, _1995, _1995)
|
fs.utimesSync(`${dir}/lego/main.kcl`, _1995, _1995)
|
||||||
})
|
})
|
||||||
|
|
||||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
await page.setBodyDimensions({ width: 1200, height: 600 })
|
||||||
|
|
||||||
page.on('console', console.log)
|
page.on('console', console.log)
|
||||||
|
|
||||||
|
|||||||
@ -886,7 +886,7 @@ test.describe('Sketch tests', () => {
|
|||||||
// sketch selection should already have been made. "Selection: 1 face" only show up when the selection has been made already
|
// sketch selection should already have been made. "Selection: 1 face" only show up when the selection has been made already
|
||||||
// otherwise the cmdbar would be waiting for a selection.
|
// otherwise the cmdbar would be waiting for a selection.
|
||||||
await expect(
|
await expect(
|
||||||
page.getByRole('button', { name: 'selection : 1 face', exact: false })
|
page.getByRole('button', { name: 'selection : 1 segment', exact: false })
|
||||||
).toBeVisible({
|
).toBeVisible({
|
||||||
timeout: 10_000,
|
timeout: 10_000,
|
||||||
})
|
})
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 54 KiB After Width: | Height: | Size: 54 KiB |
|
Before Width: | Height: | Size: 52 KiB After Width: | Height: | Size: 52 KiB |
|
Before Width: | Height: | Size: 74 KiB After Width: | Height: | Size: 74 KiB |
|
Before Width: | Height: | Size: 66 KiB After Width: | Height: | Size: 66 KiB |
|
Before Width: | Height: | Size: 145 KiB After Width: | Height: | Size: 145 KiB |
|
Before Width: | Height: | Size: 129 KiB After Width: | Height: | Size: 129 KiB |
@ -75,3 +75,6 @@ publish:
|
|||||||
channel: latest
|
channel: latest
|
||||||
releaseInfo:
|
releaseInfo:
|
||||||
releaseNotesFile: release-notes.md
|
releaseNotesFile: release-notes.md
|
||||||
|
protocols:
|
||||||
|
- name: Zoo Studio
|
||||||
|
schemes: ['zoo-studio']
|
||||||
|
|||||||
@ -9,23 +9,8 @@ const rootDir = process.cwd()
|
|||||||
const config: ForgeConfig = {
|
const config: ForgeConfig = {
|
||||||
packagerConfig: {
|
packagerConfig: {
|
||||||
asar: true,
|
asar: true,
|
||||||
osxSign: (process.env.BUILD_RELEASE === 'true' && {}) || undefined,
|
|
||||||
osxNotarize:
|
|
||||||
(process.env.BUILD_RELEASE === 'true' && {
|
|
||||||
appleId: process.env.APPLE_ID || '',
|
|
||||||
appleIdPassword: process.env.APPLE_PASSWORD || '',
|
|
||||||
teamId: process.env.APPLE_TEAM_ID || '',
|
|
||||||
}) ||
|
|
||||||
undefined,
|
|
||||||
executableName: 'zoo-modeling-app',
|
executableName: 'zoo-modeling-app',
|
||||||
icon: path.resolve(rootDir, 'assets', 'icon'),
|
icon: path.resolve(rootDir, 'assets', 'icon'),
|
||||||
protocols: [
|
|
||||||
{
|
|
||||||
name: 'Zoo Studio',
|
|
||||||
schemes: ['zoo-studio'],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
extendInfo: 'Info.plist', // Information for file associations.
|
|
||||||
},
|
},
|
||||||
rebuildConfig: {},
|
rebuildConfig: {},
|
||||||
makers: [],
|
makers: [],
|
||||||
|
|||||||
1
interface.d.ts
vendored
@ -65,6 +65,7 @@ export interface IElectronAPI {
|
|||||||
VITE_KC_API_WS_MODELING_URL: string
|
VITE_KC_API_WS_MODELING_URL: string
|
||||||
VITE_KC_API_BASE_URL: string
|
VITE_KC_API_BASE_URL: string
|
||||||
VITE_KC_SITE_BASE_URL: string
|
VITE_KC_SITE_BASE_URL: string
|
||||||
|
VITE_KC_SITE_APP_URL: string
|
||||||
VITE_KC_SKIP_AUTH: string
|
VITE_KC_SKIP_AUTH: string
|
||||||
VITE_KC_CONNECTION_TIMEOUT_MS: string
|
VITE_KC_CONNECTION_TIMEOUT_MS: string
|
||||||
VITE_KC_DEV_TOKEN: string
|
VITE_KC_DEV_TOKEN: string
|
||||||
|
|||||||
16
package.json
@ -103,11 +103,11 @@
|
|||||||
"make:dev": "make dev",
|
"make:dev": "make dev",
|
||||||
"generate:machine-api": "npx openapi-typescript ./openapi/machine-api.json -o src/lib/machine-api.d.ts",
|
"generate:machine-api": "npx openapi-typescript ./openapi/machine-api.json -o src/lib/machine-api.d.ts",
|
||||||
"tron:start": "electron-forge start",
|
"tron:start": "electron-forge start",
|
||||||
"tron:package": "electron-forge package",
|
|
||||||
"chrome:test": "PLATFORM=web NODE_ENV=development yarn playwright test --config=playwright.config.ts --project='Google Chrome' --grep-invert='@snapshot'",
|
"chrome:test": "PLATFORM=web NODE_ENV=development yarn playwright test --config=playwright.config.ts --project='Google Chrome' --grep-invert='@snapshot'",
|
||||||
"tron:test": "NODE_ENV=development yarn playwright test --config=playwright.electron.config.ts --grep-invert='@snapshot'",
|
"tronb:vite:dev": "vite build -c vite.main.config.ts -m development && vite build -c vite.preload.config.ts -m development && vite build -c vite.renderer.config.ts -m development",
|
||||||
"tronb:vite": "vite build -c vite.main.config.ts && vite build -c vite.preload.config.ts && vite build -c vite.renderer.config.ts",
|
"tronb:vite:prod": "vite build -c vite.main.config.ts && vite build -c vite.preload.config.ts && vite build -c vite.renderer.config.ts",
|
||||||
"tronb:package": "electron-builder --config electron-builder.yml",
|
"tronb:package:dev": "yarn tronb:vite:dev && electron-builder --config electron-builder.yml",
|
||||||
|
"tronb:package:prod": "yarn tronb:vite:prod && electron-builder --config electron-builder.yml --publish always",
|
||||||
"test-setup": "yarn install && yarn build:wasm",
|
"test-setup": "yarn install && yarn build:wasm",
|
||||||
"test": "vitest --mode development",
|
"test": "vitest --mode development",
|
||||||
"test:unit": "vitest run --mode development --exclude **/kclSamples.test.ts",
|
"test:unit": "vitest run --mode development --exclude **/kclSamples.test.ts",
|
||||||
@ -116,10 +116,10 @@
|
|||||||
"test:playwright:electron:windows": "playwright test --config=playwright.electron.config.ts --grep-invert=\"@skipWin|@snapshot\" --quiet",
|
"test:playwright:electron:windows": "playwright test --config=playwright.electron.config.ts --grep-invert=\"@skipWin|@snapshot\" --quiet",
|
||||||
"test:playwright:electron:macos": "playwright test --config=playwright.electron.config.ts --grep-invert='@skipMacos|@snapshot' --quiet",
|
"test:playwright:electron:macos": "playwright test --config=playwright.electron.config.ts --grep-invert='@skipMacos|@snapshot' --quiet",
|
||||||
"test:playwright:electron:ubuntu": "playwright test --config=playwright.electron.config.ts --grep-invert='@skipLinux|@snapshot' --quiet",
|
"test:playwright:electron:ubuntu": "playwright test --config=playwright.electron.config.ts --grep-invert='@skipLinux|@snapshot' --quiet",
|
||||||
"test:playwright:electron:local": "yarn tron:package && NODE_ENV=development playwright test --config=playwright.electron.config.ts --grep-invert='@snapshot'",
|
"test:playwright:electron:local": "yarn tronb:package:dev && NODE_ENV=development playwright test --config=playwright.electron.config.ts --grep-invert='@snapshot'",
|
||||||
"test:playwright:electron:windows:local": "yarn tron:package && NODE_ENV=development playwright test --config=playwright.electron.config.ts --grep-invert=\"@skipWin|@snapshot\"",
|
"test:playwright:electron:windows:local": "yarn tronb:package:dev && set NODE_ENV='development' && playwright test --config=playwright.electron.config.ts --grep-invert=\"@skipWin|@snapshot\"",
|
||||||
"test:playwright:electron:macos:local": "yarn tron:package && NODE_ENV=development playwright test --config=playwright.electron.config.ts --grep-invert='@skipMacos|@snapshot'",
|
"test:playwright:electron:macos:local": "yarn tronb:package:dev && NODE_ENV=development playwright test --config=playwright.electron.config.ts --grep-invert='@skipMacos|@snapshot'",
|
||||||
"test:playwright:electron:ubuntu:local": "yarn tron:package && NODE_ENV=development playwright test --config=playwright.electron.config.ts --grep-invert='@skipLinux|@snapshot'",
|
"test:playwright:electron:ubuntu:local": "yarn tronb:package:dev && NODE_ENV=development playwright test --config=playwright.electron.config.ts --grep-invert='@skipLinux|@snapshot'",
|
||||||
"test:unit:local": "yarn simpleserver:bg && yarn test:unit; kill-port 3000",
|
"test:unit:local": "yarn simpleserver:bg && yarn test:unit; kill-port 3000",
|
||||||
"test:unit:kcl-samples:local": "yarn simpleserver:bg && yarn test:unit:kcl-samples; kill-port 3000"
|
"test:unit:kcl-samples:local": "yarn simpleserver:bg && yarn test:unit:kcl-samples; kill-port 3000"
|
||||||
},
|
},
|
||||||
|
|||||||
34
src/App.tsx
@ -1,4 +1,4 @@
|
|||||||
import { useEffect, useMemo, useRef } from 'react'
|
import { useEffect, useMemo, useRef, useState } from 'react'
|
||||||
import { useHotKeyListener } from './hooks/useHotKeyListener'
|
import { useHotKeyListener } from './hooks/useHotKeyListener'
|
||||||
import { Stream } from './components/Stream'
|
import { Stream } from './components/Stream'
|
||||||
import { AppHeader } from './components/AppHeader'
|
import { AppHeader } from './components/AppHeader'
|
||||||
@ -24,6 +24,10 @@ import { UnitsMenu } from 'components/UnitsMenu'
|
|||||||
import { CameraProjectionToggle } from 'components/CameraProjectionToggle'
|
import { CameraProjectionToggle } from 'components/CameraProjectionToggle'
|
||||||
import { useCreateFileLinkQuery } from 'hooks/useCreateFileLinkQueryWatcher'
|
import { useCreateFileLinkQuery } from 'hooks/useCreateFileLinkQueryWatcher'
|
||||||
import { maybeWriteToDisk } from 'lib/telemetry'
|
import { maybeWriteToDisk } from 'lib/telemetry'
|
||||||
|
import { takeScreenshotOfVideoStreamCanvas } from 'lib/screenshot'
|
||||||
|
import { writeProjectThumbnailFile } from 'lib/desktop'
|
||||||
|
import { useRouteLoaderData } from 'react-router-dom'
|
||||||
|
import { useEngineCommands } from 'components/EngineCommands'
|
||||||
import { commandBarActor } from 'machines/commandBarMachine'
|
import { commandBarActor } from 'machines/commandBarMachine'
|
||||||
import { useToken } from 'machines/appMachine'
|
import { useToken } from 'machines/appMachine'
|
||||||
maybeWriteToDisk()
|
maybeWriteToDisk()
|
||||||
@ -55,6 +59,12 @@ export function App() {
|
|||||||
|
|
||||||
const projectName = project?.name || null
|
const projectName = project?.name || null
|
||||||
const projectPath = project?.path || null
|
const projectPath = project?.path || null
|
||||||
|
|
||||||
|
const [commands] = useEngineCommands()
|
||||||
|
const [capturedCanvas, setCapturedCanvas] = useState(false)
|
||||||
|
const loaderData = useRouteLoaderData(PATHS.FILE) as IndexLoaderData
|
||||||
|
const lastCommandType = commands[commands.length - 1]?.type
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
onProjectOpen({ name: projectName, path: projectPath }, file || null)
|
onProjectOpen({ name: projectName, path: projectPath }, file || null)
|
||||||
}, [projectName, projectPath])
|
}, [projectName, projectPath])
|
||||||
@ -92,6 +102,28 @@ export function App() {
|
|||||||
|
|
||||||
useEngineConnectionSubscriptions()
|
useEngineConnectionSubscriptions()
|
||||||
|
|
||||||
|
// Generate thumbnail.png when loading the app
|
||||||
|
useEffect(() => {
|
||||||
|
if (!capturedCanvas && lastCommandType === 'execution-done') {
|
||||||
|
setTimeout(() => {
|
||||||
|
const projectDirectoryWithoutEndingSlash = loaderData?.project?.path
|
||||||
|
if (!projectDirectoryWithoutEndingSlash) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const dataUrl: string = takeScreenshotOfVideoStreamCanvas()
|
||||||
|
// zoom to fit command does not wait, wait 500ms to see if zoom to fit finishes
|
||||||
|
writeProjectThumbnailFile(dataUrl, projectDirectoryWithoutEndingSlash)
|
||||||
|
.then(() => {})
|
||||||
|
.catch((e) => {
|
||||||
|
console.error(
|
||||||
|
`Failed to generate thumbnail for ${projectDirectoryWithoutEndingSlash}`
|
||||||
|
)
|
||||||
|
console.error(e)
|
||||||
|
})
|
||||||
|
}, 500)
|
||||||
|
}
|
||||||
|
}, [lastCommandType])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="relative h-full flex flex-col" ref={ref}>
|
<div className="relative h-full flex flex-col" ref={ref}>
|
||||||
<AppHeader
|
<AppHeader
|
||||||
|
|||||||
@ -29,6 +29,7 @@ import * as TWEEN from '@tweenjs/tween.js'
|
|||||||
import { isQuaternionVertical } from './helpers'
|
import { isQuaternionVertical } from './helpers'
|
||||||
import { reportRejection } from 'lib/trap'
|
import { reportRejection } from 'lib/trap'
|
||||||
import { CameraProjectionType } from 'wasm-lib/kcl/bindings/CameraProjectionType'
|
import { CameraProjectionType } from 'wasm-lib/kcl/bindings/CameraProjectionType'
|
||||||
|
import { CameraDragInteractionType_type } from '@kittycad/lib/dist/types/src/models'
|
||||||
|
|
||||||
const ORTHOGRAPHIC_CAMERA_SIZE = 20
|
const ORTHOGRAPHIC_CAMERA_SIZE = 20
|
||||||
const FRAMES_TO_ANIMATE_IN = 30
|
const FRAMES_TO_ANIMATE_IN = 30
|
||||||
@ -406,7 +407,7 @@ export class CameraControls {
|
|||||||
.sub(this.mouseDownPosition)
|
.sub(this.mouseDownPosition)
|
||||||
this.mouseDownPosition.copy(this.mouseNewPosition)
|
this.mouseDownPosition.copy(this.mouseNewPosition)
|
||||||
|
|
||||||
const interaction = this.getInteractionType(event)
|
let interaction = this.getInteractionType(event)
|
||||||
if (interaction === 'none') return
|
if (interaction === 'none') return
|
||||||
|
|
||||||
// If there's a valid interaction and the mouse is moving,
|
// If there's a valid interaction and the mouse is moving,
|
||||||
@ -753,8 +754,6 @@ export class CameraControls {
|
|||||||
didChange = true
|
didChange = true
|
||||||
}
|
}
|
||||||
|
|
||||||
this.safeLookAtTarget(this.camera.up)
|
|
||||||
|
|
||||||
// Update the camera's matrices
|
// Update the camera's matrices
|
||||||
this.camera.updateMatrixWorld()
|
this.camera.updateMatrixWorld()
|
||||||
if (didChange || forceUpdate) {
|
if (didChange || forceUpdate) {
|
||||||
@ -1189,14 +1188,24 @@ export class CameraControls {
|
|||||||
this.deferReactUpdate(this.reactCameraProperties)
|
this.deferReactUpdate(this.reactCameraProperties)
|
||||||
Object.values(this._camChangeCallbacks).forEach((cb) => cb())
|
Object.values(this._camChangeCallbacks).forEach((cb) => cb())
|
||||||
}
|
}
|
||||||
getInteractionType = (event: MouseEvent) =>
|
getInteractionType = (
|
||||||
_getInteractionType(
|
event: MouseEvent
|
||||||
|
): CameraDragInteractionType_type | 'none' => {
|
||||||
|
const initialInteractionType = _getInteractionType(
|
||||||
this.interactionGuards,
|
this.interactionGuards,
|
||||||
event,
|
event,
|
||||||
this.enablePan,
|
this.enablePan,
|
||||||
this.enableRotate,
|
this.enableRotate,
|
||||||
this.enableZoom
|
this.enableZoom
|
||||||
)
|
)
|
||||||
|
if (
|
||||||
|
initialInteractionType === 'rotate' &&
|
||||||
|
this.engineCommandManager.settings.cameraOrbit === 'trackball'
|
||||||
|
) {
|
||||||
|
return 'rotatetrackball'
|
||||||
|
}
|
||||||
|
return initialInteractionType
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pure function helpers
|
// Pure function helpers
|
||||||
|
|||||||
@ -1,10 +1,8 @@
|
|||||||
import { useEngineCommands } from './EngineCommands'
|
import { useEngineCommands } from './EngineCommands'
|
||||||
import { Spinner } from './Spinner'
|
import { Spinner } from './Spinner'
|
||||||
import { CustomIcon } from './CustomIcon'
|
import { CustomIcon } from './CustomIcon'
|
||||||
|
|
||||||
export const ModelStateIndicator = () => {
|
export const ModelStateIndicator = () => {
|
||||||
const [commands] = useEngineCommands()
|
const [commands] = useEngineCommands()
|
||||||
|
|
||||||
const lastCommandType = commands[commands.length - 1]?.type
|
const lastCommandType = commands[commands.length - 1]?.type
|
||||||
|
|
||||||
let className = 'w-6 h-6 '
|
let className = 'w-6 h-6 '
|
||||||
|
|||||||
@ -119,6 +119,7 @@ export const ModelingMachineProvider = ({
|
|||||||
cameraProjection,
|
cameraProjection,
|
||||||
highlightEdges,
|
highlightEdges,
|
||||||
showScaleGrid,
|
showScaleGrid,
|
||||||
|
cameraOrbit,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -1154,6 +1155,7 @@ export const ModelingMachineProvider = ({
|
|||||||
enableSSAO: enableSSAO.current,
|
enableSSAO: enableSSAO.current,
|
||||||
showScaleGrid: showScaleGrid.current,
|
showScaleGrid: showScaleGrid.current,
|
||||||
cameraProjection: cameraProjection.current,
|
cameraProjection: cameraProjection.current,
|
||||||
|
cameraOrbit: cameraOrbit.current,
|
||||||
},
|
},
|
||||||
token
|
token
|
||||||
)
|
)
|
||||||
@ -1183,6 +1185,13 @@ export const ModelingMachineProvider = ({
|
|||||||
editorManager.selectionRanges = modelingState.context.selectionRanges
|
editorManager.selectionRanges = modelingState.context.selectionRanges
|
||||||
}, [modelingState.context.selectionRanges])
|
}, [modelingState.context.selectionRanges])
|
||||||
|
|
||||||
|
// When changing camera modes reset the camera to the default orientation to correct
|
||||||
|
// the up vector otherwise the conconical orientation for the camera modes will be
|
||||||
|
// wrong
|
||||||
|
useEffect(() => {
|
||||||
|
sceneInfra.camControls.resetCameraPosition().catch(reportRejection)
|
||||||
|
}, [cameraOrbit.current])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const onConnectionStateChanged = ({ detail }: CustomEvent) => {
|
const onConnectionStateChanged = ({ detail }: CustomEvent) => {
|
||||||
// If we are in sketch mode we need to exit it.
|
// If we are in sketch mode we need to exit it.
|
||||||
|
|||||||
@ -33,7 +33,7 @@ export const OpenInDesktopAppHandler = (props: React.PropsWithChildren) => {
|
|||||||
function onOpenInDesktopApp() {
|
function onOpenInDesktopApp() {
|
||||||
const newSearchParams = new URLSearchParams(globalThis.location.search)
|
const newSearchParams = new URLSearchParams(globalThis.location.search)
|
||||||
newSearchParams.delete(ASK_TO_OPEN_QUERY_PARAM)
|
newSearchParams.delete(ASK_TO_OPEN_QUERY_PARAM)
|
||||||
const newURL = `${ZOO_STUDIO_PROTOCOL}${globalThis.location.pathname.replace(
|
const newURL = `${ZOO_STUDIO_PROTOCOL}://${globalThis.location.pathname.replace(
|
||||||
'/',
|
'/',
|
||||||
''
|
''
|
||||||
)}${searchParams.size > 0 ? `?${newSearchParams.toString()}` : ''}`
|
)}${searchParams.size > 0 ? `?${newSearchParams.toString()}` : ''}`
|
||||||
|
|||||||
@ -2,7 +2,7 @@ import { FormEvent, useEffect, useRef, useState } from 'react'
|
|||||||
import { PATHS } from 'lib/paths'
|
import { PATHS } from 'lib/paths'
|
||||||
import { Link } from 'react-router-dom'
|
import { Link } from 'react-router-dom'
|
||||||
import { ActionButton } from '../ActionButton'
|
import { ActionButton } from '../ActionButton'
|
||||||
import { FILE_EXT } from 'lib/constants'
|
import { FILE_EXT, PROJECT_IMAGE_NAME } from 'lib/constants'
|
||||||
import { useHotkeys } from 'react-hotkeys-hook'
|
import { useHotkeys } from 'react-hotkeys-hook'
|
||||||
import Tooltip from '../Tooltip'
|
import Tooltip from '../Tooltip'
|
||||||
import { DeleteConfirmationDialog } from './DeleteProjectDialog'
|
import { DeleteConfirmationDialog } from './DeleteProjectDialog'
|
||||||
@ -29,7 +29,7 @@ function ProjectCard({
|
|||||||
const [isConfirmingDelete, setIsConfirmingDelete] = useState(false)
|
const [isConfirmingDelete, setIsConfirmingDelete] = useState(false)
|
||||||
const [numberOfFiles, setNumberOfFiles] = useState(1)
|
const [numberOfFiles, setNumberOfFiles] = useState(1)
|
||||||
const [numberOfFolders, setNumberOfFolders] = useState(0)
|
const [numberOfFolders, setNumberOfFolders] = useState(0)
|
||||||
// const [imageUrl, setImageUrl] = useState('')
|
const [imageUrl, setImageUrl] = useState('')
|
||||||
|
|
||||||
let inputRef = useRef<HTMLInputElement>(null)
|
let inputRef = useRef<HTMLInputElement>(null)
|
||||||
|
|
||||||
@ -53,18 +53,21 @@ function ProjectCard({
|
|||||||
setNumberOfFolders(project.directory_count)
|
setNumberOfFolders(project.directory_count)
|
||||||
}
|
}
|
||||||
|
|
||||||
// async function setupImageUrl() {
|
async function setupImageUrl() {
|
||||||
// const projectImagePath = await join(project.file.path, PROJECT_IMAGE_NAME)
|
const projectImagePath = window.electron.path.join(
|
||||||
// if (await exists(projectImagePath)) {
|
project.path,
|
||||||
// const imageData = await readFile(projectImagePath)
|
PROJECT_IMAGE_NAME
|
||||||
// const blob = new Blob([imageData], { type: 'image/jpg' })
|
)
|
||||||
// const imageUrl = URL.createObjectURL(blob)
|
if (await window.electron.exists(projectImagePath)) {
|
||||||
// setImageUrl(imageUrl)
|
const imageData = await window.electron.readFile(projectImagePath)
|
||||||
// }
|
const blob = new Blob([imageData], { type: 'image/png' })
|
||||||
// }
|
const imageUrl = URL.createObjectURL(blob)
|
||||||
|
setImageUrl(imageUrl)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void getNumberOfFiles()
|
void getNumberOfFiles()
|
||||||
// void setupImageUrl()
|
void setupImageUrl()
|
||||||
}, [project.kcl_file_count, project.directory_count])
|
}, [project.kcl_file_count, project.directory_count])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -84,7 +87,7 @@ function ProjectCard({
|
|||||||
to={`${PATHS.FILE}/${encodeURIComponent(project.default_file)}`}
|
to={`${PATHS.FILE}/${encodeURIComponent(project.default_file)}`}
|
||||||
className="flex flex-col flex-1 !no-underline !text-chalkboard-110 dark:!text-chalkboard-10 group-hover:!hue-rotate-0 min-h-[5em] divide-y divide-primary/40 dark:divide-chalkboard-80 group-hover:!divide-primary"
|
className="flex flex-col flex-1 !no-underline !text-chalkboard-110 dark:!text-chalkboard-10 group-hover:!hue-rotate-0 min-h-[5em] divide-y divide-primary/40 dark:divide-chalkboard-80 group-hover:!divide-primary"
|
||||||
>
|
>
|
||||||
{/* <div className="h-36 relative overflow-hidden bg-gradient-to-b from-transparent to-primary/10 rounded-t-sm">
|
<div className="h-36 relative overflow-hidden bg-gradient-to-b from-transparent to-primary/10 rounded-t-sm">
|
||||||
{imageUrl && (
|
{imageUrl && (
|
||||||
<img
|
<img
|
||||||
src={imageUrl}
|
src={imageUrl}
|
||||||
@ -92,7 +95,7 @@ function ProjectCard({
|
|||||||
className="h-full w-full transition-transform group-hover:scale-105 object-cover"
|
className="h-full w-full transition-transform group-hover:scale-105 object-cover"
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</div> */}
|
</div>
|
||||||
<div className="pb-2 flex flex-col flex-grow flex-auto gap-2 rounded-b-sm">
|
<div className="pb-2 flex flex-col flex-grow flex-auto gap-2 rounded-b-sm">
|
||||||
{isEditing ? (
|
{isEditing ? (
|
||||||
<ProjectCardRenameForm
|
<ProjectCardRenameForm
|
||||||
|
|||||||
@ -19,7 +19,7 @@ import { commandBarActor } from 'machines/commandBarMachine'
|
|||||||
import { useSelector } from '@xstate/react'
|
import { useSelector } from '@xstate/react'
|
||||||
import { copyFileShareLink } from 'lib/links'
|
import { copyFileShareLink } from 'lib/links'
|
||||||
import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext'
|
import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext'
|
||||||
import { DEV } from 'env'
|
import { IS_NIGHTLY_OR_DEBUG } from 'routes/Settings'
|
||||||
import { useToken } from 'machines/appMachine'
|
import { useToken } from 'machines/appMachine'
|
||||||
|
|
||||||
const ProjectSidebarMenu = ({
|
const ProjectSidebarMenu = ({
|
||||||
@ -112,6 +112,7 @@ function ProjectMenuPopover({
|
|||||||
const { onProjectClose } = useLspContext()
|
const { onProjectClose } = useLspContext()
|
||||||
const exportCommandInfo = { name: 'Export', groupId: 'modeling' }
|
const exportCommandInfo = { name: 'Export', groupId: 'modeling' }
|
||||||
const makeCommandInfo = { name: 'Make', groupId: 'modeling' }
|
const makeCommandInfo = { name: 'Make', groupId: 'modeling' }
|
||||||
|
const shareCommandInfo = { name: 'share-file-link', groupId: 'code' }
|
||||||
const findCommand = (obj: { name: string; groupId: string }) =>
|
const findCommand = (obj: { name: string; groupId: string }) =>
|
||||||
Boolean(
|
Boolean(
|
||||||
commands.find((c) => c.name === obj.name && c.groupId === obj.groupId)
|
commands.find((c) => c.name === obj.name && c.groupId === obj.groupId)
|
||||||
@ -193,7 +194,7 @@ function ProjectMenuPopover({
|
|||||||
id: 'share-link',
|
id: 'share-link',
|
||||||
Element: 'button',
|
Element: 'button',
|
||||||
children: 'Share link to file',
|
children: 'Share link to file',
|
||||||
disabled: !DEV,
|
disabled: IS_NIGHTLY_OR_DEBUG || !findCommand(shareCommandInfo),
|
||||||
onClick: async () => {
|
onClick: async () => {
|
||||||
await copyFileShareLink({
|
await copyFileShareLink({
|
||||||
token: token ?? '',
|
token: token ?? '',
|
||||||
|
|||||||
@ -10,6 +10,7 @@ export const VITE_KC_API_WS_MODELING_URL = env.VITE_KC_API_WS_MODELING_URL as
|
|||||||
| undefined
|
| undefined
|
||||||
export const VITE_KC_API_BASE_URL = env.VITE_KC_API_BASE_URL as string
|
export const VITE_KC_API_BASE_URL = env.VITE_KC_API_BASE_URL as string
|
||||||
export const VITE_KC_SITE_BASE_URL = env.VITE_KC_SITE_BASE_URL as string
|
export const VITE_KC_SITE_BASE_URL = env.VITE_KC_SITE_BASE_URL as string
|
||||||
|
export const VITE_KC_SITE_APP_URL = env.VITE_KC_SITE_APP_URL as string
|
||||||
export const VITE_KC_SKIP_AUTH = env.VITE_KC_SKIP_AUTH as string | undefined
|
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
|
||||||
|
|||||||
@ -16,14 +16,15 @@ export function useSetupEngineManager(
|
|||||||
streamRef: React.RefObject<HTMLDivElement>,
|
streamRef: React.RefObject<HTMLDivElement>,
|
||||||
modelingSend: ReturnType<typeof useModelingContext>['send'],
|
modelingSend: ReturnType<typeof useModelingContext>['send'],
|
||||||
modelingContext: ReturnType<typeof useModelingContext>['context'],
|
modelingContext: ReturnType<typeof useModelingContext>['context'],
|
||||||
settings = {
|
settings: SettingsViaQueryString = {
|
||||||
pool: null,
|
pool: null,
|
||||||
theme: Themes.System,
|
theme: Themes.System,
|
||||||
highlightEdges: true,
|
highlightEdges: true,
|
||||||
enableSSAO: true,
|
enableSSAO: true,
|
||||||
showScaleGrid: false,
|
showScaleGrid: false,
|
||||||
cameraProjection: 'perspective',
|
cameraProjection: 'perspective',
|
||||||
} as SettingsViaQueryString,
|
cameraOrbit: 'spherical',
|
||||||
|
},
|
||||||
token?: string
|
token?: string
|
||||||
) {
|
) {
|
||||||
const networkContext = useNetworkContext()
|
const networkContext = useNetworkContext()
|
||||||
|
|||||||
@ -47,6 +47,7 @@ import { Models } from '@kittycad/lib'
|
|||||||
import { ExtrudeFacePlane } from 'machines/modelingMachine'
|
import { ExtrudeFacePlane } from 'machines/modelingMachine'
|
||||||
import { Node } from 'wasm-lib/kcl/bindings/Node'
|
import { Node } from 'wasm-lib/kcl/bindings/Node'
|
||||||
import { KclExpressionWithVariable } from 'lib/commandTypes'
|
import { KclExpressionWithVariable } from 'lib/commandTypes'
|
||||||
|
import { deleteEdgeTreatment } from './modifyAst/addEdgeTreatment'
|
||||||
|
|
||||||
export function startSketchOnDefault(
|
export function startSketchOnDefault(
|
||||||
node: Node<Program>,
|
node: Node<Program>,
|
||||||
@ -1371,6 +1372,8 @@ export async function deleteFromSelection(
|
|||||||
}
|
}
|
||||||
// await prom
|
// await prom
|
||||||
return astClone
|
return astClone
|
||||||
|
} else if (selection.artifact?.type === 'edgeCut') {
|
||||||
|
return deleteEdgeTreatment(astClone, selection)
|
||||||
} else if (varDec.node.init.type === 'PipeExpression') {
|
} else if (varDec.node.init.type === 'PipeExpression') {
|
||||||
const pipeBody = varDec.node.init.body
|
const pipeBody = varDec.node.init.body
|
||||||
if (
|
if (
|
||||||
|
|||||||
@ -20,6 +20,7 @@ import {
|
|||||||
FilletParameters,
|
FilletParameters,
|
||||||
ChamferParameters,
|
ChamferParameters,
|
||||||
EdgeTreatmentParameters,
|
EdgeTreatmentParameters,
|
||||||
|
deleteEdgeTreatment,
|
||||||
} from './addEdgeTreatment'
|
} from './addEdgeTreatment'
|
||||||
import { getNodeFromPath } from '../queryAst'
|
import { getNodeFromPath } from '../queryAst'
|
||||||
import { getNodePathFromSourceRange } from 'lang/queryAstNodePathUtils'
|
import { getNodePathFromSourceRange } from 'lang/queryAstNodePathUtils'
|
||||||
@ -287,7 +288,7 @@ const runModifyAstCloneWithEdgeTreatmentAndTag = async (
|
|||||||
otherSelections: [],
|
otherSelections: [],
|
||||||
}
|
}
|
||||||
|
|
||||||
// apply edge treatment to seleciton
|
// apply edge treatment to selection
|
||||||
const result = modifyAstWithEdgeTreatmentAndTag(ast, selection, parameters)
|
const result = modifyAstWithEdgeTreatmentAndTag(ast, selection, parameters)
|
||||||
if (err(result)) {
|
if (err(result)) {
|
||||||
return result
|
return result
|
||||||
@ -298,6 +299,46 @@ const runModifyAstCloneWithEdgeTreatmentAndTag = async (
|
|||||||
|
|
||||||
expect(newCode).toContain(expectedCode)
|
expect(newCode).toContain(expectedCode)
|
||||||
}
|
}
|
||||||
|
const runDeleteEdgeTreatmentTest = async (
|
||||||
|
code: string,
|
||||||
|
edgeTreatmentSnippet: string,
|
||||||
|
expectedCode: string
|
||||||
|
) => {
|
||||||
|
// parse ast
|
||||||
|
const ast = assertParse(code)
|
||||||
|
|
||||||
|
// update artifact graph
|
||||||
|
await kclManager.executeAst({ ast })
|
||||||
|
const artifactGraph = engineCommandManager.artifactGraph
|
||||||
|
|
||||||
|
// define snippet range
|
||||||
|
const edgeTreatmentRange = topLevelRange(
|
||||||
|
code.indexOf(edgeTreatmentSnippet),
|
||||||
|
code.indexOf(edgeTreatmentSnippet) + edgeTreatmentSnippet.length
|
||||||
|
)
|
||||||
|
|
||||||
|
// find artifact
|
||||||
|
const maybeArtifact = [...artifactGraph].find(([, artifact]) => {
|
||||||
|
if (!('codeRef' in artifact)) return false
|
||||||
|
return isOverlap(artifact.codeRef.range, edgeTreatmentRange)
|
||||||
|
})
|
||||||
|
|
||||||
|
// build selection
|
||||||
|
const selection: Selection = {
|
||||||
|
codeRef: codeRefFromRange(edgeTreatmentRange, ast),
|
||||||
|
artifact: maybeArtifact ? maybeArtifact[1] : undefined,
|
||||||
|
}
|
||||||
|
|
||||||
|
// delete edge treatment
|
||||||
|
const result = await deleteEdgeTreatment(ast, selection)
|
||||||
|
if (err(result)) {
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// recast and check
|
||||||
|
const newCode = recast(result)
|
||||||
|
expect(newCode).toContain(expectedCode)
|
||||||
|
}
|
||||||
const createFilletParameters = (radiusValue: number): FilletParameters => ({
|
const createFilletParameters = (radiusValue: number): FilletParameters => ({
|
||||||
type: EdgeTreatmentType.Fillet,
|
type: EdgeTreatmentType.Fillet,
|
||||||
radius: {
|
radius: {
|
||||||
@ -574,6 +615,191 @@ extrude002 = extrude(-25, sketch002)
|
|||||||
)
|
)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
describe(`Testing deleteEdgeTreatment with ${edgeTreatmentType}s`, () => {
|
||||||
|
// simple cases
|
||||||
|
it(`should delete a piped ${edgeTreatmentType} from a single segment`, async () => {
|
||||||
|
const code = `sketch001 = startSketchOn('XY')
|
||||||
|
|> startProfileAt([-10, 10], %)
|
||||||
|
|> line([20, 0], %)
|
||||||
|
|> line([0, -20], %)
|
||||||
|
|> line([-20, 0], %, $seg01)
|
||||||
|
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||||
|
|> close(%)
|
||||||
|
extrude001 = extrude(-15, sketch001)
|
||||||
|
|> ${edgeTreatmentType}({ ${parameterName} = 3, tags = [seg01] }, %)`
|
||||||
|
const edgeTreatmentSnippet = `${edgeTreatmentType}({ ${parameterName} = 3, tags = [seg01] }, %)`
|
||||||
|
const expectedCode = `sketch001 = startSketchOn('XY')
|
||||||
|
|> startProfileAt([-10, 10], %)
|
||||||
|
|> line([20, 0], %)
|
||||||
|
|> line([0, -20], %)
|
||||||
|
|> line([-20, 0], %, $seg01)
|
||||||
|
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||||
|
|> close(%)
|
||||||
|
extrude001 = extrude(-15, sketch001)`
|
||||||
|
|
||||||
|
await runDeleteEdgeTreatmentTest(
|
||||||
|
code,
|
||||||
|
edgeTreatmentSnippet,
|
||||||
|
expectedCode
|
||||||
|
)
|
||||||
|
})
|
||||||
|
it(`should delete a non-piped ${edgeTreatmentType} from a single segment`, async () => {
|
||||||
|
const code = `sketch001 = startSketchOn('XY')
|
||||||
|
|> startProfileAt([-10, 10], %)
|
||||||
|
|> line([20, 0], %)
|
||||||
|
|> line([0, -20], %)
|
||||||
|
|> line([-20, 0], %, $seg01)
|
||||||
|
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||||
|
|> close(%)
|
||||||
|
extrude001 = extrude(-15, sketch001)
|
||||||
|
fillet001 = ${edgeTreatmentType}({ ${parameterName} = 3, tags = [seg01] }, extrude001)`
|
||||||
|
const edgeTreatmentSnippet = `fillet001 = ${edgeTreatmentType}({ ${parameterName} = 3, tags = [seg01] }, extrude001)`
|
||||||
|
const expectedCode = `sketch001 = startSketchOn('XY')
|
||||||
|
|> startProfileAt([-10, 10], %)
|
||||||
|
|> line([20, 0], %)
|
||||||
|
|> line([0, -20], %)
|
||||||
|
|> line([-20, 0], %, $seg01)
|
||||||
|
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||||
|
|> close(%)
|
||||||
|
extrude001 = extrude(-15, sketch001)`
|
||||||
|
|
||||||
|
await runDeleteEdgeTreatmentTest(
|
||||||
|
code,
|
||||||
|
edgeTreatmentSnippet,
|
||||||
|
expectedCode
|
||||||
|
)
|
||||||
|
})
|
||||||
|
// getOppositeEdge and getNextAdjacentEdge cases
|
||||||
|
it(`should delete a piped ${edgeTreatmentType} tagged with getOppositeEdge`, async () => {
|
||||||
|
const code = `sketch001 = startSketchOn('XY')
|
||||||
|
|> startProfileAt([-10, 10], %)
|
||||||
|
|> line([20, 0], %)
|
||||||
|
|> line([0, -20], %)
|
||||||
|
|> line([-20, 0], %, $seg01)
|
||||||
|
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||||
|
|> close(%)
|
||||||
|
extrude001 = extrude(-15, sketch001)
|
||||||
|
fillet001 = ${edgeTreatmentType}({ ${parameterName} = 3, tags = [getOppositeEdge(seg01)] }, extrude001)`
|
||||||
|
const edgeTreatmentSnippet = `fillet001 = ${edgeTreatmentType}({ ${parameterName} = 3, tags = [getOppositeEdge(seg01)] }, extrude001)`
|
||||||
|
const expectedCode = `sketch001 = startSketchOn('XY')
|
||||||
|
|> startProfileAt([-10, 10], %)
|
||||||
|
|> line([20, 0], %)
|
||||||
|
|> line([0, -20], %)
|
||||||
|
|> line([-20, 0], %, $seg01)
|
||||||
|
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||||
|
|> close(%)
|
||||||
|
extrude001 = extrude(-15, sketch001)`
|
||||||
|
|
||||||
|
await runDeleteEdgeTreatmentTest(
|
||||||
|
code,
|
||||||
|
edgeTreatmentSnippet,
|
||||||
|
expectedCode
|
||||||
|
)
|
||||||
|
})
|
||||||
|
it(`should delete a non-piped ${edgeTreatmentType} tagged with getNextAdjacentEdge`, async () => {
|
||||||
|
const code = `sketch001 = startSketchOn('XY')
|
||||||
|
|> startProfileAt([-10, 10], %)
|
||||||
|
|> line([20, 0], %)
|
||||||
|
|> line([0, -20], %)
|
||||||
|
|> line([-20, 0], %, $seg01)
|
||||||
|
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||||
|
|> close(%)
|
||||||
|
extrude001 = extrude(-15, sketch001)
|
||||||
|
fillet001 = ${edgeTreatmentType}({ ${parameterName} = 3, tags = [getNextAdjacentEdge(seg01)] }, extrude001)`
|
||||||
|
const edgeTreatmentSnippet = `fillet001 = ${edgeTreatmentType}({ ${parameterName} = 3, tags = [getNextAdjacentEdge(seg01)] }, extrude001)`
|
||||||
|
const expectedCode = `sketch001 = startSketchOn('XY')
|
||||||
|
|> startProfileAt([-10, 10], %)
|
||||||
|
|> line([20, 0], %)
|
||||||
|
|> line([0, -20], %)
|
||||||
|
|> line([-20, 0], %, $seg01)
|
||||||
|
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||||
|
|> close(%)
|
||||||
|
extrude001 = extrude(-15, sketch001)`
|
||||||
|
|
||||||
|
await runDeleteEdgeTreatmentTest(
|
||||||
|
code,
|
||||||
|
edgeTreatmentSnippet,
|
||||||
|
expectedCode
|
||||||
|
)
|
||||||
|
})
|
||||||
|
// cases with several edge treatments
|
||||||
|
it(`should delete a piped ${edgeTreatmentType} from a body with multiple treatments`, async () => {
|
||||||
|
const code = `sketch001 = startSketchOn('XY')
|
||||||
|
|> startProfileAt([-10, 10], %)
|
||||||
|
|> line([20, 0], %, $seg01)
|
||||||
|
|> line([0, -20], %)
|
||||||
|
|> line([-20, 0], %, $seg02)
|
||||||
|
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||||
|
|> close(%)
|
||||||
|
extrude001 = extrude(-15, sketch001)
|
||||||
|
|> ${edgeTreatmentType}({ ${parameterName} = 3, tags = [seg01] }, %)
|
||||||
|
|> fillet({ radius = 5, tags = [getOppositeEdge(seg02)] }, %)
|
||||||
|
fillet001 = ${edgeTreatmentType}({ ${parameterName} = 6, tags = [seg02] }, extrude001)
|
||||||
|
chamfer001 = chamfer({ length = 5, tags = [getOppositeEdge(seg01)] }, extrude001)`
|
||||||
|
const edgeTreatmentSnippet = `${edgeTreatmentType}({ ${parameterName} = 3, tags = [seg01] }, %)`
|
||||||
|
const expectedCode = `sketch001 = startSketchOn('XY')
|
||||||
|
|> startProfileAt([-10, 10], %)
|
||||||
|
|> line([20, 0], %, $seg01)
|
||||||
|
|> line([0, -20], %)
|
||||||
|
|> line([-20, 0], %, $seg02)
|
||||||
|
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||||
|
|> close(%)
|
||||||
|
extrude001 = extrude(-15, sketch001)
|
||||||
|
|> fillet({
|
||||||
|
radius = 5,
|
||||||
|
tags = [getOppositeEdge(seg02)]
|
||||||
|
}, %)
|
||||||
|
fillet001 = ${edgeTreatmentType}({ ${parameterName} = 6, tags = [seg02] }, extrude001)
|
||||||
|
chamfer001 = chamfer({
|
||||||
|
length = 5,
|
||||||
|
tags = [getOppositeEdge(seg01)]
|
||||||
|
}, extrude001)`
|
||||||
|
|
||||||
|
await runDeleteEdgeTreatmentTest(
|
||||||
|
code,
|
||||||
|
edgeTreatmentSnippet,
|
||||||
|
expectedCode
|
||||||
|
)
|
||||||
|
})
|
||||||
|
it(`should delete a non-piped ${edgeTreatmentType} from a body with multiple treatments`, async () => {
|
||||||
|
const code = `sketch001 = startSketchOn('XY')
|
||||||
|
|> startProfileAt([-10, 10], %)
|
||||||
|
|> line([20, 0], %, $seg01)
|
||||||
|
|> line([0, -20], %)
|
||||||
|
|> line([-20, 0], %, $seg02)
|
||||||
|
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||||
|
|> close(%)
|
||||||
|
extrude001 = extrude(-15, sketch001)
|
||||||
|
|> ${edgeTreatmentType}({ ${parameterName} = 3, tags = [seg01] }, %)
|
||||||
|
|> fillet({ radius = 5, tags = [getOppositeEdge(seg02)] }, %)
|
||||||
|
fillet001 = ${edgeTreatmentType}({ ${parameterName} = 6, tags = [seg02] }, extrude001)
|
||||||
|
chamfer001 = chamfer({ length = 5, tags = [getOppositeEdge(seg01)] }, extrude001)`
|
||||||
|
const edgeTreatmentSnippet = `fillet001 = ${edgeTreatmentType}({ ${parameterName} = 6, tags = [seg02] }, extrude001)`
|
||||||
|
const expectedCode = `sketch001 = startSketchOn('XY')
|
||||||
|
|> startProfileAt([-10, 10], %)
|
||||||
|
|> line([20, 0], %, $seg01)
|
||||||
|
|> line([0, -20], %)
|
||||||
|
|> line([-20, 0], %, $seg02)
|
||||||
|
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||||
|
|> close(%)
|
||||||
|
extrude001 = extrude(-15, sketch001)
|
||||||
|
|> ${edgeTreatmentType}({ ${parameterName} = 3, tags = [seg01] }, %)
|
||||||
|
|> fillet({
|
||||||
|
radius = 5,
|
||||||
|
tags = [getOppositeEdge(seg02)]
|
||||||
|
}, %)
|
||||||
|
chamfer001 = chamfer({
|
||||||
|
length = 5,
|
||||||
|
tags = [getOppositeEdge(seg01)]
|
||||||
|
}, extrude001)`
|
||||||
|
|
||||||
|
await runDeleteEdgeTreatmentTest(
|
||||||
|
code,
|
||||||
|
edgeTreatmentSnippet,
|
||||||
|
expectedCode
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@ -5,6 +5,7 @@ import {
|
|||||||
Identifier,
|
Identifier,
|
||||||
ObjectExpression,
|
ObjectExpression,
|
||||||
PathToNode,
|
PathToNode,
|
||||||
|
PipeExpression,
|
||||||
Program,
|
Program,
|
||||||
VariableDeclaration,
|
VariableDeclaration,
|
||||||
VariableDeclarator,
|
VariableDeclarator,
|
||||||
@ -722,3 +723,148 @@ export const isTagUsedInEdgeTreatment = ({
|
|||||||
|
|
||||||
return edges
|
return edges
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Delete Edge Treatment
|
||||||
|
export async function deleteEdgeTreatment(
|
||||||
|
ast: Node<Program>,
|
||||||
|
selection: Selection
|
||||||
|
): Promise<Node<Program> | Error> {
|
||||||
|
/**
|
||||||
|
* Deletes an edge treatment (fillet or chamfer)
|
||||||
|
* from the AST based on the selection.
|
||||||
|
* Handles both standalone treatments
|
||||||
|
* and those within a PipeExpression.
|
||||||
|
*
|
||||||
|
* Supported cases:
|
||||||
|
* [+] fillet and chamfer
|
||||||
|
* [+] piped and non-piped edge treatments
|
||||||
|
* [-] delete single tag from array of tags (currently whole expression is deleted)
|
||||||
|
* [-] multiple selections with different edge treatments (currently single selection is supported)
|
||||||
|
*/
|
||||||
|
|
||||||
|
// 1. Validate Selection Type
|
||||||
|
const { artifact } = selection
|
||||||
|
if (!artifact || artifact.type !== 'edgeCut') {
|
||||||
|
return new Error('Selection is not an edge cut')
|
||||||
|
}
|
||||||
|
|
||||||
|
const { subType: edgeTreatmentType } = artifact
|
||||||
|
if (
|
||||||
|
!edgeTreatmentType ||
|
||||||
|
!['fillet', 'chamfer'].includes(edgeTreatmentType)
|
||||||
|
) {
|
||||||
|
return new Error('Unsupported or missing edge treatment type')
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Clone ast and retrieve the VariableDeclarator
|
||||||
|
const astClone = structuredClone(ast)
|
||||||
|
const varDec = getNodeFromPath<VariableDeclarator>(
|
||||||
|
ast,
|
||||||
|
selection?.codeRef?.pathToNode,
|
||||||
|
'VariableDeclarator'
|
||||||
|
)
|
||||||
|
if (err(varDec)) return varDec
|
||||||
|
|
||||||
|
// 3: Check if edge treatment is in a pipe
|
||||||
|
const inPipe = varDec.node.init.type === 'PipeExpression'
|
||||||
|
|
||||||
|
// 4A. Handle standalone edge treatment
|
||||||
|
if (!inPipe) {
|
||||||
|
const varDecPathStep = varDec.shallowPath[1]
|
||||||
|
|
||||||
|
if (
|
||||||
|
!Array.isArray(varDecPathStep) ||
|
||||||
|
typeof varDecPathStep[0] !== 'number'
|
||||||
|
) {
|
||||||
|
return new Error(
|
||||||
|
'Invalid shallowPath structure: expected a number at shallowPath[1][0]'
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const varDecIndex: number = varDecPathStep[0]
|
||||||
|
|
||||||
|
// Remove entire VariableDeclarator from the ast
|
||||||
|
astClone.body.splice(varDecIndex, 1)
|
||||||
|
return astClone
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4B. Handle edge treatment within pipe
|
||||||
|
if (inPipe) {
|
||||||
|
// Retrieve the CallExpression path
|
||||||
|
const callExp =
|
||||||
|
getNodeFromPath<CallExpression>(
|
||||||
|
ast,
|
||||||
|
selection?.codeRef?.pathToNode,
|
||||||
|
'CallExpression'
|
||||||
|
) ?? null
|
||||||
|
if (err(callExp)) return callExp
|
||||||
|
|
||||||
|
const shallowPath = callExp.shallowPath
|
||||||
|
|
||||||
|
// Initialize variables to hold the PipeExpression path and callIndex
|
||||||
|
let pipeExpressionPath: PathToNode | null = null
|
||||||
|
let callIndex: number | null = null
|
||||||
|
|
||||||
|
// Iterate through the shallowPath to find the PipeExpression and callIndex
|
||||||
|
for (let i = 0; i < shallowPath.length - 1; i++) {
|
||||||
|
const [key, value] = shallowPath[i]
|
||||||
|
|
||||||
|
if (key === 'body' && value === 'PipeExpression') {
|
||||||
|
pipeExpressionPath = shallowPath.slice(0, i + 1)
|
||||||
|
|
||||||
|
const nextStep = shallowPath[i + 1]
|
||||||
|
if (
|
||||||
|
nextStep &&
|
||||||
|
nextStep[1] === 'index' &&
|
||||||
|
typeof nextStep[0] === 'number'
|
||||||
|
) {
|
||||||
|
callIndex = nextStep[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!pipeExpressionPath) {
|
||||||
|
return new Error('PipeExpression not found in path')
|
||||||
|
}
|
||||||
|
|
||||||
|
if (callIndex === null) {
|
||||||
|
return new Error('Failed to extract CallExpression index')
|
||||||
|
}
|
||||||
|
// Retrieve the PipeExpression node
|
||||||
|
const pipeExpressionNode = getNodeFromPath<PipeExpression>(
|
||||||
|
astClone,
|
||||||
|
pipeExpressionPath,
|
||||||
|
'PipeExpression'
|
||||||
|
)
|
||||||
|
if (err(pipeExpressionNode)) return pipeExpressionNode
|
||||||
|
|
||||||
|
// Ensure that the PipeExpression.body is an array
|
||||||
|
if (!Array.isArray(pipeExpressionNode.node.body)) {
|
||||||
|
return new Error('PipeExpression body is not an array')
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove the CallExpression at the specified index
|
||||||
|
pipeExpressionNode.node.body.splice(callIndex, 1)
|
||||||
|
|
||||||
|
// Remove VariableDeclarator if PipeExpression.body is empty
|
||||||
|
if (pipeExpressionNode.node.body.length === 0) {
|
||||||
|
const varDecPathStep = varDec.shallowPath[1]
|
||||||
|
if (
|
||||||
|
!Array.isArray(varDecPathStep) ||
|
||||||
|
typeof varDecPathStep[0] !== 'number'
|
||||||
|
) {
|
||||||
|
return new Error(
|
||||||
|
'Invalid shallowPath structure: expected a number at shallowPath[1][0]'
|
||||||
|
)
|
||||||
|
}
|
||||||
|
const varDecIndex: number = varDecPathStep[0]
|
||||||
|
astClone.body.splice(varDecIndex, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
return astClone
|
||||||
|
}
|
||||||
|
|
||||||
|
return Error('Delete fillets not implemented')
|
||||||
|
}
|
||||||
|
|||||||
@ -1389,6 +1389,7 @@ export class EngineCommandManager extends EventTarget {
|
|||||||
enableSSAO: true,
|
enableSSAO: true,
|
||||||
showScaleGrid: false,
|
showScaleGrid: false,
|
||||||
cameraProjection: 'perspective',
|
cameraProjection: 'perspective',
|
||||||
|
cameraOrbit: 'spherical',
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1437,6 +1438,7 @@ export class EngineCommandManager extends EventTarget {
|
|||||||
enableSSAO: true,
|
enableSSAO: true,
|
||||||
showScaleGrid: false,
|
showScaleGrid: false,
|
||||||
cameraProjection: 'orthographic',
|
cameraProjection: 'orthographic',
|
||||||
|
cameraOrbit: 'spherical',
|
||||||
},
|
},
|
||||||
// When passed, use a completely separate connecting code path that simply
|
// When passed, use a completely separate connecting code path that simply
|
||||||
// opens a websocket and this is a function that is called when connected.
|
// opens a websocket and this is a function that is called when connected.
|
||||||
|
|||||||
@ -308,7 +308,6 @@ export const modelingMachineCommandConfig: StateMachineCommandSetConfig<
|
|||||||
description:
|
description:
|
||||||
'Create a 3D body by moving a sketch region along an arbitrary path.',
|
'Create a 3D body by moving a sketch region along an arbitrary path.',
|
||||||
icon: 'sweep',
|
icon: 'sweep',
|
||||||
status: 'development',
|
|
||||||
needsReview: false,
|
needsReview: false,
|
||||||
args: {
|
args: {
|
||||||
target: {
|
target: {
|
||||||
@ -317,8 +316,6 @@ export const modelingMachineCommandConfig: StateMachineCommandSetConfig<
|
|||||||
required: true,
|
required: true,
|
||||||
skip: true,
|
skip: true,
|
||||||
multiple: false,
|
multiple: false,
|
||||||
warningMessage:
|
|
||||||
'The sweep workflow is new and under tested. Please break it and report issues.',
|
|
||||||
},
|
},
|
||||||
trajectory: {
|
trajectory: {
|
||||||
inputType: 'selection',
|
inputType: 'selection',
|
||||||
@ -355,20 +352,18 @@ export const modelingMachineCommandConfig: StateMachineCommandSetConfig<
|
|||||||
selectionTypes: ['cap', 'wall'],
|
selectionTypes: ['cap', 'wall'],
|
||||||
multiple: true,
|
multiple: true,
|
||||||
required: true,
|
required: true,
|
||||||
validation: shellValidator,
|
|
||||||
},
|
},
|
||||||
thickness: {
|
thickness: {
|
||||||
inputType: 'kcl',
|
inputType: 'kcl',
|
||||||
defaultValue: KCL_DEFAULT_LENGTH,
|
defaultValue: KCL_DEFAULT_LENGTH,
|
||||||
required: true,
|
required: true,
|
||||||
// TODO: add dry-run validation on thickness param
|
validation: shellValidator,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Revolve: {
|
Revolve: {
|
||||||
description: 'Create a 3D body by rotating a sketch region about an axis.',
|
description: 'Create a 3D body by rotating a sketch region about an axis.',
|
||||||
icon: 'revolve',
|
icon: 'revolve',
|
||||||
status: 'development',
|
|
||||||
needsReview: true,
|
needsReview: true,
|
||||||
args: {
|
args: {
|
||||||
selection: {
|
selection: {
|
||||||
@ -377,8 +372,6 @@ export const modelingMachineCommandConfig: StateMachineCommandSetConfig<
|
|||||||
multiple: false, // TODO: multiple selection
|
multiple: false, // TODO: multiple selection
|
||||||
required: true,
|
required: true,
|
||||||
skip: true,
|
skip: true,
|
||||||
warningMessage:
|
|
||||||
'The revolve workflow is new and under tested. Please break it and report issues.',
|
|
||||||
},
|
},
|
||||||
axisOrEdge: {
|
axisOrEdge: {
|
||||||
inputType: 'options',
|
inputType: 'options',
|
||||||
|
|||||||
@ -3,6 +3,7 @@ import { engineCommandManager } from 'lib/singletons'
|
|||||||
import { uuidv4 } from 'lib/utils'
|
import { uuidv4 } from 'lib/utils'
|
||||||
import { CommandBarContext } from 'machines/commandBarMachine'
|
import { CommandBarContext } from 'machines/commandBarMachine'
|
||||||
import { Selections } from 'lib/selections'
|
import { Selections } from 'lib/selections'
|
||||||
|
import { KclCommandValue } from 'lib/commandTypes'
|
||||||
import { ApiError_type } from '@kittycad/lib/dist/types/src/models'
|
import { ApiError_type } from '@kittycad/lib/dist/types/src/models'
|
||||||
|
|
||||||
export const disableDryRunWithRetry = async (numberOfRetries = 3) => {
|
export const disableDryRunWithRetry = async (numberOfRetries = 3) => {
|
||||||
@ -107,7 +108,8 @@ export const revolveAxisValidator = async ({
|
|||||||
angle: angleInDegrees,
|
angle: angleInDegrees,
|
||||||
edge_id: edgeSelection,
|
edge_id: edgeSelection,
|
||||||
target: sketchSelection,
|
target: sketchSelection,
|
||||||
tolerance: 0.0001,
|
// Gotcha: Playwright will fail with larger tolerances, need to use a smaller one.
|
||||||
|
tolerance: 1e-7,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -170,16 +172,21 @@ export const loftValidator = async ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const shellValidator = async ({
|
export const shellValidator = async ({
|
||||||
|
context,
|
||||||
data,
|
data,
|
||||||
}: {
|
}: {
|
||||||
data: { selection: Selections }
|
context: CommandBarContext
|
||||||
|
data: { thickness: KclCommandValue }
|
||||||
}): Promise<boolean | string> => {
|
}): Promise<boolean | string> => {
|
||||||
if (!isSelections(data.selection)) {
|
const thicknessArg = data.thickness
|
||||||
|
const selectionArg = context.argumentsToSubmit['selection'] as Selections
|
||||||
|
if (!isSelections(selectionArg)) {
|
||||||
return 'Unable to shell, selections are missing'
|
return 'Unable to shell, selections are missing'
|
||||||
}
|
}
|
||||||
|
|
||||||
// No validation on the faces, filtering is done upstream and we have the dry run validation just below
|
// No validation on the args, filtering is done upstream and we have the dry run validation just below
|
||||||
const face_ids = data.selection.graphSelections.flatMap((s) =>
|
const shell_thickness = Number(thicknessArg.valueCalculated)
|
||||||
|
const face_ids = selectionArg.graphSelections.flatMap((s) =>
|
||||||
s.artifact ? s.artifact.id : []
|
s.artifact ? s.artifact.id : []
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -196,14 +203,12 @@ export const shellValidator = async ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
const command = async () => {
|
const command = async () => {
|
||||||
// TODO: figure out something better than an arbitrarily small value
|
|
||||||
const DEFAULT_THICKNESS: Models['LengthUnit_type'] = 1e-9
|
|
||||||
const DEFAULT_HOLLOW = false
|
const DEFAULT_HOLLOW = false
|
||||||
const cmdArgs = {
|
const cmdArgs = {
|
||||||
face_ids,
|
face_ids,
|
||||||
object_id,
|
object_id,
|
||||||
|
shell_thickness,
|
||||||
hollow: DEFAULT_HOLLOW,
|
hollow: DEFAULT_HOLLOW,
|
||||||
shell_thickness: DEFAULT_THICKNESS,
|
|
||||||
}
|
}
|
||||||
return await engineCommandManager.sendSceneCommand({
|
return await engineCommandManager.sendSceneCommand({
|
||||||
type: 'modeling_cmd_req',
|
type: 'modeling_cmd_req',
|
||||||
|
|||||||
@ -171,6 +171,13 @@ export type CommandArgumentConfig<
|
|||||||
commandBarContext: ContextFrom<typeof commandBarMachine>,
|
commandBarContext: ContextFrom<typeof commandBarMachine>,
|
||||||
machineContext?: C
|
machineContext?: C
|
||||||
) => string)
|
) => string)
|
||||||
|
validation?: ({
|
||||||
|
data,
|
||||||
|
context,
|
||||||
|
}: {
|
||||||
|
data: any
|
||||||
|
context: CommandBarContext
|
||||||
|
}) => Promise<boolean | string>
|
||||||
}
|
}
|
||||||
| {
|
| {
|
||||||
inputType: 'string'
|
inputType: 'string'
|
||||||
@ -267,6 +274,13 @@ export type CommandArgument<
|
|||||||
commandBarContext: ContextFrom<typeof commandBarMachine>,
|
commandBarContext: ContextFrom<typeof commandBarMachine>,
|
||||||
machineContext?: ContextFrom<T>
|
machineContext?: ContextFrom<T>
|
||||||
) => string)
|
) => string)
|
||||||
|
validation?: ({
|
||||||
|
data,
|
||||||
|
context,
|
||||||
|
}: {
|
||||||
|
data: any
|
||||||
|
context: CommandBarContext
|
||||||
|
}) => Promise<boolean | string>
|
||||||
}
|
}
|
||||||
| {
|
| {
|
||||||
inputType: 'string'
|
inputType: 'string'
|
||||||
|
|||||||
@ -26,7 +26,7 @@ export const FILE_EXT = '.kcl'
|
|||||||
/** Default file to open when a project is opened */
|
/** Default file to open when a project is opened */
|
||||||
export const PROJECT_ENTRYPOINT = `main${FILE_EXT}` as const
|
export const PROJECT_ENTRYPOINT = `main${FILE_EXT}` as const
|
||||||
/** Thumbnail file name */
|
/** Thumbnail file name */
|
||||||
export const PROJECT_IMAGE_NAME = `main.jpg` as const
|
export const PROJECT_IMAGE_NAME = `thumbnail.png` as const
|
||||||
/** The localStorage key for last-opened projects */
|
/** The localStorage key for last-opened projects */
|
||||||
export const FILE_PERSIST_KEY = `${PROJECT_FOLDER}-last-opened` as const
|
export const FILE_PERSIST_KEY = `${PROJECT_FOLDER}-last-opened` as const
|
||||||
/** The default name given to new kcl files in a project */
|
/** The default name given to new kcl files in a project */
|
||||||
@ -68,8 +68,6 @@ export const KCL_DEFAULT_DEGREE = `360`
|
|||||||
/** localStorage key for the playwright test-specific app settings file */
|
/** localStorage key for the playwright test-specific app settings file */
|
||||||
export const TEST_SETTINGS_FILE_KEY = 'playwright-test-settings'
|
export const TEST_SETTINGS_FILE_KEY = 'playwright-test-settings'
|
||||||
|
|
||||||
export const DEFAULT_HOST = 'https://api.zoo.dev'
|
|
||||||
export const PROD_APP_URL = 'https://app.zoo.dev'
|
|
||||||
export const SETTINGS_FILE_NAME = 'settings.toml'
|
export const SETTINGS_FILE_NAME = 'settings.toml'
|
||||||
export const TOKEN_FILE_NAME = 'token.txt'
|
export const TOKEN_FILE_NAME = 'token.txt'
|
||||||
export const PROJECT_SETTINGS_FILE_NAME = 'project.toml'
|
export const PROJECT_SETTINGS_FILE_NAME = 'project.toml'
|
||||||
@ -145,7 +143,7 @@ export const VIEW_NAMES_SEMANTIC = {
|
|||||||
export const SIDEBAR_BUTTON_SUFFIX = '-pane-button'
|
export const SIDEBAR_BUTTON_SUFFIX = '-pane-button'
|
||||||
|
|
||||||
/** Custom URL protocol our desktop registers */
|
/** Custom URL protocol our desktop registers */
|
||||||
export const ZOO_STUDIO_PROTOCOL = 'zoo-studio:'
|
export const ZOO_STUDIO_PROTOCOL = 'zoo-studio'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A query parameter that triggers a modal
|
* A query parameter that triggers a modal
|
||||||
|
|||||||
@ -193,6 +193,7 @@ export function buildCommandArgument<
|
|||||||
createVariableByDefault: arg.createVariableByDefault,
|
createVariableByDefault: arg.createVariableByDefault,
|
||||||
variableName: arg.variableName,
|
variableName: arg.variableName,
|
||||||
defaultValue: arg.defaultValue,
|
defaultValue: arg.defaultValue,
|
||||||
|
validation: arg.validation,
|
||||||
...baseCommandArgument,
|
...baseCommandArgument,
|
||||||
} satisfies CommandArgument<O, T> & { inputType: 'kcl' }
|
} satisfies CommandArgument<O, T> & { inputType: 'kcl' }
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@ -10,6 +10,7 @@ import {
|
|||||||
import {
|
import {
|
||||||
PROJECT_ENTRYPOINT,
|
PROJECT_ENTRYPOINT,
|
||||||
PROJECT_FOLDER,
|
PROJECT_FOLDER,
|
||||||
|
PROJECT_IMAGE_NAME,
|
||||||
PROJECT_SETTINGS_FILE_NAME,
|
PROJECT_SETTINGS_FILE_NAME,
|
||||||
SETTINGS_FILE_NAME,
|
SETTINGS_FILE_NAME,
|
||||||
TELEMETRY_FILE_NAME,
|
TELEMETRY_FILE_NAME,
|
||||||
@ -625,3 +626,19 @@ export const getUser = async (
|
|||||||
}
|
}
|
||||||
return Promise.reject(new Error('unreachable'))
|
return Promise.reject(new Error('unreachable'))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const writeProjectThumbnailFile = async (
|
||||||
|
dataUrl: string,
|
||||||
|
projectDirectoryPath: string
|
||||||
|
) => {
|
||||||
|
const filePath = window.electron.path.join(
|
||||||
|
projectDirectoryPath,
|
||||||
|
PROJECT_IMAGE_NAME
|
||||||
|
)
|
||||||
|
const data = atob(dataUrl.substring('data:image/png;base64,'.length))
|
||||||
|
const asArray = new Uint8Array(data.length)
|
||||||
|
for (let i = 0, len = data.length; i < len; ++i) {
|
||||||
|
asArray[i] = data.charCodeAt(i)
|
||||||
|
}
|
||||||
|
return window.electron.writeFile(filePath, asArray)
|
||||||
|
}
|
||||||
|
|||||||
@ -1,11 +1,13 @@
|
|||||||
import { CommandBarOverwriteWarning } from 'components/CommandBarOverwriteWarning'
|
import { CommandBarOverwriteWarning } from 'components/CommandBarOverwriteWarning'
|
||||||
import { Command, CommandArgumentOption } from './commandTypes'
|
import { Command, CommandArgumentOption } from './commandTypes'
|
||||||
import { kclManager } from './singletons'
|
import { codeManager, kclManager } from './singletons'
|
||||||
import { isDesktop } from './isDesktop'
|
import { isDesktop } from './isDesktop'
|
||||||
import { FILE_EXT } from './constants'
|
import { FILE_EXT } from './constants'
|
||||||
import { UnitLength_type } from '@kittycad/lib/dist/types/src/models'
|
import { UnitLength_type } from '@kittycad/lib/dist/types/src/models'
|
||||||
import { reportRejection } from './trap'
|
import { reportRejection } from './trap'
|
||||||
import { IndexLoaderData } from './types'
|
import { IndexLoaderData } from './types'
|
||||||
|
import { IS_NIGHTLY_OR_DEBUG } from 'routes/Settings'
|
||||||
|
import { copyFileShareLink } from './links'
|
||||||
|
|
||||||
interface OnSubmitProps {
|
interface OnSubmitProps {
|
||||||
sampleName: string
|
sampleName: string
|
||||||
@ -132,21 +134,22 @@ export function kclCommands(commandProps: KclCommandConfig): Command[] {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
// {
|
{
|
||||||
// name: 'share-file-link',
|
name: 'share-file-link',
|
||||||
// displayName: 'Share file',
|
displayName: 'Share file',
|
||||||
// description: 'Create a link that contains a copy of the current file.',
|
hide: IS_NIGHTLY_OR_DEBUG ? undefined : 'desktop',
|
||||||
// groupId: 'code',
|
description: 'Create a link that contains a copy of the current file.',
|
||||||
// needsReview: false,
|
groupId: 'code',
|
||||||
// icon: 'link',
|
needsReview: false,
|
||||||
// onSubmit: () => {
|
icon: 'link',
|
||||||
// copyFileShareLink({
|
onSubmit: () => {
|
||||||
// token: commandProps.authToken,
|
copyFileShareLink({
|
||||||
// code: codeManager.code,
|
token: commandProps.authToken,
|
||||||
// name: commandProps.projectData.project?.name || '',
|
code: codeManager.code,
|
||||||
// units: commandProps.settings.defaultUnit,
|
name: commandProps.projectData.project?.name || '',
|
||||||
// }).catch(reportRejection)
|
units: commandProps.settings.defaultUnit,
|
||||||
// },
|
}).catch(reportRejection)
|
||||||
// },
|
},
|
||||||
|
},
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
import { VITE_KC_SITE_APP_URL } from 'env'
|
||||||
import { createCreateFileUrl } from './links'
|
import { createCreateFileUrl } from './links'
|
||||||
|
|
||||||
describe(`link creation tests`, () => {
|
describe(`link creation tests`, () => {
|
||||||
@ -8,7 +9,7 @@ describe(`link creation tests`, () => {
|
|||||||
|
|
||||||
// Converted with external online tools
|
// Converted with external online tools
|
||||||
const expectedEncodedCode = `ZXh0cnVzaW9uRGlzdGFuY2UgPSAxMg%3D%3D`
|
const expectedEncodedCode = `ZXh0cnVzaW9uRGlzdGFuY2UgPSAxMg%3D%3D`
|
||||||
const expectedLink = `http://localhost:3000/?create-file=true&name=test&units=mm&code=${expectedEncodedCode}&ask-open-desktop=true`
|
const expectedLink = `${VITE_KC_SITE_APP_URL}/?create-file=true&name=test&units=mm&code=${expectedEncodedCode}&ask-open-desktop=true`
|
||||||
|
|
||||||
const result = createCreateFileUrl({ code, name, units })
|
const result = createCreateFileUrl({ code, name, units })
|
||||||
expect(result.toString()).toBe(expectedLink)
|
expect(result.toString()).toBe(expectedLink)
|
||||||
|
|||||||
@ -1,11 +1,7 @@
|
|||||||
import { UnitLength_type } from '@kittycad/lib/dist/types/src/models'
|
import { UnitLength_type } from '@kittycad/lib/dist/types/src/models'
|
||||||
import {
|
import { ASK_TO_OPEN_QUERY_PARAM, CREATE_FILE_URL_PARAM } from './constants'
|
||||||
ASK_TO_OPEN_QUERY_PARAM,
|
|
||||||
CREATE_FILE_URL_PARAM,
|
|
||||||
PROD_APP_URL,
|
|
||||||
} from './constants'
|
|
||||||
import { stringToBase64 } from './base64'
|
import { stringToBase64 } from './base64'
|
||||||
import { DEV, VITE_KC_API_BASE_URL } from 'env'
|
import { VITE_KC_API_BASE_URL, VITE_KC_SITE_APP_URL } from 'env'
|
||||||
import toast from 'react-hot-toast'
|
import toast from 'react-hot-toast'
|
||||||
import { err } from './trap'
|
import { err } from './trap'
|
||||||
export interface FileLinkParams {
|
export interface FileLinkParams {
|
||||||
@ -51,8 +47,7 @@ export async function copyFileShareLink(
|
|||||||
* open the URL in the desktop app.
|
* open the URL in the desktop app.
|
||||||
*/
|
*/
|
||||||
export function createCreateFileUrl({ code, name, units }: FileLinkParams) {
|
export function createCreateFileUrl({ code, name, units }: FileLinkParams) {
|
||||||
// Use the dev server if we are in development mode
|
let origin = VITE_KC_SITE_APP_URL
|
||||||
let origin = DEV ? 'http://localhost:3000' : PROD_APP_URL
|
|
||||||
const searchParams = new URLSearchParams({
|
const searchParams = new URLSearchParams({
|
||||||
[CREATE_FILE_URL_PARAM]: String(true),
|
[CREATE_FILE_URL_PARAM]: String(true),
|
||||||
name,
|
name,
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
function takeScreenshotOfVideoStreamCanvas() {
|
export function takeScreenshotOfVideoStreamCanvas() {
|
||||||
const canvas = document.querySelector('[data-engine]')
|
const canvas = document.querySelector('[data-engine]')
|
||||||
const video = document.getElementById('video-stream')
|
const video = document.getElementById('video-stream')
|
||||||
if (
|
if (
|
||||||
|
|||||||
@ -577,10 +577,9 @@ export function getSelectionTypeDisplayText(
|
|||||||
.map(
|
.map(
|
||||||
// Hack for showing "face" instead of "extrude-wall" in command bar text
|
// Hack for showing "face" instead of "extrude-wall" in command bar text
|
||||||
([type, count]) =>
|
([type, count]) =>
|
||||||
`${count} ${type
|
`${count} ${type.replace('wall', 'face').replace('solid2d', 'face')}${
|
||||||
.replace('wall', 'face')
|
count > 1 ? 's' : ''
|
||||||
.replace('solid2d', 'face')
|
}`
|
||||||
.replace('segment', 'face')}${count > 1 ? 's' : ''}`
|
|
||||||
)
|
)
|
||||||
.toArray()
|
.toArray()
|
||||||
.join(', ')
|
.join(', ')
|
||||||
|
|||||||
@ -20,6 +20,7 @@ import { toSync } from 'lib/utils'
|
|||||||
import { reportRejection } from 'lib/trap'
|
import { reportRejection } from 'lib/trap'
|
||||||
import { CameraProjectionType } from 'wasm-lib/kcl/bindings/CameraProjectionType'
|
import { CameraProjectionType } from 'wasm-lib/kcl/bindings/CameraProjectionType'
|
||||||
import { OnboardingStatus } from 'wasm-lib/kcl/bindings/OnboardingStatus'
|
import { OnboardingStatus } from 'wasm-lib/kcl/bindings/OnboardingStatus'
|
||||||
|
import { CameraOrbitType } from 'wasm-lib/kcl/bindings/CameraOrbitType'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A setting that can be set at the user or project level
|
* A setting that can be set at the user or project level
|
||||||
@ -380,6 +381,30 @@ export function createSettings() {
|
|||||||
})),
|
})),
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
|
/**
|
||||||
|
* What methodology to use for orbiting the camera
|
||||||
|
*/
|
||||||
|
cameraOrbit: new Setting<CameraOrbitType>({
|
||||||
|
defaultValue: 'spherical',
|
||||||
|
hideOnLevel: 'project',
|
||||||
|
description: 'What methodology to use for orbiting the camera',
|
||||||
|
validate: (v) => ['spherical', 'trackball'].includes(v),
|
||||||
|
commandConfig: {
|
||||||
|
inputType: 'options',
|
||||||
|
defaultValueFromContext: (context) =>
|
||||||
|
context.modeling.cameraOrbit.current,
|
||||||
|
options: (cmdContext, settingsContext) =>
|
||||||
|
(['spherical', 'trackball'] as const).map((v) => ({
|
||||||
|
name: v.charAt(0).toUpperCase() + v.slice(1),
|
||||||
|
value: v,
|
||||||
|
isCurrent:
|
||||||
|
settingsContext.modeling.cameraOrbit.shouldShowCurrentLabel(
|
||||||
|
cmdContext.argumentsToSubmit.level as SettingsLevel,
|
||||||
|
v
|
||||||
|
),
|
||||||
|
})),
|
||||||
|
},
|
||||||
|
}),
|
||||||
/**
|
/**
|
||||||
* Whether to highlight edges of 3D objects
|
* Whether to highlight edges of 3D objects
|
||||||
*/
|
*/
|
||||||
|
|||||||
@ -4,6 +4,7 @@ import { AtLeast, PathValue, Paths } from 'lib/types'
|
|||||||
import { CommandArgumentConfig } from 'lib/commandTypes'
|
import { CommandArgumentConfig } from 'lib/commandTypes'
|
||||||
import { Themes } from 'lib/theme'
|
import { Themes } from 'lib/theme'
|
||||||
import { CameraProjectionType } from 'wasm-lib/kcl/bindings/CameraProjectionType'
|
import { CameraProjectionType } from 'wasm-lib/kcl/bindings/CameraProjectionType'
|
||||||
|
import { CameraOrbitType } from 'wasm-lib/kcl/bindings/CameraOrbitType'
|
||||||
|
|
||||||
export interface SettingsViaQueryString {
|
export interface SettingsViaQueryString {
|
||||||
pool: string | null
|
pool: string | null
|
||||||
@ -12,6 +13,7 @@ export interface SettingsViaQueryString {
|
|||||||
enableSSAO: boolean
|
enableSSAO: boolean
|
||||||
showScaleGrid: boolean
|
showScaleGrid: boolean
|
||||||
cameraProjection: CameraProjectionType
|
cameraProjection: CameraProjectionType
|
||||||
|
cameraOrbit: CameraOrbitType
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum UnitSystem {
|
export enum UnitSystem {
|
||||||
|
|||||||
@ -49,6 +49,7 @@ export function configurationToSettingsPayload(
|
|||||||
modeling: {
|
modeling: {
|
||||||
defaultUnit: configuration?.settings?.modeling?.base_unit,
|
defaultUnit: configuration?.settings?.modeling?.base_unit,
|
||||||
cameraProjection: configuration?.settings?.modeling?.camera_projection,
|
cameraProjection: configuration?.settings?.modeling?.camera_projection,
|
||||||
|
cameraOrbit: configuration?.settings?.modeling?.camera_orbit,
|
||||||
mouseControls: mouseControlsToCameraSystem(
|
mouseControls: mouseControlsToCameraSystem(
|
||||||
configuration?.settings?.modeling?.mouse_controls
|
configuration?.settings?.modeling?.mouse_controls
|
||||||
),
|
),
|
||||||
|
|||||||
@ -103,7 +103,7 @@ export const toolbarConfig: Record<ToolbarModeName, ToolbarMode> = {
|
|||||||
data: { name: 'Revolve', groupId: 'modeling' },
|
data: { name: 'Revolve', groupId: 'modeling' },
|
||||||
}),
|
}),
|
||||||
icon: 'revolve',
|
icon: 'revolve',
|
||||||
status: DEV || IS_NIGHTLY_OR_DEBUG ? 'available' : 'kcl-only',
|
status: 'available',
|
||||||
title: 'Revolve',
|
title: 'Revolve',
|
||||||
hotkey: 'R',
|
hotkey: 'R',
|
||||||
description:
|
description:
|
||||||
@ -124,7 +124,7 @@ export const toolbarConfig: Record<ToolbarModeName, ToolbarMode> = {
|
|||||||
data: { name: 'Sweep', groupId: 'modeling' },
|
data: { name: 'Sweep', groupId: 'modeling' },
|
||||||
}),
|
}),
|
||||||
icon: 'sweep',
|
icon: 'sweep',
|
||||||
status: DEV || IS_NIGHTLY_OR_DEBUG ? 'available' : 'kcl-only',
|
status: 'available',
|
||||||
title: 'Sweep',
|
title: 'Sweep',
|
||||||
hotkey: 'W',
|
hotkey: 'W',
|
||||||
description:
|
description:
|
||||||
|
|||||||
@ -294,7 +294,8 @@ export const commandBarMachine = setup({
|
|||||||
if (
|
if (
|
||||||
context.currentArgument &&
|
context.currentArgument &&
|
||||||
context.selectedCommand &&
|
context.selectedCommand &&
|
||||||
argConfig?.inputType === 'selection' &&
|
(argConfig?.inputType === 'selection' ||
|
||||||
|
argConfig?.inputType === 'kcl') &&
|
||||||
argConfig?.validation
|
argConfig?.validation
|
||||||
) {
|
) {
|
||||||
argConfig
|
argConfig
|
||||||
|
|||||||
71
src/main.ts
@ -31,23 +31,27 @@ let mainWindow: BrowserWindow | null = null
|
|||||||
// Check the command line arguments for a project path
|
// Check the command line arguments for a project path
|
||||||
const args = parseCLIArgs()
|
const args = parseCLIArgs()
|
||||||
|
|
||||||
// If it's not set, scream.
|
// @ts-ignore: TS1343
|
||||||
const NODE_ENV = process.env.NODE_ENV || 'production'
|
const viteEnv = import.meta.env
|
||||||
if (!process.env.NODE_ENV)
|
const NODE_ENV = process.env.NODE_ENV || viteEnv.MODE
|
||||||
console.warn(
|
|
||||||
'*FOX SCREAM* process.env.NODE_ENV is not explicitly set!, defaulting to production'
|
|
||||||
)
|
|
||||||
// Default prod values
|
|
||||||
|
|
||||||
// dotenv override when present
|
// dotenv override when present
|
||||||
dotenv.config({ path: [`.env.${NODE_ENV}.local`, `.env.${NODE_ENV}`] })
|
dotenv.config({ path: [`.env.${NODE_ENV}.local`, `.env.${NODE_ENV}`] })
|
||||||
|
|
||||||
process.env.VITE_KC_API_WS_MODELING_URL ??=
|
// default vite values based on mode
|
||||||
'wss://api.zoo.dev/ws/modeling/commands'
|
process.env.NODE_ENV ??= viteEnv.MODE
|
||||||
process.env.VITE_KC_API_BASE_URL ??= 'https://api.zoo.dev'
|
process.env.DEV ??= viteEnv.DEV + ''
|
||||||
process.env.VITE_KC_SITE_BASE_URL ??= 'https://zoo.dev'
|
process.env.BASE_URL ??= viteEnv.VITE_KC_API_BASE_URL
|
||||||
process.env.VITE_KC_SKIP_AUTH ??= 'false'
|
process.env.VITE_KC_API_WS_MODELING_URL ??= viteEnv.VITE_KC_API_WS_MODELING_URL
|
||||||
process.env.VITE_KC_CONNECTION_TIMEOUT_MS ??= '15000'
|
process.env.VITE_KC_API_BASE_URL ??= viteEnv.VITE_KC_API_BASE_URL
|
||||||
|
process.env.VITE_KC_SITE_BASE_URL ??= viteEnv.VITE_KC_SITE_BASE_URL
|
||||||
|
process.env.VITE_KC_SITE_APP_URL ??= viteEnv.VITE_KC_SITE_APP_URL
|
||||||
|
process.env.VITE_KC_SKIP_AUTH ??= viteEnv.VITE_KC_SKIP_AUTH
|
||||||
|
process.env.VITE_KC_CONNECTION_TIMEOUT_MS ??=
|
||||||
|
viteEnv.VITE_KC_CONNECTION_TIMEOUT_MS
|
||||||
|
|
||||||
|
// Likely convenient to keep for debugging
|
||||||
|
console.log('process.env', process.env)
|
||||||
|
|
||||||
/// Register our application to handle all "zoo-studio:" protocols.
|
/// Register our application to handle all "zoo-studio:" protocols.
|
||||||
if (process.defaultApp) {
|
if (process.defaultApp) {
|
||||||
@ -89,22 +93,43 @@ const createWindow = (pathToOpen?: string, reuse?: boolean): BrowserWindow => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Deep Link: Case of a cold start from Windows or Linux
|
||||||
|
if (
|
||||||
|
!pathToOpen &&
|
||||||
|
process.argv.length > 1 &&
|
||||||
|
process.argv[1].indexOf(ZOO_STUDIO_PROTOCOL + '://') > -1
|
||||||
|
) {
|
||||||
|
pathToOpen = process.argv[1]
|
||||||
|
console.log('Retrieved deep link from argv', pathToOpen)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deep Link: Case of a second window opened for macOS
|
||||||
|
// @ts-ignore
|
||||||
|
if (!pathToOpen && global['openUrls'] && global['openUrls'][0]) {
|
||||||
|
// @ts-ignore
|
||||||
|
pathToOpen = global['openUrls'][0]
|
||||||
|
console.log('Retrieved deep link from open-url', pathToOpen)
|
||||||
|
}
|
||||||
|
|
||||||
const pathIsCustomProtocolLink =
|
const pathIsCustomProtocolLink =
|
||||||
pathToOpen?.startsWith(ZOO_STUDIO_PROTOCOL) ?? false
|
pathToOpen?.startsWith(ZOO_STUDIO_PROTOCOL) ?? false
|
||||||
|
|
||||||
// and load the index.html of the app.
|
// and load the index.html of the app.
|
||||||
if (MAIN_WINDOW_VITE_DEV_SERVER_URL) {
|
if (MAIN_WINDOW_VITE_DEV_SERVER_URL) {
|
||||||
const filteredPath = pathToOpen
|
const filteredPath = pathToOpen
|
||||||
? decodeURI(pathToOpen.replace(ZOO_STUDIO_PROTOCOL, ''))
|
? decodeURI(pathToOpen.replace(ZOO_STUDIO_PROTOCOL + '://', ''))
|
||||||
: ''
|
: ''
|
||||||
const fullHashBasedUrl = `${MAIN_WINDOW_VITE_DEV_SERVER_URL}/#/${filteredPath}`
|
const fullHashBasedUrl = `${MAIN_WINDOW_VITE_DEV_SERVER_URL}/#/${filteredPath}`
|
||||||
newWindow.loadURL(fullHashBasedUrl).catch(reportRejection)
|
newWindow.loadURL(fullHashBasedUrl).catch(reportRejection)
|
||||||
} else {
|
} else {
|
||||||
if (pathIsCustomProtocolLink && pathToOpen) {
|
if (pathIsCustomProtocolLink && pathToOpen) {
|
||||||
// We're trying to open a custom protocol link
|
// We're trying to open a custom protocol link
|
||||||
const filteredPath = pathToOpen
|
// TODO: fix the replace %3 thing
|
||||||
? decodeURI(pathToOpen.replace(ZOO_STUDIO_PROTOCOL, ''))
|
const urlNoProtocol = pathToOpen
|
||||||
: ''
|
.replace(ZOO_STUDIO_PROTOCOL + '://', '')
|
||||||
|
.replaceAll('%3D', '')
|
||||||
|
.replaceAll('%3', '')
|
||||||
|
const filteredPath = decodeURI(urlNoProtocol)
|
||||||
const startIndex = path.join(
|
const startIndex = path.join(
|
||||||
__dirname,
|
__dirname,
|
||||||
`../renderer/${MAIN_WINDOW_VITE_NAME}/index.html`
|
`../renderer/${MAIN_WINDOW_VITE_NAME}/index.html`
|
||||||
@ -342,7 +367,7 @@ export function getAutoUpdater(): AppUpdater {
|
|||||||
|
|
||||||
app.on('ready', () => {
|
app.on('ready', () => {
|
||||||
// Disable auto updater on non-versioned builds
|
// Disable auto updater on non-versioned builds
|
||||||
if (packageJSON.version === '0.0.0') {
|
if (packageJSON.version === '0.0.0' && viteEnv.MODE !== 'production') {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -459,6 +484,14 @@ function parseCLIArgs(): minimist.ParsedArgs {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function registerStartupListeners() {
|
function registerStartupListeners() {
|
||||||
|
// Linux and Windows from https://www.electronjs.org/docs/latest/tutorial/launch-app-from-url-in-another-app
|
||||||
|
app.on('second-instance', (event, commandLine, workingDirectory) => {
|
||||||
|
// Deep Link: second instance for Windows and Linux
|
||||||
|
const url = commandLine.pop()?.slice(0, -1)
|
||||||
|
console.log('Retrieved deep link from commandLine', url)
|
||||||
|
createWindow(url)
|
||||||
|
})
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* macOS: when someone drops a file to the not-yet running VSCode, the open-file event fires even before
|
* macOS: when someone drops a file to the not-yet running VSCode, the open-file event fires even before
|
||||||
* the app-ready event. We listen very early for open-file and remember this upon startup as path to open.
|
* the app-ready event. We listen very early for open-file and remember this upon startup as path to open.
|
||||||
@ -478,7 +511,7 @@ function registerStartupListeners() {
|
|||||||
})
|
})
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* macOS: react to open-url requests.
|
* macOS: react to open-url requests (including Deep Link on second instances)
|
||||||
*/
|
*/
|
||||||
const openUrls: string[] = []
|
const openUrls: string[] = []
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
|
|||||||
@ -185,6 +185,7 @@ contextBridge.exposeInMainWorld('electron', {
|
|||||||
'VITE_KC_API_WS_MODELING_URL',
|
'VITE_KC_API_WS_MODELING_URL',
|
||||||
'VITE_KC_API_BASE_URL',
|
'VITE_KC_API_BASE_URL',
|
||||||
'VITE_KC_SITE_BASE_URL',
|
'VITE_KC_SITE_BASE_URL',
|
||||||
|
'VITE_KC_SITE_APP_URL',
|
||||||
'VITE_KC_SKIP_AUTH',
|
'VITE_KC_SKIP_AUTH',
|
||||||
'VITE_KC_CONNECTION_TIMEOUT_MS',
|
'VITE_KC_CONNECTION_TIMEOUT_MS',
|
||||||
'VITE_KC_DEV_TOKEN',
|
'VITE_KC_DEV_TOKEN',
|
||||||
|
|||||||
@ -259,6 +259,9 @@ pub struct ModelingSettings {
|
|||||||
/// The projection mode the camera should use while modeling.
|
/// The projection mode the camera should use while modeling.
|
||||||
#[serde(default, alias = "cameraProjection", skip_serializing_if = "is_default")]
|
#[serde(default, alias = "cameraProjection", skip_serializing_if = "is_default")]
|
||||||
pub camera_projection: CameraProjectionType,
|
pub camera_projection: CameraProjectionType,
|
||||||
|
/// The methodology the camera should use to orbit around the model.
|
||||||
|
#[serde(default, alias = "cameraOrbit", skip_serializing_if = "is_default")]
|
||||||
|
pub camera_orbit: CameraOrbitType,
|
||||||
/// The controls for how to navigate the 3D view.
|
/// The controls for how to navigate the 3D view.
|
||||||
#[serde(default, alias = "mouseControls", skip_serializing_if = "is_default")]
|
#[serde(default, alias = "mouseControls", skip_serializing_if = "is_default")]
|
||||||
pub mouse_controls: MouseControlType,
|
pub mouse_controls: MouseControlType,
|
||||||
@ -415,6 +418,21 @@ pub enum CameraProjectionType {
|
|||||||
Orthographic,
|
Orthographic,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The types of camera orbit methods.
|
||||||
|
#[derive(Debug, Default, Eq, PartialEq, Clone, Deserialize, Serialize, JsonSchema, ts_rs::TS, Display, FromStr)]
|
||||||
|
#[ts(export)]
|
||||||
|
#[serde(rename_all = "snake_case")]
|
||||||
|
#[display(style = "snake_case")]
|
||||||
|
pub enum CameraOrbitType {
|
||||||
|
/// Orbit using a spherical camera movement.
|
||||||
|
#[default]
|
||||||
|
#[display("spherical")]
|
||||||
|
Spherical,
|
||||||
|
/// Orbit using a trackball camera movement.
|
||||||
|
#[display("trackball")]
|
||||||
|
Trackball,
|
||||||
|
}
|
||||||
|
|
||||||
/// Settings that affect the behavior of the KCL text editor.
|
/// Settings that affect the behavior of the KCL text editor.
|
||||||
#[derive(Debug, Default, Clone, Deserialize, Serialize, JsonSchema, ts_rs::TS, PartialEq, Eq, Validate)]
|
#[derive(Debug, Default, Clone, Deserialize, Serialize, JsonSchema, ts_rs::TS, PartialEq, Eq, Validate)]
|
||||||
#[serde(rename_all = "snake_case")]
|
#[serde(rename_all = "snake_case")]
|
||||||
@ -543,6 +561,8 @@ mod tests {
|
|||||||
use pretty_assertions::assert_eq;
|
use pretty_assertions::assert_eq;
|
||||||
use validator::Validate;
|
use validator::Validate;
|
||||||
|
|
||||||
|
use crate::settings::types::CameraOrbitType;
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
AppColor, AppSettings, AppTheme, AppearanceSettings, CameraProjectionType, CommandBarSettings, Configuration,
|
AppColor, AppSettings, AppTheme, AppearanceSettings, CameraProjectionType, CommandBarSettings, Configuration,
|
||||||
ModelingSettings, OnboardingStatus, ProjectSettings, Settings, TextEditorSettings, UnitLength,
|
ModelingSettings, OnboardingStatus, ProjectSettings, Settings, TextEditorSettings, UnitLength,
|
||||||
@ -594,6 +614,7 @@ textWrapping = true
|
|||||||
modeling: ModelingSettings {
|
modeling: ModelingSettings {
|
||||||
base_unit: UnitLength::In,
|
base_unit: UnitLength::In,
|
||||||
camera_projection: CameraProjectionType::Orthographic,
|
camera_projection: CameraProjectionType::Orthographic,
|
||||||
|
camera_orbit: Default::default(),
|
||||||
mouse_controls: Default::default(),
|
mouse_controls: Default::default(),
|
||||||
highlight_edges: Default::default(),
|
highlight_edges: Default::default(),
|
||||||
show_debug_panel: true,
|
show_debug_panel: true,
|
||||||
@ -656,6 +677,7 @@ includeSettings = false
|
|||||||
modeling: ModelingSettings {
|
modeling: ModelingSettings {
|
||||||
base_unit: UnitLength::Yd,
|
base_unit: UnitLength::Yd,
|
||||||
camera_projection: Default::default(),
|
camera_projection: Default::default(),
|
||||||
|
camera_orbit: Default::default(),
|
||||||
mouse_controls: Default::default(),
|
mouse_controls: Default::default(),
|
||||||
highlight_edges: Default::default(),
|
highlight_edges: Default::default(),
|
||||||
show_debug_panel: true,
|
show_debug_panel: true,
|
||||||
@ -723,6 +745,7 @@ defaultProjectName = "projects-$nnn"
|
|||||||
modeling: ModelingSettings {
|
modeling: ModelingSettings {
|
||||||
base_unit: UnitLength::Yd,
|
base_unit: UnitLength::Yd,
|
||||||
camera_projection: Default::default(),
|
camera_projection: Default::default(),
|
||||||
|
camera_orbit: CameraOrbitType::Spherical,
|
||||||
mouse_controls: Default::default(),
|
mouse_controls: Default::default(),
|
||||||
highlight_edges: Default::default(),
|
highlight_edges: Default::default(),
|
||||||
show_debug_panel: true,
|
show_debug_panel: true,
|
||||||
@ -802,6 +825,7 @@ projectDirectory = "/Users/macinatormax/Documents/kittycad-modeling-projects""#;
|
|||||||
modeling: ModelingSettings {
|
modeling: ModelingSettings {
|
||||||
base_unit: UnitLength::Mm,
|
base_unit: UnitLength::Mm,
|
||||||
camera_projection: Default::default(),
|
camera_projection: Default::default(),
|
||||||
|
camera_orbit: Default::default(),
|
||||||
mouse_controls: Default::default(),
|
mouse_controls: Default::default(),
|
||||||
highlight_edges: true.into(),
|
highlight_edges: true.into(),
|
||||||
show_debug_panel: false,
|
show_debug_panel: false,
|
||||||
|
|||||||
@ -129,6 +129,7 @@ includeSettings = false
|
|||||||
modeling: ModelingSettings {
|
modeling: ModelingSettings {
|
||||||
base_unit: UnitLength::Yd,
|
base_unit: UnitLength::Yd,
|
||||||
camera_projection: Default::default(),
|
camera_projection: Default::default(),
|
||||||
|
camera_orbit: Default::default(),
|
||||||
mouse_controls: Default::default(),
|
mouse_controls: Default::default(),
|
||||||
highlight_edges: Default::default(),
|
highlight_edges: Default::default(),
|
||||||
show_debug_panel: true,
|
show_debug_panel: true,
|
||||||
|
|||||||
@ -4,6 +4,8 @@ use insta::rounded_redaction;
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
errors::KclError,
|
errors::KclError,
|
||||||
|
exec::ArtifactCommand,
|
||||||
|
execution::{ArtifactGraph, Operation},
|
||||||
parsing::ast::types::{Node, Program},
|
parsing::ast::types::{Node, Program},
|
||||||
source_range::ModuleId,
|
source_range::ModuleId,
|
||||||
};
|
};
|
||||||
@ -104,36 +106,12 @@ async fn execute(test_name: &str, render_to_png: bool) {
|
|||||||
".environments[].**[].z[]" => rounded_redaction(4),
|
".environments[].**[].z[]" => rounded_redaction(4),
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
assert_snapshot(test_name, "Operations executed", || {
|
assert_common_snapshots(
|
||||||
insta::assert_json_snapshot!("ops", exec_state.mod_local.operations);
|
test_name,
|
||||||
});
|
exec_state.mod_local.operations,
|
||||||
assert_snapshot(test_name, "Artifact commands", || {
|
exec_state.global.artifact_commands,
|
||||||
insta::assert_json_snapshot!("artifact_commands", exec_state.global.artifact_commands, {
|
exec_state.global.artifact_graph,
|
||||||
"[].command.segment.*.x" => rounded_redaction(4),
|
);
|
||||||
"[].command.segment.*.y" => rounded_redaction(4),
|
|
||||||
"[].command.segment.*.z" => rounded_redaction(4),
|
|
||||||
});
|
|
||||||
});
|
|
||||||
assert_snapshot(test_name, "Artifact graph flowchart", || {
|
|
||||||
let flowchart = exec_state
|
|
||||||
.global
|
|
||||||
.artifact_graph
|
|
||||||
.to_mermaid_flowchart()
|
|
||||||
.unwrap_or_else(|e| format!("Failed to convert artifact graph to flowchart: {e}"));
|
|
||||||
// Change the snapshot suffix so that it is rendered as a
|
|
||||||
// Markdown file in GitHub.
|
|
||||||
insta::assert_binary_snapshot!("artifact_graph_flowchart.md", flowchart.as_bytes().to_owned());
|
|
||||||
});
|
|
||||||
assert_snapshot(test_name, "Artifact graph mind map", || {
|
|
||||||
let mind_map = exec_state
|
|
||||||
.global
|
|
||||||
.artifact_graph
|
|
||||||
.to_mermaid_mind_map()
|
|
||||||
.unwrap_or_else(|e| format!("Failed to convert artifact graph to mind map: {e}"));
|
|
||||||
// Change the snapshot suffix so that it is rendered as a
|
|
||||||
// Markdown file in GitHub.
|
|
||||||
insta::assert_binary_snapshot!("artifact_graph_mind_map.md", mind_map.as_bytes().to_owned());
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
match e.error {
|
match e.error {
|
||||||
@ -153,17 +131,12 @@ async fn execute(test_name: &str, render_to_png: bool) {
|
|||||||
insta::assert_snapshot!("execution_error", report);
|
insta::assert_snapshot!("execution_error", report);
|
||||||
});
|
});
|
||||||
|
|
||||||
assert_snapshot(test_name, "Operations executed", || {
|
assert_common_snapshots(
|
||||||
insta::assert_json_snapshot!("ops", error.operations);
|
test_name,
|
||||||
});
|
error.operations,
|
||||||
|
error.artifact_commands,
|
||||||
assert_snapshot(test_name, "Artifact commands", || {
|
error.artifact_graph,
|
||||||
insta::assert_json_snapshot!("artifact_commands", error.artifact_commands, {
|
);
|
||||||
"[].command.segment.*.x" => rounded_redaction(4),
|
|
||||||
"[].command.segment.*.y" => rounded_redaction(4),
|
|
||||||
"[].command.segment.*.z" => rounded_redaction(4),
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
e => {
|
e => {
|
||||||
// These kinds of errors aren't expected to occur. We don't
|
// These kinds of errors aren't expected to occur. We don't
|
||||||
@ -176,6 +149,42 @@ async fn execute(test_name: &str, render_to_png: bool) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Assert snapshots that should happen both when KCL execution succeeds and
|
||||||
|
/// when it results in an error.
|
||||||
|
fn assert_common_snapshots(
|
||||||
|
test_name: &str,
|
||||||
|
operations: Vec<Operation>,
|
||||||
|
artifact_commands: Vec<ArtifactCommand>,
|
||||||
|
artifact_graph: ArtifactGraph,
|
||||||
|
) {
|
||||||
|
assert_snapshot(test_name, "Operations executed", || {
|
||||||
|
insta::assert_json_snapshot!("ops", operations);
|
||||||
|
});
|
||||||
|
assert_snapshot(test_name, "Artifact commands", || {
|
||||||
|
insta::assert_json_snapshot!("artifact_commands", artifact_commands, {
|
||||||
|
"[].command.segment.*.x" => rounded_redaction(4),
|
||||||
|
"[].command.segment.*.y" => rounded_redaction(4),
|
||||||
|
"[].command.segment.*.z" => rounded_redaction(4),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
assert_snapshot(test_name, "Artifact graph flowchart", || {
|
||||||
|
let flowchart = artifact_graph
|
||||||
|
.to_mermaid_flowchart()
|
||||||
|
.unwrap_or_else(|e| format!("Failed to convert artifact graph to flowchart: {e}"));
|
||||||
|
// Change the snapshot suffix so that it is rendered as a Markdown file
|
||||||
|
// in GitHub.
|
||||||
|
insta::assert_binary_snapshot!("artifact_graph_flowchart.md", flowchart.as_bytes().to_owned());
|
||||||
|
});
|
||||||
|
assert_snapshot(test_name, "Artifact graph mind map", || {
|
||||||
|
let mind_map = artifact_graph
|
||||||
|
.to_mermaid_mind_map()
|
||||||
|
.unwrap_or_else(|e| format!("Failed to convert artifact graph to mind map: {e}"));
|
||||||
|
// Change the snapshot suffix so that it is rendered as a Markdown file
|
||||||
|
// in GitHub.
|
||||||
|
insta::assert_binary_snapshot!("artifact_graph_mind_map.md", mind_map.as_bytes().to_owned());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
mod cube {
|
mod cube {
|
||||||
const TEST_NAME: &str = "cube";
|
const TEST_NAME: &str = "cube";
|
||||||
|
|
||||||
@ -197,6 +206,27 @@ mod cube {
|
|||||||
super::execute(TEST_NAME, true).await
|
super::execute(TEST_NAME, true).await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
mod cube_with_error {
|
||||||
|
const TEST_NAME: &str = "cube_with_error";
|
||||||
|
|
||||||
|
/// Test parsing KCL.
|
||||||
|
#[test]
|
||||||
|
fn parse() {
|
||||||
|
super::parse(TEST_NAME)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Test that parsing and unparsing KCL produces the original KCL input.
|
||||||
|
#[test]
|
||||||
|
fn unparse() {
|
||||||
|
super::unparse(TEST_NAME)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Test that KCL is executed correctly.
|
||||||
|
#[tokio::test(flavor = "multi_thread")]
|
||||||
|
async fn kcl_test_execute() {
|
||||||
|
super::execute(TEST_NAME, true).await
|
||||||
|
}
|
||||||
|
}
|
||||||
mod artifact_graph_example_code1 {
|
mod artifact_graph_example_code1 {
|
||||||
const TEST_NAME: &str = "artifact_graph_example_code1";
|
const TEST_NAME: &str = "artifact_graph_example_code1";
|
||||||
|
|
||||||
|
|||||||
@ -0,0 +1,6 @@
|
|||||||
|
---
|
||||||
|
source: kcl/src/simulation_tests.rs
|
||||||
|
description: Artifact graph flowchart argument_error.kcl
|
||||||
|
extension: md
|
||||||
|
snapshot_kind: binary
|
||||||
|
---
|
||||||
@ -0,0 +1,3 @@
|
|||||||
|
```mermaid
|
||||||
|
flowchart LR
|
||||||
|
```
|
||||||
@ -0,0 +1,6 @@
|
|||||||
|
---
|
||||||
|
source: kcl/src/simulation_tests.rs
|
||||||
|
description: Artifact graph mind map argument_error.kcl
|
||||||
|
extension: md
|
||||||
|
snapshot_kind: binary
|
||||||
|
---
|
||||||
@ -0,0 +1,4 @@
|
|||||||
|
```mermaid
|
||||||
|
mindmap
|
||||||
|
root
|
||||||
|
```
|
||||||
@ -0,0 +1,6 @@
|
|||||||
|
---
|
||||||
|
source: kcl/src/simulation_tests.rs
|
||||||
|
description: Artifact graph flowchart array_elem_pop_empty_fail.kcl
|
||||||
|
extension: md
|
||||||
|
snapshot_kind: binary
|
||||||
|
---
|
||||||
@ -0,0 +1,3 @@
|
|||||||
|
```mermaid
|
||||||
|
flowchart LR
|
||||||
|
```
|
||||||
@ -0,0 +1,6 @@
|
|||||||
|
---
|
||||||
|
source: kcl/src/simulation_tests.rs
|
||||||
|
description: Artifact graph mind map array_elem_pop_empty_fail.kcl
|
||||||
|
extension: md
|
||||||
|
snapshot_kind: binary
|
||||||
|
---
|
||||||
@ -0,0 +1,4 @@
|
|||||||
|
```mermaid
|
||||||
|
mindmap
|
||||||
|
root
|
||||||
|
```
|
||||||
@ -0,0 +1,6 @@
|
|||||||
|
---
|
||||||
|
source: kcl/src/simulation_tests.rs
|
||||||
|
description: Artifact graph flowchart array_elem_pop_fail.kcl
|
||||||
|
extension: md
|
||||||
|
snapshot_kind: binary
|
||||||
|
---
|
||||||
@ -0,0 +1,3 @@
|
|||||||
|
```mermaid
|
||||||
|
flowchart LR
|
||||||
|
```
|
||||||
@ -0,0 +1,6 @@
|
|||||||
|
---
|
||||||
|
source: kcl/src/simulation_tests.rs
|
||||||
|
description: Artifact graph mind map array_elem_pop_fail.kcl
|
||||||
|
extension: md
|
||||||
|
snapshot_kind: binary
|
||||||
|
---
|
||||||
@ -0,0 +1,4 @@
|
|||||||
|
```mermaid
|
||||||
|
mindmap
|
||||||
|
root
|
||||||
|
```
|
||||||
@ -0,0 +1,6 @@
|
|||||||
|
---
|
||||||
|
source: kcl/src/simulation_tests.rs
|
||||||
|
description: Artifact graph flowchart array_elem_push_fail.kcl
|
||||||
|
extension: md
|
||||||
|
snapshot_kind: binary
|
||||||
|
---
|
||||||
@ -0,0 +1,3 @@
|
|||||||
|
```mermaid
|
||||||
|
flowchart LR
|
||||||
|
```
|
||||||
@ -0,0 +1,6 @@
|
|||||||
|
---
|
||||||
|
source: kcl/src/simulation_tests.rs
|
||||||
|
description: Artifact graph mind map array_elem_push_fail.kcl
|
||||||
|
extension: md
|
||||||
|
snapshot_kind: binary
|
||||||
|
---
|
||||||
@ -0,0 +1,4 @@
|
|||||||
|
```mermaid
|
||||||
|
mindmap
|
||||||
|
root
|
||||||
|
```
|
||||||
@ -0,0 +1,6 @@
|
|||||||
|
---
|
||||||
|
source: kcl/src/simulation_tests.rs
|
||||||
|
description: Artifact graph flowchart array_index_oob.kcl
|
||||||
|
extension: md
|
||||||
|
snapshot_kind: binary
|
||||||
|
---
|
||||||
@ -0,0 +1,3 @@
|
|||||||
|
```mermaid
|
||||||
|
flowchart LR
|
||||||
|
```
|
||||||
@ -0,0 +1,6 @@
|
|||||||
|
---
|
||||||
|
source: kcl/src/simulation_tests.rs
|
||||||
|
description: Artifact graph mind map array_index_oob.kcl
|
||||||
|
extension: md
|
||||||
|
snapshot_kind: binary
|
||||||
|
---
|
||||||
@ -0,0 +1,4 @@
|
|||||||
|
```mermaid
|
||||||
|
mindmap
|
||||||
|
root
|
||||||
|
```
|
||||||
@ -0,0 +1,6 @@
|
|||||||
|
---
|
||||||
|
source: kcl/src/simulation_tests.rs
|
||||||
|
description: Artifact graph flowchart comparisons_multiple.kcl
|
||||||
|
extension: md
|
||||||
|
snapshot_kind: binary
|
||||||
|
---
|
||||||
@ -0,0 +1,3 @@
|
|||||||
|
```mermaid
|
||||||
|
flowchart LR
|
||||||
|
```
|
||||||
@ -0,0 +1,6 @@
|
|||||||
|
---
|
||||||
|
source: kcl/src/simulation_tests.rs
|
||||||
|
description: Artifact graph mind map comparisons_multiple.kcl
|
||||||
|
extension: md
|
||||||
|
snapshot_kind: binary
|
||||||
|
---
|
||||||
@ -0,0 +1,4 @@
|
|||||||
|
```mermaid
|
||||||
|
mindmap
|
||||||
|
root
|
||||||
|
```
|
||||||
538
src/wasm-lib/kcl/tests/cube_with_error/artifact_commands.snap
Normal file
@ -0,0 +1,538 @@
|
|||||||
|
---
|
||||||
|
source: kcl/src/simulation_tests.rs
|
||||||
|
description: Artifact commands cube_with_error.kcl
|
||||||
|
---
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"cmdId": "[uuid]",
|
||||||
|
"range": [
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"command": {
|
||||||
|
"type": "make_plane",
|
||||||
|
"origin": {
|
||||||
|
"x": 0.0,
|
||||||
|
"y": 0.0,
|
||||||
|
"z": 0.0
|
||||||
|
},
|
||||||
|
"x_axis": {
|
||||||
|
"x": 1.0,
|
||||||
|
"y": 0.0,
|
||||||
|
"z": 0.0
|
||||||
|
},
|
||||||
|
"y_axis": {
|
||||||
|
"x": 0.0,
|
||||||
|
"y": 1.0,
|
||||||
|
"z": 0.0
|
||||||
|
},
|
||||||
|
"size": 100.0,
|
||||||
|
"clobber": false,
|
||||||
|
"hide": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cmdId": "[uuid]",
|
||||||
|
"range": [
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"command": {
|
||||||
|
"type": "plane_set_color",
|
||||||
|
"plane_id": "[uuid]",
|
||||||
|
"color": {
|
||||||
|
"r": 0.7,
|
||||||
|
"g": 0.28,
|
||||||
|
"b": 0.28,
|
||||||
|
"a": 0.4
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cmdId": "[uuid]",
|
||||||
|
"range": [
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"command": {
|
||||||
|
"type": "make_plane",
|
||||||
|
"origin": {
|
||||||
|
"x": 0.0,
|
||||||
|
"y": 0.0,
|
||||||
|
"z": 0.0
|
||||||
|
},
|
||||||
|
"x_axis": {
|
||||||
|
"x": 0.0,
|
||||||
|
"y": 1.0,
|
||||||
|
"z": 0.0
|
||||||
|
},
|
||||||
|
"y_axis": {
|
||||||
|
"x": 0.0,
|
||||||
|
"y": 0.0,
|
||||||
|
"z": 1.0
|
||||||
|
},
|
||||||
|
"size": 100.0,
|
||||||
|
"clobber": false,
|
||||||
|
"hide": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cmdId": "[uuid]",
|
||||||
|
"range": [
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"command": {
|
||||||
|
"type": "plane_set_color",
|
||||||
|
"plane_id": "[uuid]",
|
||||||
|
"color": {
|
||||||
|
"r": 0.28,
|
||||||
|
"g": 0.7,
|
||||||
|
"b": 0.28,
|
||||||
|
"a": 0.4
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cmdId": "[uuid]",
|
||||||
|
"range": [
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"command": {
|
||||||
|
"type": "make_plane",
|
||||||
|
"origin": {
|
||||||
|
"x": 0.0,
|
||||||
|
"y": 0.0,
|
||||||
|
"z": 0.0
|
||||||
|
},
|
||||||
|
"x_axis": {
|
||||||
|
"x": 1.0,
|
||||||
|
"y": 0.0,
|
||||||
|
"z": 0.0
|
||||||
|
},
|
||||||
|
"y_axis": {
|
||||||
|
"x": 0.0,
|
||||||
|
"y": 0.0,
|
||||||
|
"z": 1.0
|
||||||
|
},
|
||||||
|
"size": 100.0,
|
||||||
|
"clobber": false,
|
||||||
|
"hide": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cmdId": "[uuid]",
|
||||||
|
"range": [
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"command": {
|
||||||
|
"type": "plane_set_color",
|
||||||
|
"plane_id": "[uuid]",
|
||||||
|
"color": {
|
||||||
|
"r": 0.28,
|
||||||
|
"g": 0.28,
|
||||||
|
"b": 0.7,
|
||||||
|
"a": 0.4
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cmdId": "[uuid]",
|
||||||
|
"range": [
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"command": {
|
||||||
|
"type": "make_plane",
|
||||||
|
"origin": {
|
||||||
|
"x": 0.0,
|
||||||
|
"y": 0.0,
|
||||||
|
"z": 0.0
|
||||||
|
},
|
||||||
|
"x_axis": {
|
||||||
|
"x": -1.0,
|
||||||
|
"y": 0.0,
|
||||||
|
"z": 0.0
|
||||||
|
},
|
||||||
|
"y_axis": {
|
||||||
|
"x": 0.0,
|
||||||
|
"y": 1.0,
|
||||||
|
"z": 0.0
|
||||||
|
},
|
||||||
|
"size": 100.0,
|
||||||
|
"clobber": false,
|
||||||
|
"hide": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cmdId": "[uuid]",
|
||||||
|
"range": [
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"command": {
|
||||||
|
"type": "make_plane",
|
||||||
|
"origin": {
|
||||||
|
"x": 0.0,
|
||||||
|
"y": 0.0,
|
||||||
|
"z": 0.0
|
||||||
|
},
|
||||||
|
"x_axis": {
|
||||||
|
"x": 0.0,
|
||||||
|
"y": -1.0,
|
||||||
|
"z": 0.0
|
||||||
|
},
|
||||||
|
"y_axis": {
|
||||||
|
"x": 0.0,
|
||||||
|
"y": 0.0,
|
||||||
|
"z": 1.0
|
||||||
|
},
|
||||||
|
"size": 100.0,
|
||||||
|
"clobber": false,
|
||||||
|
"hide": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cmdId": "[uuid]",
|
||||||
|
"range": [
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"command": {
|
||||||
|
"type": "make_plane",
|
||||||
|
"origin": {
|
||||||
|
"x": 0.0,
|
||||||
|
"y": 0.0,
|
||||||
|
"z": 0.0
|
||||||
|
},
|
||||||
|
"x_axis": {
|
||||||
|
"x": -1.0,
|
||||||
|
"y": 0.0,
|
||||||
|
"z": 0.0
|
||||||
|
},
|
||||||
|
"y_axis": {
|
||||||
|
"x": 0.0,
|
||||||
|
"y": 0.0,
|
||||||
|
"z": 1.0
|
||||||
|
},
|
||||||
|
"size": 100.0,
|
||||||
|
"clobber": false,
|
||||||
|
"hide": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cmdId": "[uuid]",
|
||||||
|
"range": [
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"command": {
|
||||||
|
"type": "edge_lines_visible",
|
||||||
|
"hidden": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cmdId": "[uuid]",
|
||||||
|
"range": [
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"command": {
|
||||||
|
"type": "set_scene_units",
|
||||||
|
"unit": "mm"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cmdId": "[uuid]",
|
||||||
|
"range": [
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"command": {
|
||||||
|
"type": "object_visible",
|
||||||
|
"object_id": "[uuid]",
|
||||||
|
"hidden": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cmdId": "[uuid]",
|
||||||
|
"range": [
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"command": {
|
||||||
|
"type": "object_visible",
|
||||||
|
"object_id": "[uuid]",
|
||||||
|
"hidden": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cmdId": "[uuid]",
|
||||||
|
"range": [
|
||||||
|
177,
|
||||||
|
194,
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"command": {
|
||||||
|
"type": "make_plane",
|
||||||
|
"origin": {
|
||||||
|
"x": 0.0,
|
||||||
|
"y": 0.0,
|
||||||
|
"z": 0.0
|
||||||
|
},
|
||||||
|
"x_axis": {
|
||||||
|
"x": 1.0,
|
||||||
|
"y": 0.0,
|
||||||
|
"z": 0.0
|
||||||
|
},
|
||||||
|
"y_axis": {
|
||||||
|
"x": 0.0,
|
||||||
|
"y": 1.0,
|
||||||
|
"z": 0.0
|
||||||
|
},
|
||||||
|
"size": 60.0,
|
||||||
|
"clobber": false,
|
||||||
|
"hide": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cmdId": "[uuid]",
|
||||||
|
"range": [
|
||||||
|
177,
|
||||||
|
194,
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"command": {
|
||||||
|
"type": "enable_sketch_mode",
|
||||||
|
"entity_id": "[uuid]",
|
||||||
|
"ortho": false,
|
||||||
|
"animated": false,
|
||||||
|
"adjust_camera": false,
|
||||||
|
"planar_normal": {
|
||||||
|
"x": 0.0,
|
||||||
|
"y": 0.0,
|
||||||
|
"z": 1.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cmdId": "[uuid]",
|
||||||
|
"range": [
|
||||||
|
177,
|
||||||
|
194,
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"command": {
|
||||||
|
"type": "start_path"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cmdId": "[uuid]",
|
||||||
|
"range": [
|
||||||
|
177,
|
||||||
|
194,
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"command": {
|
||||||
|
"type": "move_path_pen",
|
||||||
|
"path": "[uuid]",
|
||||||
|
"to": {
|
||||||
|
"x": -20.0,
|
||||||
|
"y": -20.0,
|
||||||
|
"z": 0.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cmdId": "[uuid]",
|
||||||
|
"range": [
|
||||||
|
202,
|
||||||
|
215,
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"command": {
|
||||||
|
"type": "extend_path",
|
||||||
|
"path": "[uuid]",
|
||||||
|
"segment": {
|
||||||
|
"type": "line",
|
||||||
|
"end": {
|
||||||
|
"x": -20.0,
|
||||||
|
"y": 20.0,
|
||||||
|
"z": 0.0
|
||||||
|
},
|
||||||
|
"relative": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cmdId": "[uuid]",
|
||||||
|
"range": [
|
||||||
|
223,
|
||||||
|
236,
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"command": {
|
||||||
|
"type": "extend_path",
|
||||||
|
"path": "[uuid]",
|
||||||
|
"segment": {
|
||||||
|
"type": "line",
|
||||||
|
"end": {
|
||||||
|
"x": 20.0,
|
||||||
|
"y": 20.0,
|
||||||
|
"z": 0.0
|
||||||
|
},
|
||||||
|
"relative": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cmdId": "[uuid]",
|
||||||
|
"range": [
|
||||||
|
244,
|
||||||
|
257,
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"command": {
|
||||||
|
"type": "extend_path",
|
||||||
|
"path": "[uuid]",
|
||||||
|
"segment": {
|
||||||
|
"type": "line",
|
||||||
|
"end": {
|
||||||
|
"x": 20.0,
|
||||||
|
"y": -20.0,
|
||||||
|
"z": 0.0
|
||||||
|
},
|
||||||
|
"relative": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cmdId": "[uuid]",
|
||||||
|
"range": [
|
||||||
|
265,
|
||||||
|
278,
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"command": {
|
||||||
|
"type": "extend_path",
|
||||||
|
"path": "[uuid]",
|
||||||
|
"segment": {
|
||||||
|
"type": "line",
|
||||||
|
"end": {
|
||||||
|
"x": -20.0,
|
||||||
|
"y": -20.0,
|
||||||
|
"z": 0.0
|
||||||
|
},
|
||||||
|
"relative": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cmdId": "[uuid]",
|
||||||
|
"range": [
|
||||||
|
286,
|
||||||
|
294,
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"command": {
|
||||||
|
"type": "close_path",
|
||||||
|
"path_id": "[uuid]"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cmdId": "[uuid]",
|
||||||
|
"range": [
|
||||||
|
286,
|
||||||
|
294,
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"command": {
|
||||||
|
"type": "sketch_mode_disable"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cmdId": "[uuid]",
|
||||||
|
"range": [
|
||||||
|
302,
|
||||||
|
320,
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"command": {
|
||||||
|
"type": "enable_sketch_mode",
|
||||||
|
"entity_id": "[uuid]",
|
||||||
|
"ortho": false,
|
||||||
|
"animated": false,
|
||||||
|
"adjust_camera": false,
|
||||||
|
"planar_normal": {
|
||||||
|
"x": 0.0,
|
||||||
|
"y": 0.0,
|
||||||
|
"z": 1.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cmdId": "[uuid]",
|
||||||
|
"range": [
|
||||||
|
302,
|
||||||
|
320,
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"command": {
|
||||||
|
"type": "extrude",
|
||||||
|
"target": "[uuid]",
|
||||||
|
"distance": 40.0,
|
||||||
|
"faces": null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cmdId": "[uuid]",
|
||||||
|
"range": [
|
||||||
|
302,
|
||||||
|
320,
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"command": {
|
||||||
|
"type": "sketch_mode_disable"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cmdId": "[uuid]",
|
||||||
|
"range": [
|
||||||
|
302,
|
||||||
|
320,
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"command": {
|
||||||
|
"type": "object_bring_to_front",
|
||||||
|
"object_id": "[uuid]"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cmdId": "[uuid]",
|
||||||
|
"range": [
|
||||||
|
302,
|
||||||
|
320,
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"command": {
|
||||||
|
"type": "solid3d_get_extrusion_face_info",
|
||||||
|
"object_id": "[uuid]",
|
||||||
|
"edge_id": "[uuid]"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
@ -0,0 +1,6 @@
|
|||||||
|
---
|
||||||
|
source: kcl/src/simulation_tests.rs
|
||||||
|
description: Artifact graph flowchart cube_with_error.kcl
|
||||||
|
extension: md
|
||||||
|
snapshot_kind: binary
|
||||||
|
---
|
||||||
@ -0,0 +1,38 @@
|
|||||||
|
```mermaid
|
||||||
|
flowchart LR
|
||||||
|
subgraph path2 [Path]
|
||||||
|
2["Path<br>[177, 194, 0]"]
|
||||||
|
3["Segment<br>[202, 215, 0]"]
|
||||||
|
4["Segment<br>[223, 236, 0]"]
|
||||||
|
5["Segment<br>[244, 257, 0]"]
|
||||||
|
6["Segment<br>[265, 278, 0]"]
|
||||||
|
7["Segment<br>[286, 294, 0]"]
|
||||||
|
8[Solid2d]
|
||||||
|
end
|
||||||
|
1["Plane<br>[177, 194, 0]"]
|
||||||
|
9["Sweep Extrusion<br>[302, 320, 0]"]
|
||||||
|
10[Wall]
|
||||||
|
11[Wall]
|
||||||
|
12[Wall]
|
||||||
|
13[Wall]
|
||||||
|
14["Cap Start"]
|
||||||
|
15["Cap End"]
|
||||||
|
1 --- 2
|
||||||
|
2 --- 3
|
||||||
|
2 --- 4
|
||||||
|
2 --- 5
|
||||||
|
2 --- 6
|
||||||
|
2 --- 7
|
||||||
|
2 ---- 9
|
||||||
|
2 --- 8
|
||||||
|
3 --- 13
|
||||||
|
4 --- 12
|
||||||
|
5 --- 11
|
||||||
|
6 --- 10
|
||||||
|
9 --- 10
|
||||||
|
9 --- 11
|
||||||
|
9 --- 12
|
||||||
|
9 --- 13
|
||||||
|
9 --- 14
|
||||||
|
9 --- 15
|
||||||
|
```
|
||||||
@ -0,0 +1,6 @@
|
|||||||
|
---
|
||||||
|
source: kcl/src/simulation_tests.rs
|
||||||
|
description: Artifact graph mind map cube_with_error.kcl
|
||||||
|
extension: md
|
||||||
|
snapshot_kind: binary
|
||||||
|
---
|
||||||
@ -0,0 +1,23 @@
|
|||||||
|
```mermaid
|
||||||
|
mindmap
|
||||||
|
root
|
||||||
|
Plane
|
||||||
|
Path
|
||||||
|
Segment
|
||||||
|
Wall
|
||||||
|
Segment
|
||||||
|
Wall
|
||||||
|
Segment
|
||||||
|
Wall
|
||||||
|
Segment
|
||||||
|
Wall
|
||||||
|
Segment
|
||||||
|
Sweep Extrusion
|
||||||
|
Wall
|
||||||
|
Wall
|
||||||
|
Wall
|
||||||
|
Wall
|
||||||
|
Cap Start
|
||||||
|
Cap End
|
||||||
|
Solid2d
|
||||||
|
```
|
||||||
809
src/wasm-lib/kcl/tests/cube_with_error/ast.snap
Normal file
@ -0,0 +1,809 @@
|
|||||||
|
---
|
||||||
|
source: kcl/src/simulation_tests.rs
|
||||||
|
description: Result of parsing cube_with_error.kcl
|
||||||
|
---
|
||||||
|
{
|
||||||
|
"Ok": {
|
||||||
|
"body": [
|
||||||
|
{
|
||||||
|
"declaration": {
|
||||||
|
"end": 322,
|
||||||
|
"id": {
|
||||||
|
"end": 7,
|
||||||
|
"name": "cube",
|
||||||
|
"start": 3,
|
||||||
|
"type": "Identifier"
|
||||||
|
},
|
||||||
|
"init": {
|
||||||
|
"body": {
|
||||||
|
"body": [
|
||||||
|
{
|
||||||
|
"declaration": {
|
||||||
|
"end": 42,
|
||||||
|
"id": {
|
||||||
|
"end": 29,
|
||||||
|
"name": "l",
|
||||||
|
"start": 28,
|
||||||
|
"type": "Identifier"
|
||||||
|
},
|
||||||
|
"init": {
|
||||||
|
"end": 42,
|
||||||
|
"left": {
|
||||||
|
"end": 38,
|
||||||
|
"name": "length",
|
||||||
|
"start": 32,
|
||||||
|
"type": "Identifier",
|
||||||
|
"type": "Identifier"
|
||||||
|
},
|
||||||
|
"operator": "/",
|
||||||
|
"right": {
|
||||||
|
"end": 42,
|
||||||
|
"raw": "2",
|
||||||
|
"start": 41,
|
||||||
|
"type": "Literal",
|
||||||
|
"type": "Literal",
|
||||||
|
"value": {
|
||||||
|
"value": 2.0,
|
||||||
|
"suffix": "None"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"start": 32,
|
||||||
|
"type": "BinaryExpression",
|
||||||
|
"type": "BinaryExpression"
|
||||||
|
},
|
||||||
|
"start": 28,
|
||||||
|
"type": "VariableDeclarator"
|
||||||
|
},
|
||||||
|
"end": 42,
|
||||||
|
"kind": "const",
|
||||||
|
"start": 28,
|
||||||
|
"type": "VariableDeclaration",
|
||||||
|
"type": "VariableDeclaration"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"declaration": {
|
||||||
|
"end": 58,
|
||||||
|
"id": {
|
||||||
|
"end": 46,
|
||||||
|
"name": "x",
|
||||||
|
"start": 45,
|
||||||
|
"type": "Identifier"
|
||||||
|
},
|
||||||
|
"init": {
|
||||||
|
"computed": false,
|
||||||
|
"end": 58,
|
||||||
|
"object": {
|
||||||
|
"end": 55,
|
||||||
|
"name": "center",
|
||||||
|
"start": 49,
|
||||||
|
"type": "Identifier",
|
||||||
|
"type": "Identifier"
|
||||||
|
},
|
||||||
|
"property": {
|
||||||
|
"end": 57,
|
||||||
|
"raw": "0",
|
||||||
|
"start": 56,
|
||||||
|
"type": "Literal",
|
||||||
|
"type": "Literal",
|
||||||
|
"value": {
|
||||||
|
"value": 0.0,
|
||||||
|
"suffix": "None"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"start": 49,
|
||||||
|
"type": "MemberExpression",
|
||||||
|
"type": "MemberExpression"
|
||||||
|
},
|
||||||
|
"start": 45,
|
||||||
|
"type": "VariableDeclarator"
|
||||||
|
},
|
||||||
|
"end": 58,
|
||||||
|
"kind": "const",
|
||||||
|
"start": 45,
|
||||||
|
"type": "VariableDeclaration",
|
||||||
|
"type": "VariableDeclaration"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"declaration": {
|
||||||
|
"end": 74,
|
||||||
|
"id": {
|
||||||
|
"end": 62,
|
||||||
|
"name": "y",
|
||||||
|
"start": 61,
|
||||||
|
"type": "Identifier"
|
||||||
|
},
|
||||||
|
"init": {
|
||||||
|
"computed": false,
|
||||||
|
"end": 74,
|
||||||
|
"object": {
|
||||||
|
"end": 71,
|
||||||
|
"name": "center",
|
||||||
|
"start": 65,
|
||||||
|
"type": "Identifier",
|
||||||
|
"type": "Identifier"
|
||||||
|
},
|
||||||
|
"property": {
|
||||||
|
"end": 73,
|
||||||
|
"raw": "1",
|
||||||
|
"start": 72,
|
||||||
|
"type": "Literal",
|
||||||
|
"type": "Literal",
|
||||||
|
"value": {
|
||||||
|
"value": 1.0,
|
||||||
|
"suffix": "None"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"start": 65,
|
||||||
|
"type": "MemberExpression",
|
||||||
|
"type": "MemberExpression"
|
||||||
|
},
|
||||||
|
"start": 61,
|
||||||
|
"type": "VariableDeclarator"
|
||||||
|
},
|
||||||
|
"end": 74,
|
||||||
|
"kind": "const",
|
||||||
|
"start": 61,
|
||||||
|
"type": "VariableDeclaration",
|
||||||
|
"type": "VariableDeclaration"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"declaration": {
|
||||||
|
"end": 98,
|
||||||
|
"id": {
|
||||||
|
"end": 79,
|
||||||
|
"name": "p0",
|
||||||
|
"start": 77,
|
||||||
|
"type": "Identifier"
|
||||||
|
},
|
||||||
|
"init": {
|
||||||
|
"elements": [
|
||||||
|
{
|
||||||
|
"end": 89,
|
||||||
|
"left": {
|
||||||
|
"argument": {
|
||||||
|
"end": 85,
|
||||||
|
"name": "l",
|
||||||
|
"start": 84,
|
||||||
|
"type": "Identifier",
|
||||||
|
"type": "Identifier"
|
||||||
|
},
|
||||||
|
"end": 85,
|
||||||
|
"operator": "-",
|
||||||
|
"start": 83,
|
||||||
|
"type": "UnaryExpression",
|
||||||
|
"type": "UnaryExpression"
|
||||||
|
},
|
||||||
|
"operator": "+",
|
||||||
|
"right": {
|
||||||
|
"end": 89,
|
||||||
|
"name": "x",
|
||||||
|
"start": 88,
|
||||||
|
"type": "Identifier",
|
||||||
|
"type": "Identifier"
|
||||||
|
},
|
||||||
|
"start": 83,
|
||||||
|
"type": "BinaryExpression",
|
||||||
|
"type": "BinaryExpression"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"end": 97,
|
||||||
|
"left": {
|
||||||
|
"argument": {
|
||||||
|
"end": 93,
|
||||||
|
"name": "l",
|
||||||
|
"start": 92,
|
||||||
|
"type": "Identifier",
|
||||||
|
"type": "Identifier"
|
||||||
|
},
|
||||||
|
"end": 93,
|
||||||
|
"operator": "-",
|
||||||
|
"start": 91,
|
||||||
|
"type": "UnaryExpression",
|
||||||
|
"type": "UnaryExpression"
|
||||||
|
},
|
||||||
|
"operator": "+",
|
||||||
|
"right": {
|
||||||
|
"end": 97,
|
||||||
|
"name": "y",
|
||||||
|
"start": 96,
|
||||||
|
"type": "Identifier",
|
||||||
|
"type": "Identifier"
|
||||||
|
},
|
||||||
|
"start": 91,
|
||||||
|
"type": "BinaryExpression",
|
||||||
|
"type": "BinaryExpression"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"end": 98,
|
||||||
|
"start": 82,
|
||||||
|
"type": "ArrayExpression",
|
||||||
|
"type": "ArrayExpression"
|
||||||
|
},
|
||||||
|
"start": 77,
|
||||||
|
"type": "VariableDeclarator"
|
||||||
|
},
|
||||||
|
"end": 98,
|
||||||
|
"kind": "const",
|
||||||
|
"start": 77,
|
||||||
|
"type": "VariableDeclaration",
|
||||||
|
"type": "VariableDeclaration"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"declaration": {
|
||||||
|
"end": 121,
|
||||||
|
"id": {
|
||||||
|
"end": 103,
|
||||||
|
"name": "p1",
|
||||||
|
"start": 101,
|
||||||
|
"type": "Identifier"
|
||||||
|
},
|
||||||
|
"init": {
|
||||||
|
"elements": [
|
||||||
|
{
|
||||||
|
"end": 113,
|
||||||
|
"left": {
|
||||||
|
"argument": {
|
||||||
|
"end": 109,
|
||||||
|
"name": "l",
|
||||||
|
"start": 108,
|
||||||
|
"type": "Identifier",
|
||||||
|
"type": "Identifier"
|
||||||
|
},
|
||||||
|
"end": 109,
|
||||||
|
"operator": "-",
|
||||||
|
"start": 107,
|
||||||
|
"type": "UnaryExpression",
|
||||||
|
"type": "UnaryExpression"
|
||||||
|
},
|
||||||
|
"operator": "+",
|
||||||
|
"right": {
|
||||||
|
"end": 113,
|
||||||
|
"name": "x",
|
||||||
|
"start": 112,
|
||||||
|
"type": "Identifier",
|
||||||
|
"type": "Identifier"
|
||||||
|
},
|
||||||
|
"start": 107,
|
||||||
|
"type": "BinaryExpression",
|
||||||
|
"type": "BinaryExpression"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"end": 120,
|
||||||
|
"left": {
|
||||||
|
"end": 116,
|
||||||
|
"name": "l",
|
||||||
|
"start": 115,
|
||||||
|
"type": "Identifier",
|
||||||
|
"type": "Identifier"
|
||||||
|
},
|
||||||
|
"operator": "+",
|
||||||
|
"right": {
|
||||||
|
"end": 120,
|
||||||
|
"name": "y",
|
||||||
|
"start": 119,
|
||||||
|
"type": "Identifier",
|
||||||
|
"type": "Identifier"
|
||||||
|
},
|
||||||
|
"start": 115,
|
||||||
|
"type": "BinaryExpression",
|
||||||
|
"type": "BinaryExpression"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"end": 121,
|
||||||
|
"start": 106,
|
||||||
|
"type": "ArrayExpression",
|
||||||
|
"type": "ArrayExpression"
|
||||||
|
},
|
||||||
|
"start": 101,
|
||||||
|
"type": "VariableDeclarator"
|
||||||
|
},
|
||||||
|
"end": 121,
|
||||||
|
"kind": "const",
|
||||||
|
"start": 101,
|
||||||
|
"type": "VariableDeclaration",
|
||||||
|
"type": "VariableDeclaration"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"declaration": {
|
||||||
|
"end": 143,
|
||||||
|
"id": {
|
||||||
|
"end": 126,
|
||||||
|
"name": "p2",
|
||||||
|
"start": 124,
|
||||||
|
"type": "Identifier"
|
||||||
|
},
|
||||||
|
"init": {
|
||||||
|
"elements": [
|
||||||
|
{
|
||||||
|
"end": 135,
|
||||||
|
"left": {
|
||||||
|
"end": 131,
|
||||||
|
"name": "l",
|
||||||
|
"start": 130,
|
||||||
|
"type": "Identifier",
|
||||||
|
"type": "Identifier"
|
||||||
|
},
|
||||||
|
"operator": "+",
|
||||||
|
"right": {
|
||||||
|
"end": 135,
|
||||||
|
"name": "x",
|
||||||
|
"start": 134,
|
||||||
|
"type": "Identifier",
|
||||||
|
"type": "Identifier"
|
||||||
|
},
|
||||||
|
"start": 130,
|
||||||
|
"type": "BinaryExpression",
|
||||||
|
"type": "BinaryExpression"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"end": 142,
|
||||||
|
"left": {
|
||||||
|
"end": 138,
|
||||||
|
"name": "l",
|
||||||
|
"start": 137,
|
||||||
|
"type": "Identifier",
|
||||||
|
"type": "Identifier"
|
||||||
|
},
|
||||||
|
"operator": "+",
|
||||||
|
"right": {
|
||||||
|
"end": 142,
|
||||||
|
"name": "y",
|
||||||
|
"start": 141,
|
||||||
|
"type": "Identifier",
|
||||||
|
"type": "Identifier"
|
||||||
|
},
|
||||||
|
"start": 137,
|
||||||
|
"type": "BinaryExpression",
|
||||||
|
"type": "BinaryExpression"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"end": 143,
|
||||||
|
"start": 129,
|
||||||
|
"type": "ArrayExpression",
|
||||||
|
"type": "ArrayExpression"
|
||||||
|
},
|
||||||
|
"start": 124,
|
||||||
|
"type": "VariableDeclarator"
|
||||||
|
},
|
||||||
|
"end": 143,
|
||||||
|
"kind": "const",
|
||||||
|
"start": 124,
|
||||||
|
"type": "VariableDeclaration",
|
||||||
|
"type": "VariableDeclaration"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"declaration": {
|
||||||
|
"end": 166,
|
||||||
|
"id": {
|
||||||
|
"end": 148,
|
||||||
|
"name": "p3",
|
||||||
|
"start": 146,
|
||||||
|
"type": "Identifier"
|
||||||
|
},
|
||||||
|
"init": {
|
||||||
|
"elements": [
|
||||||
|
{
|
||||||
|
"end": 157,
|
||||||
|
"left": {
|
||||||
|
"end": 153,
|
||||||
|
"name": "l",
|
||||||
|
"start": 152,
|
||||||
|
"type": "Identifier",
|
||||||
|
"type": "Identifier"
|
||||||
|
},
|
||||||
|
"operator": "+",
|
||||||
|
"right": {
|
||||||
|
"end": 157,
|
||||||
|
"name": "x",
|
||||||
|
"start": 156,
|
||||||
|
"type": "Identifier",
|
||||||
|
"type": "Identifier"
|
||||||
|
},
|
||||||
|
"start": 152,
|
||||||
|
"type": "BinaryExpression",
|
||||||
|
"type": "BinaryExpression"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"end": 165,
|
||||||
|
"left": {
|
||||||
|
"argument": {
|
||||||
|
"end": 161,
|
||||||
|
"name": "l",
|
||||||
|
"start": 160,
|
||||||
|
"type": "Identifier",
|
||||||
|
"type": "Identifier"
|
||||||
|
},
|
||||||
|
"end": 161,
|
||||||
|
"operator": "-",
|
||||||
|
"start": 159,
|
||||||
|
"type": "UnaryExpression",
|
||||||
|
"type": "UnaryExpression"
|
||||||
|
},
|
||||||
|
"operator": "+",
|
||||||
|
"right": {
|
||||||
|
"end": 165,
|
||||||
|
"name": "y",
|
||||||
|
"start": 164,
|
||||||
|
"type": "Identifier",
|
||||||
|
"type": "Identifier"
|
||||||
|
},
|
||||||
|
"start": 159,
|
||||||
|
"type": "BinaryExpression",
|
||||||
|
"type": "BinaryExpression"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"end": 166,
|
||||||
|
"start": 151,
|
||||||
|
"type": "ArrayExpression",
|
||||||
|
"type": "ArrayExpression"
|
||||||
|
},
|
||||||
|
"start": 146,
|
||||||
|
"type": "VariableDeclarator"
|
||||||
|
},
|
||||||
|
"end": 166,
|
||||||
|
"kind": "const",
|
||||||
|
"start": 146,
|
||||||
|
"type": "VariableDeclaration",
|
||||||
|
"type": "VariableDeclaration"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"argument": {
|
||||||
|
"body": [
|
||||||
|
{
|
||||||
|
"arguments": [
|
||||||
|
{
|
||||||
|
"end": 193,
|
||||||
|
"name": "p0",
|
||||||
|
"start": 191,
|
||||||
|
"type": "Identifier",
|
||||||
|
"type": "Identifier"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"callee": {
|
||||||
|
"end": 190,
|
||||||
|
"name": "startSketchAt",
|
||||||
|
"start": 177,
|
||||||
|
"type": "Identifier"
|
||||||
|
},
|
||||||
|
"end": 194,
|
||||||
|
"start": 177,
|
||||||
|
"type": "CallExpression",
|
||||||
|
"type": "CallExpression"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"arguments": [
|
||||||
|
{
|
||||||
|
"end": 211,
|
||||||
|
"name": "p1",
|
||||||
|
"start": 209,
|
||||||
|
"type": "Identifier",
|
||||||
|
"type": "Identifier"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"end": 214,
|
||||||
|
"start": 213,
|
||||||
|
"type": "PipeSubstitution",
|
||||||
|
"type": "PipeSubstitution"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"callee": {
|
||||||
|
"end": 208,
|
||||||
|
"name": "lineTo",
|
||||||
|
"start": 202,
|
||||||
|
"type": "Identifier"
|
||||||
|
},
|
||||||
|
"end": 215,
|
||||||
|
"start": 202,
|
||||||
|
"type": "CallExpression",
|
||||||
|
"type": "CallExpression"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"arguments": [
|
||||||
|
{
|
||||||
|
"end": 232,
|
||||||
|
"name": "p2",
|
||||||
|
"start": 230,
|
||||||
|
"type": "Identifier",
|
||||||
|
"type": "Identifier"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"end": 235,
|
||||||
|
"start": 234,
|
||||||
|
"type": "PipeSubstitution",
|
||||||
|
"type": "PipeSubstitution"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"callee": {
|
||||||
|
"end": 229,
|
||||||
|
"name": "lineTo",
|
||||||
|
"start": 223,
|
||||||
|
"type": "Identifier"
|
||||||
|
},
|
||||||
|
"end": 236,
|
||||||
|
"start": 223,
|
||||||
|
"type": "CallExpression",
|
||||||
|
"type": "CallExpression"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"arguments": [
|
||||||
|
{
|
||||||
|
"end": 253,
|
||||||
|
"name": "p3",
|
||||||
|
"start": 251,
|
||||||
|
"type": "Identifier",
|
||||||
|
"type": "Identifier"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"end": 256,
|
||||||
|
"start": 255,
|
||||||
|
"type": "PipeSubstitution",
|
||||||
|
"type": "PipeSubstitution"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"callee": {
|
||||||
|
"end": 250,
|
||||||
|
"name": "lineTo",
|
||||||
|
"start": 244,
|
||||||
|
"type": "Identifier"
|
||||||
|
},
|
||||||
|
"end": 257,
|
||||||
|
"start": 244,
|
||||||
|
"type": "CallExpression",
|
||||||
|
"type": "CallExpression"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"arguments": [
|
||||||
|
{
|
||||||
|
"end": 274,
|
||||||
|
"name": "p0",
|
||||||
|
"start": 272,
|
||||||
|
"type": "Identifier",
|
||||||
|
"type": "Identifier"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"end": 277,
|
||||||
|
"start": 276,
|
||||||
|
"type": "PipeSubstitution",
|
||||||
|
"type": "PipeSubstitution"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"callee": {
|
||||||
|
"end": 271,
|
||||||
|
"name": "lineTo",
|
||||||
|
"start": 265,
|
||||||
|
"type": "Identifier"
|
||||||
|
},
|
||||||
|
"end": 278,
|
||||||
|
"start": 265,
|
||||||
|
"type": "CallExpression",
|
||||||
|
"type": "CallExpression"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"arguments": [
|
||||||
|
{
|
||||||
|
"end": 293,
|
||||||
|
"start": 292,
|
||||||
|
"type": "PipeSubstitution",
|
||||||
|
"type": "PipeSubstitution"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"callee": {
|
||||||
|
"end": 291,
|
||||||
|
"name": "close",
|
||||||
|
"start": 286,
|
||||||
|
"type": "Identifier"
|
||||||
|
},
|
||||||
|
"end": 294,
|
||||||
|
"start": 286,
|
||||||
|
"type": "CallExpression",
|
||||||
|
"type": "CallExpression"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"arguments": [
|
||||||
|
{
|
||||||
|
"end": 316,
|
||||||
|
"name": "length",
|
||||||
|
"start": 310,
|
||||||
|
"type": "Identifier",
|
||||||
|
"type": "Identifier"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"end": 319,
|
||||||
|
"start": 318,
|
||||||
|
"type": "PipeSubstitution",
|
||||||
|
"type": "PipeSubstitution"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"callee": {
|
||||||
|
"end": 309,
|
||||||
|
"name": "extrude",
|
||||||
|
"start": 302,
|
||||||
|
"type": "Identifier"
|
||||||
|
},
|
||||||
|
"end": 320,
|
||||||
|
"start": 302,
|
||||||
|
"type": "CallExpression",
|
||||||
|
"type": "CallExpression"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"end": 320,
|
||||||
|
"start": 177,
|
||||||
|
"type": "PipeExpression",
|
||||||
|
"type": "PipeExpression"
|
||||||
|
},
|
||||||
|
"end": 320,
|
||||||
|
"start": 170,
|
||||||
|
"type": "ReturnStatement",
|
||||||
|
"type": "ReturnStatement"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"end": 322,
|
||||||
|
"nonCodeMeta": {
|
||||||
|
"nonCodeNodes": {
|
||||||
|
"6": [
|
||||||
|
{
|
||||||
|
"end": 170,
|
||||||
|
"start": 166,
|
||||||
|
"type": "NonCodeNode",
|
||||||
|
"value": {
|
||||||
|
"type": "newLine"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"startNodes": []
|
||||||
|
},
|
||||||
|
"start": 24
|
||||||
|
},
|
||||||
|
"end": 322,
|
||||||
|
"params": [
|
||||||
|
{
|
||||||
|
"type": "Parameter",
|
||||||
|
"identifier": {
|
||||||
|
"end": 14,
|
||||||
|
"name": "length",
|
||||||
|
"start": 8,
|
||||||
|
"type": "Identifier"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "Parameter",
|
||||||
|
"identifier": {
|
||||||
|
"end": 22,
|
||||||
|
"name": "center",
|
||||||
|
"start": 16,
|
||||||
|
"type": "Identifier"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"start": 7,
|
||||||
|
"type": "FunctionExpression",
|
||||||
|
"type": "FunctionExpression"
|
||||||
|
},
|
||||||
|
"start": 3,
|
||||||
|
"type": "VariableDeclarator"
|
||||||
|
},
|
||||||
|
"end": 322,
|
||||||
|
"kind": "fn",
|
||||||
|
"start": 0,
|
||||||
|
"type": "VariableDeclaration",
|
||||||
|
"type": "VariableDeclaration"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"declaration": {
|
||||||
|
"end": 349,
|
||||||
|
"id": {
|
||||||
|
"end": 330,
|
||||||
|
"name": "myCube",
|
||||||
|
"start": 324,
|
||||||
|
"type": "Identifier"
|
||||||
|
},
|
||||||
|
"init": {
|
||||||
|
"arguments": [
|
||||||
|
{
|
||||||
|
"end": 340,
|
||||||
|
"raw": "40",
|
||||||
|
"start": 338,
|
||||||
|
"type": "Literal",
|
||||||
|
"type": "Literal",
|
||||||
|
"value": {
|
||||||
|
"value": 40.0,
|
||||||
|
"suffix": "None"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"elements": [
|
||||||
|
{
|
||||||
|
"end": 344,
|
||||||
|
"raw": "0",
|
||||||
|
"start": 343,
|
||||||
|
"type": "Literal",
|
||||||
|
"type": "Literal",
|
||||||
|
"value": {
|
||||||
|
"value": 0.0,
|
||||||
|
"suffix": "None"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"end": 347,
|
||||||
|
"raw": "0",
|
||||||
|
"start": 346,
|
||||||
|
"type": "Literal",
|
||||||
|
"type": "Literal",
|
||||||
|
"value": {
|
||||||
|
"value": 0.0,
|
||||||
|
"suffix": "None"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"end": 348,
|
||||||
|
"start": 342,
|
||||||
|
"type": "ArrayExpression",
|
||||||
|
"type": "ArrayExpression"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"callee": {
|
||||||
|
"end": 337,
|
||||||
|
"name": "cube",
|
||||||
|
"start": 333,
|
||||||
|
"type": "Identifier"
|
||||||
|
},
|
||||||
|
"end": 349,
|
||||||
|
"start": 333,
|
||||||
|
"type": "CallExpression",
|
||||||
|
"type": "CallExpression"
|
||||||
|
},
|
||||||
|
"start": 324,
|
||||||
|
"type": "VariableDeclarator"
|
||||||
|
},
|
||||||
|
"end": 349,
|
||||||
|
"kind": "const",
|
||||||
|
"start": 324,
|
||||||
|
"type": "VariableDeclaration",
|
||||||
|
"type": "VariableDeclaration"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"end": 398,
|
||||||
|
"expression": {
|
||||||
|
"end": 398,
|
||||||
|
"name": "foo",
|
||||||
|
"start": 395,
|
||||||
|
"type": "Identifier",
|
||||||
|
"type": "Identifier"
|
||||||
|
},
|
||||||
|
"start": 395,
|
||||||
|
"type": "ExpressionStatement",
|
||||||
|
"type": "ExpressionStatement"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"end": 399,
|
||||||
|
"nonCodeMeta": {
|
||||||
|
"nonCodeNodes": {
|
||||||
|
"0": [
|
||||||
|
{
|
||||||
|
"end": 324,
|
||||||
|
"start": 322,
|
||||||
|
"type": "NonCodeNode",
|
||||||
|
"value": {
|
||||||
|
"type": "newLine"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"1": [
|
||||||
|
{
|
||||||
|
"end": 394,
|
||||||
|
"start": 349,
|
||||||
|
"type": "NonCodeNode",
|
||||||
|
"value": {
|
||||||
|
"type": "newLineBlockComment",
|
||||||
|
"value": "Error, after creating meaningful output.",
|
||||||
|
"style": "line"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"startNodes": []
|
||||||
|
},
|
||||||
|
"start": 0
|
||||||
|
}
|
||||||
|
}
|
||||||
12
src/wasm-lib/kcl/tests/cube_with_error/execution_error.snap
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
---
|
||||||
|
source: kcl/src/simulation_tests.rs
|
||||||
|
description: Error from executing cube_with_error.kcl
|
||||||
|
---
|
||||||
|
KCL UndefinedValue error
|
||||||
|
|
||||||
|
× undefined value: memory item key `foo` is not defined
|
||||||
|
╭─[22:1]
|
||||||
|
21 │ // Error, after creating meaningful output.
|
||||||
|
22 │ foo
|
||||||
|
· ───
|
||||||
|
╰────
|
||||||
22
src/wasm-lib/kcl/tests/cube_with_error/input.kcl
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
fn cube(length, center) {
|
||||||
|
l = length / 2
|
||||||
|
x = center[0]
|
||||||
|
y = center[1]
|
||||||
|
p0 = [-l + x, -l + y]
|
||||||
|
p1 = [-l + x, l + y]
|
||||||
|
p2 = [l + x, l + y]
|
||||||
|
p3 = [l + x, -l + y]
|
||||||
|
|
||||||
|
return startSketchAt(p0)
|
||||||
|
|> lineTo(p1, %)
|
||||||
|
|> lineTo(p2, %)
|
||||||
|
|> lineTo(p3, %)
|
||||||
|
|> lineTo(p0, %)
|
||||||
|
|> close(%)
|
||||||
|
|> extrude(length, %)
|
||||||
|
}
|
||||||
|
|
||||||
|
myCube = cube(40, [0, 0])
|
||||||
|
|
||||||
|
// Error, after creating meaningful output.
|
||||||
|
foo
|
||||||
51
src/wasm-lib/kcl/tests/cube_with_error/ops.snap
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
---
|
||||||
|
source: kcl/src/simulation_tests.rs
|
||||||
|
description: Operations executed cube_with_error.kcl
|
||||||
|
---
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"type": "UserDefinedFunctionCall",
|
||||||
|
"name": "cube",
|
||||||
|
"functionSourceRange": [
|
||||||
|
7,
|
||||||
|
322,
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"unlabeledArg": null,
|
||||||
|
"labeledArgs": {},
|
||||||
|
"sourceRange": [
|
||||||
|
333,
|
||||||
|
349,
|
||||||
|
0
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"labeledArgs": {
|
||||||
|
"length": {
|
||||||
|
"sourceRange": [
|
||||||
|
310,
|
||||||
|
316,
|
||||||
|
0
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"sketch_set": {
|
||||||
|
"sourceRange": [
|
||||||
|
318,
|
||||||
|
319,
|
||||||
|
0
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"name": "extrude",
|
||||||
|
"sourceRange": [
|
||||||
|
302,
|
||||||
|
320,
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"type": "StdLibCall",
|
||||||
|
"unlabeledArg": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "UserDefinedFunctionReturn"
|
||||||
|
}
|
||||||
|
]
|
||||||
@ -0,0 +1,6 @@
|
|||||||
|
---
|
||||||
|
source: kcl/src/simulation_tests.rs
|
||||||
|
description: Artifact graph flowchart import_cycle1.kcl
|
||||||
|
extension: md
|
||||||
|
snapshot_kind: binary
|
||||||
|
---
|
||||||
@ -0,0 +1,3 @@
|
|||||||
|
```mermaid
|
||||||
|
flowchart LR
|
||||||
|
```
|
||||||
@ -0,0 +1,6 @@
|
|||||||
|
---
|
||||||
|
source: kcl/src/simulation_tests.rs
|
||||||
|
description: Artifact graph mind map import_cycle1.kcl
|
||||||
|
extension: md
|
||||||
|
snapshot_kind: binary
|
||||||
|
---
|
||||||
@ -0,0 +1,4 @@
|
|||||||
|
```mermaid
|
||||||
|
mindmap
|
||||||
|
root
|
||||||
|
```
|
||||||
@ -0,0 +1,6 @@
|
|||||||
|
---
|
||||||
|
source: kcl/src/simulation_tests.rs
|
||||||
|
description: Artifact graph flowchart invalid_index_fractional.kcl
|
||||||
|
extension: md
|
||||||
|
snapshot_kind: binary
|
||||||
|
---
|
||||||
@ -0,0 +1,3 @@
|
|||||||
|
```mermaid
|
||||||
|
flowchart LR
|
||||||
|
```
|
||||||
@ -0,0 +1,6 @@
|
|||||||
|
---
|
||||||
|
source: kcl/src/simulation_tests.rs
|
||||||
|
description: Artifact graph mind map invalid_index_fractional.kcl
|
||||||
|
extension: md
|
||||||
|
snapshot_kind: binary
|
||||||
|
---
|
||||||
@ -0,0 +1,4 @@
|
|||||||
|
```mermaid
|
||||||
|
mindmap
|
||||||
|
root
|
||||||
|
```
|
||||||
@ -0,0 +1,6 @@
|
|||||||
|
---
|
||||||
|
source: kcl/src/simulation_tests.rs
|
||||||
|
description: Artifact graph flowchart invalid_index_negative.kcl
|
||||||
|
extension: md
|
||||||
|
snapshot_kind: binary
|
||||||
|
---
|
||||||
@ -0,0 +1,3 @@
|
|||||||
|
```mermaid
|
||||||
|
flowchart LR
|
||||||
|
```
|
||||||
@ -0,0 +1,6 @@
|
|||||||
|
---
|
||||||
|
source: kcl/src/simulation_tests.rs
|
||||||
|
description: Artifact graph mind map invalid_index_negative.kcl
|
||||||
|
extension: md
|
||||||
|
snapshot_kind: binary
|
||||||
|
---
|
||||||
@ -0,0 +1,4 @@
|
|||||||
|
```mermaid
|
||||||
|
mindmap
|
||||||
|
root
|
||||||
|
```
|
||||||
@ -0,0 +1,6 @@
|
|||||||
|
---
|
||||||
|
source: kcl/src/simulation_tests.rs
|
||||||
|
description: Artifact graph flowchart invalid_index_str.kcl
|
||||||
|
extension: md
|
||||||
|
snapshot_kind: binary
|
||||||
|
---
|
||||||