Compare commits
1 Commits
api-deux-p
...
jtran/anim
Author | SHA1 | Date | |
---|---|---|---|
bfefa0f51a |
2
.github/workflows/build-apps.yml
vendored
2
.github/workflows/build-apps.yml
vendored
@ -362,7 +362,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Authenticate to Google Cloud
|
- name: Authenticate to Google Cloud
|
||||||
if: ${{ env.IS_STAGING == 'true' }}
|
if: ${{ env.IS_STAGING == 'true' }}
|
||||||
uses: 'google-github-actions/auth@v2.1.10'
|
uses: 'google-github-actions/auth@v2.1.8'
|
||||||
with:
|
with:
|
||||||
credentials_json: '${{ secrets.GOOGLE_CLOUD_DL_SA }}'
|
credentials_json: '${{ secrets.GOOGLE_CLOUD_DL_SA }}'
|
||||||
|
|
||||||
|
2
.github/workflows/kcl-language-server.yml
vendored
2
.github/workflows/kcl-language-server.yml
vendored
@ -328,7 +328,7 @@ jobs:
|
|||||||
mkdir -p releases/language-server/${{ env.TAG }}
|
mkdir -p releases/language-server/${{ env.TAG }}
|
||||||
cp -r build/* releases/language-server/${{ env.TAG }}
|
cp -r build/* releases/language-server/${{ env.TAG }}
|
||||||
- name: "Authenticate to Google Cloud"
|
- name: "Authenticate to Google Cloud"
|
||||||
uses: "google-github-actions/auth@v2.1.10"
|
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 Cloud SDK
|
- name: Set up Cloud SDK
|
||||||
|
2
.github/workflows/publish-apps-release.yml
vendored
2
.github/workflows/publish-apps-release.yml
vendored
@ -108,7 +108,7 @@ jobs:
|
|||||||
run: npm run files:set-notes
|
run: npm run files:set-notes
|
||||||
|
|
||||||
- name: Authenticate to Google Cloud
|
- name: Authenticate to Google Cloud
|
||||||
uses: 'google-github-actions/auth@v2.1.10'
|
uses: 'google-github-actions/auth@v2.1.8'
|
||||||
with:
|
with:
|
||||||
credentials_json: '${{ secrets.GOOGLE_CLOUD_DL_SA }}'
|
credentials_json: '${{ secrets.GOOGLE_CLOUD_DL_SA }}'
|
||||||
|
|
||||||
|
3
Makefile
3
Makefile
@ -62,10 +62,7 @@ else
|
|||||||
endif
|
endif
|
||||||
|
|
||||||
public/kcl-samples/manifest.json: $(KCL_SOURCES)
|
public/kcl-samples/manifest.json: $(KCL_SOURCES)
|
||||||
ifndef WINDOWS
|
|
||||||
cd rust/kcl-lib && EXPECTORATE=overwrite cargo test generate_manifest
|
cd rust/kcl-lib && EXPECTORATE=overwrite cargo test generate_manifest
|
||||||
@ touch $@
|
|
||||||
endif
|
|
||||||
|
|
||||||
.vite/build/main.js: $(REACT_SOURCES) $(TYPESCRIPT_SOURCES) $(VITE_SOURCES)
|
.vite/build/main.js: $(REACT_SOURCES) $(TYPESCRIPT_SOURCES) $(VITE_SOURCES)
|
||||||
npm run tronb:vite:dev
|
npm run tronb:vite:dev
|
||||||
|
@ -12,7 +12,7 @@ test.describe('Point and click for boolean workflows', () => {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'subtract',
|
name: 'subtract',
|
||||||
code: 'subtract(extrude001, tools = extrude006)',
|
code: 'subtract([extrude001], tools = [extrude006])',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'intersect',
|
name: 'intersect',
|
||||||
@ -81,8 +81,6 @@ test.describe('Point and click for boolean workflows', () => {
|
|||||||
if (operationName !== 'subtract') {
|
if (operationName !== 'subtract') {
|
||||||
// should down shift key to select multiple objects
|
// should down shift key to select multiple objects
|
||||||
await page.keyboard.down('Shift')
|
await page.keyboard.down('Shift')
|
||||||
} else {
|
|
||||||
await cmdBar.progressCmdBar()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Select second object
|
// Select second object
|
||||||
@ -105,8 +103,8 @@ test.describe('Point and click for boolean workflows', () => {
|
|||||||
await cmdBar.expectState({
|
await cmdBar.expectState({
|
||||||
stage: 'review',
|
stage: 'review',
|
||||||
headerArguments: {
|
headerArguments: {
|
||||||
Solids: '1 path',
|
Tool: '1 path',
|
||||||
Tools: '1 path',
|
Target: '1 path',
|
||||||
},
|
},
|
||||||
commandName,
|
commandName,
|
||||||
})
|
})
|
||||||
|
@ -288,7 +288,7 @@ a1 = startSketchOn(offsetPlane(XY, offset = 10))
|
|||||||
// error text on hover
|
// error text on hover
|
||||||
await page.hover('.cm-lint-marker-info')
|
await page.hover('.cm-lint-marker-info')
|
||||||
await expect(
|
await expect(
|
||||||
page.getByText('Identifiers should be lowerCamelCase').first()
|
page.getByText('Identifiers must be lowerCamelCase').first()
|
||||||
).toBeVisible()
|
).toBeVisible()
|
||||||
|
|
||||||
await page.locator('#code-pane button:first-child').click()
|
await page.locator('#code-pane button:first-child').click()
|
||||||
@ -314,7 +314,7 @@ sketch_001 = startSketchOn(XY)
|
|||||||
// error text on hover
|
// error text on hover
|
||||||
await page.hover('.cm-lint-marker-info')
|
await page.hover('.cm-lint-marker-info')
|
||||||
await expect(
|
await expect(
|
||||||
page.getByText('Identifiers should be lowerCamelCase').first()
|
page.getByText('Identifiers must be lowerCamelCase').first()
|
||||||
).toBeVisible()
|
).toBeVisible()
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -511,7 +511,7 @@ sketch_001 = startSketchOn(XY)
|
|||||||
// error text on hover
|
// error text on hover
|
||||||
await page.hover('.cm-lint-marker-info')
|
await page.hover('.cm-lint-marker-info')
|
||||||
await expect(
|
await expect(
|
||||||
page.getByText('Identifiers should be lowerCamelCase').first()
|
page.getByText('Identifiers must be lowerCamelCase').first()
|
||||||
).toBeVisible()
|
).toBeVisible()
|
||||||
|
|
||||||
// focus the editor
|
// focus the editor
|
||||||
@ -539,7 +539,7 @@ sketch_001 = startSketchOn(XY)
|
|||||||
// error text on hover
|
// error text on hover
|
||||||
await page.hover('.cm-lint-marker-info')
|
await page.hover('.cm-lint-marker-info')
|
||||||
await expect(
|
await expect(
|
||||||
page.getByText('Identifiers should be lowerCamelCase').first()
|
page.getByText('Identifiers must be lowerCamelCase').first()
|
||||||
).toBeVisible()
|
).toBeVisible()
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -681,7 +681,7 @@ a1 = startSketchOn(offsetPlane(XY, offset = 10))
|
|||||||
// error text on hover
|
// error text on hover
|
||||||
await page.hover('.cm-lint-marker-info')
|
await page.hover('.cm-lint-marker-info')
|
||||||
await expect(
|
await expect(
|
||||||
page.getByText('Identifiers should be lowerCamelCase').first()
|
page.getByText('Identifiers must be lowerCamelCase').first()
|
||||||
).toBeVisible()
|
).toBeVisible()
|
||||||
|
|
||||||
// select the line that's causing the error and delete it
|
// select the line that's causing the error and delete it
|
||||||
|
@ -7,7 +7,6 @@ import type { SceneFixture } from '@e2e/playwright/fixtures/sceneFixture'
|
|||||||
import type { ToolbarFixture } from '@e2e/playwright/fixtures/toolbarFixture'
|
import type { ToolbarFixture } from '@e2e/playwright/fixtures/toolbarFixture'
|
||||||
import { expect, test } from '@e2e/playwright/zoo-test'
|
import { expect, test } from '@e2e/playwright/zoo-test'
|
||||||
import { bracket } from '@e2e/playwright/fixtures/bracket'
|
import { bracket } from '@e2e/playwright/fixtures/bracket'
|
||||||
import type { CmdBarSerialised } from '@e2e/playwright/fixtures/cmdBarFixture'
|
|
||||||
|
|
||||||
// test file is for testing point an click code gen functionality that's not sketch mode related
|
// test file is for testing point an click code gen functionality that's not sketch mode related
|
||||||
|
|
||||||
@ -1142,20 +1141,6 @@ openSketch = startSketchOn(XY)
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
const initialCmdBarStateHelix: CmdBarSerialised = {
|
|
||||||
stage: 'arguments',
|
|
||||||
currentArgKey: 'mode',
|
|
||||||
currentArgValue: '',
|
|
||||||
headerArguments: {
|
|
||||||
Mode: '',
|
|
||||||
AngleStart: '',
|
|
||||||
Revolutions: '',
|
|
||||||
Radius: '',
|
|
||||||
},
|
|
||||||
highlightedHeaderArg: 'mode',
|
|
||||||
commandName: 'Helix',
|
|
||||||
}
|
|
||||||
|
|
||||||
test('Helix point-and-click on default axis', async ({
|
test('Helix point-and-click on default axis', async ({
|
||||||
context,
|
context,
|
||||||
page,
|
page,
|
||||||
@ -1165,14 +1150,30 @@ openSketch = startSketchOn(XY)
|
|||||||
toolbar,
|
toolbar,
|
||||||
cmdBar,
|
cmdBar,
|
||||||
}) => {
|
}) => {
|
||||||
const expectedOutput = `helix001 = helix( axis = X, radius = 5, length = 5, revolutions = 1, angleStart = 270,)`
|
// One dumb hardcoded screen pixel value
|
||||||
|
const testPoint = { x: 620, y: 257 }
|
||||||
|
const expectedOutput = `helix001 = helix( axis = X, radius = 5, length = 5, revolutions = 1, angleStart = 270, ccw = false,)`
|
||||||
const expectedLine = `axis=X,`
|
const expectedLine = `axis=X,`
|
||||||
|
|
||||||
await homePage.goToModelingScene()
|
await homePage.goToModelingScene()
|
||||||
await scene.connectionEstablished()
|
await scene.connectionEstablished()
|
||||||
|
|
||||||
await test.step(`Go through the command bar flow`, async () => {
|
await test.step(`Go through the command bar flow`, async () => {
|
||||||
await toolbar.helixButton.click()
|
await toolbar.helixButton.click()
|
||||||
await cmdBar.expectState(initialCmdBarStateHelix)
|
await cmdBar.expectState({
|
||||||
|
stage: 'arguments',
|
||||||
|
currentArgKey: 'mode',
|
||||||
|
currentArgValue: '',
|
||||||
|
headerArguments: {
|
||||||
|
Mode: '',
|
||||||
|
AngleStart: '',
|
||||||
|
Revolutions: '',
|
||||||
|
Radius: '',
|
||||||
|
CounterClockWise: '',
|
||||||
|
},
|
||||||
|
highlightedHeaderArg: 'mode',
|
||||||
|
commandName: 'Helix',
|
||||||
|
})
|
||||||
await cmdBar.progressCmdBar()
|
await cmdBar.progressCmdBar()
|
||||||
await expect.poll(() => page.getByText('Axis').count()).toBe(6)
|
await expect.poll(() => page.getByText('Axis').count()).toBe(6)
|
||||||
await cmdBar.progressCmdBar()
|
await cmdBar.progressCmdBar()
|
||||||
@ -1189,6 +1190,7 @@ openSketch = startSketchOn(XY)
|
|||||||
AngleStart: '',
|
AngleStart: '',
|
||||||
Length: '',
|
Length: '',
|
||||||
Radius: '',
|
Radius: '',
|
||||||
|
CounterClockWise: '',
|
||||||
},
|
},
|
||||||
commandName: 'Helix',
|
commandName: 'Helix',
|
||||||
})
|
})
|
||||||
@ -1205,10 +1207,11 @@ openSketch = startSketchOn(XY)
|
|||||||
Revolutions: '1',
|
Revolutions: '1',
|
||||||
Length: '5',
|
Length: '5',
|
||||||
Radius: '5',
|
Radius: '5',
|
||||||
|
CounterClockWise: '',
|
||||||
},
|
},
|
||||||
commandName: 'Helix',
|
commandName: 'Helix',
|
||||||
})
|
})
|
||||||
await cmdBar.submit()
|
await cmdBar.progressCmdBar()
|
||||||
})
|
})
|
||||||
|
|
||||||
await test.step(`Confirm code is added to the editor, scene has changed`, async () => {
|
await test.step(`Confirm code is added to the editor, scene has changed`, async () => {
|
||||||
@ -1218,6 +1221,8 @@ openSketch = startSketchOn(XY)
|
|||||||
activeLines: [expectedLine],
|
activeLines: [expectedLine],
|
||||||
highlightedCode: '',
|
highlightedCode: '',
|
||||||
})
|
})
|
||||||
|
// Red plane is now gone, white helix is there
|
||||||
|
await scene.expectPixelColor([250, 250, 250], testPoint, 15)
|
||||||
})
|
})
|
||||||
|
|
||||||
await test.step(`Edit helix through the feature tree`, async () => {
|
await test.step(`Edit helix through the feature tree`, async () => {
|
||||||
@ -1229,18 +1234,21 @@ openSketch = startSketchOn(XY)
|
|||||||
await cmdBar.expectState({
|
await cmdBar.expectState({
|
||||||
commandName: 'Helix',
|
commandName: 'Helix',
|
||||||
stage: 'arguments',
|
stage: 'arguments',
|
||||||
currentArgKey: 'length',
|
currentArgKey: 'CounterClockWise',
|
||||||
currentArgValue: '5',
|
currentArgValue: '',
|
||||||
headerArguments: {
|
headerArguments: {
|
||||||
Axis: 'X',
|
Axis: 'X',
|
||||||
AngleStart: '270',
|
AngleStart: '270',
|
||||||
Revolutions: '1',
|
Revolutions: '1',
|
||||||
Radius: '5',
|
Radius: '5',
|
||||||
Length: initialInput,
|
Length: initialInput,
|
||||||
|
CounterClockWise: '',
|
||||||
},
|
},
|
||||||
highlightedHeaderArg: 'length',
|
highlightedHeaderArg: 'CounterClockWise',
|
||||||
})
|
})
|
||||||
await page.keyboard.insertText(newInput)
|
await page.keyboard.press('Shift+Backspace')
|
||||||
|
await expect(cmdBar.currentArgumentInput).toBeVisible()
|
||||||
|
await cmdBar.currentArgumentInput.locator('.cm-content').fill(newInput)
|
||||||
await cmdBar.progressCmdBar()
|
await cmdBar.progressCmdBar()
|
||||||
await cmdBar.expectState({
|
await cmdBar.expectState({
|
||||||
stage: 'review',
|
stage: 'review',
|
||||||
@ -1250,10 +1258,11 @@ openSketch = startSketchOn(XY)
|
|||||||
Revolutions: '1',
|
Revolutions: '1',
|
||||||
Radius: '5',
|
Radius: '5',
|
||||||
Length: newInput,
|
Length: newInput,
|
||||||
|
CounterClockWise: '',
|
||||||
},
|
},
|
||||||
commandName: 'Helix',
|
commandName: 'Helix',
|
||||||
})
|
})
|
||||||
await cmdBar.submit()
|
await cmdBar.progressCmdBar()
|
||||||
await toolbar.closeFeatureTreePane()
|
await toolbar.closeFeatureTreePane()
|
||||||
await editor.openPane()
|
await editor.openPane()
|
||||||
await editor.expectEditor.toContain('length = ' + newInput)
|
await editor.expectEditor.toContain('length = ' + newInput)
|
||||||
@ -1264,238 +1273,174 @@ openSketch = startSketchOn(XY)
|
|||||||
const operationButton = await toolbar.getFeatureTreeOperation('Helix', 0)
|
const operationButton = await toolbar.getFeatureTreeOperation('Helix', 0)
|
||||||
await operationButton.click({ button: 'left' })
|
await operationButton.click({ button: 'left' })
|
||||||
await page.keyboard.press('Delete')
|
await page.keyboard.press('Delete')
|
||||||
await scene.settled(cmdBar)
|
// Red plane is back
|
||||||
await editor.expectEditor.not.toContain('helix')
|
await scene.expectPixelColor([96, 52, 52], testPoint, 15)
|
||||||
await expect(
|
|
||||||
await toolbar.getFeatureTreeOperation('Helix', 0)
|
|
||||||
).not.toBeVisible()
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
test(`Helix point-and-click around segment`, async ({
|
const helixCases = [
|
||||||
context,
|
{
|
||||||
page,
|
selectionType: 'segment',
|
||||||
homePage,
|
testPoint: { x: 513, y: 221 },
|
||||||
scene,
|
expectedOutput: `helix001 = helix( axis = seg01, radius = 1, revolutions = 20, angleStart = 0, ccw = false,)`,
|
||||||
editor,
|
expectedEditedOutput: `helix001 = helix( axis = seg01, radius = 5, revolutions = 20, angleStart = 0, ccw = false,)`,
|
||||||
toolbar,
|
},
|
||||||
cmdBar,
|
{
|
||||||
}) => {
|
selectionType: 'sweepEdge',
|
||||||
const initialCode = `sketch001 = startSketchOn(XZ)
|
testPoint: { x: 564, y: 364 },
|
||||||
profile001 = startProfile(sketch001, at = [0, 0])
|
expectedOutput: `helix001 = helix( axis = getOppositeEdge(seg01), radius = 1, revolutions = 20, angleStart = 0, ccw = false,)`,
|
||||||
|> yLine(length = 100)
|
expectedEditedOutput: `helix001 = helix( axis = getOppositeEdge(seg01), radius = 5, revolutions = 20, angleStart = 0, ccw = false,)`,
|
||||||
|> line(endAbsolute = [100, 0])
|
},
|
||||||
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|
]
|
||||||
|> close()`
|
helixCases.map(
|
||||||
await context.addInitScript((initialCode) => {
|
({ selectionType, testPoint, expectedOutput, expectedEditedOutput }) => {
|
||||||
localStorage.setItem('persistCode', initialCode)
|
test(`Helix point-and-click around ${selectionType}`, async ({
|
||||||
}, initialCode)
|
context,
|
||||||
await page.setBodyDimensions({ width: 1000, height: 500 })
|
page,
|
||||||
await homePage.goToModelingScene()
|
homePage,
|
||||||
await scene.settled(cmdBar)
|
scene,
|
||||||
|
editor,
|
||||||
|
toolbar,
|
||||||
|
cmdBar,
|
||||||
|
}) => {
|
||||||
|
page.on('console', console.log)
|
||||||
|
const initialCode = `sketch001 = startSketchOn(XZ)
|
||||||
|
profile001 = startProfile(sketch001, at = [0, 0])
|
||||||
|
|> yLine(length = 100)
|
||||||
|
|> line(endAbsolute = [100, 0])
|
||||||
|
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|
||||||
|
|> close()
|
||||||
|
extrude001 = extrude(profile001, length = 100)`
|
||||||
|
|
||||||
await test.step(`Go through the command bar flow`, async () => {
|
// One dumb hardcoded screen pixel value
|
||||||
await toolbar.closePane('code')
|
const [clickOnEdge] = scene.makeMouseHelpers(testPoint.x, testPoint.y)
|
||||||
await toolbar.helixButton.click()
|
|
||||||
await cmdBar.expectState(initialCmdBarStateHelix)
|
await context.addInitScript((initialCode) => {
|
||||||
await cmdBar.selectOption({ name: 'Edge' }).click()
|
localStorage.setItem('persistCode', initialCode)
|
||||||
await editor.selectText('yLine(length = 100)')
|
}, initialCode)
|
||||||
await cmdBar.progressCmdBar()
|
await page.setBodyDimensions({ width: 1000, height: 500 })
|
||||||
await page.keyboard.insertText('1')
|
await homePage.goToModelingScene()
|
||||||
await cmdBar.progressCmdBar()
|
await scene.settled(cmdBar)
|
||||||
await page.keyboard.insertText('2')
|
|
||||||
await cmdBar.progressCmdBar()
|
await test.step(`Go through the command bar flow`, async () => {
|
||||||
await page.keyboard.insertText('3')
|
await toolbar.closePane('code')
|
||||||
await cmdBar.progressCmdBar()
|
await toolbar.helixButton.click()
|
||||||
await cmdBar.expectState({
|
await cmdBar.expectState({
|
||||||
stage: 'review',
|
stage: 'arguments',
|
||||||
headerArguments: {
|
currentArgKey: 'mode',
|
||||||
Mode: 'Edge',
|
currentArgValue: '',
|
||||||
Edge: `1 segment`,
|
headerArguments: {
|
||||||
AngleStart: '2',
|
AngleStart: '',
|
||||||
Revolutions: '1',
|
Mode: '',
|
||||||
Radius: '3',
|
CounterClockWise: '',
|
||||||
},
|
Radius: '',
|
||||||
commandName: 'Helix',
|
Revolutions: '',
|
||||||
|
},
|
||||||
|
highlightedHeaderArg: 'mode',
|
||||||
|
commandName: 'Helix',
|
||||||
|
})
|
||||||
|
await cmdBar.selectOption({ name: 'Edge' }).click()
|
||||||
|
await expect
|
||||||
|
.poll(() => page.getByText('Please select one').count())
|
||||||
|
.toBe(1)
|
||||||
|
await clickOnEdge()
|
||||||
|
await page.waitForTimeout(1000)
|
||||||
|
await cmdBar.progressCmdBar()
|
||||||
|
await page.waitForTimeout(1000)
|
||||||
|
await cmdBar.argumentInput.focus()
|
||||||
|
await page.waitForTimeout(1000)
|
||||||
|
await page.keyboard.insertText('20')
|
||||||
|
await cmdBar.progressCmdBar()
|
||||||
|
await page.keyboard.insertText('0')
|
||||||
|
await cmdBar.progressCmdBar()
|
||||||
|
await page.keyboard.insertText('1')
|
||||||
|
await cmdBar.progressCmdBar()
|
||||||
|
await page.keyboard.insertText('100')
|
||||||
|
await cmdBar.expectState({
|
||||||
|
stage: 'review',
|
||||||
|
headerArguments: {
|
||||||
|
Mode: 'Edge',
|
||||||
|
Edge: `1 ${selectionType}`,
|
||||||
|
AngleStart: '0',
|
||||||
|
Revolutions: '20',
|
||||||
|
Radius: '1',
|
||||||
|
CounterClockWise: '',
|
||||||
|
},
|
||||||
|
commandName: 'Helix',
|
||||||
|
})
|
||||||
|
await cmdBar.progressCmdBar()
|
||||||
|
await page.waitForTimeout(1000)
|
||||||
|
})
|
||||||
|
|
||||||
|
await test.step(`Confirm code is added to the editor, scene has changed`, async () => {
|
||||||
|
await toolbar.openPane('code')
|
||||||
|
await editor.expectEditor.toContain(expectedOutput)
|
||||||
|
await toolbar.closePane('code')
|
||||||
|
})
|
||||||
|
|
||||||
|
await test.step(`Edit helix through the feature tree`, async () => {
|
||||||
|
await toolbar.openPane('feature-tree')
|
||||||
|
const operationButton = await toolbar.getFeatureTreeOperation(
|
||||||
|
'Helix',
|
||||||
|
0
|
||||||
|
)
|
||||||
|
await operationButton.dblclick()
|
||||||
|
const initialInput = '1'
|
||||||
|
const newInput = '5'
|
||||||
|
await cmdBar.expectState({
|
||||||
|
commandName: 'Helix',
|
||||||
|
stage: 'arguments',
|
||||||
|
currentArgKey: 'CounterClockWise',
|
||||||
|
currentArgValue: '',
|
||||||
|
headerArguments: {
|
||||||
|
AngleStart: '0',
|
||||||
|
Revolutions: '20',
|
||||||
|
Radius: initialInput,
|
||||||
|
CounterClockWise: '',
|
||||||
|
},
|
||||||
|
highlightedHeaderArg: 'CounterClockWise',
|
||||||
|
})
|
||||||
|
await page
|
||||||
|
.getByRole('button', { name: 'radius', exact: false })
|
||||||
|
.click()
|
||||||
|
await expect(cmdBar.currentArgumentInput).toBeVisible()
|
||||||
|
await cmdBar.currentArgumentInput
|
||||||
|
.locator('.cm-content')
|
||||||
|
.fill(newInput)
|
||||||
|
await cmdBar.progressCmdBar()
|
||||||
|
await cmdBar.expectState({
|
||||||
|
stage: 'review',
|
||||||
|
headerArguments: {
|
||||||
|
AngleStart: '0',
|
||||||
|
Revolutions: '20',
|
||||||
|
Radius: newInput,
|
||||||
|
CounterClockWise: '',
|
||||||
|
},
|
||||||
|
commandName: 'Helix',
|
||||||
|
})
|
||||||
|
await cmdBar.progressCmdBar()
|
||||||
|
await toolbar.closePane('feature-tree')
|
||||||
|
await toolbar.openPane('code')
|
||||||
|
await editor.expectEditor.toContain(expectedEditedOutput)
|
||||||
|
await toolbar.closePane('code')
|
||||||
|
})
|
||||||
|
|
||||||
|
await test.step('Delete helix via feature tree selection', async () => {
|
||||||
|
await toolbar.openPane('feature-tree')
|
||||||
|
const operationButton = await toolbar.getFeatureTreeOperation(
|
||||||
|
'Helix',
|
||||||
|
0
|
||||||
|
)
|
||||||
|
await operationButton.click({ button: 'left' })
|
||||||
|
await page.keyboard.press('Delete')
|
||||||
|
await editor.expectEditor.not.toContain(expectedEditedOutput)
|
||||||
|
await expect(
|
||||||
|
await toolbar.getFeatureTreeOperation('Helix', 0)
|
||||||
|
).not.toBeVisible()
|
||||||
|
})
|
||||||
})
|
})
|
||||||
await cmdBar.submit()
|
}
|
||||||
await scene.settled(cmdBar)
|
)
|
||||||
})
|
|
||||||
|
|
||||||
await test.step(`Confirm code is added to the editor, scene has changed`, async () => {
|
|
||||||
await toolbar.openPane('code')
|
|
||||||
await editor.expectEditor.toContain(
|
|
||||||
`
|
|
||||||
helix001 = helix(
|
|
||||||
axis = seg01,
|
|
||||||
radius = 3,
|
|
||||||
revolutions = 1,
|
|
||||||
angleStart = 2,
|
|
||||||
)`,
|
|
||||||
{ shouldNormalise: true }
|
|
||||||
)
|
|
||||||
await toolbar.closePane('code')
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
test(`Helix point-and-click around sweepEdge with edit and delete flows`, async ({
|
|
||||||
context,
|
|
||||||
page,
|
|
||||||
homePage,
|
|
||||||
scene,
|
|
||||||
editor,
|
|
||||||
toolbar,
|
|
||||||
cmdBar,
|
|
||||||
}) => {
|
|
||||||
const initialCode = `sketch001 = startSketchOn(XZ)
|
|
||||||
profile001 = startProfile(sketch001, at = [0, 0])
|
|
||||||
|> yLine(length = 100)
|
|
||||||
|> line(endAbsolute = [100, 0])
|
|
||||||
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|
|
||||||
|> close()
|
|
||||||
extrude001 = extrude(profile001, length = 100)`
|
|
||||||
|
|
||||||
// One dumb hardcoded screen pixel value to click on the sweepEdge, can't think of another way?
|
|
||||||
const testPoint = { x: 564, y: 364 }
|
|
||||||
const [clickOnEdge] = scene.makeMouseHelpers(testPoint.x, testPoint.y)
|
|
||||||
|
|
||||||
await context.addInitScript((initialCode) => {
|
|
||||||
localStorage.setItem('persistCode', initialCode)
|
|
||||||
}, initialCode)
|
|
||||||
await page.setBodyDimensions({ width: 1000, height: 500 })
|
|
||||||
await homePage.goToModelingScene()
|
|
||||||
await scene.settled(cmdBar)
|
|
||||||
|
|
||||||
await test.step(`Go through the command bar flow`, async () => {
|
|
||||||
await toolbar.closePane('code')
|
|
||||||
await toolbar.helixButton.click()
|
|
||||||
await cmdBar.expectState(initialCmdBarStateHelix)
|
|
||||||
await cmdBar.selectOption({ name: 'Edge' }).click()
|
|
||||||
await expect
|
|
||||||
.poll(() => page.getByText('Please select one').count())
|
|
||||||
.toBe(1)
|
|
||||||
await clickOnEdge()
|
|
||||||
await cmdBar.progressCmdBar()
|
|
||||||
await cmdBar.argumentInput.focus()
|
|
||||||
await page.keyboard.insertText('20')
|
|
||||||
await cmdBar.progressCmdBar()
|
|
||||||
await page.keyboard.insertText('0')
|
|
||||||
await cmdBar.progressCmdBar()
|
|
||||||
await page.keyboard.insertText('1')
|
|
||||||
await cmdBar.progressCmdBar()
|
|
||||||
await page.keyboard.insertText('100')
|
|
||||||
await cmdBar.expectState({
|
|
||||||
stage: 'review',
|
|
||||||
headerArguments: {
|
|
||||||
Mode: 'Edge',
|
|
||||||
Edge: `1 sweepEdge`,
|
|
||||||
AngleStart: '0',
|
|
||||||
Revolutions: '20',
|
|
||||||
Radius: '1',
|
|
||||||
},
|
|
||||||
commandName: 'Helix',
|
|
||||||
})
|
|
||||||
await cmdBar.submit()
|
|
||||||
await scene.settled(cmdBar)
|
|
||||||
})
|
|
||||||
|
|
||||||
await test.step(`Confirm code is added to the editor, scene has changed`, async () => {
|
|
||||||
await toolbar.openPane('code')
|
|
||||||
await editor.expectEditor.toContain(
|
|
||||||
`
|
|
||||||
helix001 = helix(
|
|
||||||
axis = getOppositeEdge(seg01),
|
|
||||||
radius = 1,
|
|
||||||
revolutions = 20,
|
|
||||||
angleStart = 0,
|
|
||||||
)`,
|
|
||||||
{ shouldNormalise: true }
|
|
||||||
)
|
|
||||||
await toolbar.closePane('code')
|
|
||||||
})
|
|
||||||
|
|
||||||
await test.step(`Edit helix through the feature tree`, async () => {
|
|
||||||
await toolbar.openPane('feature-tree')
|
|
||||||
const operationButton = await toolbar.getFeatureTreeOperation('Helix', 0)
|
|
||||||
await operationButton.dblclick()
|
|
||||||
const initialInput = '1'
|
|
||||||
const newInput = '5'
|
|
||||||
await cmdBar.expectState({
|
|
||||||
commandName: 'Helix',
|
|
||||||
stage: 'arguments',
|
|
||||||
currentArgKey: 'radius',
|
|
||||||
currentArgValue: initialInput,
|
|
||||||
headerArguments: {
|
|
||||||
AngleStart: '0',
|
|
||||||
Revolutions: '20',
|
|
||||||
Radius: initialInput,
|
|
||||||
},
|
|
||||||
highlightedHeaderArg: 'radius',
|
|
||||||
})
|
|
||||||
await page.keyboard.insertText(newInput)
|
|
||||||
await cmdBar.progressCmdBar()
|
|
||||||
await cmdBar.expectState({
|
|
||||||
stage: 'review',
|
|
||||||
headerArguments: {
|
|
||||||
AngleStart: '0',
|
|
||||||
Revolutions: '20',
|
|
||||||
Radius: newInput,
|
|
||||||
},
|
|
||||||
commandName: 'Helix',
|
|
||||||
})
|
|
||||||
await cmdBar.clickOptionalArgument('ccw')
|
|
||||||
await cmdBar.expectState({
|
|
||||||
commandName: 'Helix',
|
|
||||||
stage: 'arguments',
|
|
||||||
currentArgKey: 'CounterClockWise',
|
|
||||||
currentArgValue: '',
|
|
||||||
headerArguments: {
|
|
||||||
AngleStart: '0',
|
|
||||||
Revolutions: '20',
|
|
||||||
Radius: newInput,
|
|
||||||
CounterClockWise: '',
|
|
||||||
},
|
|
||||||
highlightedHeaderArg: 'CounterClockWise',
|
|
||||||
})
|
|
||||||
await cmdBar.selectOption({ name: 'True' }).click()
|
|
||||||
await cmdBar.expectState({
|
|
||||||
stage: 'review',
|
|
||||||
headerArguments: {
|
|
||||||
AngleStart: '0',
|
|
||||||
Revolutions: '20',
|
|
||||||
Radius: newInput,
|
|
||||||
CounterClockWise: '',
|
|
||||||
},
|
|
||||||
commandName: 'Helix',
|
|
||||||
})
|
|
||||||
await cmdBar.submit()
|
|
||||||
await toolbar.closePane('feature-tree')
|
|
||||||
await toolbar.openPane('code')
|
|
||||||
await editor.expectEditor.toContain(
|
|
||||||
`
|
|
||||||
helix001 = helix(
|
|
||||||
axis = getOppositeEdge(seg01),
|
|
||||||
radius = 5,
|
|
||||||
revolutions = 20,
|
|
||||||
angleStart = 0,
|
|
||||||
ccw = true,
|
|
||||||
)`,
|
|
||||||
{ shouldNormalise: true }
|
|
||||||
)
|
|
||||||
await toolbar.closePane('code')
|
|
||||||
})
|
|
||||||
|
|
||||||
await test.step('Delete helix via feature tree selection', async () => {
|
|
||||||
await toolbar.openPane('feature-tree')
|
|
||||||
const operationButton = await toolbar.getFeatureTreeOperation('Helix', 0)
|
|
||||||
await operationButton.click({ button: 'left' })
|
|
||||||
await page.keyboard.press('Delete')
|
|
||||||
await editor.expectEditor.not.toContain('helix')
|
|
||||||
await expect(
|
|
||||||
await toolbar.getFeatureTreeOperation('Helix', 0)
|
|
||||||
).not.toBeVisible()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
test('Helix point-and-click on cylinder', async ({
|
test('Helix point-and-click on cylinder', async ({
|
||||||
context,
|
context,
|
||||||
@ -1525,12 +1470,26 @@ extrude001 = extrude(profile001, length = 100)
|
|||||||
// One dumb hardcoded screen pixel value
|
// One dumb hardcoded screen pixel value
|
||||||
const testPoint = { x: 620, y: 257 }
|
const testPoint = { x: 620, y: 257 }
|
||||||
const [clickOnWall] = scene.makeMouseHelpers(testPoint.x, testPoint.y)
|
const [clickOnWall] = scene.makeMouseHelpers(testPoint.x, testPoint.y)
|
||||||
const expectedOutput = `helix001 = helix(cylinder = extrude001, revolutions = 1, angleStart = 360)`
|
const expectedOutput = `helix001 = helix( cylinder = extrude001, revolutions = 1, angleStart = 360, ccw = false,)`
|
||||||
const expectedEditedOutput = `helix001 = helix(cylinder = extrude001, revolutions = 1, angleStart = 10)`
|
const expectedLine = `cylinder = extrude001,`
|
||||||
|
const expectedEditedOutput = `helix001 = helix( cylinder = extrude001, revolutions = 1, angleStart = 360, ccw = true,)`
|
||||||
|
|
||||||
await test.step(`Go through the command bar flow`, async () => {
|
await test.step(`Go through the command bar flow`, async () => {
|
||||||
await toolbar.helixButton.click()
|
await toolbar.helixButton.click()
|
||||||
await cmdBar.expectState(initialCmdBarStateHelix)
|
await cmdBar.expectState({
|
||||||
|
stage: 'arguments',
|
||||||
|
currentArgKey: 'mode',
|
||||||
|
currentArgValue: '',
|
||||||
|
headerArguments: {
|
||||||
|
Mode: '',
|
||||||
|
AngleStart: '',
|
||||||
|
Revolutions: '',
|
||||||
|
Radius: '',
|
||||||
|
CounterClockWise: '',
|
||||||
|
},
|
||||||
|
highlightedHeaderArg: 'mode',
|
||||||
|
commandName: 'Helix',
|
||||||
|
})
|
||||||
await cmdBar.selectOption({ name: 'Cylinder' }).click()
|
await cmdBar.selectOption({ name: 'Cylinder' }).click()
|
||||||
await cmdBar.expectState({
|
await cmdBar.expectState({
|
||||||
stage: 'arguments',
|
stage: 'arguments',
|
||||||
@ -1541,6 +1500,7 @@ extrude001 = extrude(profile001, length = 100)
|
|||||||
Cylinder: '',
|
Cylinder: '',
|
||||||
AngleStart: '',
|
AngleStart: '',
|
||||||
Revolutions: '',
|
Revolutions: '',
|
||||||
|
CounterClockWise: '',
|
||||||
},
|
},
|
||||||
highlightedHeaderArg: 'cylinder',
|
highlightedHeaderArg: 'cylinder',
|
||||||
commandName: 'Helix',
|
commandName: 'Helix',
|
||||||
@ -1556,17 +1516,18 @@ extrude001 = extrude(profile001, length = 100)
|
|||||||
Cylinder: '1 face',
|
Cylinder: '1 face',
|
||||||
AngleStart: '360',
|
AngleStart: '360',
|
||||||
Revolutions: '1',
|
Revolutions: '1',
|
||||||
|
CounterClockWise: '',
|
||||||
},
|
},
|
||||||
commandName: 'Helix',
|
commandName: 'Helix',
|
||||||
})
|
})
|
||||||
await cmdBar.submit()
|
await cmdBar.progressCmdBar()
|
||||||
})
|
})
|
||||||
|
|
||||||
await test.step(`Confirm code is added to the editor, scene has changed`, async () => {
|
await test.step(`Confirm code is added to the editor, scene has changed`, async () => {
|
||||||
await editor.expectEditor.toContain(expectedOutput)
|
await editor.expectEditor.toContain(expectedOutput)
|
||||||
await editor.expectState({
|
await editor.expectState({
|
||||||
diagnostics: [],
|
diagnostics: [],
|
||||||
activeLines: [expectedOutput],
|
activeLines: [expectedLine],
|
||||||
highlightedCode: '',
|
highlightedCode: '',
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@ -1578,21 +1539,22 @@ extrude001 = extrude(profile001, length = 100)
|
|||||||
await cmdBar.expectState({
|
await cmdBar.expectState({
|
||||||
commandName: 'Helix',
|
commandName: 'Helix',
|
||||||
stage: 'arguments',
|
stage: 'arguments',
|
||||||
currentArgKey: 'angleStart',
|
currentArgKey: 'CounterClockWise',
|
||||||
currentArgValue: '360',
|
currentArgValue: '',
|
||||||
headerArguments: {
|
headerArguments: {
|
||||||
AngleStart: '360',
|
AngleStart: '360',
|
||||||
Revolutions: '1',
|
Revolutions: '1',
|
||||||
|
CounterClockWise: '',
|
||||||
},
|
},
|
||||||
highlightedHeaderArg: 'angleStart',
|
highlightedHeaderArg: 'CounterClockWise',
|
||||||
})
|
})
|
||||||
await page.keyboard.insertText('10')
|
await cmdBar.selectOption({ name: 'True' }).click()
|
||||||
await cmdBar.progressCmdBar()
|
|
||||||
await cmdBar.expectState({
|
await cmdBar.expectState({
|
||||||
stage: 'review',
|
stage: 'review',
|
||||||
headerArguments: {
|
headerArguments: {
|
||||||
AngleStart: '10',
|
AngleStart: '360',
|
||||||
Revolutions: '1',
|
Revolutions: '1',
|
||||||
|
CounterClockWise: 'true',
|
||||||
},
|
},
|
||||||
commandName: 'Helix',
|
commandName: 'Helix',
|
||||||
})
|
})
|
||||||
|
Binary file not shown.
Before Width: | Height: | Size: 52 KiB After Width: | Height: | Size: 52 KiB |
Binary file not shown.
Before Width: | Height: | Size: 58 KiB After Width: | Height: | Size: 58 KiB |
@ -74,7 +74,7 @@
|
|||||||
LIBCLANG_PATH = "${pkgs.libclang.lib}/lib";
|
LIBCLANG_PATH = "${pkgs.libclang.lib}/lib";
|
||||||
ELECTRON_OVERRIDE_DIST_PATH =
|
ELECTRON_OVERRIDE_DIST_PATH =
|
||||||
if pkgs.stdenv.isDarwin
|
if pkgs.stdenv.isDarwin
|
||||||
then "${pkgs.electron}/Applications"
|
then "${pkgs.electron}/Applications/Electron.app/Contents/MacOS/"
|
||||||
else "${pkgs.electron}/bin";
|
else "${pkgs.electron}/bin";
|
||||||
PLAYWRIGHT_SKIP_VALIDATE_HOST_REQUIREMENTS = true;
|
PLAYWRIGHT_SKIP_VALIDATE_HOST_REQUIREMENTS = true;
|
||||||
PLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH = "${pkgs.playwright-driver.browsers}/chromium-1091/chrome-linux/chrome";
|
PLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH = "${pkgs.playwright-driver.browsers}/chromium-1091/chrome-linux/chrome";
|
||||||
|
@ -15,14 +15,14 @@ import "car-tire.kcl" as carTire
|
|||||||
import * from "parameters.kcl"
|
import * from "parameters.kcl"
|
||||||
|
|
||||||
// Place the car rotor
|
// Place the car rotor
|
||||||
carRotor
|
rotor = carRotor
|
||||||
|> translate(x = 0, y = 0.5, z = 0)
|
|> translate(x = 0, y = 0.5, z = 0)
|
||||||
|
|
||||||
// Place the car wheel
|
// Place the car wheel
|
||||||
carWheel
|
carWheel
|
||||||
|
|
||||||
// Place the lug nuts
|
// Place the lug nuts
|
||||||
lugNut
|
lgnut = lugNut
|
||||||
|> patternCircular3d(
|
|> patternCircular3d(
|
||||||
arcDegrees = 360,
|
arcDegrees = 360,
|
||||||
axis = [0, 1, 0],
|
axis = [0, 1, 0],
|
||||||
@ -32,8 +32,19 @@ lugNut
|
|||||||
)
|
)
|
||||||
|
|
||||||
// Place the brake caliper
|
// Place the brake caliper
|
||||||
brakeCaliper
|
cal = brakeCaliper
|
||||||
|> translate(x = 0, y = 0.5, z = 0)
|
|> translate(x = 0, y = 0.5, z = 0)
|
||||||
|
|
||||||
// Place the car tire
|
// Place the car tire
|
||||||
carTire
|
carTire
|
||||||
|
|
||||||
|
|
||||||
|
fn animate(step: number(_)) {
|
||||||
|
angle = 0.6deg
|
||||||
|
rotate(rotor, pitch = angle)
|
||||||
|
rotate(lgnut, pitch = angle)
|
||||||
|
rotate(cal, pitch = angle)
|
||||||
|
rotate(carWheel, pitch = angle)
|
||||||
|
rotate(carTire, pitch = angle)
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
@ -369,7 +369,7 @@ profile007 = startProfile(
|
|||||||
|> line(%, endAbsolute = [profileStartX(%), profileStartY(%)])
|
|> line(%, endAbsolute = [profileStartX(%), profileStartY(%)])
|
||||||
|> close(%)
|
|> close(%)
|
||||||
profile008 = circle(sketch005, center = [0, 0], diameter = nubDiameter)
|
profile008 = circle(sketch005, center = [0, 0], diameter = nubDiameter)
|
||||||
subtract2d(profile007, tool = profile008)
|
hourHand = subtract2d(profile007, tool = profile008)
|
||||||
|> extrude(%, length = 5)
|
|> extrude(%, length = 5)
|
||||||
|> appearance(%, color = "#404040")
|
|> appearance(%, color = "#404040")
|
||||||
|
|
||||||
@ -413,7 +413,7 @@ profile009 = startProfile(
|
|||||||
|> line(%, endAbsolute = [profileStartX(%), profileStartY(%)])
|
|> line(%, endAbsolute = [profileStartX(%), profileStartY(%)])
|
||||||
|> close(%)
|
|> close(%)
|
||||||
profile010 = circle(sketch006, center = [0, 0], diameter = 30)
|
profile010 = circle(sketch006, center = [0, 0], diameter = 30)
|
||||||
subtract2d(profile009, tool = profile010)
|
minuteHand = subtract2d(profile009, tool = profile010)
|
||||||
|> extrude(%, length = 5)
|
|> extrude(%, length = 5)
|
||||||
|> appearance(%, color = "#404040")
|
|> appearance(%, color = "#404040")
|
||||||
|
|
||||||
@ -439,3 +439,8 @@ profile004 = startProfile(sketch003, at = [-slotWidth / 2, 200])
|
|||||||
|> extrude(%, length = -20)
|
|> extrude(%, length = -20)
|
||||||
|
|
||||||
// todo: create cavity for the screw to slide into (need csg update)
|
// todo: create cavity for the screw to slide into (need csg update)
|
||||||
|
|
||||||
|
fn animate(step: number(_)) {
|
||||||
|
rotate(hourHand, yaw = -0.1deg)
|
||||||
|
return rotate(minuteHand, yaw = -0.6deg)
|
||||||
|
}
|
||||||
|
20
rust/Cargo.lock
generated
20
rust/Cargo.lock
generated
@ -1814,7 +1814,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "kcl-bumper"
|
name = "kcl-bumper"
|
||||||
version = "0.1.83"
|
version = "0.1.82"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"clap",
|
"clap",
|
||||||
@ -1825,7 +1825,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "kcl-derive-docs"
|
name = "kcl-derive-docs"
|
||||||
version = "0.1.83"
|
version = "0.1.82"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@ -1834,7 +1834,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "kcl-directory-test-macro"
|
name = "kcl-directory-test-macro"
|
||||||
version = "0.1.83"
|
version = "0.1.82"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"convert_case",
|
"convert_case",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
@ -1844,7 +1844,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "kcl-language-server"
|
name = "kcl-language-server"
|
||||||
version = "0.2.83"
|
version = "0.2.82"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"clap",
|
"clap",
|
||||||
@ -1865,7 +1865,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "kcl-language-server-release"
|
name = "kcl-language-server-release"
|
||||||
version = "0.1.83"
|
version = "0.1.82"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"clap",
|
"clap",
|
||||||
@ -1885,7 +1885,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "kcl-lib"
|
name = "kcl-lib"
|
||||||
version = "0.2.83"
|
version = "0.2.82"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"approx 0.5.1",
|
"approx 0.5.1",
|
||||||
@ -1962,7 +1962,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "kcl-python-bindings"
|
name = "kcl-python-bindings"
|
||||||
version = "0.3.83"
|
version = "0.3.82"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"kcl-lib",
|
"kcl-lib",
|
||||||
@ -1977,7 +1977,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "kcl-test-server"
|
name = "kcl-test-server"
|
||||||
version = "0.1.83"
|
version = "0.1.82"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"hyper 0.14.32",
|
"hyper 0.14.32",
|
||||||
@ -1990,7 +1990,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "kcl-to-core"
|
name = "kcl-to-core"
|
||||||
version = "0.1.83"
|
version = "0.1.82"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
@ -2004,7 +2004,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "kcl-wasm-lib"
|
name = "kcl-wasm-lib"
|
||||||
version = "0.1.83"
|
version = "0.1.82"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"bson",
|
"bson",
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
|
|
||||||
[package]
|
[package]
|
||||||
name = "kcl-bumper"
|
name = "kcl-bumper"
|
||||||
version = "0.1.83"
|
version = "0.1.82"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
repository = "https://github.com/KittyCAD/modeling-api"
|
repository = "https://github.com/KittyCAD/modeling-api"
|
||||||
rust-version = "1.76"
|
rust-version = "1.76"
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "kcl-derive-docs"
|
name = "kcl-derive-docs"
|
||||||
description = "A tool for generating documentation from Rust derive macros"
|
description = "A tool for generating documentation from Rust derive macros"
|
||||||
version = "0.1.83"
|
version = "0.1.82"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
repository = "https://github.com/KittyCAD/modeling-app"
|
repository = "https://github.com/KittyCAD/modeling-app"
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "kcl-directory-test-macro"
|
name = "kcl-directory-test-macro"
|
||||||
description = "A tool for generating tests from a directory of kcl files"
|
description = "A tool for generating tests from a directory of kcl files"
|
||||||
version = "0.1.83"
|
version = "0.1.82"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
repository = "https://github.com/KittyCAD/modeling-app"
|
repository = "https://github.com/KittyCAD/modeling-app"
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "kcl-language-server-release"
|
name = "kcl-language-server-release"
|
||||||
version = "0.1.83"
|
version = "0.1.82"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
authors = ["KittyCAD Inc <kcl@kittycad.io>"]
|
authors = ["KittyCAD Inc <kcl@kittycad.io>"]
|
||||||
publish = false
|
publish = false
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
name = "kcl-language-server"
|
name = "kcl-language-server"
|
||||||
description = "A language server for KCL."
|
description = "A language server for KCL."
|
||||||
authors = ["KittyCAD Inc <kcl@kittycad.io>"]
|
authors = ["KittyCAD Inc <kcl@kittycad.io>"]
|
||||||
version = "0.2.83"
|
version = "0.2.82"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "kcl-lib"
|
name = "kcl-lib"
|
||||||
description = "KittyCAD Language implementation and tools"
|
description = "KittyCAD Language implementation and tools"
|
||||||
version = "0.2.83"
|
version = "0.2.82"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
repository = "https://github.com/KittyCAD/modeling-app"
|
repository = "https://github.com/KittyCAD/modeling-app"
|
||||||
|
@ -17,14 +17,11 @@ use crate::{
|
|||||||
},
|
},
|
||||||
fmt,
|
fmt,
|
||||||
modules::{ModuleId, ModulePath, ModuleRepr},
|
modules::{ModuleId, ModulePath, ModuleRepr},
|
||||||
parsing::{
|
parsing::ast::types::{
|
||||||
ast::types::{
|
Annotation, ArrayExpression, ArrayRangeExpression, AscribedExpression, BinaryExpression, BinaryOperator,
|
||||||
Annotation, ArrayExpression, ArrayRangeExpression, AscribedExpression, BinaryExpression, BinaryOperator,
|
BinaryPart, BodyItem, Expr, IfExpression, ImportPath, ImportSelector, ItemVisibility, LiteralIdentifier,
|
||||||
BinaryPart, BodyItem, Expr, IfExpression, ImportPath, ImportSelector, ItemVisibility, LiteralIdentifier,
|
LiteralValue, MemberExpression, Name, Node, NodeRef, ObjectExpression, PipeExpression, Program, TagDeclarator,
|
||||||
LiteralValue, MemberExpression, Name, Node, NodeRef, ObjectExpression, PipeExpression, Program,
|
Type, UnaryExpression, UnaryOperator,
|
||||||
TagDeclarator, Type, UnaryExpression, UnaryOperator,
|
|
||||||
},
|
|
||||||
token::NumericSuffix,
|
|
||||||
},
|
},
|
||||||
source_range::SourceRange,
|
source_range::SourceRange,
|
||||||
std::args::TyF64,
|
std::args::TyF64,
|
||||||
@ -1669,18 +1666,12 @@ impl Property {
|
|||||||
LiteralIdentifier::Literal(literal) => {
|
LiteralIdentifier::Literal(literal) => {
|
||||||
let value = literal.value.clone();
|
let value = literal.value.clone();
|
||||||
match value {
|
match value {
|
||||||
n @ LiteralValue::Number { value, suffix } => {
|
LiteralValue::Number { value, .. } => {
|
||||||
if !matches!(suffix, NumericSuffix::None | NumericSuffix::Count) {
|
|
||||||
return Err(KclError::new_semantic(KclErrorDetails::new(
|
|
||||||
format!("{n} is not a valid index, indices must be non-dimensional numbers"),
|
|
||||||
property_sr,
|
|
||||||
)));
|
|
||||||
}
|
|
||||||
if let Some(x) = crate::try_f64_to_usize(value) {
|
if let Some(x) = crate::try_f64_to_usize(value) {
|
||||||
Ok(Property::UInt(x))
|
Ok(Property::UInt(x))
|
||||||
} else {
|
} else {
|
||||||
Err(KclError::new_semantic(KclErrorDetails::new(
|
Err(KclError::new_semantic(KclErrorDetails::new(
|
||||||
format!("{n} is not a valid index, indices must be whole numbers >= 0"),
|
format!("{value} is not a valid index, indices must be whole numbers >= 0"),
|
||||||
property_sr,
|
property_sr,
|
||||||
)))
|
)))
|
||||||
}
|
}
|
||||||
@ -1699,13 +1690,10 @@ fn jvalue_to_prop(value: &KclValue, property_sr: Vec<SourceRange>, name: &str) -
|
|||||||
let make_err =
|
let make_err =
|
||||||
|message: String| Err::<Property, _>(KclError::new_semantic(KclErrorDetails::new(message, property_sr)));
|
|message: String| Err::<Property, _>(KclError::new_semantic(KclErrorDetails::new(message, property_sr)));
|
||||||
match value {
|
match value {
|
||||||
n @ KclValue::Number{value: num, ty, .. } => {
|
KclValue::Number{value: num, .. } => {
|
||||||
if !matches!(ty, NumericType::Known(crate::exec::UnitType::Count) | NumericType::Default { .. } | NumericType::Any ) {
|
|
||||||
return make_err(format!("arrays can only be indexed by non-dimensioned numbers, found {}", n.human_friendly_type()));
|
|
||||||
}
|
|
||||||
let num = *num;
|
let num = *num;
|
||||||
if num < 0.0 {
|
if num < 0.0 {
|
||||||
return make_err(format!("'{num}' is negative, so you can't index an array with it"));
|
return make_err(format!("'{num}' is negative, so you can't index an array with it"))
|
||||||
}
|
}
|
||||||
let nearest_int = crate::try_f64_to_usize(num);
|
let nearest_int = crate::try_f64_to_usize(num);
|
||||||
if let Some(nearest_int) = nearest_int {
|
if let Some(nearest_int) = nearest_int {
|
||||||
@ -2153,23 +2141,4 @@ c = ((PI * 2) / 3): number(deg)
|
|||||||
let result = parse_execute(ast).await.unwrap();
|
let result = parse_execute(ast).await.unwrap();
|
||||||
assert_eq!(result.exec_state.errors().len(), 2);
|
assert_eq!(result.exec_state.errors().len(), 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread")]
|
|
||||||
async fn non_count_indexing() {
|
|
||||||
let ast = r#"x = [0, 0]
|
|
||||||
y = x[1mm]
|
|
||||||
"#;
|
|
||||||
parse_execute(ast).await.unwrap_err();
|
|
||||||
|
|
||||||
let ast = r#"x = [0, 0]
|
|
||||||
y = 1deg
|
|
||||||
z = x[y]
|
|
||||||
"#;
|
|
||||||
parse_execute(ast).await.unwrap_err();
|
|
||||||
|
|
||||||
let ast = r#"x = [0, 0]
|
|
||||||
y = x[0mm + 1]
|
|
||||||
"#;
|
|
||||||
parse_execute(ast).await.unwrap_err();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -805,6 +805,43 @@ impl ExecutorContext {
|
|||||||
Ok(outcome)
|
Ok(outcome)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn run_additional(&self, program: crate::Program) -> Result<ExecOutcome, KclErrorWithOutputs> {
|
||||||
|
assert!(!self.is_mock());
|
||||||
|
|
||||||
|
let (program, exec_state, result) = match cache::read_old_ast().await {
|
||||||
|
Some(cached_state) => {
|
||||||
|
let mut exec_state = cached_state.reconstitute_exec_state();
|
||||||
|
exec_state.mut_stack().restore_env(cached_state.main.result_env);
|
||||||
|
|
||||||
|
let result = self.run_concurrent(&program, &mut exec_state, None, true).await;
|
||||||
|
|
||||||
|
(program, exec_state, result)
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
let mut exec_state = ExecState::new(self);
|
||||||
|
|
||||||
|
let result = self.run_concurrent(&program, &mut exec_state, None, false).await;
|
||||||
|
|
||||||
|
(program, exec_state, result)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Throw the error.
|
||||||
|
let result = result?;
|
||||||
|
|
||||||
|
// Save this as the last successful execution to the cache.
|
||||||
|
cache::write_old_ast(GlobalState::new(
|
||||||
|
exec_state.clone(),
|
||||||
|
self.settings.clone(),
|
||||||
|
program.ast,
|
||||||
|
result.0,
|
||||||
|
))
|
||||||
|
.await;
|
||||||
|
|
||||||
|
let outcome = exec_state.into_exec_outcome(result.0, self).await;
|
||||||
|
Ok(outcome)
|
||||||
|
}
|
||||||
|
|
||||||
/// Perform the execution of a program.
|
/// Perform the execution of a program.
|
||||||
///
|
///
|
||||||
/// To access non-fatal errors and warnings, extract them from the `ExecState`.
|
/// To access non-fatal errors and warnings, extract them from the `ExecState`.
|
||||||
|
@ -11,10 +11,10 @@ use crate::{
|
|||||||
|
|
||||||
def_finding!(
|
def_finding!(
|
||||||
Z0001,
|
Z0001,
|
||||||
"Identifiers should be lowerCamelCase",
|
"Identifiers must be lowerCamelCase",
|
||||||
"\
|
"\
|
||||||
By convention, variable names are lowerCamelCase, not snake_case, kebab-case,
|
By convention, variable names are lowerCamelCase, not snake_case, kebab-case,
|
||||||
nor upper CamelCase (aka PascalCase). 🐪
|
nor CammelCase. 🐪
|
||||||
|
|
||||||
For instance, a good identifier for the variable representing 'box height'
|
For instance, a good identifier for the variable representing 'box height'
|
||||||
would be 'boxHeight', not 'BOX_HEIGHT', 'box_height' nor 'BoxHeight'. For
|
would be 'boxHeight', not 'BOX_HEIGHT', 'box_height' nor 'BoxHeight'. For
|
||||||
|
@ -2359,7 +2359,7 @@ async fn test_kcl_lsp_diagnostic_has_lints() {
|
|||||||
assert_eq!(diagnostics.full_document_diagnostic_report.items.len(), 1);
|
assert_eq!(diagnostics.full_document_diagnostic_report.items.len(), 1);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
diagnostics.full_document_diagnostic_report.items[0].message,
|
diagnostics.full_document_diagnostic_report.items[0].message,
|
||||||
"Identifiers should be lowerCamelCase"
|
"Identifiers must be lowerCamelCase"
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
panic!("Expected full diagnostics");
|
panic!("Expected full diagnostics");
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "kcl-python-bindings"
|
name = "kcl-python-bindings"
|
||||||
version = "0.3.83"
|
version = "0.3.82"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
repository = "https://github.com/kittycad/modeling-app"
|
repository = "https://github.com/kittycad/modeling-app"
|
||||||
exclude = ["tests/*", "files/*", "venv/*"]
|
exclude = ["tests/*", "files/*", "venv/*"]
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "kcl-test-server"
|
name = "kcl-test-server"
|
||||||
description = "A test server for KCL"
|
description = "A test server for KCL"
|
||||||
version = "0.1.83"
|
version = "0.1.82"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "kcl-to-core"
|
name = "kcl-to-core"
|
||||||
description = "Utility methods to convert kcl to engine core executable tests"
|
description = "Utility methods to convert kcl to engine core executable tests"
|
||||||
version = "0.1.83"
|
version = "0.1.82"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
repository = "https://github.com/KittyCAD/modeling-app"
|
repository = "https://github.com/KittyCAD/modeling-app"
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "kcl-wasm-lib"
|
name = "kcl-wasm-lib"
|
||||||
version = "0.1.83"
|
version = "0.1.82"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
repository = "https://github.com/KittyCAD/modeling-app"
|
repository = "https://github.com/KittyCAD/modeling-app"
|
||||||
rust-version = "1.83"
|
rust-version = "1.83"
|
||||||
|
@ -111,6 +111,48 @@ impl Context {
|
|||||||
ctx.run_with_caching(program).await
|
ctx.run_with_caching(program).await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Execute an additional program using the cache.
|
||||||
|
#[wasm_bindgen(js_name = executeAdditional)]
|
||||||
|
pub async fn execute_additional(
|
||||||
|
&self,
|
||||||
|
program_ast_json: &str,
|
||||||
|
path: Option<String>,
|
||||||
|
settings: &str,
|
||||||
|
) -> Result<JsValue, JsValue> {
|
||||||
|
console_error_panic_hook::set_once();
|
||||||
|
|
||||||
|
self.execute_additional_typed(program_ast_json, path, settings)
|
||||||
|
.await
|
||||||
|
.and_then(|outcome| {
|
||||||
|
JsValue::from_serde(&outcome).map_err(|e| {
|
||||||
|
// The serde-wasm-bindgen does not work here because of weird HashMap issues.
|
||||||
|
// DO NOT USE serde_wasm_bindgen::to_value it will break the frontend.
|
||||||
|
KclErrorWithOutputs::no_outputs(KclError::internal(format!(
|
||||||
|
"Could not serialize successful KCL result. {TRUE_BUG} Details: {e}"
|
||||||
|
)))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.map_err(|e: KclErrorWithOutputs| JsValue::from_serde(&e).unwrap())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn execute_additional_typed(
|
||||||
|
&self,
|
||||||
|
program_ast_json: &str,
|
||||||
|
path: Option<String>,
|
||||||
|
settings: &str,
|
||||||
|
) -> Result<ExecOutcome, KclErrorWithOutputs> {
|
||||||
|
let program: Program = serde_json::from_str(program_ast_json).map_err(|e| {
|
||||||
|
let err = KclError::internal(format!("Could not deserialize KCL AST. {TRUE_BUG} Details: {e}"));
|
||||||
|
KclErrorWithOutputs::no_outputs(err)
|
||||||
|
})?;
|
||||||
|
let ctx = self.create_executor_ctx(settings, path, false).map_err(|e| {
|
||||||
|
KclErrorWithOutputs::no_outputs(KclError::internal(format!(
|
||||||
|
"Could not create KCL executor context. {TRUE_BUG} Details: {e}"
|
||||||
|
)))
|
||||||
|
})?;
|
||||||
|
ctx.run_additional(program).await
|
||||||
|
}
|
||||||
|
|
||||||
/// Reset the scene and bust the cache.
|
/// Reset the scene and bust the cache.
|
||||||
/// ONLY use this if you absolutely need to reset the scene and bust the cache.
|
/// ONLY use this if you absolutely need to reset the scene and bust the cache.
|
||||||
#[wasm_bindgen(js_name = bustCacheAndResetScene)]
|
#[wasm_bindgen(js_name = bustCacheAndResetScene)]
|
||||||
|
@ -7,6 +7,8 @@ import type { CustomIconName } from '@src/components/CustomIcon'
|
|||||||
import Tooltip from '@src/components/Tooltip'
|
import Tooltip from '@src/components/Tooltip'
|
||||||
|
|
||||||
import styles from './ModelingPane.module.css'
|
import styles from './ModelingPane.module.css'
|
||||||
|
import { reportRejection } from '@src/lib/trap'
|
||||||
|
import { kclManager } from '@src/lib/singletons'
|
||||||
|
|
||||||
export interface ModelingPaneProps {
|
export interface ModelingPaneProps {
|
||||||
id: string
|
id: string
|
||||||
@ -19,6 +21,28 @@ export interface ModelingPaneProps {
|
|||||||
onClose: () => void
|
onClose: () => void
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const ANIMATE_INTERVAL = 16 // milliseconds
|
||||||
|
let animateTimeout: NodeJS.Timeout | null = null
|
||||||
|
|
||||||
|
function doAnimate() {
|
||||||
|
;(async () => {
|
||||||
|
await kclManager.executeAnimate()
|
||||||
|
if (animateTimeout !== null) {
|
||||||
|
animateTimeout = setTimeout(doAnimate, ANIMATE_INTERVAL)
|
||||||
|
}
|
||||||
|
})().catch(reportRejection)
|
||||||
|
}
|
||||||
|
|
||||||
|
function onPlay() {
|
||||||
|
console.log('Play button clicked')
|
||||||
|
if (animateTimeout) {
|
||||||
|
clearTimeout(animateTimeout)
|
||||||
|
animateTimeout = null
|
||||||
|
} else {
|
||||||
|
animateTimeout = setTimeout(doAnimate, ANIMATE_INTERVAL)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export const ModelingPaneHeader = ({
|
export const ModelingPaneHeader = ({
|
||||||
id,
|
id,
|
||||||
icon,
|
icon,
|
||||||
@ -40,6 +64,20 @@ export const ModelingPaneHeader = ({
|
|||||||
)}
|
)}
|
||||||
<span data-testid={id + '-header'}>{title}</span>
|
<span data-testid={id + '-header'}>{title}</span>
|
||||||
</div>
|
</div>
|
||||||
|
{id === 'code' && (
|
||||||
|
<ActionButton
|
||||||
|
Element="button"
|
||||||
|
iconStart={{
|
||||||
|
icon: 'play',
|
||||||
|
iconClassName: '!text-current',
|
||||||
|
bgClassName: 'bg-transparent dark:bg-transparent',
|
||||||
|
}}
|
||||||
|
className="!p-0 !bg-transparent hover:text-primary border-transparent dark:!border-transparent hover:!border-primary dark:hover:!border-chalkboard-70 !outline-none"
|
||||||
|
onClick={() => onPlay()}
|
||||||
|
>
|
||||||
|
<Tooltip position="bottom-right">Play</Tooltip>
|
||||||
|
</ActionButton>
|
||||||
|
)}
|
||||||
{Menu instanceof Function ? <Menu /> : Menu}
|
{Menu instanceof Function ? <Menu /> : Menu}
|
||||||
<ActionButton
|
<ActionButton
|
||||||
Element="button"
|
Element="button"
|
||||||
|
@ -17,7 +17,12 @@ import {
|
|||||||
compilationErrorsToDiagnostics,
|
compilationErrorsToDiagnostics,
|
||||||
kclErrorsToDiagnostics,
|
kclErrorsToDiagnostics,
|
||||||
} from '@src/lang/errors'
|
} from '@src/lang/errors'
|
||||||
import { executeAst, executeAstMock, lintAst } from '@src/lang/langHelpers'
|
import {
|
||||||
|
executeAdditional,
|
||||||
|
executeAst,
|
||||||
|
executeAstMock,
|
||||||
|
lintAst,
|
||||||
|
} from '@src/lang/langHelpers'
|
||||||
import { getNodeFromPath, getSettingsAnnotation } from '@src/lang/queryAst'
|
import { getNodeFromPath, getSettingsAnnotation } from '@src/lang/queryAst'
|
||||||
import { CommandLogType } from '@src/lang/std/commandLog'
|
import { CommandLogType } from '@src/lang/std/commandLog'
|
||||||
import type { EngineCommandManager } from '@src/lang/std/engineConnection'
|
import type { EngineCommandManager } from '@src/lang/std/engineConnection'
|
||||||
@ -106,6 +111,7 @@ export class KclManager extends EventTarget {
|
|||||||
preComments: [],
|
preComments: [],
|
||||||
commentStart: 0,
|
commentStart: 0,
|
||||||
}
|
}
|
||||||
|
private _animateState = { step: 0 }
|
||||||
private _execState: ExecState = emptyExecState()
|
private _execState: ExecState = emptyExecState()
|
||||||
private _variables: VariableMap = {}
|
private _variables: VariableMap = {}
|
||||||
lastSuccessfulVariables: VariableMap = {}
|
lastSuccessfulVariables: VariableMap = {}
|
||||||
@ -450,6 +456,7 @@ export class KclManager extends EventTarget {
|
|||||||
const ast = args.ast || this.ast
|
const ast = args.ast || this.ast
|
||||||
markOnce('code/startExecuteAst')
|
markOnce('code/startExecuteAst')
|
||||||
|
|
||||||
|
this._animateState.step = 0
|
||||||
const currentExecutionId = args.executionId || Date.now()
|
const currentExecutionId = args.executionId || Date.now()
|
||||||
this._cancelTokens.set(currentExecutionId, false)
|
this._cancelTokens.set(currentExecutionId, false)
|
||||||
|
|
||||||
@ -541,6 +548,39 @@ export class KclManager extends EventTarget {
|
|||||||
markOnce('code/endExecuteAst')
|
markOnce('code/endExecuteAst')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async executeAnimate(): Promise<void> {
|
||||||
|
if (this.isExecuting) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const code = `animate(step = ${this._animateState.step})`
|
||||||
|
const result = parse(code)
|
||||||
|
if (err(result)) {
|
||||||
|
console.error(result)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const program = result.program
|
||||||
|
if (!program) {
|
||||||
|
console.error('No program returned from parse')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const { errors } = await executeAdditional({
|
||||||
|
ast: program,
|
||||||
|
path: this.singletons.codeManager.currentFilePath || undefined,
|
||||||
|
rustContext: this.singletons.rustContext,
|
||||||
|
})
|
||||||
|
if (errors.length > 0) {
|
||||||
|
console.error('Errors executing animate:', errors)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
this._animateState.step += 1
|
||||||
|
if (this._animateState.step === Number.MAX_SAFE_INTEGER) {
|
||||||
|
this._animateState.step = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// DO NOT CALL THIS from codemirror ever.
|
// DO NOT CALL THIS from codemirror ever.
|
||||||
async executeAstMock(ast: Program): Promise<null | Error> {
|
async executeAstMock(ast: Program): Promise<null | Error> {
|
||||||
await this.ensureWasmInit()
|
await this.ensureWasmInit()
|
||||||
|
@ -84,6 +84,31 @@ export async function executeAst({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function executeAdditional({
|
||||||
|
ast,
|
||||||
|
rustContext,
|
||||||
|
path,
|
||||||
|
}: {
|
||||||
|
ast: Node<Program>
|
||||||
|
rustContext: RustContext
|
||||||
|
path?: string
|
||||||
|
}): Promise<ExecutionResult> {
|
||||||
|
try {
|
||||||
|
const settings = await jsAppSettings()
|
||||||
|
const execState = await rustContext.executeAdditional(ast, settings, path)
|
||||||
|
|
||||||
|
await rustContext.waitForAllEngineCommands()
|
||||||
|
return {
|
||||||
|
logs: [],
|
||||||
|
errors: [],
|
||||||
|
execState,
|
||||||
|
isInterrupted: false,
|
||||||
|
}
|
||||||
|
} catch (e: any) {
|
||||||
|
return handleExecuteError(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export async function executeAstMock({
|
export async function executeAstMock({
|
||||||
ast,
|
ast,
|
||||||
rustContext,
|
rustContext,
|
||||||
|
@ -590,7 +590,7 @@ export function addHelix({
|
|||||||
angleStart: Expr
|
angleStart: Expr
|
||||||
radius?: Expr
|
radius?: Expr
|
||||||
length?: Expr
|
length?: Expr
|
||||||
ccw?: boolean
|
ccw: boolean
|
||||||
insertIndex?: number
|
insertIndex?: number
|
||||||
variableName?: string
|
variableName?: string
|
||||||
}): { modifiedAst: Node<Program>; pathToNode: PathToNode } {
|
}): { modifiedAst: Node<Program>; pathToNode: PathToNode } {
|
||||||
@ -610,9 +610,6 @@ export function addHelix({
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Extra labeled args expressions
|
|
||||||
const ccwExpr = ccw ? [createLabeledArg('ccw', createLiteral(ccw))] : []
|
|
||||||
|
|
||||||
const variable = createVariableDeclaration(
|
const variable = createVariableDeclaration(
|
||||||
name,
|
name,
|
||||||
createCallExpressionStdLibKw(
|
createCallExpressionStdLibKw(
|
||||||
@ -622,7 +619,7 @@ export function addHelix({
|
|||||||
...modeArgs,
|
...modeArgs,
|
||||||
createLabeledArg('revolutions', revolutions),
|
createLabeledArg('revolutions', revolutions),
|
||||||
createLabeledArg('angleStart', angleStart),
|
createLabeledArg('angleStart', angleStart),
|
||||||
...ccwExpr,
|
createLabeledArg('ccw', createLiteral(ccw)),
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
@ -23,13 +23,13 @@ import type {
|
|||||||
VariableDeclaration,
|
VariableDeclaration,
|
||||||
} from '@src/lang/wasm'
|
} from '@src/lang/wasm'
|
||||||
import { EXECUTION_TYPE_REAL } from '@src/lib/constants'
|
import { EXECUTION_TYPE_REAL } from '@src/lib/constants'
|
||||||
import type { Selections } from '@src/lib/selections'
|
import type { Selection, Selections } from '@src/lib/selections'
|
||||||
import { err } from '@src/lib/trap'
|
import { err } from '@src/lib/trap'
|
||||||
import { isArray } from '@src/lib/utils'
|
import { isArray } from '@src/lib/utils'
|
||||||
|
|
||||||
export async function applySubtractFromTargetOperatorSelections(
|
export async function applySubtractFromTargetOperatorSelections(
|
||||||
solids: Selections,
|
target: Selection,
|
||||||
tools: Selections,
|
tool: Selection,
|
||||||
dependencies: {
|
dependencies: {
|
||||||
kclManager: KclManager
|
kclManager: KclManager
|
||||||
engineCommandManager: EngineCommandManager
|
engineCommandManager: EngineCommandManager
|
||||||
@ -38,28 +38,28 @@ export async function applySubtractFromTargetOperatorSelections(
|
|||||||
}
|
}
|
||||||
): Promise<Error | void> {
|
): Promise<Error | void> {
|
||||||
const ast = dependencies.kclManager.ast
|
const ast = dependencies.kclManager.ast
|
||||||
const lastSolidsVars = getLastVariableDeclarationsFromSelections(
|
if (!target.artifact || !tool.artifact) {
|
||||||
solids,
|
return new Error('No artifact found')
|
||||||
ast,
|
}
|
||||||
|
const orderedChildrenTarget = findAllChildrenAndOrderByPlaceInCode(
|
||||||
|
target.artifact,
|
||||||
dependencies.kclManager.artifactGraph
|
dependencies.kclManager.artifactGraph
|
||||||
)
|
)
|
||||||
if (err(lastSolidsVars) || lastSolidsVars.length < 1) {
|
const orderedChildrenTool = findAllChildrenAndOrderByPlaceInCode(
|
||||||
return new Error('Not enough or invalid solids variables found')
|
tool.artifact,
|
||||||
}
|
|
||||||
|
|
||||||
const lastToolsVars = getLastVariableDeclarationsFromSelections(
|
|
||||||
tools,
|
|
||||||
ast,
|
|
||||||
dependencies.kclManager.artifactGraph
|
dependencies.kclManager.artifactGraph
|
||||||
)
|
)
|
||||||
if (err(lastToolsVars) || lastToolsVars.length < 1) {
|
|
||||||
return new Error('Not enough or invalid tools variables found')
|
|
||||||
}
|
|
||||||
|
|
||||||
|
const lastVarTarget = getLastVariable(orderedChildrenTarget, ast)
|
||||||
|
const lastVarTool = getLastVariable(orderedChildrenTool, ast)
|
||||||
|
|
||||||
|
if (!lastVarTarget || !lastVarTool) {
|
||||||
|
return new Error('No variable found')
|
||||||
|
}
|
||||||
const modifiedAst = booleanSubtractAstMod({
|
const modifiedAst = booleanSubtractAstMod({
|
||||||
ast,
|
ast,
|
||||||
solids: lastSolidsVars,
|
targets: [lastVarTarget?.variableDeclaration?.node],
|
||||||
tools: lastToolsVars,
|
tools: [lastVarTool?.variableDeclaration.node],
|
||||||
})
|
})
|
||||||
|
|
||||||
await updateModelingState(modifiedAst, EXECUTION_TYPE_REAL, dependencies)
|
await updateModelingState(modifiedAst, EXECUTION_TYPE_REAL, dependencies)
|
||||||
@ -75,13 +75,34 @@ export async function applyUnionFromTargetOperatorSelections(
|
|||||||
}
|
}
|
||||||
): Promise<Error | void> {
|
): Promise<Error | void> {
|
||||||
const ast = dependencies.kclManager.ast
|
const ast = dependencies.kclManager.ast
|
||||||
const lastVars = getLastVariableDeclarationsFromSelections(
|
|
||||||
solids,
|
const artifacts: Artifact[] = []
|
||||||
ast,
|
for (const selection of solids.graphSelections) {
|
||||||
dependencies.kclManager.artifactGraph
|
if (selection.artifact) {
|
||||||
|
artifacts.push(selection.artifact)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (artifacts.length < 2) {
|
||||||
|
return new Error('Not enough artifacts selected')
|
||||||
|
}
|
||||||
|
|
||||||
|
const orderedChildrenEach = artifacts.map((artifact) =>
|
||||||
|
findAllChildrenAndOrderByPlaceInCode(
|
||||||
|
artifact,
|
||||||
|
dependencies.kclManager.artifactGraph
|
||||||
|
)
|
||||||
)
|
)
|
||||||
if (err(lastVars) || lastVars.length < 2) {
|
|
||||||
return new Error('Not enough or invalid solids variables found')
|
const lastVars: VariableDeclaration[] = []
|
||||||
|
for (const orderedArtifactLeaves of orderedChildrenEach) {
|
||||||
|
const lastVar = getLastVariable(orderedArtifactLeaves, ast)
|
||||||
|
if (!lastVar) continue
|
||||||
|
lastVars.push(lastVar.variableDeclaration.node)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lastVars.length < 2) {
|
||||||
|
return new Error('Not enough variables found')
|
||||||
}
|
}
|
||||||
|
|
||||||
const modifiedAst = booleanUnionAstMod({
|
const modifiedAst = booleanUnionAstMod({
|
||||||
@ -101,36 +122,23 @@ export async function applyIntersectFromTargetOperatorSelections(
|
|||||||
}
|
}
|
||||||
): Promise<Error | void> {
|
): Promise<Error | void> {
|
||||||
const ast = dependencies.kclManager.ast
|
const ast = dependencies.kclManager.ast
|
||||||
const lastVars = getLastVariableDeclarationsFromSelections(
|
|
||||||
solids,
|
|
||||||
ast,
|
|
||||||
dependencies.kclManager.artifactGraph
|
|
||||||
)
|
|
||||||
if (err(lastVars) || lastVars.length < 2) {
|
|
||||||
return new Error('Not enough or invalid solids variables found')
|
|
||||||
}
|
|
||||||
|
|
||||||
const modifiedAst = booleanIntersectAstMod({
|
|
||||||
ast,
|
|
||||||
solids: lastVars,
|
|
||||||
})
|
|
||||||
await updateModelingState(modifiedAst, EXECUTION_TYPE_REAL, dependencies)
|
|
||||||
}
|
|
||||||
|
|
||||||
function getLastVariableDeclarationsFromSelections(
|
|
||||||
selections: Selections,
|
|
||||||
ast: Node<Program>,
|
|
||||||
artifactGraph: ArtifactGraph
|
|
||||||
): Error | VariableDeclaration[] {
|
|
||||||
const artifacts: Artifact[] = []
|
const artifacts: Artifact[] = []
|
||||||
for (const selection of selections.graphSelections) {
|
for (const selection of solids.graphSelections) {
|
||||||
if (selection.artifact) {
|
if (selection.artifact) {
|
||||||
artifacts.push(selection.artifact)
|
artifacts.push(selection.artifact)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (artifacts.length < 2) {
|
||||||
|
return new Error('Not enough artifacts selected')
|
||||||
|
}
|
||||||
|
|
||||||
const orderedChildrenEach = artifacts.map((artifact) =>
|
const orderedChildrenEach = artifacts.map((artifact) =>
|
||||||
findAllChildrenAndOrderByPlaceInCode(artifact, artifactGraph)
|
findAllChildrenAndOrderByPlaceInCode(
|
||||||
|
artifact,
|
||||||
|
dependencies.kclManager.artifactGraph
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
const lastVars: VariableDeclaration[] = []
|
const lastVars: VariableDeclaration[] = []
|
||||||
@ -140,7 +148,15 @@ function getLastVariableDeclarationsFromSelections(
|
|||||||
lastVars.push(lastVar.variableDeclaration.node)
|
lastVars.push(lastVar.variableDeclaration.node)
|
||||||
}
|
}
|
||||||
|
|
||||||
return lastVars
|
if (lastVars.length < 2) {
|
||||||
|
return new Error('Not enough variables found')
|
||||||
|
}
|
||||||
|
|
||||||
|
const modifiedAst = booleanIntersectAstMod({
|
||||||
|
ast,
|
||||||
|
solids: lastVars,
|
||||||
|
})
|
||||||
|
await updateModelingState(modifiedAst, EXECUTION_TYPE_REAL, dependencies)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** returns all children of a given artifact, and sorts them DESC by start sourceRange
|
/** returns all children of a given artifact, and sorts them DESC by start sourceRange
|
||||||
@ -255,27 +271,25 @@ export function getLastVariable(
|
|||||||
|
|
||||||
export function booleanSubtractAstMod({
|
export function booleanSubtractAstMod({
|
||||||
ast,
|
ast,
|
||||||
solids,
|
targets,
|
||||||
tools,
|
tools,
|
||||||
}: {
|
}: {
|
||||||
ast: Node<Program>
|
ast: Node<Program>
|
||||||
solids: VariableDeclaration[]
|
targets: VariableDeclaration[]
|
||||||
tools: VariableDeclaration[]
|
tools: VariableDeclaration[]
|
||||||
}): Node<Program> {
|
}): Node<Program> {
|
||||||
const newAst = structuredClone(ast)
|
const newAst = structuredClone(ast)
|
||||||
const newVarName = findUniqueName(newAst, 'solid')
|
const newVarName = findUniqueName(newAst, 'solid')
|
||||||
const createArrExpr = (varDecs: VariableDeclaration[]) => {
|
const createArrExpr = (varDecs: VariableDeclaration[]) =>
|
||||||
const names = varDecs.map((varDec) =>
|
createArrayExpression(
|
||||||
createLocalName(varDec.declaration.id.name)
|
varDecs.map((varDec) => createLocalName(varDec.declaration.id.name))
|
||||||
)
|
)
|
||||||
return names.length === 1 ? names[0] : createArrayExpression(names)
|
const targetsArrayExpression = createArrExpr(targets)
|
||||||
}
|
|
||||||
const solidsArrayExpression = createArrExpr(solids)
|
|
||||||
const toolsArrayExpression = createArrExpr(tools)
|
const toolsArrayExpression = createArrExpr(tools)
|
||||||
|
|
||||||
const newVarDec = createVariableDeclaration(
|
const newVarDec = createVariableDeclaration(
|
||||||
newVarName,
|
newVarName,
|
||||||
createCallExpressionStdLibKw('subtract', solidsArrayExpression, [
|
createCallExpressionStdLibKw('subtract', targetsArrayExpression, [
|
||||||
createLabeledArg('tools', toolsArrayExpression),
|
createLabeledArg('tools', toolsArrayExpression),
|
||||||
])
|
])
|
||||||
)
|
)
|
||||||
|
@ -204,8 +204,8 @@ export type ModelingCommandSchema = {
|
|||||||
variableName: string
|
variableName: string
|
||||||
}
|
}
|
||||||
'Boolean Subtract': {
|
'Boolean Subtract': {
|
||||||
solids: Selections
|
target: Selections
|
||||||
tools: Selections
|
tool: Selections
|
||||||
}
|
}
|
||||||
'Boolean Union': {
|
'Boolean Union': {
|
||||||
solids: Selections
|
solids: Selections
|
||||||
@ -595,21 +595,23 @@ export const modelingMachineCommandConfig: StateMachineCommandSetConfig<
|
|||||||
icon: 'booleanSubtract',
|
icon: 'booleanSubtract',
|
||||||
needsReview: true,
|
needsReview: true,
|
||||||
args: {
|
args: {
|
||||||
solids: {
|
target: {
|
||||||
inputType: 'selection',
|
inputType: 'selection',
|
||||||
selectionTypes: ['path'],
|
selectionTypes: ['path'],
|
||||||
selectionFilter: ['object'],
|
selectionFilter: ['object'],
|
||||||
multiple: true,
|
multiple: false,
|
||||||
required: true,
|
required: true,
|
||||||
|
skip: true,
|
||||||
hidden: (context) => Boolean(context.argumentsToSubmit.nodeToEdit),
|
hidden: (context) => Boolean(context.argumentsToSubmit.nodeToEdit),
|
||||||
},
|
},
|
||||||
tools: {
|
tool: {
|
||||||
clearSelectionFirst: true,
|
clearSelectionFirst: true,
|
||||||
inputType: 'selection',
|
inputType: 'selection',
|
||||||
selectionTypes: ['path'],
|
selectionTypes: ['path'],
|
||||||
selectionFilter: ['object'],
|
selectionFilter: ['object'],
|
||||||
multiple: true,
|
multiple: false,
|
||||||
required: true,
|
required: true,
|
||||||
|
skip: false,
|
||||||
hidden: (context) => Boolean(context.argumentsToSubmit.nodeToEdit),
|
hidden: (context) => Boolean(context.argumentsToSubmit.nodeToEdit),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -688,35 +690,32 @@ export const modelingMachineCommandConfig: StateMachineCommandSetConfig<
|
|||||||
},
|
},
|
||||||
axis: {
|
axis: {
|
||||||
inputType: 'options',
|
inputType: 'options',
|
||||||
|
required: (commandContext) =>
|
||||||
|
['Axis'].includes(commandContext.argumentsToSubmit.mode as string),
|
||||||
options: [
|
options: [
|
||||||
{ name: 'X Axis', value: 'X' },
|
{ name: 'X Axis', value: 'X' },
|
||||||
{ name: 'Y Axis', value: 'Y' },
|
{ name: 'Y Axis', value: 'Y' },
|
||||||
{ name: 'Z Axis', value: 'Z' },
|
{ name: 'Z Axis', value: 'Z' },
|
||||||
],
|
],
|
||||||
required: (context) =>
|
hidden: false, // for consistency here, we can actually edit here since it's not a selection
|
||||||
['Axis'].includes(context.argumentsToSubmit.mode as string),
|
|
||||||
hidden: (context) =>
|
|
||||||
!['Axis'].includes(context.argumentsToSubmit.mode as string),
|
|
||||||
},
|
},
|
||||||
edge: {
|
edge: {
|
||||||
|
required: (commandContext) =>
|
||||||
|
['Edge'].includes(commandContext.argumentsToSubmit.mode as string),
|
||||||
inputType: 'selection',
|
inputType: 'selection',
|
||||||
selectionTypes: ['segment', 'sweepEdge'],
|
selectionTypes: ['segment', 'sweepEdge'],
|
||||||
multiple: false,
|
multiple: false,
|
||||||
required: (context) =>
|
hidden: (context) => Boolean(context.argumentsToSubmit.nodeToEdit),
|
||||||
['Edge'].includes(context.argumentsToSubmit.mode as string),
|
|
||||||
hidden: (context) =>
|
|
||||||
Boolean(context.argumentsToSubmit.nodeToEdit) ||
|
|
||||||
!['Edge'].includes(context.argumentsToSubmit.mode as string),
|
|
||||||
},
|
},
|
||||||
cylinder: {
|
cylinder: {
|
||||||
|
required: (commandContext) =>
|
||||||
|
['Cylinder'].includes(
|
||||||
|
commandContext.argumentsToSubmit.mode as string
|
||||||
|
),
|
||||||
inputType: 'selection',
|
inputType: 'selection',
|
||||||
selectionTypes: ['wall'],
|
selectionTypes: ['wall'],
|
||||||
multiple: false,
|
multiple: false,
|
||||||
required: (context) =>
|
hidden: (context) => Boolean(context.argumentsToSubmit.nodeToEdit),
|
||||||
['Cylinder'].includes(context.argumentsToSubmit.mode as string),
|
|
||||||
hidden: (context) =>
|
|
||||||
Boolean(context.argumentsToSubmit.nodeToEdit) ||
|
|
||||||
!['Cylinder'].includes(context.argumentsToSubmit.mode as string),
|
|
||||||
},
|
},
|
||||||
revolutions: {
|
revolutions: {
|
||||||
inputType: 'kcl',
|
inputType: 'kcl',
|
||||||
@ -731,30 +730,34 @@ export const modelingMachineCommandConfig: StateMachineCommandSetConfig<
|
|||||||
radius: {
|
radius: {
|
||||||
inputType: 'kcl',
|
inputType: 'kcl',
|
||||||
defaultValue: KCL_DEFAULT_LENGTH,
|
defaultValue: KCL_DEFAULT_LENGTH,
|
||||||
required: (context) =>
|
required: (commandContext) =>
|
||||||
!['Cylinder'].includes(context.argumentsToSubmit.mode as string),
|
!['Cylinder'].includes(
|
||||||
hidden: (context) =>
|
commandContext.argumentsToSubmit.mode as string
|
||||||
['Cylinder'].includes(context.argumentsToSubmit.mode as string),
|
),
|
||||||
},
|
},
|
||||||
length: {
|
length: {
|
||||||
inputType: 'kcl',
|
inputType: 'kcl',
|
||||||
defaultValue: KCL_DEFAULT_LENGTH,
|
defaultValue: KCL_DEFAULT_LENGTH,
|
||||||
required: (commandContext) =>
|
required: (commandContext) =>
|
||||||
['Axis'].includes(commandContext.argumentsToSubmit.mode as string),
|
['Axis'].includes(commandContext.argumentsToSubmit.mode as string),
|
||||||
// No need for hidden here, as it works with all modes
|
|
||||||
},
|
},
|
||||||
ccw: {
|
ccw: {
|
||||||
inputType: 'options',
|
inputType: 'options',
|
||||||
required: false,
|
skip: true,
|
||||||
|
required: true,
|
||||||
|
defaultValue: false,
|
||||||
|
valueSummary: (value) => String(value),
|
||||||
displayName: 'CounterClockWise',
|
displayName: 'CounterClockWise',
|
||||||
options: [
|
options: (commandContext) => [
|
||||||
{
|
{
|
||||||
name: 'False',
|
name: 'False',
|
||||||
value: false,
|
value: false,
|
||||||
|
isCurrent: !Boolean(commandContext.argumentsToSubmit.ccw),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'True',
|
name: 'True',
|
||||||
value: true,
|
value: true,
|
||||||
|
isCurrent: Boolean(commandContext.argumentsToSubmit.ccw),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
@ -222,6 +222,3 @@ export const CODE_QUERY_PARAM = 'code'
|
|||||||
/** A query parameter to skip the sign-on view if unnecessary. */
|
/** A query parameter to skip the sign-on view if unnecessary. */
|
||||||
export const IMMEDIATE_SIGN_IN_IF_NECESSARY_QUERY_PARAM =
|
export const IMMEDIATE_SIGN_IN_IF_NECESSARY_QUERY_PARAM =
|
||||||
'immediate-sign-in-if-necessary'
|
'immediate-sign-in-if-necessary'
|
||||||
|
|
||||||
// Only used by the desktop app
|
|
||||||
export const OAUTH2_DEVICE_CLIENT_ID = '2af127fb-e14e-400a-9c57-a9ed08d1a5b7'
|
|
||||||
|
@ -96,6 +96,34 @@ export default class RustContext {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Execute an additional program using the cache. */
|
||||||
|
async executeAdditional(
|
||||||
|
node: Node<Program>,
|
||||||
|
settings: DeepPartial<Configuration>,
|
||||||
|
path?: string
|
||||||
|
): Promise<ExecState> {
|
||||||
|
const instance = await this._checkInstance()
|
||||||
|
|
||||||
|
try {
|
||||||
|
const result = await instance.executeAdditional(
|
||||||
|
JSON.stringify(node),
|
||||||
|
path,
|
||||||
|
JSON.stringify(settings)
|
||||||
|
)
|
||||||
|
// Set the default planes, safe to call after execute.
|
||||||
|
const outcome = execStateFromRust(result)
|
||||||
|
|
||||||
|
this._defaultPlanes = outcome.defaultPlanes
|
||||||
|
|
||||||
|
// Return the result.
|
||||||
|
return outcome
|
||||||
|
} catch (e: any) {
|
||||||
|
const err = errFromErrWithOutputs(e)
|
||||||
|
this._defaultPlanes = err.defaultPlanes
|
||||||
|
return Promise.reject(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** Execute a program with in mock mode. */
|
/** Execute a program with in mock mode. */
|
||||||
async executeMock(
|
async executeMock(
|
||||||
node: Node<Program>,
|
node: Node<Program>,
|
||||||
|
@ -7,7 +7,7 @@ import {
|
|||||||
} from '@src/env'
|
} from '@src/env'
|
||||||
import { assign, fromPromise, setup } from 'xstate'
|
import { assign, fromPromise, setup } from 'xstate'
|
||||||
|
|
||||||
import { COOKIE_NAME, OAUTH2_DEVICE_CLIENT_ID } from '@src/lib/constants'
|
import { COOKIE_NAME } from '@src/lib/constants'
|
||||||
import {
|
import {
|
||||||
getUser as getUserDesktop,
|
getUser as getUserDesktop,
|
||||||
readTokenFile,
|
readTokenFile,
|
||||||
@ -254,32 +254,8 @@ async function getAndSyncStoredToken(input: {
|
|||||||
async function logout() {
|
async function logout() {
|
||||||
localStorage.removeItem(TOKEN_PERSIST_KEY)
|
localStorage.removeItem(TOKEN_PERSIST_KEY)
|
||||||
if (isDesktop()) {
|
if (isDesktop()) {
|
||||||
try {
|
await writeTokenFile('')
|
||||||
let token = await readTokenFile()
|
return Promise.resolve(null)
|
||||||
|
|
||||||
if (token) {
|
|
||||||
try {
|
|
||||||
await fetch(withBaseUrl('/oauth2/token/revoke'), {
|
|
||||||
method: 'POST',
|
|
||||||
credentials: 'include',
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/x-www-form-urlencoded',
|
|
||||||
},
|
|
||||||
body: new URLSearchParams({
|
|
||||||
token: token,
|
|
||||||
client_id: OAUTH2_DEVICE_CLIENT_ID,
|
|
||||||
}).toString(),
|
|
||||||
})
|
|
||||||
} catch (e) {
|
|
||||||
console.error('Error revoking token:', e)
|
|
||||||
}
|
|
||||||
|
|
||||||
await writeTokenFile('')
|
|
||||||
return Promise.resolve(null)
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
console.error('Error reading token during logout (ignoring):', e)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return fetch(withBaseUrl('/logout'), {
|
return fetch(withBaseUrl('/logout'), {
|
||||||
|
@ -3570,17 +3570,17 @@ export const modelingMachine = setup({
|
|||||||
return Promise.reject(new Error(NO_INPUT_PROVIDED_MESSAGE))
|
return Promise.reject(new Error(NO_INPUT_PROVIDED_MESSAGE))
|
||||||
}
|
}
|
||||||
|
|
||||||
const { solids, tools } = input
|
const { target, tool } = input
|
||||||
if (
|
if (
|
||||||
!solids.graphSelections.some((selection) => selection.artifact) ||
|
!target.graphSelections[0].artifact ||
|
||||||
!tools.graphSelections.some((selection) => selection.artifact)
|
!tool.graphSelections[0].artifact
|
||||||
) {
|
) {
|
||||||
return Promise.reject(new Error('No artifact in selections found'))
|
return Promise.reject(new Error('No artifact in selections found'))
|
||||||
}
|
}
|
||||||
|
|
||||||
const result = await applySubtractFromTargetOperatorSelections(
|
await applySubtractFromTargetOperatorSelections(
|
||||||
solids,
|
target.graphSelections[0],
|
||||||
tools,
|
tool.graphSelections[0],
|
||||||
{
|
{
|
||||||
kclManager,
|
kclManager,
|
||||||
codeManager,
|
codeManager,
|
||||||
@ -3588,9 +3588,6 @@ export const modelingMachine = setup({
|
|||||||
editorManager,
|
editorManager,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
if (err(result)) {
|
|
||||||
return Promise.reject(result)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
boolUnionAstMod: fromPromise(
|
boolUnionAstMod: fromPromise(
|
||||||
@ -3608,15 +3605,12 @@ export const modelingMachine = setup({
|
|||||||
return Promise.reject(new Error('No artifact in selections found'))
|
return Promise.reject(new Error('No artifact in selections found'))
|
||||||
}
|
}
|
||||||
|
|
||||||
const result = await applyUnionFromTargetOperatorSelections(solids, {
|
await applyUnionFromTargetOperatorSelections(solids, {
|
||||||
kclManager,
|
kclManager,
|
||||||
codeManager,
|
codeManager,
|
||||||
engineCommandManager,
|
engineCommandManager,
|
||||||
editorManager,
|
editorManager,
|
||||||
})
|
})
|
||||||
if (err(result)) {
|
|
||||||
return Promise.reject(result)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
boolIntersectAstMod: fromPromise(
|
boolIntersectAstMod: fromPromise(
|
||||||
@ -3634,18 +3628,12 @@ export const modelingMachine = setup({
|
|||||||
return Promise.reject(new Error('No artifact in selections found'))
|
return Promise.reject(new Error('No artifact in selections found'))
|
||||||
}
|
}
|
||||||
|
|
||||||
const result = await applyIntersectFromTargetOperatorSelections(
|
await applyIntersectFromTargetOperatorSelections(solids, {
|
||||||
solids,
|
kclManager,
|
||||||
{
|
codeManager,
|
||||||
kclManager,
|
engineCommandManager,
|
||||||
codeManager,
|
editorManager,
|
||||||
engineCommandManager,
|
})
|
||||||
editorManager,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
if (err(result)) {
|
|
||||||
return Promise.reject(result)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
|
|
||||||
|
24
src/main.ts
24
src/main.ts
@ -15,7 +15,6 @@ import {
|
|||||||
dialog,
|
dialog,
|
||||||
ipcMain,
|
ipcMain,
|
||||||
nativeTheme,
|
nativeTheme,
|
||||||
session,
|
|
||||||
screen,
|
screen,
|
||||||
shell,
|
shell,
|
||||||
systemPreferences,
|
systemPreferences,
|
||||||
@ -29,10 +28,7 @@ import {
|
|||||||
parseCLIArgs,
|
parseCLIArgs,
|
||||||
} from '@src/commandLineArgs'
|
} from '@src/commandLineArgs'
|
||||||
import { initPromiseNode } from '@src/lang/wasmUtilsNode'
|
import { initPromiseNode } from '@src/lang/wasmUtilsNode'
|
||||||
import {
|
import { ZOO_STUDIO_PROTOCOL } from '@src/lib/constants'
|
||||||
ZOO_STUDIO_PROTOCOL,
|
|
||||||
OAUTH2_DEVICE_CLIENT_ID,
|
|
||||||
} from '@src/lib/constants'
|
|
||||||
import getCurrentProjectFile from '@src/lib/getCurrentProjectFile'
|
import getCurrentProjectFile from '@src/lib/getCurrentProjectFile'
|
||||||
import { reportRejection } from '@src/lib/trap'
|
import { reportRejection } from '@src/lib/trap'
|
||||||
import {
|
import {
|
||||||
@ -297,21 +293,7 @@ app.on('window-all-closed', () => {
|
|||||||
// This method will be called when Electron has finished
|
// This method will be called when Electron has finished
|
||||||
// initialization and is ready to create browser windows.
|
// initialization and is ready to create browser windows.
|
||||||
// Some APIs can only be used after this event occurs.
|
// Some APIs can only be used after this event occurs.
|
||||||
app.on('ready', async (event, data) => {
|
app.on('ready', (event, data) => {
|
||||||
try {
|
|
||||||
await session.defaultSession.cookies.set({
|
|
||||||
url: 'https://api.dev.zoo.dev',
|
|
||||||
name: 'preview-pr-2718',
|
|
||||||
value: 'always',
|
|
||||||
domain: '.dev.zoo.dev',
|
|
||||||
path: '/',
|
|
||||||
secure: true,
|
|
||||||
sameSite: 'no_restriction',
|
|
||||||
})
|
|
||||||
console.log('[preview] cookie seeded')
|
|
||||||
} catch (err) {
|
|
||||||
console.error('[preview] failed to set cookie', err)
|
|
||||||
}
|
|
||||||
// Avoid potentially 2 ready fires
|
// Avoid potentially 2 ready fires
|
||||||
if (mainWindow) return
|
if (mainWindow) return
|
||||||
// Create the mainWindow
|
// Create the mainWindow
|
||||||
@ -420,7 +402,7 @@ ipcMain.handle('startDeviceFlow', async (_, host: string) => {
|
|||||||
// We can hardcode the client ID.
|
// We can hardcode the client ID.
|
||||||
// This value is safe to be embedded in version control.
|
// This value is safe to be embedded in version control.
|
||||||
// This is the client ID of the KittyCAD app.
|
// This is the client ID of the KittyCAD app.
|
||||||
client_id: OAUTH2_DEVICE_CLIENT_ID,
|
client_id: '2af127fb-e14e-400a-9c57-a9ed08d1a5b7',
|
||||||
token_endpoint_auth_method: 'none',
|
token_endpoint_auth_method: 'none',
|
||||||
})
|
})
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user