Compare commits
8 Commits
v1.0.7
...
api-deux-p
Author | SHA1 | Date | |
---|---|---|---|
48beef4ae5 | |||
f49cf8281c | |||
7de27c648f | |||
344fb6f84d | |||
df808b3e58 | |||
e1ab6bbc48 | |||
0a1f35b89a | |||
78278d6889 |
2
.github/workflows/build-apps.yml
vendored
2
.github/workflows/build-apps.yml
vendored
@ -362,7 +362,7 @@ jobs:
|
||||
|
||||
- name: Authenticate to Google Cloud
|
||||
if: ${{ env.IS_STAGING == 'true' }}
|
||||
uses: 'google-github-actions/auth@v2.1.8'
|
||||
uses: 'google-github-actions/auth@v2.1.10'
|
||||
with:
|
||||
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 }}
|
||||
cp -r build/* releases/language-server/${{ env.TAG }}
|
||||
- name: "Authenticate to Google Cloud"
|
||||
uses: "google-github-actions/auth@v2.1.8"
|
||||
uses: "google-github-actions/auth@v2.1.10"
|
||||
with:
|
||||
credentials_json: "${{ secrets.GOOGLE_CLOUD_DL_SA }}"
|
||||
- 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
|
||||
|
||||
- name: Authenticate to Google Cloud
|
||||
uses: 'google-github-actions/auth@v2.1.8'
|
||||
uses: 'google-github-actions/auth@v2.1.10'
|
||||
with:
|
||||
credentials_json: '${{ secrets.GOOGLE_CLOUD_DL_SA }}'
|
||||
|
||||
|
3
Makefile
3
Makefile
@ -62,7 +62,10 @@ else
|
||||
endif
|
||||
|
||||
public/kcl-samples/manifest.json: $(KCL_SOURCES)
|
||||
ifndef WINDOWS
|
||||
cd rust/kcl-lib && EXPECTORATE=overwrite cargo test generate_manifest
|
||||
@ touch $@
|
||||
endif
|
||||
|
||||
.vite/build/main.js: $(REACT_SOURCES) $(TYPESCRIPT_SOURCES) $(VITE_SOURCES)
|
||||
npm run tronb:vite:dev
|
||||
|
@ -12,7 +12,7 @@ test.describe('Point and click for boolean workflows', () => {
|
||||
},
|
||||
{
|
||||
name: 'subtract',
|
||||
code: 'subtract([extrude001], tools = [extrude006])',
|
||||
code: 'subtract(extrude001, tools = extrude006)',
|
||||
},
|
||||
{
|
||||
name: 'intersect',
|
||||
@ -81,6 +81,8 @@ test.describe('Point and click for boolean workflows', () => {
|
||||
if (operationName !== 'subtract') {
|
||||
// should down shift key to select multiple objects
|
||||
await page.keyboard.down('Shift')
|
||||
} else {
|
||||
await cmdBar.progressCmdBar()
|
||||
}
|
||||
|
||||
// Select second object
|
||||
@ -103,8 +105,8 @@ test.describe('Point and click for boolean workflows', () => {
|
||||
await cmdBar.expectState({
|
||||
stage: 'review',
|
||||
headerArguments: {
|
||||
Tool: '1 path',
|
||||
Target: '1 path',
|
||||
Solids: '1 path',
|
||||
Tools: '1 path',
|
||||
},
|
||||
commandName,
|
||||
})
|
||||
|
@ -288,7 +288,7 @@ a1 = startSketchOn(offsetPlane(XY, offset = 10))
|
||||
// error text on hover
|
||||
await page.hover('.cm-lint-marker-info')
|
||||
await expect(
|
||||
page.getByText('Identifiers must be lowerCamelCase').first()
|
||||
page.getByText('Identifiers should be lowerCamelCase').first()
|
||||
).toBeVisible()
|
||||
|
||||
await page.locator('#code-pane button:first-child').click()
|
||||
@ -314,7 +314,7 @@ sketch_001 = startSketchOn(XY)
|
||||
// error text on hover
|
||||
await page.hover('.cm-lint-marker-info')
|
||||
await expect(
|
||||
page.getByText('Identifiers must be lowerCamelCase').first()
|
||||
page.getByText('Identifiers should be lowerCamelCase').first()
|
||||
).toBeVisible()
|
||||
})
|
||||
|
||||
@ -511,7 +511,7 @@ sketch_001 = startSketchOn(XY)
|
||||
// error text on hover
|
||||
await page.hover('.cm-lint-marker-info')
|
||||
await expect(
|
||||
page.getByText('Identifiers must be lowerCamelCase').first()
|
||||
page.getByText('Identifiers should be lowerCamelCase').first()
|
||||
).toBeVisible()
|
||||
|
||||
// focus the editor
|
||||
@ -539,7 +539,7 @@ sketch_001 = startSketchOn(XY)
|
||||
// error text on hover
|
||||
await page.hover('.cm-lint-marker-info')
|
||||
await expect(
|
||||
page.getByText('Identifiers must be lowerCamelCase').first()
|
||||
page.getByText('Identifiers should be lowerCamelCase').first()
|
||||
).toBeVisible()
|
||||
})
|
||||
|
||||
@ -681,7 +681,7 @@ a1 = startSketchOn(offsetPlane(XY, offset = 10))
|
||||
// error text on hover
|
||||
await page.hover('.cm-lint-marker-info')
|
||||
await expect(
|
||||
page.getByText('Identifiers must be lowerCamelCase').first()
|
||||
page.getByText('Identifiers should be lowerCamelCase').first()
|
||||
).toBeVisible()
|
||||
|
||||
// select the line that's causing the error and delete it
|
||||
|
@ -7,6 +7,7 @@ import type { SceneFixture } from '@e2e/playwright/fixtures/sceneFixture'
|
||||
import type { ToolbarFixture } from '@e2e/playwright/fixtures/toolbarFixture'
|
||||
import { expect, test } from '@e2e/playwright/zoo-test'
|
||||
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
|
||||
|
||||
@ -1141,6 +1142,20 @@ 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 ({
|
||||
context,
|
||||
page,
|
||||
@ -1150,30 +1165,14 @@ openSketch = startSketchOn(XY)
|
||||
toolbar,
|
||||
cmdBar,
|
||||
}) => {
|
||||
// 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 expectedOutput = `helix001 = helix( axis = X, radius = 5, length = 5, revolutions = 1, angleStart = 270,)`
|
||||
const expectedLine = `axis=X,`
|
||||
|
||||
await homePage.goToModelingScene()
|
||||
await scene.connectionEstablished()
|
||||
|
||||
await test.step(`Go through the command bar flow`, async () => {
|
||||
await toolbar.helixButton.click()
|
||||
await cmdBar.expectState({
|
||||
stage: 'arguments',
|
||||
currentArgKey: 'mode',
|
||||
currentArgValue: '',
|
||||
headerArguments: {
|
||||
Mode: '',
|
||||
AngleStart: '',
|
||||
Revolutions: '',
|
||||
Radius: '',
|
||||
CounterClockWise: '',
|
||||
},
|
||||
highlightedHeaderArg: 'mode',
|
||||
commandName: 'Helix',
|
||||
})
|
||||
await cmdBar.expectState(initialCmdBarStateHelix)
|
||||
await cmdBar.progressCmdBar()
|
||||
await expect.poll(() => page.getByText('Axis').count()).toBe(6)
|
||||
await cmdBar.progressCmdBar()
|
||||
@ -1190,7 +1189,6 @@ openSketch = startSketchOn(XY)
|
||||
AngleStart: '',
|
||||
Length: '',
|
||||
Radius: '',
|
||||
CounterClockWise: '',
|
||||
},
|
||||
commandName: 'Helix',
|
||||
})
|
||||
@ -1207,11 +1205,10 @@ openSketch = startSketchOn(XY)
|
||||
Revolutions: '1',
|
||||
Length: '5',
|
||||
Radius: '5',
|
||||
CounterClockWise: '',
|
||||
},
|
||||
commandName: 'Helix',
|
||||
})
|
||||
await cmdBar.progressCmdBar()
|
||||
await cmdBar.submit()
|
||||
})
|
||||
|
||||
await test.step(`Confirm code is added to the editor, scene has changed`, async () => {
|
||||
@ -1221,8 +1218,6 @@ openSketch = startSketchOn(XY)
|
||||
activeLines: [expectedLine],
|
||||
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 () => {
|
||||
@ -1234,21 +1229,18 @@ openSketch = startSketchOn(XY)
|
||||
await cmdBar.expectState({
|
||||
commandName: 'Helix',
|
||||
stage: 'arguments',
|
||||
currentArgKey: 'CounterClockWise',
|
||||
currentArgValue: '',
|
||||
currentArgKey: 'length',
|
||||
currentArgValue: '5',
|
||||
headerArguments: {
|
||||
Axis: 'X',
|
||||
AngleStart: '270',
|
||||
Revolutions: '1',
|
||||
Radius: '5',
|
||||
Length: initialInput,
|
||||
CounterClockWise: '',
|
||||
},
|
||||
highlightedHeaderArg: 'CounterClockWise',
|
||||
highlightedHeaderArg: 'length',
|
||||
})
|
||||
await page.keyboard.press('Shift+Backspace')
|
||||
await expect(cmdBar.currentArgumentInput).toBeVisible()
|
||||
await cmdBar.currentArgumentInput.locator('.cm-content').fill(newInput)
|
||||
await page.keyboard.insertText(newInput)
|
||||
await cmdBar.progressCmdBar()
|
||||
await cmdBar.expectState({
|
||||
stage: 'review',
|
||||
@ -1258,11 +1250,10 @@ openSketch = startSketchOn(XY)
|
||||
Revolutions: '1',
|
||||
Radius: '5',
|
||||
Length: newInput,
|
||||
CounterClockWise: '',
|
||||
},
|
||||
commandName: 'Helix',
|
||||
})
|
||||
await cmdBar.progressCmdBar()
|
||||
await cmdBar.submit()
|
||||
await toolbar.closeFeatureTreePane()
|
||||
await editor.openPane()
|
||||
await editor.expectEditor.toContain('length = ' + newInput)
|
||||
@ -1273,174 +1264,238 @@ openSketch = startSketchOn(XY)
|
||||
const operationButton = await toolbar.getFeatureTreeOperation('Helix', 0)
|
||||
await operationButton.click({ button: 'left' })
|
||||
await page.keyboard.press('Delete')
|
||||
// Red plane is back
|
||||
await scene.expectPixelColor([96, 52, 52], testPoint, 15)
|
||||
await scene.settled(cmdBar)
|
||||
await editor.expectEditor.not.toContain('helix')
|
||||
await expect(
|
||||
await toolbar.getFeatureTreeOperation('Helix', 0)
|
||||
).not.toBeVisible()
|
||||
})
|
||||
})
|
||||
|
||||
const helixCases = [
|
||||
{
|
||||
selectionType: 'segment',
|
||||
testPoint: { x: 513, y: 221 },
|
||||
expectedOutput: `helix001 = helix( axis = seg01, radius = 1, revolutions = 20, angleStart = 0, ccw = false,)`,
|
||||
expectedEditedOutput: `helix001 = helix( axis = seg01, radius = 5, revolutions = 20, angleStart = 0, ccw = false,)`,
|
||||
},
|
||||
{
|
||||
selectionType: 'sweepEdge',
|
||||
testPoint: { x: 564, y: 364 },
|
||||
expectedOutput: `helix001 = helix( axis = getOppositeEdge(seg01), radius = 1, revolutions = 20, angleStart = 0, ccw = false,)`,
|
||||
expectedEditedOutput: `helix001 = helix( axis = getOppositeEdge(seg01), radius = 5, revolutions = 20, angleStart = 0, ccw = false,)`,
|
||||
},
|
||||
]
|
||||
helixCases.map(
|
||||
({ selectionType, testPoint, expectedOutput, expectedEditedOutput }) => {
|
||||
test(`Helix point-and-click around ${selectionType}`, async ({
|
||||
context,
|
||||
page,
|
||||
homePage,
|
||||
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)`
|
||||
test(`Helix point-and-click around segment`, 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()`
|
||||
await context.addInitScript((initialCode) => {
|
||||
localStorage.setItem('persistCode', initialCode)
|
||||
}, initialCode)
|
||||
await page.setBodyDimensions({ width: 1000, height: 500 })
|
||||
await homePage.goToModelingScene()
|
||||
await scene.settled(cmdBar)
|
||||
|
||||
// One dumb hardcoded screen pixel value
|
||||
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({
|
||||
stage: 'arguments',
|
||||
currentArgKey: 'mode',
|
||||
currentArgValue: '',
|
||||
headerArguments: {
|
||||
AngleStart: '',
|
||||
Mode: '',
|
||||
CounterClockWise: '',
|
||||
Radius: '',
|
||||
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 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 editor.selectText('yLine(length = 100)')
|
||||
await cmdBar.progressCmdBar()
|
||||
await page.keyboard.insertText('1')
|
||||
await cmdBar.progressCmdBar()
|
||||
await page.keyboard.insertText('2')
|
||||
await cmdBar.progressCmdBar()
|
||||
await page.keyboard.insertText('3')
|
||||
await cmdBar.progressCmdBar()
|
||||
await cmdBar.expectState({
|
||||
stage: 'review',
|
||||
headerArguments: {
|
||||
Mode: 'Edge',
|
||||
Edge: `1 segment`,
|
||||
AngleStart: '2',
|
||||
Revolutions: '1',
|
||||
Radius: '3',
|
||||
},
|
||||
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 = 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 ({
|
||||
context,
|
||||
@ -1470,26 +1525,12 @@ extrude001 = extrude(profile001, length = 100)
|
||||
// One dumb hardcoded screen pixel value
|
||||
const testPoint = { x: 620, y: 257 }
|
||||
const [clickOnWall] = scene.makeMouseHelpers(testPoint.x, testPoint.y)
|
||||
const expectedOutput = `helix001 = helix( cylinder = extrude001, revolutions = 1, angleStart = 360, ccw = false,)`
|
||||
const expectedLine = `cylinder = extrude001,`
|
||||
const expectedEditedOutput = `helix001 = helix( cylinder = extrude001, revolutions = 1, angleStart = 360, ccw = true,)`
|
||||
const expectedOutput = `helix001 = helix(cylinder = extrude001, revolutions = 1, angleStart = 360)`
|
||||
const expectedEditedOutput = `helix001 = helix(cylinder = extrude001, revolutions = 1, angleStart = 10)`
|
||||
|
||||
await test.step(`Go through the command bar flow`, async () => {
|
||||
await toolbar.helixButton.click()
|
||||
await cmdBar.expectState({
|
||||
stage: 'arguments',
|
||||
currentArgKey: 'mode',
|
||||
currentArgValue: '',
|
||||
headerArguments: {
|
||||
Mode: '',
|
||||
AngleStart: '',
|
||||
Revolutions: '',
|
||||
Radius: '',
|
||||
CounterClockWise: '',
|
||||
},
|
||||
highlightedHeaderArg: 'mode',
|
||||
commandName: 'Helix',
|
||||
})
|
||||
await cmdBar.expectState(initialCmdBarStateHelix)
|
||||
await cmdBar.selectOption({ name: 'Cylinder' }).click()
|
||||
await cmdBar.expectState({
|
||||
stage: 'arguments',
|
||||
@ -1500,7 +1541,6 @@ extrude001 = extrude(profile001, length = 100)
|
||||
Cylinder: '',
|
||||
AngleStart: '',
|
||||
Revolutions: '',
|
||||
CounterClockWise: '',
|
||||
},
|
||||
highlightedHeaderArg: 'cylinder',
|
||||
commandName: 'Helix',
|
||||
@ -1516,18 +1556,17 @@ extrude001 = extrude(profile001, length = 100)
|
||||
Cylinder: '1 face',
|
||||
AngleStart: '360',
|
||||
Revolutions: '1',
|
||||
CounterClockWise: '',
|
||||
},
|
||||
commandName: 'Helix',
|
||||
})
|
||||
await cmdBar.progressCmdBar()
|
||||
await cmdBar.submit()
|
||||
})
|
||||
|
||||
await test.step(`Confirm code is added to the editor, scene has changed`, async () => {
|
||||
await editor.expectEditor.toContain(expectedOutput)
|
||||
await editor.expectState({
|
||||
diagnostics: [],
|
||||
activeLines: [expectedLine],
|
||||
activeLines: [expectedOutput],
|
||||
highlightedCode: '',
|
||||
})
|
||||
})
|
||||
@ -1539,22 +1578,21 @@ extrude001 = extrude(profile001, length = 100)
|
||||
await cmdBar.expectState({
|
||||
commandName: 'Helix',
|
||||
stage: 'arguments',
|
||||
currentArgKey: 'CounterClockWise',
|
||||
currentArgValue: '',
|
||||
currentArgKey: 'angleStart',
|
||||
currentArgValue: '360',
|
||||
headerArguments: {
|
||||
AngleStart: '360',
|
||||
Revolutions: '1',
|
||||
CounterClockWise: '',
|
||||
},
|
||||
highlightedHeaderArg: 'CounterClockWise',
|
||||
highlightedHeaderArg: 'angleStart',
|
||||
})
|
||||
await cmdBar.selectOption({ name: 'True' }).click()
|
||||
await page.keyboard.insertText('10')
|
||||
await cmdBar.progressCmdBar()
|
||||
await cmdBar.expectState({
|
||||
stage: 'review',
|
||||
headerArguments: {
|
||||
AngleStart: '360',
|
||||
AngleStart: '10',
|
||||
Revolutions: '1',
|
||||
CounterClockWise: 'true',
|
||||
},
|
||||
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";
|
||||
ELECTRON_OVERRIDE_DIST_PATH =
|
||||
if pkgs.stdenv.isDarwin
|
||||
then "${pkgs.electron}/Applications/Electron.app/Contents/MacOS/"
|
||||
then "${pkgs.electron}/Applications"
|
||||
else "${pkgs.electron}/bin";
|
||||
PLAYWRIGHT_SKIP_VALIDATE_HOST_REQUIREMENTS = true;
|
||||
PLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH = "${pkgs.playwright-driver.browsers}/chromium-1091/chrome-linux/chrome";
|
||||
|
20
rust/Cargo.lock
generated
20
rust/Cargo.lock
generated
@ -1814,7 +1814,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "kcl-bumper"
|
||||
version = "0.1.82"
|
||||
version = "0.1.83"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"clap",
|
||||
@ -1825,7 +1825,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "kcl-derive-docs"
|
||||
version = "0.1.82"
|
||||
version = "0.1.83"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@ -1834,7 +1834,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "kcl-directory-test-macro"
|
||||
version = "0.1.82"
|
||||
version = "0.1.83"
|
||||
dependencies = [
|
||||
"convert_case",
|
||||
"proc-macro2",
|
||||
@ -1844,7 +1844,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "kcl-language-server"
|
||||
version = "0.2.82"
|
||||
version = "0.2.83"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"clap",
|
||||
@ -1865,7 +1865,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "kcl-language-server-release"
|
||||
version = "0.1.82"
|
||||
version = "0.1.83"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"clap",
|
||||
@ -1885,7 +1885,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "kcl-lib"
|
||||
version = "0.2.82"
|
||||
version = "0.2.83"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"approx 0.5.1",
|
||||
@ -1962,7 +1962,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "kcl-python-bindings"
|
||||
version = "0.3.82"
|
||||
version = "0.3.83"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"kcl-lib",
|
||||
@ -1977,7 +1977,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "kcl-test-server"
|
||||
version = "0.1.82"
|
||||
version = "0.1.83"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"hyper 0.14.32",
|
||||
@ -1990,7 +1990,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "kcl-to-core"
|
||||
version = "0.1.82"
|
||||
version = "0.1.83"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
@ -2004,7 +2004,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "kcl-wasm-lib"
|
||||
version = "0.1.82"
|
||||
version = "0.1.83"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bson",
|
||||
|
@ -1,7 +1,7 @@
|
||||
|
||||
[package]
|
||||
name = "kcl-bumper"
|
||||
version = "0.1.82"
|
||||
version = "0.1.83"
|
||||
edition = "2021"
|
||||
repository = "https://github.com/KittyCAD/modeling-api"
|
||||
rust-version = "1.76"
|
||||
|
@ -1,7 +1,7 @@
|
||||
[package]
|
||||
name = "kcl-derive-docs"
|
||||
description = "A tool for generating documentation from Rust derive macros"
|
||||
version = "0.1.82"
|
||||
version = "0.1.83"
|
||||
edition = "2021"
|
||||
license = "MIT"
|
||||
repository = "https://github.com/KittyCAD/modeling-app"
|
||||
|
@ -1,7 +1,7 @@
|
||||
[package]
|
||||
name = "kcl-directory-test-macro"
|
||||
description = "A tool for generating tests from a directory of kcl files"
|
||||
version = "0.1.82"
|
||||
version = "0.1.83"
|
||||
edition = "2021"
|
||||
license = "MIT"
|
||||
repository = "https://github.com/KittyCAD/modeling-app"
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "kcl-language-server-release"
|
||||
version = "0.1.82"
|
||||
version = "0.1.83"
|
||||
edition = "2021"
|
||||
authors = ["KittyCAD Inc <kcl@kittycad.io>"]
|
||||
publish = false
|
||||
|
@ -2,7 +2,7 @@
|
||||
name = "kcl-language-server"
|
||||
description = "A language server for KCL."
|
||||
authors = ["KittyCAD Inc <kcl@kittycad.io>"]
|
||||
version = "0.2.82"
|
||||
version = "0.2.83"
|
||||
edition = "2021"
|
||||
license = "MIT"
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
@ -1,7 +1,7 @@
|
||||
[package]
|
||||
name = "kcl-lib"
|
||||
description = "KittyCAD Language implementation and tools"
|
||||
version = "0.2.82"
|
||||
version = "0.2.83"
|
||||
edition = "2021"
|
||||
license = "MIT"
|
||||
repository = "https://github.com/KittyCAD/modeling-app"
|
||||
|
@ -11,10 +11,10 @@ use crate::{
|
||||
|
||||
def_finding!(
|
||||
Z0001,
|
||||
"Identifiers must be lowerCamelCase",
|
||||
"Identifiers should be lowerCamelCase",
|
||||
"\
|
||||
By convention, variable names are lowerCamelCase, not snake_case, kebab-case,
|
||||
nor CammelCase. 🐪
|
||||
nor upper CamelCase (aka PascalCase). 🐪
|
||||
|
||||
For instance, a good identifier for the variable representing 'box height'
|
||||
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[0].message,
|
||||
"Identifiers must be lowerCamelCase"
|
||||
"Identifiers should be lowerCamelCase"
|
||||
);
|
||||
} else {
|
||||
panic!("Expected full diagnostics");
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "kcl-python-bindings"
|
||||
version = "0.3.82"
|
||||
version = "0.3.83"
|
||||
edition = "2021"
|
||||
repository = "https://github.com/kittycad/modeling-app"
|
||||
exclude = ["tests/*", "files/*", "venv/*"]
|
||||
|
@ -1,7 +1,7 @@
|
||||
[package]
|
||||
name = "kcl-test-server"
|
||||
description = "A test server for KCL"
|
||||
version = "0.1.82"
|
||||
version = "0.1.83"
|
||||
edition = "2021"
|
||||
license = "MIT"
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
[package]
|
||||
name = "kcl-to-core"
|
||||
description = "Utility methods to convert kcl to engine core executable tests"
|
||||
version = "0.1.82"
|
||||
version = "0.1.83"
|
||||
edition = "2021"
|
||||
license = "MIT"
|
||||
repository = "https://github.com/KittyCAD/modeling-app"
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "kcl-wasm-lib"
|
||||
version = "0.1.82"
|
||||
version = "0.1.83"
|
||||
edition = "2021"
|
||||
repository = "https://github.com/KittyCAD/modeling-app"
|
||||
rust-version = "1.83"
|
||||
|
@ -590,7 +590,7 @@ export function addHelix({
|
||||
angleStart: Expr
|
||||
radius?: Expr
|
||||
length?: Expr
|
||||
ccw: boolean
|
||||
ccw?: boolean
|
||||
insertIndex?: number
|
||||
variableName?: string
|
||||
}): { modifiedAst: Node<Program>; pathToNode: PathToNode } {
|
||||
@ -610,6 +610,9 @@ export function addHelix({
|
||||
)
|
||||
}
|
||||
|
||||
// Extra labeled args expressions
|
||||
const ccwExpr = ccw ? [createLabeledArg('ccw', createLiteral(ccw))] : []
|
||||
|
||||
const variable = createVariableDeclaration(
|
||||
name,
|
||||
createCallExpressionStdLibKw(
|
||||
@ -619,7 +622,7 @@ export function addHelix({
|
||||
...modeArgs,
|
||||
createLabeledArg('revolutions', revolutions),
|
||||
createLabeledArg('angleStart', angleStart),
|
||||
createLabeledArg('ccw', createLiteral(ccw)),
|
||||
...ccwExpr,
|
||||
]
|
||||
)
|
||||
)
|
||||
|
@ -23,13 +23,13 @@ import type {
|
||||
VariableDeclaration,
|
||||
} from '@src/lang/wasm'
|
||||
import { EXECUTION_TYPE_REAL } from '@src/lib/constants'
|
||||
import type { Selection, Selections } from '@src/lib/selections'
|
||||
import type { Selections } from '@src/lib/selections'
|
||||
import { err } from '@src/lib/trap'
|
||||
import { isArray } from '@src/lib/utils'
|
||||
|
||||
export async function applySubtractFromTargetOperatorSelections(
|
||||
target: Selection,
|
||||
tool: Selection,
|
||||
solids: Selections,
|
||||
tools: Selections,
|
||||
dependencies: {
|
||||
kclManager: KclManager
|
||||
engineCommandManager: EngineCommandManager
|
||||
@ -38,28 +38,28 @@ export async function applySubtractFromTargetOperatorSelections(
|
||||
}
|
||||
): Promise<Error | void> {
|
||||
const ast = dependencies.kclManager.ast
|
||||
if (!target.artifact || !tool.artifact) {
|
||||
return new Error('No artifact found')
|
||||
}
|
||||
const orderedChildrenTarget = findAllChildrenAndOrderByPlaceInCode(
|
||||
target.artifact,
|
||||
const lastSolidsVars = getLastVariableDeclarationsFromSelections(
|
||||
solids,
|
||||
ast,
|
||||
dependencies.kclManager.artifactGraph
|
||||
)
|
||||
const orderedChildrenTool = findAllChildrenAndOrderByPlaceInCode(
|
||||
tool.artifact,
|
||||
if (err(lastSolidsVars) || lastSolidsVars.length < 1) {
|
||||
return new Error('Not enough or invalid solids variables found')
|
||||
}
|
||||
|
||||
const lastToolsVars = getLastVariableDeclarationsFromSelections(
|
||||
tools,
|
||||
ast,
|
||||
dependencies.kclManager.artifactGraph
|
||||
)
|
||||
|
||||
const lastVarTarget = getLastVariable(orderedChildrenTarget, ast)
|
||||
const lastVarTool = getLastVariable(orderedChildrenTool, ast)
|
||||
|
||||
if (!lastVarTarget || !lastVarTool) {
|
||||
return new Error('No variable found')
|
||||
if (err(lastToolsVars) || lastToolsVars.length < 1) {
|
||||
return new Error('Not enough or invalid tools variables found')
|
||||
}
|
||||
|
||||
const modifiedAst = booleanSubtractAstMod({
|
||||
ast,
|
||||
targets: [lastVarTarget?.variableDeclaration?.node],
|
||||
tools: [lastVarTool?.variableDeclaration.node],
|
||||
solids: lastSolidsVars,
|
||||
tools: lastToolsVars,
|
||||
})
|
||||
|
||||
await updateModelingState(modifiedAst, EXECUTION_TYPE_REAL, dependencies)
|
||||
@ -75,34 +75,13 @@ export async function applyUnionFromTargetOperatorSelections(
|
||||
}
|
||||
): Promise<Error | void> {
|
||||
const ast = dependencies.kclManager.ast
|
||||
|
||||
const artifacts: Artifact[] = []
|
||||
for (const selection of solids.graphSelections) {
|
||||
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
|
||||
)
|
||||
const lastVars = getLastVariableDeclarationsFromSelections(
|
||||
solids,
|
||||
ast,
|
||||
dependencies.kclManager.artifactGraph
|
||||
)
|
||||
|
||||
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')
|
||||
if (err(lastVars) || lastVars.length < 2) {
|
||||
return new Error('Not enough or invalid solids variables found')
|
||||
}
|
||||
|
||||
const modifiedAst = booleanUnionAstMod({
|
||||
@ -122,23 +101,36 @@ export async function applyIntersectFromTargetOperatorSelections(
|
||||
}
|
||||
): Promise<Error | void> {
|
||||
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[] = []
|
||||
for (const selection of solids.graphSelections) {
|
||||
for (const selection of selections.graphSelections) {
|
||||
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
|
||||
)
|
||||
findAllChildrenAndOrderByPlaceInCode(artifact, artifactGraph)
|
||||
)
|
||||
|
||||
const lastVars: VariableDeclaration[] = []
|
||||
@ -148,15 +140,7 @@ export async function applyIntersectFromTargetOperatorSelections(
|
||||
lastVars.push(lastVar.variableDeclaration.node)
|
||||
}
|
||||
|
||||
if (lastVars.length < 2) {
|
||||
return new Error('Not enough variables found')
|
||||
}
|
||||
|
||||
const modifiedAst = booleanIntersectAstMod({
|
||||
ast,
|
||||
solids: lastVars,
|
||||
})
|
||||
await updateModelingState(modifiedAst, EXECUTION_TYPE_REAL, dependencies)
|
||||
return lastVars
|
||||
}
|
||||
|
||||
/** returns all children of a given artifact, and sorts them DESC by start sourceRange
|
||||
@ -271,25 +255,27 @@ export function getLastVariable(
|
||||
|
||||
export function booleanSubtractAstMod({
|
||||
ast,
|
||||
targets,
|
||||
solids,
|
||||
tools,
|
||||
}: {
|
||||
ast: Node<Program>
|
||||
targets: VariableDeclaration[]
|
||||
solids: VariableDeclaration[]
|
||||
tools: VariableDeclaration[]
|
||||
}): Node<Program> {
|
||||
const newAst = structuredClone(ast)
|
||||
const newVarName = findUniqueName(newAst, 'solid')
|
||||
const createArrExpr = (varDecs: VariableDeclaration[]) =>
|
||||
createArrayExpression(
|
||||
varDecs.map((varDec) => createLocalName(varDec.declaration.id.name))
|
||||
const createArrExpr = (varDecs: VariableDeclaration[]) => {
|
||||
const names = varDecs.map((varDec) =>
|
||||
createLocalName(varDec.declaration.id.name)
|
||||
)
|
||||
const targetsArrayExpression = createArrExpr(targets)
|
||||
return names.length === 1 ? names[0] : createArrayExpression(names)
|
||||
}
|
||||
const solidsArrayExpression = createArrExpr(solids)
|
||||
const toolsArrayExpression = createArrExpr(tools)
|
||||
|
||||
const newVarDec = createVariableDeclaration(
|
||||
newVarName,
|
||||
createCallExpressionStdLibKw('subtract', targetsArrayExpression, [
|
||||
createCallExpressionStdLibKw('subtract', solidsArrayExpression, [
|
||||
createLabeledArg('tools', toolsArrayExpression),
|
||||
])
|
||||
)
|
||||
|
@ -204,8 +204,8 @@ export type ModelingCommandSchema = {
|
||||
variableName: string
|
||||
}
|
||||
'Boolean Subtract': {
|
||||
target: Selections
|
||||
tool: Selections
|
||||
solids: Selections
|
||||
tools: Selections
|
||||
}
|
||||
'Boolean Union': {
|
||||
solids: Selections
|
||||
@ -595,23 +595,21 @@ export const modelingMachineCommandConfig: StateMachineCommandSetConfig<
|
||||
icon: 'booleanSubtract',
|
||||
needsReview: true,
|
||||
args: {
|
||||
target: {
|
||||
solids: {
|
||||
inputType: 'selection',
|
||||
selectionTypes: ['path'],
|
||||
selectionFilter: ['object'],
|
||||
multiple: false,
|
||||
multiple: true,
|
||||
required: true,
|
||||
skip: true,
|
||||
hidden: (context) => Boolean(context.argumentsToSubmit.nodeToEdit),
|
||||
},
|
||||
tool: {
|
||||
tools: {
|
||||
clearSelectionFirst: true,
|
||||
inputType: 'selection',
|
||||
selectionTypes: ['path'],
|
||||
selectionFilter: ['object'],
|
||||
multiple: false,
|
||||
multiple: true,
|
||||
required: true,
|
||||
skip: false,
|
||||
hidden: (context) => Boolean(context.argumentsToSubmit.nodeToEdit),
|
||||
},
|
||||
},
|
||||
@ -690,32 +688,35 @@ export const modelingMachineCommandConfig: StateMachineCommandSetConfig<
|
||||
},
|
||||
axis: {
|
||||
inputType: 'options',
|
||||
required: (commandContext) =>
|
||||
['Axis'].includes(commandContext.argumentsToSubmit.mode as string),
|
||||
options: [
|
||||
{ name: 'X Axis', value: 'X' },
|
||||
{ name: 'Y Axis', value: 'Y' },
|
||||
{ name: 'Z Axis', value: 'Z' },
|
||||
],
|
||||
hidden: false, // for consistency here, we can actually edit here since it's not a selection
|
||||
required: (context) =>
|
||||
['Axis'].includes(context.argumentsToSubmit.mode as string),
|
||||
hidden: (context) =>
|
||||
!['Axis'].includes(context.argumentsToSubmit.mode as string),
|
||||
},
|
||||
edge: {
|
||||
required: (commandContext) =>
|
||||
['Edge'].includes(commandContext.argumentsToSubmit.mode as string),
|
||||
inputType: 'selection',
|
||||
selectionTypes: ['segment', 'sweepEdge'],
|
||||
multiple: false,
|
||||
hidden: (context) => Boolean(context.argumentsToSubmit.nodeToEdit),
|
||||
required: (context) =>
|
||||
['Edge'].includes(context.argumentsToSubmit.mode as string),
|
||||
hidden: (context) =>
|
||||
Boolean(context.argumentsToSubmit.nodeToEdit) ||
|
||||
!['Edge'].includes(context.argumentsToSubmit.mode as string),
|
||||
},
|
||||
cylinder: {
|
||||
required: (commandContext) =>
|
||||
['Cylinder'].includes(
|
||||
commandContext.argumentsToSubmit.mode as string
|
||||
),
|
||||
inputType: 'selection',
|
||||
selectionTypes: ['wall'],
|
||||
multiple: false,
|
||||
hidden: (context) => Boolean(context.argumentsToSubmit.nodeToEdit),
|
||||
required: (context) =>
|
||||
['Cylinder'].includes(context.argumentsToSubmit.mode as string),
|
||||
hidden: (context) =>
|
||||
Boolean(context.argumentsToSubmit.nodeToEdit) ||
|
||||
!['Cylinder'].includes(context.argumentsToSubmit.mode as string),
|
||||
},
|
||||
revolutions: {
|
||||
inputType: 'kcl',
|
||||
@ -730,34 +731,30 @@ export const modelingMachineCommandConfig: StateMachineCommandSetConfig<
|
||||
radius: {
|
||||
inputType: 'kcl',
|
||||
defaultValue: KCL_DEFAULT_LENGTH,
|
||||
required: (commandContext) =>
|
||||
!['Cylinder'].includes(
|
||||
commandContext.argumentsToSubmit.mode as string
|
||||
),
|
||||
required: (context) =>
|
||||
!['Cylinder'].includes(context.argumentsToSubmit.mode as string),
|
||||
hidden: (context) =>
|
||||
['Cylinder'].includes(context.argumentsToSubmit.mode as string),
|
||||
},
|
||||
length: {
|
||||
inputType: 'kcl',
|
||||
defaultValue: KCL_DEFAULT_LENGTH,
|
||||
required: (commandContext) =>
|
||||
['Axis'].includes(commandContext.argumentsToSubmit.mode as string),
|
||||
// No need for hidden here, as it works with all modes
|
||||
},
|
||||
ccw: {
|
||||
inputType: 'options',
|
||||
skip: true,
|
||||
required: true,
|
||||
defaultValue: false,
|
||||
valueSummary: (value) => String(value),
|
||||
required: false,
|
||||
displayName: 'CounterClockWise',
|
||||
options: (commandContext) => [
|
||||
options: [
|
||||
{
|
||||
name: 'False',
|
||||
value: false,
|
||||
isCurrent: !Boolean(commandContext.argumentsToSubmit.ccw),
|
||||
},
|
||||
{
|
||||
name: 'True',
|
||||
value: true,
|
||||
isCurrent: Boolean(commandContext.argumentsToSubmit.ccw),
|
||||
},
|
||||
],
|
||||
},
|
||||
|
@ -222,3 +222,6 @@ export const CODE_QUERY_PARAM = 'code'
|
||||
/** A query parameter to skip the sign-on view if unnecessary. */
|
||||
export const IMMEDIATE_SIGN_IN_IF_NECESSARY_QUERY_PARAM =
|
||||
'immediate-sign-in-if-necessary'
|
||||
|
||||
// Only used by the desktop app
|
||||
export const OAUTH2_DEVICE_CLIENT_ID = '2af127fb-e14e-400a-9c57-a9ed08d1a5b7'
|
||||
|
@ -7,7 +7,7 @@ import {
|
||||
} from '@src/env'
|
||||
import { assign, fromPromise, setup } from 'xstate'
|
||||
|
||||
import { COOKIE_NAME } from '@src/lib/constants'
|
||||
import { COOKIE_NAME, OAUTH2_DEVICE_CLIENT_ID } from '@src/lib/constants'
|
||||
import {
|
||||
getUser as getUserDesktop,
|
||||
readTokenFile,
|
||||
@ -254,8 +254,32 @@ async function getAndSyncStoredToken(input: {
|
||||
async function logout() {
|
||||
localStorage.removeItem(TOKEN_PERSIST_KEY)
|
||||
if (isDesktop()) {
|
||||
await writeTokenFile('')
|
||||
return Promise.resolve(null)
|
||||
try {
|
||||
let token = await readTokenFile()
|
||||
|
||||
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'), {
|
||||
|
@ -3570,17 +3570,17 @@ export const modelingMachine = setup({
|
||||
return Promise.reject(new Error(NO_INPUT_PROVIDED_MESSAGE))
|
||||
}
|
||||
|
||||
const { target, tool } = input
|
||||
const { solids, tools } = input
|
||||
if (
|
||||
!target.graphSelections[0].artifact ||
|
||||
!tool.graphSelections[0].artifact
|
||||
!solids.graphSelections.some((selection) => selection.artifact) ||
|
||||
!tools.graphSelections.some((selection) => selection.artifact)
|
||||
) {
|
||||
return Promise.reject(new Error('No artifact in selections found'))
|
||||
}
|
||||
|
||||
await applySubtractFromTargetOperatorSelections(
|
||||
target.graphSelections[0],
|
||||
tool.graphSelections[0],
|
||||
const result = await applySubtractFromTargetOperatorSelections(
|
||||
solids,
|
||||
tools,
|
||||
{
|
||||
kclManager,
|
||||
codeManager,
|
||||
@ -3588,6 +3588,9 @@ export const modelingMachine = setup({
|
||||
editorManager,
|
||||
}
|
||||
)
|
||||
if (err(result)) {
|
||||
return Promise.reject(result)
|
||||
}
|
||||
}
|
||||
),
|
||||
boolUnionAstMod: fromPromise(
|
||||
@ -3605,12 +3608,15 @@ export const modelingMachine = setup({
|
||||
return Promise.reject(new Error('No artifact in selections found'))
|
||||
}
|
||||
|
||||
await applyUnionFromTargetOperatorSelections(solids, {
|
||||
const result = await applyUnionFromTargetOperatorSelections(solids, {
|
||||
kclManager,
|
||||
codeManager,
|
||||
engineCommandManager,
|
||||
editorManager,
|
||||
})
|
||||
if (err(result)) {
|
||||
return Promise.reject(result)
|
||||
}
|
||||
}
|
||||
),
|
||||
boolIntersectAstMod: fromPromise(
|
||||
@ -3628,12 +3634,18 @@ export const modelingMachine = setup({
|
||||
return Promise.reject(new Error('No artifact in selections found'))
|
||||
}
|
||||
|
||||
await applyIntersectFromTargetOperatorSelections(solids, {
|
||||
kclManager,
|
||||
codeManager,
|
||||
engineCommandManager,
|
||||
editorManager,
|
||||
})
|
||||
const result = await applyIntersectFromTargetOperatorSelections(
|
||||
solids,
|
||||
{
|
||||
kclManager,
|
||||
codeManager,
|
||||
engineCommandManager,
|
||||
editorManager,
|
||||
}
|
||||
)
|
||||
if (err(result)) {
|
||||
return Promise.reject(result)
|
||||
}
|
||||
}
|
||||
),
|
||||
|
||||
|
24
src/main.ts
24
src/main.ts
@ -15,6 +15,7 @@ import {
|
||||
dialog,
|
||||
ipcMain,
|
||||
nativeTheme,
|
||||
session,
|
||||
screen,
|
||||
shell,
|
||||
systemPreferences,
|
||||
@ -28,7 +29,10 @@ import {
|
||||
parseCLIArgs,
|
||||
} from '@src/commandLineArgs'
|
||||
import { initPromiseNode } from '@src/lang/wasmUtilsNode'
|
||||
import { ZOO_STUDIO_PROTOCOL } from '@src/lib/constants'
|
||||
import {
|
||||
ZOO_STUDIO_PROTOCOL,
|
||||
OAUTH2_DEVICE_CLIENT_ID,
|
||||
} from '@src/lib/constants'
|
||||
import getCurrentProjectFile from '@src/lib/getCurrentProjectFile'
|
||||
import { reportRejection } from '@src/lib/trap'
|
||||
import {
|
||||
@ -293,7 +297,21 @@ app.on('window-all-closed', () => {
|
||||
// This method will be called when Electron has finished
|
||||
// initialization and is ready to create browser windows.
|
||||
// Some APIs can only be used after this event occurs.
|
||||
app.on('ready', (event, data) => {
|
||||
app.on('ready', async (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
|
||||
if (mainWindow) return
|
||||
// Create the mainWindow
|
||||
@ -402,7 +420,7 @@ ipcMain.handle('startDeviceFlow', async (_, host: string) => {
|
||||
// We can hardcode the client ID.
|
||||
// This value is safe to be embedded in version control.
|
||||
// This is the client ID of the KittyCAD app.
|
||||
client_id: '2af127fb-e14e-400a-9c57-a9ed08d1a5b7',
|
||||
client_id: OAUTH2_DEVICE_CLIENT_ID,
|
||||
token_endpoint_auth_method: 'none',
|
||||
})
|
||||
|
||||
|
Reference in New Issue
Block a user