Compare commits
27 Commits
guptaarnav
...
v0.36.0
Author | SHA1 | Date | |
---|---|---|---|
702e322f90 | |||
e82830754d | |||
7806377a5a | |||
859afa2fd8 | |||
0a5f3093fc | |||
b65f7939f6 | |||
c35dea5e07 | |||
fc66d4745f | |||
b313d26c2a | |||
00b94ead62 | |||
0531ea1ce9 | |||
5f9a4887c1 | |||
da7dfa16d8 | |||
363ae10658 | |||
ac4a6c84cf | |||
c6fad2e2dc | |||
013cb10961 | |||
6261083cb1 | |||
2b0ba37ed0 | |||
96174f3cf6 | |||
aed62ff912 | |||
9334d64608 | |||
4fa7d2d8c8 | |||
3e615dfdbc | |||
c9860af29f | |||
23a42f0195 | |||
a77fa639f3 |
28
.eslintrc
28
.eslintrc
@ -5,16 +5,32 @@
|
||||
},
|
||||
"plugins": [
|
||||
"css-modules",
|
||||
"jest",
|
||||
"jsx-a11y",
|
||||
"react",
|
||||
"react-hooks",
|
||||
"suggest-no-throw",
|
||||
"testing-library",
|
||||
"@typescript-eslint"
|
||||
],
|
||||
"extends": [
|
||||
"react-app",
|
||||
"react-app/jest",
|
||||
"plugin:css-modules/recommended"
|
||||
"plugin:css-modules/recommended",
|
||||
"plugin:jsx-a11y/recommended",
|
||||
"plugin:react-hooks/recommended"
|
||||
],
|
||||
"rules": {
|
||||
"@typescript-eslint/no-floating-promises": "error",
|
||||
"@typescript-eslint/no-misused-promises": "error",
|
||||
"jsx-a11y/click-events-have-key-events": "off",
|
||||
"jsx-a11y/no-autofocus": "off",
|
||||
"jsx-a11y/no-noninteractive-element-interactions": "off",
|
||||
"no-restricted-globals": [
|
||||
"error",
|
||||
{
|
||||
"name": "isNaN",
|
||||
"message": "Use Number.isNaN() instead."
|
||||
}
|
||||
],
|
||||
"semi": [
|
||||
"error",
|
||||
"never"
|
||||
@ -25,6 +41,9 @@
|
||||
"overrides": [
|
||||
{
|
||||
"files": ["e2e/**/*.ts"], // Update the pattern based on your file structure
|
||||
"extends": [
|
||||
"plugin:testing-library/react"
|
||||
],
|
||||
"rules": {
|
||||
"suggest-no-throw/suggest-no-throw": "off",
|
||||
"testing-library/prefer-screen-queries": "off",
|
||||
@ -33,6 +52,9 @@
|
||||
},
|
||||
{
|
||||
"files": ["src/**/*.test.ts"],
|
||||
"extends": [
|
||||
"plugin:testing-library/react"
|
||||
],
|
||||
"rules": {
|
||||
"suggest-no-throw/suggest-no-throw": "off",
|
||||
}
|
||||
|
2
.github/ci-cd-scripts/playwright-electron.sh
vendored
2
.github/ci-cd-scripts/playwright-electron.sh
vendored
@ -21,7 +21,7 @@ if [[ ! -f "test-results/.last-run.json" ]]; then
|
||||
fi
|
||||
|
||||
retry=1
|
||||
max_retrys=4
|
||||
max_retrys=5
|
||||
|
||||
# retry failed tests, doing our own retries because using inbuilt playwright retries causes connection issues
|
||||
while [[ $retry -le $max_retrys ]]; do
|
||||
|
17
.github/dependabot.yml
vendored
17
.github/dependabot.yml
vendored
@ -6,23 +6,29 @@
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: 'npm' # See documentation for possible values
|
||||
directory: '/' # Location of package manifests
|
||||
directories:
|
||||
- '/'
|
||||
- '/packages/codemirror-lang-kcl/'
|
||||
- '/packages/codemirror-lsp-client/'
|
||||
schedule:
|
||||
interval: 'weekly'
|
||||
interval: weekly
|
||||
day: monday
|
||||
reviewers:
|
||||
- franknoirot
|
||||
- irev-dev
|
||||
- package-ecosystem: 'github-actions' # See documentation for possible values
|
||||
directory: '/' # Location of package manifests
|
||||
schedule:
|
||||
interval: 'weekly'
|
||||
interval: weekly
|
||||
day: monday
|
||||
reviewers:
|
||||
- adamchalmers
|
||||
- jessfraz
|
||||
- package-ecosystem: 'cargo' # See documentation for possible values
|
||||
directory: '/src/wasm-lib/' # Location of package manifests
|
||||
schedule:
|
||||
interval: 'weekly'
|
||||
interval: weekly
|
||||
day: monday
|
||||
reviewers:
|
||||
- adamchalmers
|
||||
- jessfraz
|
||||
@ -30,3 +36,6 @@ updates:
|
||||
serde-dependencies:
|
||||
patterns:
|
||||
- "serde*"
|
||||
wasm-bindgen-deps:
|
||||
patterns:
|
||||
- "wasm-bindgen*"
|
||||
|
32
.github/workflows/codemirror-lang-kcl.yml
vendored
Normal file
32
.github/workflows/codemirror-lang-kcl.yml
vendored
Normal file
@ -0,0 +1,32 @@
|
||||
name: CodeMirror Lang KCL
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
yarn-unit-test:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: '.nvmrc'
|
||||
cache: 'yarn'
|
||||
|
||||
- run: yarn install
|
||||
working-directory: packages/codemirror-lang-kcl
|
||||
|
||||
- run: yarn tsc
|
||||
working-directory: packages/codemirror-lang-kcl
|
||||
|
||||
- name: run unit tests
|
||||
run: yarn test
|
||||
working-directory: packages/codemirror-lang-kcl
|
File diff suppressed because one or more lines are too long
@ -75843,7 +75843,6 @@
|
||||
"required": [
|
||||
"angleStart",
|
||||
"axis",
|
||||
"length",
|
||||
"radius",
|
||||
"revolutions"
|
||||
],
|
||||
@ -75864,9 +75863,10 @@
|
||||
"type": "boolean"
|
||||
},
|
||||
"length": {
|
||||
"description": "Length of the helix.",
|
||||
"description": "Length of the helix. This is not necessary if the helix is created around an edge. If not given the length of the edge is used.",
|
||||
"type": "number",
|
||||
"format": "double"
|
||||
"format": "double",
|
||||
"nullable": true
|
||||
},
|
||||
"radius": {
|
||||
"description": "Radius of the helix.",
|
||||
@ -76962,7 +76962,7 @@
|
||||
"deprecated": false,
|
||||
"examples": [
|
||||
"// Create a helix around the Z axis.\nhelixPath = helix({\n angleStart = 0,\n ccw = true,\n revolutions = 16,\n length = 10,\n radius = 5,\n axis = 'Z'\n})\n\n// Create a spring by sweeping around the helix path.\nspringSketch = startSketchOn('YZ')\n |> circle({ center = [0, 0], radius = 1 }, %)\n// |> sweep({ path = helixPath }, %)",
|
||||
""
|
||||
"// Create a helix around an edge.\nhelper001 = startSketchOn('XZ')\n |> startProfileAt([0, 0], %)\n |> line([0, 10], %, $edge001)\n\nhelixPath = helix({\n angleStart = 0,\n ccw = true,\n revolutions = 16,\n length = 10,\n radius = 5,\n axis = edge001\n})\n\n// Create a spring by sweeping around the helix path.\nspringSketch = startSketchOn('XY')\n |> circle({ center = [0, 0], radius = 1 }, %)\n// |> sweep({ path = helixPath }, %)"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
@ -19,7 +19,7 @@ Data for a helix.
|
||||
| `revolutions` |`number`| Number of revolutions. | No |
|
||||
| `angleStart` |`number`| Start angle (in degrees). | No |
|
||||
| `ccw` |`boolean`| Is the helix rotation counter clockwise? The default is `false`. | No |
|
||||
| `length` |`number`| Length of the helix. | No |
|
||||
| `length` |`number`| Length of the helix. This is not necessary if the helix is created around an edge. If not given the length of the edge is used. | No |
|
||||
| `radius` |`number`| Radius of the helix. | No |
|
||||
| `axis` |[`Axis3dOrEdgeReference`](/docs/kcl/types/Axis3dOrEdgeReference)| Axis to use as mirror. | No |
|
||||
|
||||
|
@ -121,18 +121,23 @@ export class AuthenticatedTronApp {
|
||||
|
||||
export const fixtures = {
|
||||
cmdBar: async ({ page }: { page: Page }, use: any) => {
|
||||
// eslint-disable-next-line react-hooks/rules-of-hooks
|
||||
await use(new CmdBarFixture(page))
|
||||
},
|
||||
editor: async ({ page }: { page: Page }, use: any) => {
|
||||
// eslint-disable-next-line react-hooks/rules-of-hooks
|
||||
await use(new EditorFixture(page))
|
||||
},
|
||||
toolbar: async ({ page }: { page: Page }, use: any) => {
|
||||
// eslint-disable-next-line react-hooks/rules-of-hooks
|
||||
await use(new ToolbarFixture(page))
|
||||
},
|
||||
scene: async ({ page }: { page: Page }, use: any) => {
|
||||
// eslint-disable-next-line react-hooks/rules-of-hooks
|
||||
await use(new SceneFixture(page))
|
||||
},
|
||||
homePage: async ({ page }: { page: Page }, use: any) => {
|
||||
// eslint-disable-next-line react-hooks/rules-of-hooks
|
||||
await use(new HomePageFixture(page))
|
||||
},
|
||||
}
|
||||
|
@ -14,6 +14,7 @@ export class ToolbarFixture {
|
||||
|
||||
extrudeButton!: Locator
|
||||
loftButton!: Locator
|
||||
sweepButton!: Locator
|
||||
shellButton!: Locator
|
||||
offsetPlaneButton!: Locator
|
||||
startSketchBtn!: Locator
|
||||
@ -40,6 +41,7 @@ export class ToolbarFixture {
|
||||
this.page = page
|
||||
this.extrudeButton = page.getByTestId('extrude')
|
||||
this.loftButton = page.getByTestId('loft')
|
||||
this.sweepButton = page.getByTestId('sweep')
|
||||
this.shellButton = page.getByTestId('shell')
|
||||
this.offsetPlaneButton = page.getByTestId('plane-offset')
|
||||
this.startSketchBtn = page.getByTestId('sketch')
|
||||
|
@ -756,6 +756,17 @@ test(`Offset plane point-and-click`, async ({
|
||||
})
|
||||
await scene.expectPixelColor([74, 74, 74], testPoint, 15)
|
||||
})
|
||||
|
||||
await test.step('Delete offset plane via feature tree selection', async () => {
|
||||
await editor.closePane()
|
||||
const operationButton = await toolbar.getFeatureTreeOperation(
|
||||
'Offset Plane',
|
||||
0
|
||||
)
|
||||
await operationButton.click({ button: 'left' })
|
||||
await page.keyboard.press('Backspace')
|
||||
await scene.expectPixelColor([50, 51, 96], testPoint, 15)
|
||||
})
|
||||
})
|
||||
|
||||
const loftPointAndClickCases = [
|
||||
@ -851,6 +862,173 @@ loftPointAndClickCases.forEach(({ shouldPreselect }) => {
|
||||
})
|
||||
await scene.expectPixelColor([89, 89, 89], testPoint, 15)
|
||||
})
|
||||
|
||||
await test.step('Delete loft via feature tree selection', async () => {
|
||||
await editor.closePane()
|
||||
const operationButton = await toolbar.getFeatureTreeOperation('Loft', 0)
|
||||
await operationButton.click({ button: 'left' })
|
||||
await page.keyboard.press('Backspace')
|
||||
await scene.expectPixelColor([254, 254, 254], testPoint, 15)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
// TODO: merge with above test. Right now we're not able to delete a loft
|
||||
// right after creation via selection for some reason, so we go with a new instance
|
||||
test('Loft and offset plane deletion via selection', async ({
|
||||
context,
|
||||
page,
|
||||
homePage,
|
||||
scene,
|
||||
}) => {
|
||||
const initialCode = `sketch001 = startSketchOn('XZ')
|
||||
|> circle({ center = [0, 0], radius = 30 }, %)
|
||||
plane001 = offsetPlane('XZ', 50)
|
||||
sketch002 = startSketchOn(plane001)
|
||||
|> circle({ center = [0, 0], radius = 20 }, %)
|
||||
loft001 = loft([sketch001, sketch002])
|
||||
`
|
||||
await context.addInitScript((initialCode) => {
|
||||
localStorage.setItem('persistCode', initialCode)
|
||||
}, initialCode)
|
||||
await page.setBodyDimensions({ width: 1000, height: 500 })
|
||||
await homePage.goToModelingScene()
|
||||
|
||||
// One dumb hardcoded screen pixel value
|
||||
const testPoint = { x: 575, y: 200 }
|
||||
const [clickOnSketch1] = scene.makeMouseHelpers(testPoint.x, testPoint.y)
|
||||
const [clickOnSketch2] = scene.makeMouseHelpers(testPoint.x, testPoint.y + 80)
|
||||
|
||||
await test.step(`Delete loft`, async () => {
|
||||
// Check for loft
|
||||
await scene.expectPixelColor([89, 89, 89], testPoint, 15)
|
||||
await clickOnSketch1()
|
||||
await expect(page.locator('.cm-activeLine')).toHaveText(`
|
||||
|> circle({ center = [0, 0], radius = 30 }, %)
|
||||
`)
|
||||
await page.keyboard.press('Backspace')
|
||||
// Check for sketch 1
|
||||
await scene.expectPixelColor([254, 254, 254], testPoint, 15)
|
||||
})
|
||||
|
||||
await test.step('Delete sketch002', async () => {
|
||||
await page.waitForTimeout(1000)
|
||||
await clickOnSketch2()
|
||||
await expect(page.locator('.cm-activeLine')).toHaveText(`
|
||||
|> circle({ center = [0, 0], radius = 20 }, %)
|
||||
`)
|
||||
await page.keyboard.press('Backspace')
|
||||
// Check for plane001
|
||||
await scene.expectPixelColor([228, 228, 228], testPoint, 15)
|
||||
})
|
||||
|
||||
await test.step('Delete plane001', async () => {
|
||||
await page.waitForTimeout(1000)
|
||||
await clickOnSketch2()
|
||||
await expect(page.locator('.cm-activeLine')).toHaveText(`
|
||||
plane001 = offsetPlane('XZ', 50)
|
||||
`)
|
||||
await page.keyboard.press('Backspace')
|
||||
// Check for sketch 1
|
||||
await scene.expectPixelColor([254, 254, 254], testPoint, 15)
|
||||
})
|
||||
})
|
||||
|
||||
test(`Sweep point-and-click`, async ({
|
||||
context,
|
||||
page,
|
||||
homePage,
|
||||
scene,
|
||||
editor,
|
||||
toolbar,
|
||||
cmdBar,
|
||||
}) => {
|
||||
const initialCode = `sketch001 = startSketchOn('YZ')
|
||||
|> circle({
|
||||
center = [0, 0],
|
||||
radius = 500
|
||||
}, %)
|
||||
sketch002 = startSketchOn('XZ')
|
||||
|> startProfileAt([0, 0], %)
|
||||
|> xLine(-500, %)
|
||||
|> tangentialArcTo([-2000, 500], %)
|
||||
`
|
||||
await context.addInitScript((initialCode) => {
|
||||
localStorage.setItem('persistCode', initialCode)
|
||||
}, initialCode)
|
||||
await page.setBodyDimensions({ width: 1000, height: 500 })
|
||||
await homePage.goToModelingScene()
|
||||
await scene.waitForExecutionDone()
|
||||
|
||||
// One dumb hardcoded screen pixel value
|
||||
const testPoint = { x: 700, y: 250 }
|
||||
const [clickOnSketch1] = scene.makeMouseHelpers(testPoint.x, testPoint.y)
|
||||
const [clickOnSketch2] = scene.makeMouseHelpers(testPoint.x - 50, testPoint.y)
|
||||
const sweepDeclaration = 'sweep001 = sweep({ path = sketch002 }, sketch001)'
|
||||
|
||||
await test.step(`Look for sketch001`, async () => {
|
||||
await toolbar.closePane('code')
|
||||
await scene.expectPixelColor([53, 53, 53], testPoint, 15)
|
||||
})
|
||||
|
||||
await test.step(`Go through the command bar flow`, async () => {
|
||||
await toolbar.sweepButton.click()
|
||||
await cmdBar.expectState({
|
||||
commandName: 'Sweep',
|
||||
currentArgKey: 'profile',
|
||||
currentArgValue: '',
|
||||
headerArguments: {
|
||||
Path: '',
|
||||
Profile: '',
|
||||
},
|
||||
highlightedHeaderArg: 'profile',
|
||||
stage: 'arguments',
|
||||
})
|
||||
await clickOnSketch1()
|
||||
await cmdBar.expectState({
|
||||
commandName: 'Sweep',
|
||||
currentArgKey: 'path',
|
||||
currentArgValue: '',
|
||||
headerArguments: {
|
||||
Path: '',
|
||||
Profile: '1 face',
|
||||
},
|
||||
highlightedHeaderArg: 'path',
|
||||
stage: 'arguments',
|
||||
})
|
||||
await clickOnSketch2()
|
||||
await cmdBar.expectState({
|
||||
commandName: 'Sweep',
|
||||
headerArguments: {
|
||||
Path: '1 face',
|
||||
Profile: '1 face',
|
||||
},
|
||||
stage: 'review',
|
||||
})
|
||||
await cmdBar.progressCmdBar()
|
||||
})
|
||||
|
||||
await test.step(`Confirm code is added to the editor, scene has changed`, async () => {
|
||||
await scene.expectPixelColor([135, 64, 73], testPoint, 15)
|
||||
await toolbar.openPane('code')
|
||||
await editor.expectEditor.toContain(sweepDeclaration)
|
||||
await editor.expectState({
|
||||
diagnostics: [],
|
||||
activeLines: [sweepDeclaration],
|
||||
highlightedCode: '',
|
||||
})
|
||||
await toolbar.closePane('code')
|
||||
})
|
||||
|
||||
await test.step('Delete sweep via feature tree selection', async () => {
|
||||
await toolbar.openPane('feature-tree')
|
||||
await page.waitForTimeout(500)
|
||||
const operationButton = await toolbar.getFeatureTreeOperation('Sweep', 0)
|
||||
await operationButton.click({ button: 'left' })
|
||||
await page.keyboard.press('Backspace')
|
||||
await page.waitForTimeout(500)
|
||||
await toolbar.closePane('feature-tree')
|
||||
await scene.expectPixelColor([53, 53, 53], testPoint, 15)
|
||||
})
|
||||
})
|
||||
|
||||
@ -1030,4 +1208,104 @@ extrude001 = extrude(40, sketch001)
|
||||
})
|
||||
await scene.expectPixelColor([49, 49, 49], testPoint, 15)
|
||||
})
|
||||
|
||||
await test.step('Delete shell via feature tree selection', async () => {
|
||||
await editor.closePane()
|
||||
const operationButton = await toolbar.getFeatureTreeOperation('Shell', 0)
|
||||
await operationButton.click({ button: 'left' })
|
||||
await page.keyboard.press('Backspace')
|
||||
await scene.expectPixelColor([99, 99, 99], testPoint, 15)
|
||||
})
|
||||
})
|
||||
|
||||
const shellSketchOnFacesCases = [
|
||||
`sketch001 = startSketchOn('XZ')
|
||||
|> circle({ center = [0, 0], radius = 100 }, %)
|
||||
|> extrude(100, %)
|
||||
|
||||
sketch002 = startSketchOn(sketch001, 'END')
|
||||
|> circle({ center = [0, 0], radius = 50 }, %)
|
||||
|> extrude(50, %)
|
||||
`,
|
||||
`sketch001 = startSketchOn('XZ')
|
||||
|> circle({ center = [0, 0], radius = 100 }, %)
|
||||
extrude001 = extrude(100, sketch001)
|
||||
|
||||
sketch002 = startSketchOn(extrude001, 'END')
|
||||
|> circle({ center = [0, 0], radius = 50 }, %)
|
||||
extrude002 = extrude(50, sketch002)
|
||||
`,
|
||||
]
|
||||
shellSketchOnFacesCases.forEach((initialCode, index) => {
|
||||
const hasExtrudesInPipe = index === 0
|
||||
test(`Shell point-and-click sketch on face (extrudes in pipes: ${hasExtrudesInPipe})`, async ({
|
||||
context,
|
||||
page,
|
||||
homePage,
|
||||
scene,
|
||||
editor,
|
||||
toolbar,
|
||||
cmdBar,
|
||||
}) => {
|
||||
await context.addInitScript((initialCode) => {
|
||||
localStorage.setItem('persistCode', initialCode)
|
||||
}, initialCode)
|
||||
await page.setBodyDimensions({ width: 1000, height: 500 })
|
||||
await homePage.goToModelingScene()
|
||||
await scene.waitForExecutionDone()
|
||||
|
||||
// One dumb hardcoded screen pixel value
|
||||
const testPoint = { x: 550, y: 295 }
|
||||
const [clickOnCap] = scene.makeMouseHelpers(testPoint.x, testPoint.y)
|
||||
const shellDeclaration = `shell001 = shell({ faces = ['end'], thickness = 5 }, ${
|
||||
hasExtrudesInPipe ? 'sketch002' : 'extrude002'
|
||||
})`
|
||||
|
||||
await test.step(`Look for the grey of the shape`, async () => {
|
||||
await toolbar.closePane('code')
|
||||
await scene.expectPixelColor([128, 128, 128], testPoint, 15)
|
||||
})
|
||||
|
||||
await test.step(`Go through the command bar flow, selecting a cap and keeping default thickness`, async () => {
|
||||
await toolbar.shellButton.click()
|
||||
await cmdBar.expectState({
|
||||
stage: 'arguments',
|
||||
currentArgKey: 'selection',
|
||||
currentArgValue: '',
|
||||
headerArguments: {
|
||||
Selection: '',
|
||||
Thickness: '',
|
||||
},
|
||||
highlightedHeaderArg: 'selection',
|
||||
commandName: 'Shell',
|
||||
})
|
||||
await clickOnCap()
|
||||
await page.waitForTimeout(500)
|
||||
await cmdBar.progressCmdBar()
|
||||
await page.waitForTimeout(500)
|
||||
await cmdBar.progressCmdBar()
|
||||
await page.waitForTimeout(500)
|
||||
await cmdBar.expectState({
|
||||
stage: 'review',
|
||||
headerArguments: {
|
||||
Selection: '1 cap',
|
||||
Thickness: '5',
|
||||
},
|
||||
commandName: 'Shell',
|
||||
})
|
||||
await cmdBar.progressCmdBar()
|
||||
})
|
||||
|
||||
await test.step(`Confirm code is added to the editor, scene has changed`, async () => {
|
||||
await toolbar.openPane('code')
|
||||
await editor.expectEditor.toContain(shellDeclaration)
|
||||
await editor.expectState({
|
||||
diagnostics: [],
|
||||
activeLines: [shellDeclaration],
|
||||
highlightedCode: '',
|
||||
})
|
||||
await toolbar.closePane('code')
|
||||
await scene.expectPixelColor([73, 73, 73], testPoint, 15)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
Binary file not shown.
After Width: | Height: | Size: 42 KiB |
Binary file not shown.
After Width: | Height: | Size: 47 KiB |
18
flake.lock
generated
18
flake.lock
generated
@ -2,11 +2,11 @@
|
||||
"nodes": {
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1721933792,
|
||||
"narHash": "sha256-zYVwABlQnxpbaHMfX6Wt9jhyQstFYwN2XjleOJV3VVg=",
|
||||
"lastModified": 1736320768,
|
||||
"narHash": "sha256-nIYdTAiKIGnFNugbomgBJR+Xv5F1ZQU+HfaBqJKroC0=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "2122a9b35b35719ad9a395fe783eabb092df01b1",
|
||||
"rev": "4bc9c909d9ac828a039f288cf872d16d38185db8",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@ -18,11 +18,11 @@
|
||||
},
|
||||
"nixpkgs_2": {
|
||||
"locked": {
|
||||
"lastModified": 1718428119,
|
||||
"narHash": "sha256-WdWDpNaq6u1IPtxtYHHWpl5BmabtpmLnMAx0RdJ/vo8=",
|
||||
"lastModified": 1728538411,
|
||||
"narHash": "sha256-f0SBJz1eZ2yOuKUr5CA9BHULGXVSn6miBuUWdTyhUhU=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "e6cea36f83499eb4e9cd184c8a8e823296b50ad5",
|
||||
"rev": "b69de56fac8c2b6f8fd27f2eca01dcda8e0a4221",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@ -43,11 +43,11 @@
|
||||
"nixpkgs": "nixpkgs_2"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1721960387,
|
||||
"narHash": "sha256-o21ax+745ETGXrcgc/yUuLw1SI77ymp3xEpJt+w/kks=",
|
||||
"lastModified": 1736476219,
|
||||
"narHash": "sha256-+qyv3QqdZCdZ3cSO/cbpEY6tntyYjfe1bB12mdpNFaY=",
|
||||
"owner": "oxalica",
|
||||
"repo": "rust-overlay",
|
||||
"rev": "9cbf831c5b20a53354fc12758abd05966f9f1699",
|
||||
"rev": "de30cc5963da22e9742bbbbb9a3344570ed237b9",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
17
package.json
17
package.json
@ -26,7 +26,7 @@
|
||||
"@fortawesome/react-fontawesome": "^0.2.0",
|
||||
"@headlessui/react": "^1.7.19",
|
||||
"@headlessui/tailwindcss": "^0.2.0",
|
||||
"@kittycad/lib": "2.0.12",
|
||||
"@kittycad/lib": "2.0.13",
|
||||
"@lezer/highlight": "^1.2.1",
|
||||
"@lezer/lr": "^1.4.1",
|
||||
"@react-hook/resize-observer": "^2.0.1",
|
||||
@ -91,8 +91,8 @@
|
||||
"build:wasm": "yarn wasm-prep && cd src/wasm-lib && wasm-pack build --release --target web --out-dir pkg && cargo test -p kcl-lib export_bindings && cd ../.. && yarn isomorphic-copy-wasm && yarn fmt",
|
||||
"remove-importmeta": "sed -i 's/import.meta.url/window.location.origin/g' \"./src/wasm-lib/pkg/wasm_lib.js\"; sed -i '' 's/import.meta.url/window.location.origin/g' \"./src/wasm-lib/pkg/wasm_lib.js\" || echo \"sed for both mac and linux\"",
|
||||
"wasm-prep": "rimraf src/wasm-lib/pkg && mkdirp src/wasm-lib/pkg && rimraf src/wasm-lib/kcl/bindings",
|
||||
"lint-fix": "eslint --fix src e2e packages/codemirror-lsp-client",
|
||||
"lint": "eslint --max-warnings 0 src e2e packages/codemirror-lsp-client",
|
||||
"lint-fix": "eslint --fix --ext .ts --ext .tsx src e2e packages/codemirror-lsp-client/src",
|
||||
"lint": "eslint --max-warnings 0 --ext .ts --ext .tsx src e2e packages/codemirror-lsp-client/src",
|
||||
"files:set-version": "echo \"$(jq --arg v \"$VERSION\" '.version=$v' package.json --indent 2)\" > package.json",
|
||||
"files:set-notes": "./scripts/set-files-notes.sh",
|
||||
"files:flip-to-nightly": "./scripts/flip-files-to-nightly.sh",
|
||||
@ -149,7 +149,7 @@
|
||||
"@electron-forge/plugin-vite": "7.4.0",
|
||||
"@electron/fuses": "1.8.0",
|
||||
"@iarna/toml": "^2.2.5",
|
||||
"@lezer/generator": "^1.7.1",
|
||||
"@lezer/generator": "^1.7.2",
|
||||
"@nabla/vite-plugin-eslint": "^2.0.5",
|
||||
"@playwright/test": "^1.49.0",
|
||||
"@testing-library/jest-dom": "^5.14.1",
|
||||
@ -171,8 +171,6 @@
|
||||
"@types/uuid": "^9.0.8",
|
||||
"@types/wicg-file-system-access": "^2023.10.5",
|
||||
"@types/ws": "^8.5.13",
|
||||
"@typescript-eslint/eslint-plugin": "^5.0.0",
|
||||
"@typescript-eslint/parser": "^5.0.0",
|
||||
"@vitejs/plugin-react": "^4.3.0",
|
||||
"@vitest/web-worker": "^1.5.0",
|
||||
"@xstate/cli": "^0.5.17",
|
||||
@ -182,10 +180,14 @@
|
||||
"electron-builder": "24.13.3",
|
||||
"electron-notarize": "1.2.2",
|
||||
"eslint": "^8.0.1",
|
||||
"eslint-config-react-app": "^7.0.1",
|
||||
"eslint-plugin-css-modules": "^2.12.0",
|
||||
"eslint-plugin-import": "^2.30.0",
|
||||
"eslint-plugin-jest": "^28.10.0",
|
||||
"eslint-plugin-jsx-a11y": "^6.10.2",
|
||||
"eslint-plugin-react": "^7.37.3",
|
||||
"eslint-plugin-react-hooks": "^5.1.0",
|
||||
"eslint-plugin-suggest-no-throw": "^1.0.0",
|
||||
"eslint-plugin-testing-library": "^7.1.1",
|
||||
"happy-dom": "^16.3.0",
|
||||
"http-server": "^14.1.1",
|
||||
"husky": "^9.1.5",
|
||||
@ -200,6 +202,7 @@
|
||||
"tailwindcss": "^3.4.1",
|
||||
"ts-node": "^10.0.0",
|
||||
"typescript": "^5.7.2",
|
||||
"typescript-eslint": "^8.19.1",
|
||||
"vite": "^5.4.6",
|
||||
"vite-plugin-package-version": "^1.1.0",
|
||||
"vite-tsconfig-paths": "^4.3.2",
|
||||
|
1
packages/codemirror-lang-kcl/.gitignore
vendored
1
packages/codemirror-lang-kcl/.gitignore
vendored
@ -4,4 +4,5 @@ dist
|
||||
tsconfig.tsbuildinfo
|
||||
*.d.ts
|
||||
*.js
|
||||
!postcss.config.js
|
||||
!rollup.config.js
|
||||
|
@ -28,6 +28,7 @@
|
||||
"@rollup/plugin-typescript": "^12.1.2",
|
||||
"rollup": "^4.29.1",
|
||||
"rollup-plugin-dts": "^6.1.1",
|
||||
"vite-tsconfig-paths": "^4.3.2",
|
||||
"vitest": "^2.1.8"
|
||||
},
|
||||
"files": [
|
||||
|
1
packages/codemirror-lang-kcl/postcss.config.js
Normal file
1
packages/codemirror-lang-kcl/postcss.config.js
Normal file
@ -0,0 +1 @@
|
||||
// This is here to prevent using the one in the root of the project.
|
@ -398,7 +398,7 @@ check-error@^2.1.1:
|
||||
resolved "https://registry.yarnpkg.com/check-error/-/check-error-2.1.1.tgz#87eb876ae71ee388fa0471fe423f494be1d96ccc"
|
||||
integrity sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==
|
||||
|
||||
debug@^4.3.7:
|
||||
debug@^4.1.1, debug@^4.3.7:
|
||||
version "4.4.0"
|
||||
resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.0.tgz#2b3f2aea2ffeb776477460267377dc8710faba8a"
|
||||
integrity sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==
|
||||
@ -471,6 +471,11 @@ function-bind@^1.1.2:
|
||||
resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c"
|
||||
integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==
|
||||
|
||||
globrex@^0.1.2:
|
||||
version "0.1.2"
|
||||
resolved "https://registry.yarnpkg.com/globrex/-/globrex-0.1.2.tgz#dd5d9ec826232730cd6793a5e33a9302985e6098"
|
||||
integrity sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==
|
||||
|
||||
hasown@^2.0.2:
|
||||
version "2.0.2"
|
||||
resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.2.tgz#003eaf91be7adc372e84ec59dc37252cedb80003"
|
||||
@ -647,6 +652,11 @@ tinyspy@^3.0.2:
|
||||
resolved "https://registry.yarnpkg.com/tinyspy/-/tinyspy-3.0.2.tgz#86dd3cf3d737b15adcf17d7887c84a75201df20a"
|
||||
integrity sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q==
|
||||
|
||||
tsconfck@^3.0.3:
|
||||
version "3.1.4"
|
||||
resolved "https://registry.yarnpkg.com/tsconfck/-/tsconfck-3.1.4.tgz#de01a15334962e2feb526824339b51be26712229"
|
||||
integrity sha512-kdqWFGVJqe+KGYvlSO9NIaWn9jT1Ny4oKVzAJsKii5eoE9snzTJzL4+MMVOMn+fikWGFmKEylcXL710V/kIPJQ==
|
||||
|
||||
typescript@^5.7.2:
|
||||
version "5.7.2"
|
||||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.7.2.tgz#3169cf8c4c8a828cde53ba9ecb3d2b1d5dd67be6"
|
||||
@ -663,6 +673,15 @@ vite-node@2.1.8:
|
||||
pathe "^1.1.2"
|
||||
vite "^5.0.0"
|
||||
|
||||
vite-tsconfig-paths@^4.3.2:
|
||||
version "4.3.2"
|
||||
resolved "https://registry.yarnpkg.com/vite-tsconfig-paths/-/vite-tsconfig-paths-4.3.2.tgz#321f02e4b736a90ff62f9086467faf4e2da857a9"
|
||||
integrity sha512-0Vd/a6po6Q+86rPlntHye7F31zA2URZMbH8M3saAZ/xR9QoGN/L21bxEGfXdWmFdNkqPpRdxFT7nmNe12e9/uA==
|
||||
dependencies:
|
||||
debug "^4.1.1"
|
||||
globrex "^0.1.2"
|
||||
tsconfck "^3.0.3"
|
||||
|
||||
vite@^5.0.0:
|
||||
version "5.4.11"
|
||||
resolved "https://registry.yarnpkg.com/vite/-/vite-5.4.11.tgz#3b415cd4aed781a356c1de5a9ebafb837715f6e5"
|
||||
|
@ -42,7 +42,7 @@ export default class StreamDemuxer extends Queue<Uint8Array> {
|
||||
// try to parse the content-length from the headers
|
||||
const length = parseInt(match[1])
|
||||
|
||||
if (isNaN(length))
|
||||
if (Number.isNaN(length))
|
||||
return Promise.reject(new Error('invalid content length'))
|
||||
|
||||
// slice the headers since we now have the content length
|
||||
|
@ -32,10 +32,9 @@ export default defineConfig({
|
||||
},
|
||||
projects: [
|
||||
{
|
||||
name: 'Google Chrome',
|
||||
name: 'chromium',
|
||||
use: {
|
||||
...devices['Desktop Chrome'],
|
||||
channel: 'chrome',
|
||||
contextOptions: {
|
||||
/* Chromium is the only one with these permission types */
|
||||
permissions: ['clipboard-write', 'clipboard-read'],
|
||||
|
@ -148,6 +148,7 @@ function HelpMenuItem({
|
||||
return (
|
||||
<li className="p-0 m-0">
|
||||
{as === 'a' ? (
|
||||
// eslint-disable-next-line jsx-a11y/no-static-element-interactions
|
||||
<a
|
||||
{...(props as React.ComponentProps<'a'>)}
|
||||
onClick={openExternalBrowserIfDesktop(
|
||||
|
@ -157,39 +157,38 @@ export const ModelingMachineProvider = ({
|
||||
'enable copilot': () => {
|
||||
editorManager.setCopilotEnabled(true)
|
||||
},
|
||||
// tsc reports this typing as perfectly fine, but eslint is complaining.
|
||||
// It's actually nonsensical, so I'm quieting.
|
||||
// eslint-disable-next-line @typescript-eslint/no-misused-promises
|
||||
'sketch exit execute': async ({
|
||||
context: { store },
|
||||
}): Promise<void> => {
|
||||
// When cancelling the sketch mode we should disable sketch mode within the engine.
|
||||
await engineCommandManager.sendSceneCommand({
|
||||
type: 'modeling_cmd_req',
|
||||
cmd_id: uuidv4(),
|
||||
cmd: { type: 'sketch_mode_disable' },
|
||||
})
|
||||
|
||||
sceneInfra.camControls.syncDirection = 'clientToEngine'
|
||||
|
||||
if (cameraProjection.current === 'perspective') {
|
||||
await sceneInfra.camControls.snapToPerspectiveBeforeHandingBackControlToEngine()
|
||||
}
|
||||
|
||||
sceneInfra.camControls.syncDirection = 'engineToClient'
|
||||
|
||||
store.videoElement?.pause()
|
||||
|
||||
return kclManager
|
||||
.executeCode()
|
||||
.then(() => {
|
||||
if (engineCommandManager.engineConnection?.idleMode) return
|
||||
|
||||
store.videoElement?.play().catch((e) => {
|
||||
console.warn('Video playing was prevented', e)
|
||||
})
|
||||
'sketch exit execute': ({ context: { store } }) => {
|
||||
// TODO: Remove this async callback. For some reason eslint wouldn't
|
||||
// let me disable @typescript-eslint/no-misused-promises for the line.
|
||||
;(async () => {
|
||||
// When cancelling the sketch mode we should disable sketch mode within the engine.
|
||||
await engineCommandManager.sendSceneCommand({
|
||||
type: 'modeling_cmd_req',
|
||||
cmd_id: uuidv4(),
|
||||
cmd: { type: 'sketch_mode_disable' },
|
||||
})
|
||||
.catch(reportRejection)
|
||||
|
||||
sceneInfra.camControls.syncDirection = 'clientToEngine'
|
||||
|
||||
if (cameraProjection.current === 'perspective') {
|
||||
await sceneInfra.camControls.snapToPerspectiveBeforeHandingBackControlToEngine()
|
||||
}
|
||||
|
||||
sceneInfra.camControls.syncDirection = 'engineToClient'
|
||||
|
||||
store.videoElement?.pause()
|
||||
|
||||
return kclManager
|
||||
.executeCode()
|
||||
.then(() => {
|
||||
if (engineCommandManager.engineConnection?.idleMode) return
|
||||
|
||||
store.videoElement?.play().catch((e) => {
|
||||
console.warn('Video playing was prevented', e)
|
||||
})
|
||||
})
|
||||
.catch(reportRejection)
|
||||
})().catch(reportRejection)
|
||||
},
|
||||
'Set mouse state': assign(({ context, event }) => {
|
||||
if (event.type !== 'Set mouse state') return {}
|
||||
@ -271,6 +270,7 @@ export const ModelingMachineProvider = ({
|
||||
cmd_id: uuidv4(),
|
||||
cmd: {
|
||||
type: 'default_camera_center_to_selection',
|
||||
camera_movement: 'vantage',
|
||||
},
|
||||
})
|
||||
.catch(reportRejection)
|
||||
|
@ -18,6 +18,7 @@ export const KclEditorMenu = ({ children }: PropsWithChildren) => {
|
||||
|
||||
return (
|
||||
<Menu>
|
||||
{/* eslint-disable-next-line jsx-a11y/no-static-element-interactions */}
|
||||
<div
|
||||
className="relative"
|
||||
onClick={(e) => {
|
||||
|
@ -313,6 +313,7 @@ export const Stream = () => {
|
||||
}
|
||||
|
||||
return (
|
||||
// eslint-disable-next-line jsx-a11y/no-static-element-interactions
|
||||
<div
|
||||
ref={videoWrapperRef}
|
||||
className="absolute inset-0 z-0"
|
||||
|
@ -374,6 +374,37 @@ export function loftSketches(
|
||||
}
|
||||
}
|
||||
|
||||
export function addSweep(
|
||||
node: Node<Program>,
|
||||
profileDeclarator: VariableDeclarator,
|
||||
pathDeclarator: VariableDeclarator
|
||||
): {
|
||||
modifiedAst: Node<Program>
|
||||
pathToNode: PathToNode
|
||||
} {
|
||||
const modifiedAst = structuredClone(node)
|
||||
const name = findUniqueName(node, KCL_DEFAULT_CONSTANT_PREFIXES.SWEEP)
|
||||
const sweep = createCallExpressionStdLib('sweep', [
|
||||
createObjectExpression({ path: createIdentifier(pathDeclarator.id.name) }),
|
||||
createIdentifier(profileDeclarator.id.name),
|
||||
])
|
||||
const declaration = createVariableDeclaration(name, sweep)
|
||||
modifiedAst.body.push(declaration)
|
||||
const pathToNode: PathToNode = [
|
||||
['body', ''],
|
||||
[modifiedAst.body.length - 1, 'index'],
|
||||
['declaration', 'VariableDeclaration'],
|
||||
['init', 'VariableDeclarator'],
|
||||
['arguments', 'CallExpression'],
|
||||
[0, 'index'],
|
||||
]
|
||||
|
||||
return {
|
||||
modifiedAst,
|
||||
pathToNode,
|
||||
}
|
||||
}
|
||||
|
||||
export function revolveSketch(
|
||||
node: Node<Program>,
|
||||
pathToNode: PathToNode,
|
||||
@ -1149,11 +1180,17 @@ export async function deleteFromSelection(
|
||||
((selection?.artifact?.type === 'wall' ||
|
||||
selection?.artifact?.type === 'cap') &&
|
||||
varDec.node.init.type === 'PipeExpression') ||
|
||||
selection.artifact?.type === 'sweep'
|
||||
selection.artifact?.type === 'sweep' ||
|
||||
selection.artifact?.type === 'plane' ||
|
||||
!selection.artifact // aka expected to be a shell at this point
|
||||
) {
|
||||
let extrudeNameToDelete = ''
|
||||
let pathToNode: PathToNode | null = null
|
||||
if (selection.artifact?.type !== 'sweep') {
|
||||
if (
|
||||
selection.artifact &&
|
||||
selection.artifact.type !== 'sweep' &&
|
||||
selection.artifact.type !== 'plane'
|
||||
) {
|
||||
const varDecName = varDec.node.id.name
|
||||
traverse(astClone, {
|
||||
enter: (node, path) => {
|
||||
@ -1169,6 +1206,17 @@ export async function deleteFromSelection(
|
||||
pathToNode = path
|
||||
extrudeNameToDelete = dec.id.name
|
||||
}
|
||||
if (
|
||||
dec.init.type === 'CallExpression' &&
|
||||
dec.init.callee.name === 'loft' &&
|
||||
dec.init.arguments?.[0].type === 'ArrayExpression' &&
|
||||
dec.init.arguments?.[0].elements.some(
|
||||
(a) => a.type === 'Identifier' && a.name === varDecName
|
||||
)
|
||||
) {
|
||||
pathToNode = path
|
||||
extrudeNameToDelete = dec.id.name
|
||||
}
|
||||
}
|
||||
},
|
||||
})
|
||||
|
@ -29,7 +29,9 @@ export function revolveSketch(
|
||||
pathToSketchNode: PathToNode,
|
||||
shouldPipe = false,
|
||||
angle: Expr = createLiteral(4),
|
||||
axis: Selections
|
||||
axisOrEdge: string,
|
||||
axis: string,
|
||||
edge: Selections
|
||||
):
|
||||
| {
|
||||
modifiedAst: Node<Program>
|
||||
@ -41,31 +43,34 @@ export function revolveSketch(
|
||||
const sketchNode = getNodeFromPath(clonedAst, pathToSketchNode)
|
||||
if (err(sketchNode)) return sketchNode
|
||||
|
||||
// testing code
|
||||
const pathToAxisSelection = getNodePathFromSourceRange(
|
||||
clonedAst,
|
||||
axis.graphSelections[0]?.codeRef.range
|
||||
)
|
||||
let generatedAxis
|
||||
|
||||
const lineNode = getNodeFromPath<CallExpression>(
|
||||
clonedAst,
|
||||
pathToAxisSelection,
|
||||
'CallExpression'
|
||||
)
|
||||
if (err(lineNode)) return lineNode
|
||||
if (axisOrEdge === 'Edge') {
|
||||
const pathToAxisSelection = getNodePathFromSourceRange(
|
||||
clonedAst,
|
||||
edge.graphSelections[0]?.codeRef.range
|
||||
)
|
||||
const lineNode = getNodeFromPath<CallExpression>(
|
||||
clonedAst,
|
||||
pathToAxisSelection,
|
||||
'CallExpression'
|
||||
)
|
||||
if (err(lineNode)) return lineNode
|
||||
|
||||
// TODO Kevin: What if |> close(%)?
|
||||
// TODO Kevin: What if opposite edge
|
||||
// TODO Kevin: What if the edge isn't planar to the sketch?
|
||||
// TODO Kevin: add a tag.
|
||||
const tagResult = mutateAstWithTagForSketchSegment(
|
||||
clonedAst,
|
||||
pathToAxisSelection
|
||||
)
|
||||
const tagResult = mutateAstWithTagForSketchSegment(
|
||||
clonedAst,
|
||||
pathToAxisSelection
|
||||
)
|
||||
|
||||
// Have the tag whether it is already created or a new one is generated
|
||||
if (err(tagResult)) return tagResult
|
||||
const { tag } = tagResult
|
||||
// Have the tag whether it is already created or a new one is generated
|
||||
if (err(tagResult)) return tagResult
|
||||
const { tag } = tagResult
|
||||
const axisSelection = edge?.graphSelections[0]?.artifact
|
||||
if (!axisSelection) return new Error('Generated axis selection is missing.')
|
||||
generatedAxis = getEdgeTagCall(tag, axisSelection)
|
||||
} else {
|
||||
generatedAxis = createLiteral(axis)
|
||||
}
|
||||
|
||||
/* Original Code */
|
||||
const { node: sketchExpression } = sketchNode
|
||||
@ -91,14 +96,12 @@ export function revolveSketch(
|
||||
shallowPath: sketchPathToDecleration,
|
||||
} = sketchVariableDeclaratorNode
|
||||
|
||||
const axisSelection = axis?.graphSelections[0]?.artifact
|
||||
|
||||
if (!axisSelection) return new Error('Axis selection is missing.')
|
||||
if (!generatedAxis) return new Error('Generated axis selection is missing.')
|
||||
|
||||
const revolveCall = createCallExpressionStdLib('revolve', [
|
||||
createObjectExpression({
|
||||
angle: angle,
|
||||
axis: getEdgeTagCall(tag, axisSelection),
|
||||
axis: generatedAxis,
|
||||
}),
|
||||
createIdentifier(sketchVariableDeclarator.id.name),
|
||||
])
|
||||
|
@ -49,17 +49,27 @@ export function addShell({
|
||||
return new Error("Couldn't find extrude")
|
||||
}
|
||||
|
||||
pathToExtrudeNode = extrudeLookupResult.pathToExtrudeNode
|
||||
// Get the sketch ref from the selection
|
||||
// TODO: this assumes the segment is piped directly from the sketch, with no intermediate `VariableDeclarator` between.
|
||||
// We must find a technique for these situations that is robust to intermediate declarations
|
||||
const sketchNode = getNodeFromPath<VariableDeclarator>(
|
||||
const extrudeNode = getNodeFromPath<VariableDeclarator>(
|
||||
modifiedAst,
|
||||
graphSelection.codeRef.pathToNode,
|
||||
extrudeLookupResult.pathToExtrudeNode,
|
||||
'VariableDeclarator'
|
||||
)
|
||||
if (err(sketchNode)) {
|
||||
return sketchNode
|
||||
const segmentNode = getNodeFromPath<VariableDeclarator>(
|
||||
modifiedAst,
|
||||
extrudeLookupResult.pathToSegmentNode,
|
||||
'VariableDeclarator'
|
||||
)
|
||||
if (err(extrudeNode) || err(segmentNode)) {
|
||||
return new Error("Couldn't find extrude")
|
||||
}
|
||||
if (extrudeNode.node.init.type === 'CallExpression') {
|
||||
pathToExtrudeNode = extrudeLookupResult.pathToExtrudeNode
|
||||
} else if (segmentNode.node.init.type === 'PipeExpression') {
|
||||
pathToExtrudeNode = extrudeLookupResult.pathToSegmentNode
|
||||
} else {
|
||||
return new Error("Couldn't find extrude")
|
||||
}
|
||||
|
||||
const selectedArtifact = graphSelection.artifact
|
||||
|
@ -705,7 +705,7 @@ describe('testing getArtifactsToUpdate', () => {
|
||||
segIds: [],
|
||||
id: expect.any(String),
|
||||
planeId: 'UUID-1',
|
||||
sweepId: '',
|
||||
sweepId: undefined,
|
||||
codeRef: {
|
||||
pathToNode: [['body', '']],
|
||||
range: [37, 64, true],
|
||||
@ -743,7 +743,7 @@ describe('testing getArtifactsToUpdate', () => {
|
||||
type: 'segment',
|
||||
id: expect.any(String),
|
||||
pathId: expect.any(String),
|
||||
surfaceId: '',
|
||||
surfaceId: undefined,
|
||||
edgeIds: [],
|
||||
codeRef: {
|
||||
range: [70, 86, true],
|
||||
@ -770,7 +770,7 @@ describe('testing getArtifactsToUpdate', () => {
|
||||
id: expect.any(String),
|
||||
consumedEdgeId: expect.any(String),
|
||||
edgeIds: [],
|
||||
surfaceId: '',
|
||||
surfaceId: undefined,
|
||||
codeRef: {
|
||||
range: [260, 299, true],
|
||||
pathToNode: [['body', '']],
|
||||
|
@ -37,7 +37,7 @@ export interface PathArtifact extends BaseArtifact {
|
||||
type: 'path'
|
||||
planeId: ArtifactId
|
||||
segIds: Array<ArtifactId>
|
||||
sweepId: ArtifactId
|
||||
sweepId?: ArtifactId
|
||||
solid2dId?: ArtifactId
|
||||
codeRef: CodeRef
|
||||
}
|
||||
@ -60,7 +60,7 @@ export interface PathArtifactRich extends BaseArtifact {
|
||||
export interface SegmentArtifact extends BaseArtifact {
|
||||
type: 'segment'
|
||||
pathId: ArtifactId
|
||||
surfaceId: ArtifactId
|
||||
surfaceId?: ArtifactId
|
||||
edgeIds: Array<ArtifactId>
|
||||
edgeCutId?: ArtifactId
|
||||
codeRef: CodeRef
|
||||
@ -68,7 +68,7 @@ export interface SegmentArtifact extends BaseArtifact {
|
||||
interface SegmentArtifactRich extends BaseArtifact {
|
||||
type: 'segment'
|
||||
path: PathArtifact
|
||||
surf: WallArtifact
|
||||
surf?: WallArtifact
|
||||
edges: Array<SweepEdge>
|
||||
edgeCut?: EdgeCut
|
||||
codeRef: CodeRef
|
||||
@ -77,7 +77,7 @@ interface SegmentArtifactRich extends BaseArtifact {
|
||||
/** A Sweep is a more generic term for extrude, revolve, loft and sweep*/
|
||||
interface SweepArtifact extends BaseArtifact {
|
||||
type: 'sweep'
|
||||
subType: 'extrusion' | 'revolve'
|
||||
subType: 'extrusion' | 'revolve' | 'loft' | 'sweep'
|
||||
pathId: string
|
||||
surfaceIds: Array<string>
|
||||
edgeIds: Array<string>
|
||||
@ -85,7 +85,7 @@ interface SweepArtifact extends BaseArtifact {
|
||||
}
|
||||
interface SweepArtifactRich extends BaseArtifact {
|
||||
type: 'sweep'
|
||||
subType: 'extrusion' | 'revolve'
|
||||
subType: 'extrusion' | 'revolve' | 'loft' | 'sweep'
|
||||
path: PathArtifact
|
||||
surfaces: Array<WallArtifact | CapArtifact>
|
||||
edges: Array<SweepEdge>
|
||||
@ -120,7 +120,7 @@ interface EdgeCut extends BaseArtifact {
|
||||
subType: 'fillet' | 'chamfer'
|
||||
consumedEdgeId: ArtifactId
|
||||
edgeIds: Array<ArtifactId>
|
||||
surfaceId: ArtifactId
|
||||
surfaceId?: ArtifactId
|
||||
codeRef: CodeRef
|
||||
}
|
||||
|
||||
@ -308,7 +308,7 @@ export function getArtifactsToUpdate({
|
||||
id,
|
||||
segIds: [],
|
||||
planeId: currentPlaneId,
|
||||
sweepId: '',
|
||||
sweepId: undefined,
|
||||
codeRef: { range, pathToNode },
|
||||
},
|
||||
})
|
||||
@ -343,7 +343,7 @@ export function getArtifactsToUpdate({
|
||||
type: 'segment',
|
||||
id,
|
||||
pathId,
|
||||
surfaceId: '',
|
||||
surfaceId: undefined,
|
||||
edgeIds: [],
|
||||
codeRef: { range, pathToNode },
|
||||
},
|
||||
@ -377,7 +377,11 @@ export function getArtifactsToUpdate({
|
||||
})
|
||||
}
|
||||
return returnArr
|
||||
} else if (cmd.type === 'extrude' || cmd.type === 'revolve') {
|
||||
} else if (
|
||||
cmd.type === 'extrude' ||
|
||||
cmd.type === 'revolve' ||
|
||||
cmd.type === 'sweep'
|
||||
) {
|
||||
const subType = cmd.type === 'extrude' ? 'extrusion' : cmd.type
|
||||
returnArr.push({
|
||||
id,
|
||||
@ -398,6 +402,33 @@ export function getArtifactsToUpdate({
|
||||
artifact: { ...path, sweepId: id },
|
||||
})
|
||||
return returnArr
|
||||
} else if (
|
||||
cmd.type === 'loft' &&
|
||||
response.type === 'modeling' &&
|
||||
response.data.modeling_response.type === 'loft'
|
||||
) {
|
||||
returnArr.push({
|
||||
id,
|
||||
artifact: {
|
||||
type: 'sweep',
|
||||
subType: 'loft',
|
||||
id,
|
||||
// TODO: make sure to revisit this choice, don't think it matters for now
|
||||
pathId: cmd.section_ids[0],
|
||||
surfaceIds: [],
|
||||
edgeIds: [],
|
||||
codeRef: { range, pathToNode },
|
||||
},
|
||||
})
|
||||
for (const sectionId of cmd.section_ids) {
|
||||
const path = getArtifact(sectionId)
|
||||
if (path?.type === 'path')
|
||||
returnArr.push({
|
||||
id: sectionId,
|
||||
artifact: { ...path, sweepId: id },
|
||||
})
|
||||
}
|
||||
return returnArr
|
||||
} else if (
|
||||
cmd.type === 'solid3d_get_extrusion_face_info' &&
|
||||
response?.type === 'modeling' &&
|
||||
@ -419,7 +450,8 @@ export function getArtifactsToUpdate({
|
||||
id: face_id,
|
||||
segId: curve_id,
|
||||
edgeCutEdgeIds: [],
|
||||
sweepId: path.sweepId,
|
||||
// TODO: Add explicit check for sweepId. Should never use ''
|
||||
sweepId: path.sweepId ?? '',
|
||||
pathIds: [],
|
||||
},
|
||||
})
|
||||
@ -427,15 +459,17 @@ export function getArtifactsToUpdate({
|
||||
id: curve_id,
|
||||
artifact: { ...seg, surfaceId: face_id },
|
||||
})
|
||||
const sweep = getArtifact(path.sweepId)
|
||||
if (sweep?.type === 'sweep') {
|
||||
returnArr.push({
|
||||
id: path.sweepId,
|
||||
artifact: {
|
||||
...sweep,
|
||||
surfaceIds: [face_id],
|
||||
},
|
||||
})
|
||||
if (path.sweepId) {
|
||||
const sweep = getArtifact(path.sweepId)
|
||||
if (sweep?.type === 'sweep') {
|
||||
returnArr.push({
|
||||
id: path.sweepId,
|
||||
artifact: {
|
||||
...sweep,
|
||||
surfaceIds: [face_id],
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -452,19 +486,22 @@ export function getArtifactsToUpdate({
|
||||
id: face_id,
|
||||
subType: cap === 'bottom' ? 'start' : 'end',
|
||||
edgeCutEdgeIds: [],
|
||||
sweepId: path.sweepId,
|
||||
// TODO: Add explicit check for sweepId. Should never use ''
|
||||
sweepId: path.sweepId ?? '',
|
||||
pathIds: [],
|
||||
},
|
||||
})
|
||||
const sweep = getArtifact(path.sweepId)
|
||||
if (sweep?.type !== 'sweep') return
|
||||
returnArr.push({
|
||||
id: path.sweepId,
|
||||
artifact: {
|
||||
...sweep,
|
||||
surfaceIds: [face_id],
|
||||
},
|
||||
})
|
||||
if (path.sweepId) {
|
||||
const sweep = getArtifact(path.sweepId)
|
||||
if (sweep?.type !== 'sweep') return
|
||||
returnArr.push({
|
||||
id: path.sweepId,
|
||||
artifact: {
|
||||
...sweep,
|
||||
surfaceIds: [face_id],
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
@ -502,7 +539,8 @@ export function getArtifactsToUpdate({
|
||||
? 'adjacent'
|
||||
: 'opposite',
|
||||
segId: cmd.edge_id,
|
||||
sweepId: path.sweepId,
|
||||
// TODO: Add explicit check for sweepId. Should never use ''
|
||||
sweepId: path.sweepId ?? '',
|
||||
},
|
||||
},
|
||||
{
|
||||
@ -513,7 +551,7 @@ export function getArtifactsToUpdate({
|
||||
},
|
||||
},
|
||||
{
|
||||
id: path.sweepId,
|
||||
id: sweep.id,
|
||||
artifact: {
|
||||
...sweep,
|
||||
edgeIds: [response.data.modeling_response.data.edge],
|
||||
@ -529,7 +567,7 @@ export function getArtifactsToUpdate({
|
||||
subType: cmd.cut_type,
|
||||
consumedEdgeId: cmd.edge_id,
|
||||
edgeIds: [],
|
||||
surfaceId: '',
|
||||
surfaceId: undefined,
|
||||
codeRef: { range, pathToNode },
|
||||
},
|
||||
})
|
||||
@ -691,10 +729,12 @@ export function expandSegment(
|
||||
{ key: segment.pathId, types: ['path'] },
|
||||
artifactGraph
|
||||
)
|
||||
const surf = getArtifactOfTypes(
|
||||
{ key: segment.surfaceId, types: ['wall'] },
|
||||
artifactGraph
|
||||
)
|
||||
const surf = segment.surfaceId
|
||||
? getArtifactOfTypes(
|
||||
{ key: segment.surfaceId, types: ['wall'] },
|
||||
artifactGraph
|
||||
)
|
||||
: undefined
|
||||
const edges = getArtifactsOfTypes(
|
||||
{ keys: segment.edgeIds, types: ['sweepEdge'] },
|
||||
artifactGraph
|
||||
@ -811,6 +851,7 @@ export function getSweepFromSuspectedSweepSurface(
|
||||
artifactGraph
|
||||
)
|
||||
if (err(path)) return path
|
||||
if (!path.sweepId) return new Error('Path does not have a sweepId')
|
||||
return getArtifactOfTypes(
|
||||
{ key: path.sweepId, types: ['sweep'] },
|
||||
artifactGraph
|
||||
@ -828,6 +869,7 @@ export function getSweepFromSuspectedPath(
|
||||
): SweepArtifact | Error {
|
||||
const path = getArtifactOfTypes({ key: id, types: ['path'] }, artifactGraph)
|
||||
if (err(path)) return path
|
||||
if (!path.sweepId) return new Error('Path does not have a sweepId')
|
||||
return getArtifactOfTypes(
|
||||
{ key: path.sweepId, types: ['sweep'] },
|
||||
artifactGraph
|
||||
|
@ -37,6 +37,10 @@ export type ModelingCommandSchema = {
|
||||
// result: (typeof EXTRUSION_RESULTS)[number]
|
||||
distance: KclCommandValue
|
||||
}
|
||||
Sweep: {
|
||||
path: Selections
|
||||
profile: Selections
|
||||
}
|
||||
Loft: {
|
||||
selection: Selections
|
||||
}
|
||||
@ -47,7 +51,9 @@ export type ModelingCommandSchema = {
|
||||
Revolve: {
|
||||
selection: Selections
|
||||
angle: KclCommandValue
|
||||
axis: Selections
|
||||
axisOrEdge: string
|
||||
axis: string
|
||||
edge: Selections
|
||||
}
|
||||
Fillet: {
|
||||
// todo
|
||||
@ -290,6 +296,33 @@ export const modelingMachineCommandConfig: StateMachineCommandSetConfig<
|
||||
},
|
||||
},
|
||||
},
|
||||
Sweep: {
|
||||
description:
|
||||
'Create a 3D body by moving a sketch region along an arbitrary path.',
|
||||
icon: 'sweep',
|
||||
status: 'development',
|
||||
needsReview: true,
|
||||
args: {
|
||||
profile: {
|
||||
inputType: 'selection',
|
||||
selectionTypes: ['solid2D'],
|
||||
required: true,
|
||||
skip: true,
|
||||
multiple: false,
|
||||
// TODO: add dry-run validation
|
||||
warningMessage:
|
||||
'The sweep workflow is new and under tested. Please break it and report issues.',
|
||||
},
|
||||
path: {
|
||||
inputType: 'selection',
|
||||
selectionTypes: ['segment', 'path'],
|
||||
required: true,
|
||||
skip: true,
|
||||
multiple: false,
|
||||
// TODO: add dry-run validation
|
||||
},
|
||||
},
|
||||
},
|
||||
Loft: {
|
||||
description: 'Create a 3D body by blending between two or more sketches',
|
||||
icon: 'loft',
|
||||
@ -324,10 +357,10 @@ export const modelingMachineCommandConfig: StateMachineCommandSetConfig<
|
||||
},
|
||||
},
|
||||
},
|
||||
// TODO: Update this configuration, copied from extrude for MVP of revolve, specifically the args.selection
|
||||
Revolve: {
|
||||
description: 'Create a 3D body by rotating a sketch region about an axis.',
|
||||
icon: 'revolve',
|
||||
status: 'development',
|
||||
needsReview: true,
|
||||
args: {
|
||||
selection: {
|
||||
@ -336,9 +369,34 @@ export const modelingMachineCommandConfig: StateMachineCommandSetConfig<
|
||||
multiple: false, // TODO: multiple selection
|
||||
required: true,
|
||||
skip: true,
|
||||
warningMessage:
|
||||
'The revolve workflow is new and under tested. Please break it and report issues.',
|
||||
},
|
||||
axisOrEdge: {
|
||||
inputType: 'options',
|
||||
required: true,
|
||||
defaultValue: 'Axis',
|
||||
options: [
|
||||
{ name: 'Axis', isCurrent: true, value: 'Axis' },
|
||||
{ name: 'Edge', isCurrent: false, value: 'Edge' },
|
||||
],
|
||||
},
|
||||
axis: {
|
||||
required: true,
|
||||
required: (commandContext) =>
|
||||
['Axis'].includes(
|
||||
commandContext.argumentsToSubmit.axisOrEdge as string
|
||||
),
|
||||
inputType: 'options',
|
||||
options: [
|
||||
{ name: 'X Axis', isCurrent: true, value: 'X' },
|
||||
{ name: 'Y Axis', isCurrent: false, value: 'Y' },
|
||||
],
|
||||
},
|
||||
edge: {
|
||||
required: (commandContext) =>
|
||||
['Edge'].includes(
|
||||
commandContext.argumentsToSubmit.axisOrEdge as string
|
||||
),
|
||||
inputType: 'selection',
|
||||
selectionTypes: ['segment', 'sweepEdge', 'edgeCutEdge'],
|
||||
multiple: false,
|
||||
|
@ -68,7 +68,7 @@ export const revolveAxisValidator = async ({
|
||||
}
|
||||
|
||||
const sketchSelection = artifact.pathId
|
||||
let edgeSelection = data.axis.graphSelections[0].artifact?.id
|
||||
let edgeSelection = data.edge.graphSelections[0].artifact?.id
|
||||
|
||||
if (!sketchSelection) {
|
||||
return 'Unable to revolve, sketch is missing'
|
||||
@ -101,7 +101,7 @@ export const revolveAxisValidator = async ({
|
||||
return true
|
||||
} else {
|
||||
// return error message for the toast
|
||||
return 'Unable to revolve with selected axis'
|
||||
return 'Unable to revolve with selected edge'
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -53,6 +53,7 @@ export const KCL_DEFAULT_CONSTANT_PREFIXES = {
|
||||
SKETCH: 'sketch',
|
||||
EXTRUDE: 'extrude',
|
||||
LOFT: 'loft',
|
||||
SWEEP: 'sweep',
|
||||
SHELL: 'shell',
|
||||
SEGMENT: 'seg',
|
||||
REVOLVE: 'revolve',
|
||||
|
@ -15,6 +15,7 @@ import {
|
||||
StateMachineCommandSetSchema,
|
||||
} from './commandTypes'
|
||||
import { DEV } from 'env'
|
||||
import { IS_NIGHTLY_OR_DEBUG } from 'routes/Settings'
|
||||
|
||||
interface CreateMachineCommandProps<
|
||||
T extends AnyStateMachine,
|
||||
@ -84,7 +85,7 @@ export function createMachineCommand<
|
||||
} else if ('status' in commandConfig) {
|
||||
const { status } = commandConfig
|
||||
if (status === 'inactive') return null
|
||||
if (status === 'development' && !DEV) return null
|
||||
if (status === 'development' && !(DEV || IS_NIGHTLY_OR_DEBUG)) return null
|
||||
}
|
||||
|
||||
const icon = ('icon' in commandConfig && commandConfig.icon) || undefined
|
||||
|
@ -137,7 +137,7 @@ See later source ranges for more context. about the sweep`,
|
||||
{ key: artifact.pathId, types: ['path'] },
|
||||
artifactGraph
|
||||
)
|
||||
if (!err(path)) {
|
||||
if (!err(path) && path.sweepId) {
|
||||
const sweep = getArtifactOfTypes(
|
||||
{ key: path.sweepId, types: ['sweep'] },
|
||||
artifactGraph
|
||||
|
@ -670,6 +670,7 @@ export function codeToIdSelections(
|
||||
}
|
||||
}
|
||||
if (type === 'extrude-wall' && entry.artifact.type === 'segment') {
|
||||
if (!entry.artifact.surfaceId) return
|
||||
const wall = engineCommandManager.artifactGraph.get(
|
||||
entry.artifact.surfaceId
|
||||
)
|
||||
@ -714,6 +715,7 @@ export function codeToIdSelections(
|
||||
(type === 'end-cap' || type === 'start-cap') &&
|
||||
entry.artifact.type === 'path'
|
||||
) {
|
||||
if (!entry.artifact.sweepId) return
|
||||
const extrusion = getArtifactOfTypes(
|
||||
{
|
||||
key: entry.artifact.sweepId,
|
||||
|
@ -8,6 +8,7 @@ import {
|
||||
modelingMachine,
|
||||
pipeHasCircle,
|
||||
} from 'machines/modelingMachine'
|
||||
import { IS_NIGHTLY_OR_DEBUG } from 'routes/Settings'
|
||||
import { EventFrom, StateFrom } from 'xstate'
|
||||
|
||||
export type ToolbarModeName = 'modeling' | 'sketching'
|
||||
@ -103,7 +104,7 @@ export const toolbarConfig: Record<ToolbarModeName, ToolbarMode> = {
|
||||
data: { name: 'Revolve', groupId: 'modeling' },
|
||||
}),
|
||||
icon: 'revolve',
|
||||
status: DEV ? 'available' : 'kcl-only',
|
||||
status: DEV || IS_NIGHTLY_OR_DEBUG ? 'available' : 'kcl-only',
|
||||
title: 'Revolve',
|
||||
hotkey: 'R',
|
||||
description:
|
||||
@ -118,17 +119,21 @@ export const toolbarConfig: Record<ToolbarModeName, ToolbarMode> = {
|
||||
},
|
||||
{
|
||||
id: 'sweep',
|
||||
onClick: () => console.error('Sweep not yet implemented'),
|
||||
onClick: ({ commandBarSend }) =>
|
||||
commandBarSend({
|
||||
type: 'Find and select command',
|
||||
data: { name: 'Sweep', groupId: 'modeling' },
|
||||
}),
|
||||
icon: 'sweep',
|
||||
status: 'unavailable',
|
||||
status: DEV || IS_NIGHTLY_OR_DEBUG ? 'available' : 'kcl-only',
|
||||
title: 'Sweep',
|
||||
hotkey: 'W',
|
||||
description:
|
||||
'Create a 3D body by moving a sketch region along an arbitrary path.',
|
||||
links: [
|
||||
{
|
||||
label: 'GitHub discussion',
|
||||
url: 'https://github.com/KittyCAD/modeling-app/discussions/498',
|
||||
label: 'KCL docs',
|
||||
url: 'https://zoo.dev/docs/kcl/sweep',
|
||||
},
|
||||
],
|
||||
},
|
||||
@ -161,7 +166,7 @@ export const toolbarConfig: Record<ToolbarModeName, ToolbarMode> = {
|
||||
data: { name: 'Fillet', groupId: 'modeling' },
|
||||
}),
|
||||
icon: 'fillet3d',
|
||||
status: DEV ? 'available' : 'kcl-only',
|
||||
status: DEV || IS_NIGHTLY_OR_DEBUG ? 'available' : 'kcl-only',
|
||||
title: 'Fillet',
|
||||
hotkey: 'F',
|
||||
description: 'Round the edges of a 3D solid.',
|
||||
|
@ -345,7 +345,7 @@ export function onDragNumberCalculation(text: string, e: MouseEvent) {
|
||||
)
|
||||
const newVal = roundOff(addition, precision)
|
||||
|
||||
if (isNaN(newVal)) {
|
||||
if (Number.isNaN(newVal)) {
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -45,6 +45,7 @@ import {
|
||||
import { revolveSketch } from 'lang/modifyAst/addRevolve'
|
||||
import {
|
||||
addOffsetPlane,
|
||||
addSweep,
|
||||
deleteFromSelection,
|
||||
extrudeSketch,
|
||||
loftSketches,
|
||||
@ -266,6 +267,7 @@ export type ModelingMachineEvent =
|
||||
| { type: 'Export'; data: ModelingCommandSchema['Export'] }
|
||||
| { type: 'Make'; data: ModelingCommandSchema['Make'] }
|
||||
| { type: 'Extrude'; data?: ModelingCommandSchema['Extrude'] }
|
||||
| { type: 'Sweep'; data?: ModelingCommandSchema['Sweep'] }
|
||||
| { type: 'Loft'; data?: ModelingCommandSchema['Loft'] }
|
||||
| { type: 'Shell'; data?: ModelingCommandSchema['Shell'] }
|
||||
| { type: 'Revolve'; data?: ModelingCommandSchema['Revolve'] }
|
||||
@ -685,7 +687,7 @@ export const modelingMachine = setup({
|
||||
if (event.type !== 'Revolve') return
|
||||
;(async () => {
|
||||
if (!event.data) return
|
||||
const { selection, angle, axis } = event.data
|
||||
const { selection, angle, axis, edge, axisOrEdge } = event.data
|
||||
let ast = kclManager.ast
|
||||
if (
|
||||
'variableName' in angle &&
|
||||
@ -710,7 +712,9 @@ export const modelingMachine = setup({
|
||||
'variableName' in angle
|
||||
? angle.variableIdentifierAst
|
||||
: angle.valueAst,
|
||||
axis
|
||||
axisOrEdge,
|
||||
axis,
|
||||
edge
|
||||
)
|
||||
if (trap(revolveSketchRes)) return
|
||||
const { modifiedAst, pathToRevolveArg } = revolveSketchRes
|
||||
@ -1542,6 +1546,66 @@ export const modelingMachine = setup({
|
||||
}
|
||||
}
|
||||
),
|
||||
sweepAstMod: fromPromise(
|
||||
async ({
|
||||
input,
|
||||
}: {
|
||||
input: ModelingCommandSchema['Sweep'] | undefined
|
||||
}) => {
|
||||
if (!input) return new Error('No input provided')
|
||||
// Extract inputs
|
||||
const ast = kclManager.ast
|
||||
const { profile, path } = input
|
||||
|
||||
// Find the profile declaration
|
||||
const profileNodePath = getNodePathFromSourceRange(
|
||||
ast,
|
||||
profile.graphSelections[0].codeRef.range
|
||||
)
|
||||
const profileNode = getNodeFromPath<VariableDeclarator>(
|
||||
ast,
|
||||
profileNodePath,
|
||||
'VariableDeclarator'
|
||||
)
|
||||
if (err(profileNode)) {
|
||||
return new Error("Couldn't parse profile selection")
|
||||
}
|
||||
const profileDeclarator = profileNode.node
|
||||
|
||||
// Find the path declaration
|
||||
const pathNodePath = getNodePathFromSourceRange(
|
||||
ast,
|
||||
path.graphSelections[0].codeRef.range
|
||||
)
|
||||
const pathNode = getNodeFromPath<VariableDeclarator>(
|
||||
ast,
|
||||
pathNodePath,
|
||||
'VariableDeclarator'
|
||||
)
|
||||
if (err(pathNode)) {
|
||||
return new Error("Couldn't parse path selection")
|
||||
}
|
||||
const pathDeclarator = pathNode.node
|
||||
|
||||
// Perform the sweep
|
||||
const sweepRes = addSweep(ast, profileDeclarator, pathDeclarator)
|
||||
const updateAstResult = await kclManager.updateAst(
|
||||
sweepRes.modifiedAst,
|
||||
true,
|
||||
{
|
||||
focusPath: [sweepRes.pathToNode],
|
||||
}
|
||||
)
|
||||
|
||||
await codeManager.updateEditorWithAstAndWriteToFile(
|
||||
updateAstResult.newAst
|
||||
)
|
||||
|
||||
if (updateAstResult?.selections) {
|
||||
editorManager.selectRange(updateAstResult?.selections)
|
||||
}
|
||||
}
|
||||
),
|
||||
loftAstMod: fromPromise(
|
||||
async ({
|
||||
input,
|
||||
@ -1737,6 +1801,11 @@ export const modelingMachine = setup({
|
||||
reenter: false,
|
||||
},
|
||||
|
||||
Sweep: {
|
||||
target: 'Applying sweep',
|
||||
reenter: true,
|
||||
},
|
||||
|
||||
Loft: {
|
||||
target: 'Applying loft',
|
||||
reenter: true,
|
||||
@ -2529,6 +2598,19 @@ export const modelingMachine = setup({
|
||||
},
|
||||
},
|
||||
|
||||
'Applying sweep': {
|
||||
invoke: {
|
||||
src: 'sweepAstMod',
|
||||
id: 'sweepAstMod',
|
||||
input: ({ event }) => {
|
||||
if (event.type !== 'Sweep') return undefined
|
||||
return event.data
|
||||
},
|
||||
onDone: ['idle'],
|
||||
onError: ['idle'],
|
||||
},
|
||||
},
|
||||
|
||||
'Applying loft': {
|
||||
invoke: {
|
||||
src: 'loftAstMod',
|
||||
|
@ -320,6 +320,11 @@ export function getAutoUpdater(): AppUpdater {
|
||||
}
|
||||
|
||||
app.on('ready', () => {
|
||||
// Disable auto updater on non-versioned builds
|
||||
if (packageJSON.version === '0.0.0') {
|
||||
return
|
||||
}
|
||||
|
||||
const autoUpdater = getAutoUpdater()
|
||||
// TODO: we're getting `Error: Response ends without calling any handlers` with our setup,
|
||||
// so at the moment this isn't worth enabling
|
||||
|
@ -41,13 +41,13 @@ export default function Export() {
|
||||
export to almost any CAD software.
|
||||
</p>
|
||||
<p className="my-4">
|
||||
Our teammate David is working on the file format, check out{' '}
|
||||
Our teammate Katie is working on the file format, check out{' '}
|
||||
<a
|
||||
href="https://www.youtube.com/watch?v=8SuW0qkYCZo"
|
||||
href="https://github.com/KhronosGroup/glTF/pull/2343"
|
||||
target="_blank"
|
||||
rel="noreferrer noopener"
|
||||
>
|
||||
his talk with the Metaverse Standards Forum
|
||||
her standards proposal on GitHub
|
||||
</a>
|
||||
!
|
||||
</p>
|
||||
|
@ -32,6 +32,8 @@ export const PACKAGE_NAME = isDesktop()
|
||||
|
||||
export const IS_NIGHTLY = PACKAGE_NAME.indexOf('-nightly') > -1
|
||||
|
||||
export const IS_NIGHTLY_OR_DEBUG = IS_NIGHTLY || APP_VERSION === '0.0.0'
|
||||
|
||||
export function getReleaseUrl(version: string = APP_VERSION) {
|
||||
return `https://github.com/KittyCAD/modeling-app/releases/tag/${
|
||||
IS_NIGHTLY ? 'nightly-' : ''
|
||||
|
146
src/wasm-lib/Cargo.lock
generated
146
src/wasm-lib/Cargo.lock
generated
@ -176,7 +176,7 @@ checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.95",
|
||||
"syn 2.0.96",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -187,7 +187,7 @@ checksum = "3f934833b4b7233644e5848f235df3f57ed8c80f1528a26c3dfa13d2147fa056"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.95",
|
||||
"syn 2.0.96",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -204,7 +204,7 @@ checksum = "3c87f3f15e7794432337fc718554eaa4dc8f04c9677a950ffe366f20a162ae42"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.95",
|
||||
"syn 2.0.96",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -474,7 +474,7 @@ dependencies = [
|
||||
"heck 0.5.0",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.95",
|
||||
"syn 2.0.96",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -665,7 +665,7 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"strsim",
|
||||
"syn 2.0.95",
|
||||
"syn 2.0.96",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -676,7 +676,7 @@ checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806"
|
||||
dependencies = [
|
||||
"darling_core",
|
||||
"quote",
|
||||
"syn 2.0.95",
|
||||
"syn 2.0.96",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -723,7 +723,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "derive-docs"
|
||||
version = "0.1.33"
|
||||
version = "0.1.34"
|
||||
dependencies = [
|
||||
"Inflector",
|
||||
"anyhow",
|
||||
@ -737,7 +737,7 @@ dependencies = [
|
||||
"rustfmt-wrapper",
|
||||
"serde",
|
||||
"serde_tokenstream",
|
||||
"syn 2.0.95",
|
||||
"syn 2.0.96",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -748,7 +748,38 @@ checksum = "67e77553c4162a157adbf834ebae5b415acbecbeafc7a74b0e886657506a7611"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.95",
|
||||
"syn 2.0.96",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "derive_builder"
|
||||
version = "0.20.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "507dfb09ea8b7fa618fcf76e953f4f5e192547945816d5358edffe39f6f94947"
|
||||
dependencies = [
|
||||
"derive_builder_macro",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "derive_builder_core"
|
||||
version = "0.20.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2d5bcf7b024d6835cfb3d473887cd966994907effbe9227e8c8219824d06c4e8"
|
||||
dependencies = [
|
||||
"darling",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.96",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "derive_builder_macro"
|
||||
version = "0.20.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ab63b0e2bf4d5928aff72e83a7dace85d7bba5fe12dcc3c5a572d78caffd3f3c"
|
||||
dependencies = [
|
||||
"derive_builder_core",
|
||||
"syn 2.0.96",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -791,7 +822,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.95",
|
||||
"syn 2.0.96",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -829,7 +860,7 @@ checksum = "a1ab991c1362ac86c61ab6f556cff143daa22e5a15e4e189df818b2fd19fe65b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.95",
|
||||
"syn 2.0.96",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -990,7 +1021,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.95",
|
||||
"syn 2.0.96",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1086,7 +1117,7 @@ dependencies = [
|
||||
"inflections",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.95",
|
||||
"syn 2.0.96",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1132,17 +1163,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "handlebars"
|
||||
version = "6.2.0"
|
||||
version = "6.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fd4ccde012831f9a071a637b0d4e31df31c0f6c525784b35ae76a9ac6bc1e315"
|
||||
checksum = "3d6b224b95c1e668ac0270325ad563b2eef1469fbbb8959bc7c692c844b813d9"
|
||||
dependencies = [
|
||||
"derive_builder",
|
||||
"log",
|
||||
"num-order",
|
||||
"pest",
|
||||
"pest_derive",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"thiserror 1.0.68",
|
||||
"thiserror 2.0.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1494,7 +1526,7 @@ checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.95",
|
||||
"syn 2.0.96",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1684,7 +1716,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "kcl-lib"
|
||||
version = "0.2.29"
|
||||
version = "0.2.30"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"approx 0.5.1",
|
||||
@ -1752,7 +1784,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "kcl-test-server"
|
||||
version = "0.1.19"
|
||||
version = "0.1.20"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"hyper 0.14.30",
|
||||
@ -1819,9 +1851,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "kittycad-modeling-cmds"
|
||||
version = "0.2.86"
|
||||
version = "0.2.89"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "65e34a8eeb4fff5167666d1f2bc36c95d08ab3a0f736a02c8d33a8cde21cfd8d"
|
||||
checksum = "ce9e58b34645facea36bc9f4868877bbe6fcac01b92896825e8d4f2f7c71dbd6"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"chrono",
|
||||
@ -1852,7 +1884,7 @@ dependencies = [
|
||||
"kittycad-modeling-cmds-macros-impl",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.95",
|
||||
"syn 2.0.96",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1863,7 +1895,7 @@ checksum = "fdb4ee23cc996aa2dca7584d410e8826e08161e1ac4335bb646d5ede33f37cb3"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.95",
|
||||
"syn 2.0.96",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -2013,7 +2045,7 @@ checksum = "dcf09caffaac8068c346b6df2a7fc27a177fd20b39421a39ce0a211bde679a6c"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.95",
|
||||
"syn 2.0.96",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -2312,7 +2344,7 @@ dependencies = [
|
||||
"regex",
|
||||
"regex-syntax 0.8.5",
|
||||
"structmeta",
|
||||
"syn 2.0.95",
|
||||
"syn 2.0.96",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -2326,7 +2358,7 @@ dependencies = [
|
||||
"regex",
|
||||
"regex-syntax 0.8.5",
|
||||
"structmeta",
|
||||
"syn 2.0.95",
|
||||
"syn 2.0.96",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -2366,7 +2398,7 @@ dependencies = [
|
||||
"pest_meta",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.95",
|
||||
"syn 2.0.96",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -2424,7 +2456,7 @@ checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.95",
|
||||
"syn 2.0.96",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -2554,7 +2586,7 @@ dependencies = [
|
||||
"proc-macro-error-attr2",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.95",
|
||||
"syn 2.0.96",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -2613,7 +2645,7 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"pyo3-macros-backend",
|
||||
"quote",
|
||||
"syn 2.0.95",
|
||||
"syn 2.0.96",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -2626,7 +2658,7 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"pyo3-build-config",
|
||||
"quote",
|
||||
"syn 2.0.95",
|
||||
"syn 2.0.96",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -3161,7 +3193,7 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"serde_derive_internals",
|
||||
"syn 2.0.95",
|
||||
"syn 2.0.96",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -3225,7 +3257,7 @@ checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.95",
|
||||
"syn 2.0.96",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -3236,7 +3268,7 @@ checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.95",
|
||||
"syn 2.0.96",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -3260,7 +3292,7 @@ checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.95",
|
||||
"syn 2.0.96",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -3281,7 +3313,7 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"serde",
|
||||
"syn 2.0.95",
|
||||
"syn 2.0.96",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -3430,7 +3462,7 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"structmeta-derive",
|
||||
"syn 2.0.95",
|
||||
"syn 2.0.96",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -3441,7 +3473,7 @@ checksum = "152a0b65a590ff6c3da95cabe2353ee04e6167c896b28e3b14478c2636c922fc"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.95",
|
||||
"syn 2.0.96",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -3463,7 +3495,7 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"rustversion",
|
||||
"syn 2.0.95",
|
||||
"syn 2.0.96",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -3506,9 +3538,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.95"
|
||||
version = "2.0.96"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "46f71c0377baf4ef1cc3e3402ded576dccc315800fbc62dfc7fe04b009773b4a"
|
||||
checksum = "d5d0adab1ae378d7f53bdebc67a39f1f151407ef230f0ce2883572f5d8985c80"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@ -3532,7 +3564,7 @@ checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.95",
|
||||
"syn 2.0.96",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -3640,7 +3672,7 @@ checksum = "a7c61ec9a6f64d2793d8a45faba21efbe3ced62a886d44c36a009b2b519b4c7e"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.95",
|
||||
"syn 2.0.96",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -3651,7 +3683,7 @@ checksum = "22efd00f33f93fa62848a7cab956c3d38c8d43095efda1decfc2b3a5dc0b8972"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.95",
|
||||
"syn 2.0.96",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -3763,7 +3795,7 @@ checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.95",
|
||||
"syn 2.0.96",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -3905,7 +3937,7 @@ checksum = "84fd902d4e0b9a4b27f2f440108dc034e1758628a9b702f8ec61ad66355422fa"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.95",
|
||||
"syn 2.0.96",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -3933,7 +3965,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.95",
|
||||
"syn 2.0.96",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -4016,7 +4048,7 @@ checksum = "0e9d8656589772eeec2cf7a8264d9cda40fb28b9bc53118ceb9e8c07f8f38730"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.95",
|
||||
"syn 2.0.96",
|
||||
"termcolor",
|
||||
]
|
||||
|
||||
@ -4159,9 +4191,9 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
|
||||
|
||||
[[package]]
|
||||
name = "uuid"
|
||||
version = "1.11.0"
|
||||
version = "1.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f8c5f0a0af699448548ad1a2fbf920fb4bee257eae39953ba95cb84891a0446a"
|
||||
checksum = "b913a3b5fe84142e269d63cc62b64319ccaf89b748fc31fe025177f767a756c4"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
"serde",
|
||||
@ -4195,7 +4227,7 @@ dependencies = [
|
||||
"proc-macro-error2",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.95",
|
||||
"syn 2.0.96",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -4256,7 +4288,7 @@ dependencies = [
|
||||
"log",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.95",
|
||||
"syn 2.0.96",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
@ -4292,7 +4324,7 @@ checksum = "30d7a95b763d3c45903ed6c81f156801839e5ee968bb07e534c44df0fcd330c2"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.95",
|
||||
"syn 2.0.96",
|
||||
"wasm-bindgen-backend",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
@ -4673,7 +4705,7 @@ checksum = "28cc31741b18cb6f1d5ff12f5b7523e3d6eb0852bbbad19d73905511d9849b95"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.95",
|
||||
"syn 2.0.96",
|
||||
"synstructure",
|
||||
]
|
||||
|
||||
@ -4695,7 +4727,7 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.95",
|
||||
"syn 2.0.96",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -4715,7 +4747,7 @@ checksum = "0ea7b4a3637ea8669cedf0f1fd5c286a17f3de97b8dd5a70a6c167a1730e63a5"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.95",
|
||||
"syn 2.0.96",
|
||||
"synstructure",
|
||||
]
|
||||
|
||||
@ -4744,7 +4776,7 @@ checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.95",
|
||||
"syn 2.0.96",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -76,7 +76,7 @@ members = [
|
||||
[workspace.dependencies]
|
||||
http = "1"
|
||||
kittycad = { version = "0.3.28", default-features = false, features = ["js", "requests"] }
|
||||
kittycad-modeling-cmds = { version = "0.2.86", features = [
|
||||
kittycad-modeling-cmds = { version = "0.2.89", features = [
|
||||
"ts-rs",
|
||||
"websocket",
|
||||
] }
|
||||
|
@ -1,7 +1,7 @@
|
||||
[package]
|
||||
name = "derive-docs"
|
||||
description = "A tool for generating documentation from Rust derive macros"
|
||||
version = "0.1.33"
|
||||
version = "0.1.34"
|
||||
edition = "2021"
|
||||
license = "MIT"
|
||||
repository = "https://github.com/KittyCAD/modeling-app"
|
||||
@ -20,7 +20,7 @@ quote = "1"
|
||||
regex = "1.11"
|
||||
serde = { version = "1.0.217", features = ["derive"] }
|
||||
serde_tokenstream = "0.2"
|
||||
syn = { version = "2.0.95", features = ["full"] }
|
||||
syn = { version = "2.0.96", features = ["full"] }
|
||||
|
||||
[dev-dependencies]
|
||||
anyhow = "1.0.95"
|
||||
|
@ -1,7 +1,7 @@
|
||||
[package]
|
||||
name = "kcl-test-server"
|
||||
description = "A test server for KCL"
|
||||
version = "0.1.19"
|
||||
version = "0.1.20"
|
||||
edition = "2021"
|
||||
license = "MIT"
|
||||
|
||||
|
@ -502,4 +502,6 @@ impl kcl_lib::EngineManager for EngineConnection {
|
||||
})),
|
||||
}
|
||||
}
|
||||
|
||||
async fn close(&self) {}
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
[package]
|
||||
name = "kcl-lib"
|
||||
description = "KittyCAD Language implementation and tools"
|
||||
version = "0.2.29"
|
||||
version = "0.2.30"
|
||||
edition = "2021"
|
||||
license = "MIT"
|
||||
repository = "https://github.com/KittyCAD/modeling-app"
|
||||
@ -22,7 +22,7 @@ clap = { version = "4.5.23", default-features = false, optional = true, features
|
||||
] }
|
||||
convert_case = "0.6.0"
|
||||
dashmap = "6.1.0"
|
||||
derive-docs = { version = "0.1.33", path = "../derive-docs" }
|
||||
derive-docs = { version = "0.1.34", path = "../derive-docs" }
|
||||
dhat = { version = "0.3", optional = true }
|
||||
fnv = "1.0.7"
|
||||
form_urlencoded = "1.2.1"
|
||||
@ -112,7 +112,7 @@ tabled = ["dep:tabled"]
|
||||
base64 = "0.22.1"
|
||||
criterion = { version = "0.5.1", features = ["async_tokio"] }
|
||||
expectorate = "1.1.0"
|
||||
handlebars = "6.2.0"
|
||||
handlebars = "6.3.0"
|
||||
iai = "0.1"
|
||||
image = { version = "0.25.5", default-features = false, features = ["png"] }
|
||||
insta = { version = "1.41.1", features = ["json", "filters", "redactions"] }
|
||||
|
@ -1024,6 +1024,36 @@ mod tests {
|
||||
assert_eq!(snippet, r#"hole(${0:holeSketch}, ${1:%})${}"#);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn get_autocomplete_snippet_helix() {
|
||||
let helix_fn: Box<dyn StdLibFn> = Box::new(crate::std::helix::Helix);
|
||||
let snippet = helix_fn.to_autocomplete_snippet().unwrap();
|
||||
assert_eq!(
|
||||
snippet,
|
||||
r#"helix({
|
||||
revolutions = ${0:3.14},
|
||||
angleStart = ${1:3.14},
|
||||
ccw = ${2:false},
|
||||
radius = ${3:3.14},
|
||||
axis = ${4:"X"},
|
||||
})${}"#
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn get_autocomplete_snippet_helix_revolutions() {
|
||||
let helix_fn: Box<dyn StdLibFn> = Box::new(crate::std::helix::HelixRevolutions);
|
||||
let snippet = helix_fn.to_autocomplete_snippet().unwrap();
|
||||
assert_eq!(
|
||||
snippet,
|
||||
r#"helixRevolutions({
|
||||
revolutions = ${0:3.14},
|
||||
angleStart = ${1:3.14},
|
||||
ccw = ${2:false},
|
||||
}, ${3:%})${}"#
|
||||
);
|
||||
}
|
||||
|
||||
// We want to test the snippets we compile at lsp start.
|
||||
#[test]
|
||||
fn get_all_stdlib_autocomplete_snippets() {
|
||||
|
@ -37,9 +37,10 @@ enum SocketHealth {
|
||||
}
|
||||
|
||||
type WebSocketTcpWrite = futures::stream::SplitSink<tokio_tungstenite::WebSocketStream<reqwest::Upgraded>, WsMsg>;
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Debug)]
|
||||
pub struct EngineConnection {
|
||||
engine_req_tx: mpsc::Sender<ToEngineReq>,
|
||||
shutdown_tx: mpsc::Sender<()>,
|
||||
responses: Arc<DashMap<uuid::Uuid, WebSocketResponse>>,
|
||||
pending_errors: Arc<Mutex<Vec<String>>>,
|
||||
#[allow(dead_code)]
|
||||
@ -130,21 +131,49 @@ struct ToEngineReq {
|
||||
|
||||
impl EngineConnection {
|
||||
/// Start waiting for incoming engine requests, and send each one over the WebSocket to the engine.
|
||||
async fn start_write_actor(mut tcp_write: WebSocketTcpWrite, mut engine_req_rx: mpsc::Receiver<ToEngineReq>) {
|
||||
while let Some(req) = engine_req_rx.recv().await {
|
||||
let ToEngineReq { req, request_sent } = req;
|
||||
let res = if let WebSocketRequest::ModelingCmdReq(ModelingCmdReq {
|
||||
cmd: ModelingCmd::ImportFiles { .. },
|
||||
cmd_id: _,
|
||||
}) = &req
|
||||
{
|
||||
// Send it as binary.
|
||||
Self::inner_send_to_engine_binary(req, &mut tcp_write).await
|
||||
} else {
|
||||
Self::inner_send_to_engine(req, &mut tcp_write).await
|
||||
};
|
||||
let _ = request_sent.send(res);
|
||||
async fn start_write_actor(
|
||||
mut tcp_write: WebSocketTcpWrite,
|
||||
mut engine_req_rx: mpsc::Receiver<ToEngineReq>,
|
||||
mut shutdown_rx: mpsc::Receiver<()>,
|
||||
) {
|
||||
loop {
|
||||
tokio::select! {
|
||||
maybe_req = engine_req_rx.recv() => {
|
||||
match maybe_req {
|
||||
Some(ToEngineReq { req, request_sent }) => {
|
||||
// Decide whether to send as binary or text,
|
||||
// then send to the engine.
|
||||
let res = if let WebSocketRequest::ModelingCmdReq(ModelingCmdReq {
|
||||
cmd: ModelingCmd::ImportFiles { .. },
|
||||
cmd_id: _,
|
||||
}) = &req
|
||||
{
|
||||
Self::inner_send_to_engine_binary(req, &mut tcp_write).await
|
||||
} else {
|
||||
Self::inner_send_to_engine(req, &mut tcp_write).await
|
||||
};
|
||||
|
||||
// Let the caller know we’ve sent the request (ok or error).
|
||||
let _ = request_sent.send(res);
|
||||
}
|
||||
None => {
|
||||
// The engine_req_rx channel has closed, so no more requests.
|
||||
// We'll gracefully exit the loop and close the engine.
|
||||
break;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// If we get a shutdown signal, close the engine immediately and return.
|
||||
_ = shutdown_rx.recv() => {
|
||||
let _ = Self::inner_close_engine(&mut tcp_write).await;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If we exit the loop (e.g. engine_req_rx was closed),
|
||||
// still gracefully close the engine before returning.
|
||||
let _ = Self::inner_close_engine(&mut tcp_write).await;
|
||||
}
|
||||
|
||||
@ -194,7 +223,8 @@ impl EngineConnection {
|
||||
|
||||
let (tcp_write, tcp_read) = ws_stream.split();
|
||||
let (engine_req_tx, engine_req_rx) = mpsc::channel(10);
|
||||
tokio::task::spawn(Self::start_write_actor(tcp_write, engine_req_rx));
|
||||
let (shutdown_tx, shutdown_rx) = mpsc::channel(1);
|
||||
tokio::task::spawn(Self::start_write_actor(tcp_write, engine_req_rx, shutdown_rx));
|
||||
|
||||
let mut tcp_read = TcpRead { stream: tcp_read };
|
||||
|
||||
@ -304,6 +334,7 @@ impl EngineConnection {
|
||||
|
||||
Ok(EngineConnection {
|
||||
engine_req_tx,
|
||||
shutdown_tx,
|
||||
tcp_read_handle: Arc::new(TcpReadHandle {
|
||||
handle: Arc::new(tcp_read_handle),
|
||||
}),
|
||||
@ -484,4 +515,15 @@ impl EngineManager for EngineConnection {
|
||||
fn get_session_data(&self) -> Option<ModelingSessionData> {
|
||||
self.session_data.lock().unwrap().clone()
|
||||
}
|
||||
|
||||
async fn close(&self) {
|
||||
let _ = self.shutdown_tx.send(()).await;
|
||||
loop {
|
||||
if let Ok(guard) = self.socket_health.lock() {
|
||||
if *guard == SocketHealth::Inactive {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -160,4 +160,6 @@ impl crate::engine::EngineManager for EngineConnection {
|
||||
})),
|
||||
}
|
||||
}
|
||||
|
||||
async fn close(&self) {}
|
||||
}
|
||||
|
@ -267,4 +267,7 @@ impl crate::engine::EngineManager for EngineConnection {
|
||||
|
||||
Ok(ws_result)
|
||||
}
|
||||
|
||||
// maybe we can actually impl this here? not sure how atm.
|
||||
async fn close(&self) {}
|
||||
}
|
||||
|
@ -600,6 +600,9 @@ pub trait EngineManager: std::fmt::Debug + Send + Sync + 'static {
|
||||
fn get_session_data(&self) -> Option<ModelingSessionData> {
|
||||
None
|
||||
}
|
||||
|
||||
/// Close the engine connection and wait for it to finish.
|
||||
async fn close(&self);
|
||||
}
|
||||
|
||||
#[derive(Debug, Hash, Eq, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||
|
@ -2013,10 +2013,13 @@ impl ExecutorContext {
|
||||
// AND if we aren't in wasm it doesn't really matter.
|
||||
Ok(())
|
||||
}
|
||||
// Given an old ast, old program memory and new ast, find the parts of the code that need to be
|
||||
// re-executed.
|
||||
// This function should never error, because in the case of any internal error, we should just pop
|
||||
// the cache.
|
||||
/// Given an old ast, old program memory and new ast, find the parts of the code that need to be
|
||||
/// re-executed.
|
||||
/// This function should never error, because in the case of any internal error, we should just pop
|
||||
/// the cache.
|
||||
///
|
||||
/// Returns `None` when there are no changes to the program, i.e. it is
|
||||
/// fully cached.
|
||||
pub async fn get_changed_program(&self, info: CacheInformation) -> Option<CacheResult> {
|
||||
let Some(old) = info.old else {
|
||||
// We have no old info, we need to re-execute the whole thing.
|
||||
@ -2137,7 +2140,7 @@ impl ExecutorContext {
|
||||
}
|
||||
}
|
||||
std::cmp::Ordering::Equal => {
|
||||
// currently unreachable, but lets pretend like the code
|
||||
// currently unreachable, but let's pretend like the code
|
||||
// above can do something meaningful here for when we get
|
||||
// to diffing and yanking chunks of the program apart.
|
||||
|
||||
@ -2236,7 +2239,10 @@ impl ExecutorContext {
|
||||
)
|
||||
})?;
|
||||
// Move the artifact commands to simplify cache management.
|
||||
exec_state.global.artifact_commands = self.engine.take_artifact_commands();
|
||||
exec_state
|
||||
.global
|
||||
.artifact_commands
|
||||
.extend(self.engine.take_artifact_commands());
|
||||
let session_data = self.engine.get_session_data();
|
||||
Ok(session_data)
|
||||
}
|
||||
@ -2626,6 +2632,10 @@ impl ExecutorContext {
|
||||
|
||||
self.prepare_snapshot().await
|
||||
}
|
||||
|
||||
pub async fn close(&self) {
|
||||
self.engine.close().await;
|
||||
}
|
||||
}
|
||||
|
||||
/// For each argument given,
|
||||
|
@ -1108,7 +1108,7 @@ impl<'a> FromKclValue<'a> for super::helix::HelixData {
|
||||
fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
|
||||
let obj = arg.as_object()?;
|
||||
let_field_of!(obj, revolutions);
|
||||
let_field_of!(obj, length);
|
||||
let_field_of!(obj, length?);
|
||||
let_field_of!(obj, ccw?);
|
||||
let_field_of!(obj, radius);
|
||||
let_field_of!(obj, axis);
|
||||
|
@ -26,8 +26,9 @@ pub struct HelixData {
|
||||
/// The default is `false`.
|
||||
#[serde(default)]
|
||||
pub ccw: bool,
|
||||
/// Length of the helix.
|
||||
pub length: f64,
|
||||
/// Length of the helix. This is not necessary if the helix is created around an edge. If not
|
||||
/// given the length of the edge is used.
|
||||
pub length: Option<f64>,
|
||||
/// Radius of the helix.
|
||||
pub radius: f64,
|
||||
/// Axis to use as mirror.
|
||||
@ -64,7 +65,7 @@ pub async fn helix(exec_state: &mut ExecState, args: Args) -> Result<KclValue, K
|
||||
///
|
||||
/// ```no_run
|
||||
/// // Create a helix around an edge.
|
||||
/// /*helper001 = startSketchOn('XZ')
|
||||
/// helper001 = startSketchOn('XZ')
|
||||
/// |> startProfileAt([0, 0], %)
|
||||
/// |> line([0, 10], %, $edge001)
|
||||
///
|
||||
@ -80,7 +81,7 @@ pub async fn helix(exec_state: &mut ExecState, args: Args) -> Result<KclValue, K
|
||||
/// // Create a spring by sweeping around the helix path.
|
||||
/// springSketch = startSketchOn('XY')
|
||||
/// |> circle({ center = [0, 0], radius = 1 }, %)
|
||||
/// |> sweep({ path = helixPath }, %)*/
|
||||
/// //|> sweep({ path = helixPath }, %)
|
||||
/// ```
|
||||
#[stdlib {
|
||||
name = "helix",
|
||||
@ -105,12 +106,20 @@ async fn inner_helix(data: HelixData, exec_state: &mut ExecState, args: Args) ->
|
||||
Axis3dOrEdgeReference::Axis(axis) => {
|
||||
let (axis, origin) = axis.axis_and_origin()?;
|
||||
|
||||
// Make sure they gave us a length.
|
||||
let Some(length) = data.length else {
|
||||
return Err(KclError::Semantic(crate::errors::KclErrorDetails {
|
||||
message: "Length is required when creating a helix around an axis.".to_string(),
|
||||
source_ranges: vec![args.source_range],
|
||||
}));
|
||||
};
|
||||
|
||||
args.batch_modeling_cmd(
|
||||
exec_state.next_uuid(),
|
||||
ModelingCmd::from(mcmd::EntityMakeHelixFromParams {
|
||||
radius: data.radius,
|
||||
is_clockwise: !data.ccw,
|
||||
length: LengthUnit(data.length),
|
||||
length: LengthUnit(length),
|
||||
revolutions: data.revolutions,
|
||||
start_angle: Angle::from_degrees(data.angle_start),
|
||||
axis,
|
||||
@ -119,25 +128,21 @@ async fn inner_helix(data: HelixData, exec_state: &mut ExecState, args: Args) ->
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
Axis3dOrEdgeReference::Edge(_edge) => {
|
||||
/*let edge_id = edge.get_engine_id(exec_state, &args)?;
|
||||
Axis3dOrEdgeReference::Edge(edge) => {
|
||||
let edge_id = edge.get_engine_id(exec_state, &args)?;
|
||||
|
||||
args.batch_modeling_cmd(
|
||||
exec_state.next_uuid(),
|
||||
ModelingCmd::from(mcmd::EntityMakeHelixFromEdge {
|
||||
radius: data.radius,
|
||||
is_clockwise: !data.ccw,
|
||||
length: LengthUnit(data.length),
|
||||
length: data.length.map(LengthUnit),
|
||||
revolutions: data.revolutions,
|
||||
start_angle: Angle::from_degrees(data.angle_start),
|
||||
edge_id,
|
||||
}),
|
||||
)
|
||||
.await?;*/
|
||||
return Err(KclError::Unimplemented(crate::errors::KclErrorDetails {
|
||||
message: "Helix around edge is not yet implemented".to_string(),
|
||||
source_ranges: vec![args.source_range],
|
||||
}));
|
||||
.await?;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -156,5 +156,8 @@ async fn inner_loft(
|
||||
.await?;
|
||||
|
||||
// Using the first sketch as the base curve, idk we might want to change this later.
|
||||
do_post_extrude(sketches[0].clone(), 0.0, exec_state, args).await
|
||||
let mut sketch = sketches[0].clone();
|
||||
// Override its id with the loft id so we can get its faces later
|
||||
sketch.id = id;
|
||||
do_post_extrude(sketch, 0.0, exec_state, args).await
|
||||
}
|
||||
|
@ -25,10 +25,12 @@ pub async fn execute_and_snapshot(
|
||||
) -> Result<image::DynamicImage, ExecError> {
|
||||
let ctx = new_context(units, true, project_directory).await?;
|
||||
let program = Program::parse_no_errs(code).map_err(KclErrorWithOutputs::no_outputs)?;
|
||||
do_execute_and_snapshot(&ctx, program)
|
||||
let res = do_execute_and_snapshot(&ctx, program)
|
||||
.await
|
||||
.map(|(_state, snap)| snap)
|
||||
.map_err(|err| err.error)
|
||||
.map_err(|err| err.error);
|
||||
ctx.close().await;
|
||||
res
|
||||
}
|
||||
|
||||
/// Executes a kcl program and takes a snapshot of the result.
|
||||
@ -39,14 +41,16 @@ pub async fn execute_and_snapshot_ast(
|
||||
project_directory: Option<PathBuf>,
|
||||
) -> Result<(ProgramMemory, Vec<Operation>, Vec<ArtifactCommand>, image::DynamicImage), ExecErrorWithState> {
|
||||
let ctx = new_context(units, true, project_directory).await?;
|
||||
do_execute_and_snapshot(&ctx, ast).await.map(|(state, snap)| {
|
||||
let res = do_execute_and_snapshot(&ctx, ast).await.map(|(state, snap)| {
|
||||
(
|
||||
state.mod_local.memory,
|
||||
state.mod_local.operations,
|
||||
state.global.artifact_commands,
|
||||
snap,
|
||||
)
|
||||
})
|
||||
});
|
||||
ctx.close().await;
|
||||
res
|
||||
}
|
||||
|
||||
pub async fn execute_and_snapshot_no_auth(
|
||||
@ -56,10 +60,12 @@ pub async fn execute_and_snapshot_no_auth(
|
||||
) -> Result<image::DynamicImage, ExecError> {
|
||||
let ctx = new_context(units, false, project_directory).await?;
|
||||
let program = Program::parse_no_errs(code).map_err(KclErrorWithOutputs::no_outputs)?;
|
||||
do_execute_and_snapshot(&ctx, program)
|
||||
let res = do_execute_and_snapshot(&ctx, program)
|
||||
.await
|
||||
.map(|(_state, snap)| snap)
|
||||
.map_err(|err| err.error)
|
||||
.map_err(|err| err.error);
|
||||
ctx.close().await;
|
||||
res
|
||||
}
|
||||
|
||||
async fn do_execute_and_snapshot(
|
||||
@ -80,6 +86,9 @@ async fn do_execute_and_snapshot(
|
||||
.map_err(|e| ExecError::BadPng(e.to_string()))
|
||||
.and_then(|x| x.decode().map_err(|e| ExecError::BadPng(e.to_string())))
|
||||
.map_err(|err| ExecErrorWithState::new(err, exec_state.clone()))?;
|
||||
|
||||
ctx.close().await;
|
||||
|
||||
Ok((exec_state, img))
|
||||
}
|
||||
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 198 KiB |
@ -1,14 +1,18 @@
|
||||
//! Cache testing framework.
|
||||
|
||||
use anyhow::Result;
|
||||
use kcl_lib::ExecError;
|
||||
use kcl_lib::{ExecError, ExecState};
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Variation<'a> {
|
||||
code: &'a str,
|
||||
settings: &'a kcl_lib::ExecutorSettings,
|
||||
}
|
||||
|
||||
async fn cache_test(test_name: &str, variations: Vec<Variation<'_>>) -> Result<Vec<(String, image::DynamicImage)>> {
|
||||
async fn cache_test(
|
||||
test_name: &str,
|
||||
variations: Vec<Variation<'_>>,
|
||||
) -> Result<Vec<(String, image::DynamicImage, ExecState)>> {
|
||||
let first = variations
|
||||
.first()
|
||||
.ok_or_else(|| anyhow::anyhow!("No variations provided for test '{}'", test_name))?;
|
||||
@ -42,7 +46,7 @@ async fn cache_test(test_name: &str, variations: Vec<Variation<'_>>) -> Result<V
|
||||
// Save the snapshot.
|
||||
let path = crate::assert_out(&format!("cache_{}_{}", test_name, index), &img);
|
||||
|
||||
img_results.push((path, img));
|
||||
img_results.push((path, img, exec_state.clone()));
|
||||
|
||||
// Prepare the last state.
|
||||
old_ast_state = Some(kcl_lib::OldAstState {
|
||||
@ -52,6 +56,8 @@ async fn cache_test(test_name: &str, variations: Vec<Variation<'_>>) -> Result<V
|
||||
});
|
||||
}
|
||||
|
||||
ctx.close().await;
|
||||
|
||||
Ok(img_results)
|
||||
}
|
||||
|
||||
@ -214,3 +220,47 @@ async fn kcl_test_cache_change_highlight_edges_changes_visual() {
|
||||
|
||||
assert!(first.1 != second.1);
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn kcl_test_cache_add_line_preserves_artifact_commands() {
|
||||
let code = r#"sketch001 = startSketchOn('XY')
|
||||
|> startProfileAt([5.5229, 5.25217], %)
|
||||
|> line([10.50433, -1.19122], %)
|
||||
|> line([8.01362, -5.48731], %)
|
||||
|> line([-1.02877, -6.76825], %)
|
||||
|> line([-11.53311, 2.81559], %)
|
||||
|> close(%)
|
||||
"#;
|
||||
// Use a new statement; don't extend the prior pipeline. This allows us to
|
||||
// detect a prefix.
|
||||
let code_with_extrude = code.to_owned()
|
||||
+ r#"
|
||||
extrude(4, sketch001)
|
||||
"#;
|
||||
|
||||
let result = cache_test(
|
||||
"add_line_preserves_artifact_commands",
|
||||
vec![
|
||||
Variation {
|
||||
code,
|
||||
settings: &Default::default(),
|
||||
},
|
||||
Variation {
|
||||
code: code_with_extrude.as_str(),
|
||||
settings: &Default::default(),
|
||||
},
|
||||
],
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let first = result.first().unwrap();
|
||||
let second = result.last().unwrap();
|
||||
|
||||
assert!(
|
||||
first.2.global.artifact_commands.len() < second.2.global.artifact_commands.len(),
|
||||
"Second should have all the artifact commands of the first, plus more. first={:?}, second={:?}",
|
||||
first.2.global.artifact_commands.len(),
|
||||
second.2.global.artifact_commands.len()
|
||||
);
|
||||
}
|
||||
|
Binary file not shown.
After Width: | Height: | Size: 28 KiB |
Binary file not shown.
After Width: | Height: | Size: 47 KiB |
10
vitest.workspace.ts
Normal file
10
vitest.workspace.ts
Normal file
@ -0,0 +1,10 @@
|
||||
import { defineWorkspace } from 'vitest/config'
|
||||
|
||||
export default defineWorkspace([
|
||||
'./vite.main.config.ts',
|
||||
'./vite.base.config.ts',
|
||||
'./vite.config.ts',
|
||||
'./vite.preload.config.ts',
|
||||
'./vite.renderer.config.ts',
|
||||
'./packages/codemirror-lang-kcl/vitest.main.config.ts',
|
||||
])
|
Reference in New Issue
Block a user