Compare commits

..

1 Commits

Author SHA1 Message Date
fcd4fc49d4 push, reduce, and map with kw args 2025-01-09 10:39:25 -05:00
86 changed files with 1623 additions and 2604 deletions

View File

@ -5,32 +5,16 @@
}, },
"plugins": [ "plugins": [
"css-modules", "css-modules",
"jest",
"jsx-a11y",
"react",
"react-hooks",
"suggest-no-throw", "suggest-no-throw",
"testing-library",
"@typescript-eslint"
], ],
"extends": [ "extends": [
"plugin:css-modules/recommended", "react-app",
"plugin:jsx-a11y/recommended", "react-app/jest",
"plugin:react-hooks/recommended" "plugin:css-modules/recommended"
], ],
"rules": { "rules": {
"@typescript-eslint/no-floating-promises": "error", "@typescript-eslint/no-floating-promises": "error",
"@typescript-eslint/no-misused-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": [ "semi": [
"error", "error",
"never" "never"
@ -41,9 +25,6 @@
"overrides": [ "overrides": [
{ {
"files": ["e2e/**/*.ts"], // Update the pattern based on your file structure "files": ["e2e/**/*.ts"], // Update the pattern based on your file structure
"extends": [
"plugin:testing-library/react"
],
"rules": { "rules": {
"suggest-no-throw/suggest-no-throw": "off", "suggest-no-throw/suggest-no-throw": "off",
"testing-library/prefer-screen-queries": "off", "testing-library/prefer-screen-queries": "off",
@ -52,9 +33,6 @@
}, },
{ {
"files": ["src/**/*.test.ts"], "files": ["src/**/*.test.ts"],
"extends": [
"plugin:testing-library/react"
],
"rules": { "rules": {
"suggest-no-throw/suggest-no-throw": "off", "suggest-no-throw/suggest-no-throw": "off",
} }

View File

@ -21,7 +21,7 @@ if [[ ! -f "test-results/.last-run.json" ]]; then
fi fi
retry=1 retry=1
max_retrys=5 max_retrys=4
# retry failed tests, doing our own retries because using inbuilt playwright retries causes connection issues # retry failed tests, doing our own retries because using inbuilt playwright retries causes connection issues
while [[ $retry -le $max_retrys ]]; do while [[ $retry -le $max_retrys ]]; do

View File

@ -6,29 +6,23 @@
version: 2 version: 2
updates: updates:
- package-ecosystem: 'npm' # See documentation for possible values - package-ecosystem: 'npm' # See documentation for possible values
directories: directory: '/' # Location of package manifests
- '/'
- '/packages/codemirror-lang-kcl/'
- '/packages/codemirror-lsp-client/'
schedule: schedule:
interval: weekly interval: 'weekly'
day: monday
reviewers: reviewers:
- franknoirot - franknoirot
- irev-dev - irev-dev
- package-ecosystem: 'github-actions' # See documentation for possible values - package-ecosystem: 'github-actions' # See documentation for possible values
directory: '/' # Location of package manifests directory: '/' # Location of package manifests
schedule: schedule:
interval: weekly interval: 'weekly'
day: monday
reviewers: reviewers:
- adamchalmers - adamchalmers
- jessfraz - jessfraz
- package-ecosystem: 'cargo' # See documentation for possible values - package-ecosystem: 'cargo' # See documentation for possible values
directory: '/src/wasm-lib/' # Location of package manifests directory: '/src/wasm-lib/' # Location of package manifests
schedule: schedule:
interval: weekly interval: 'weekly'
day: monday
reviewers: reviewers:
- adamchalmers - adamchalmers
- jessfraz - jessfraz
@ -36,6 +30,3 @@ updates:
serde-dependencies: serde-dependencies:
patterns: patterns:
- "serde*" - "serde*"
wasm-bindgen-deps:
patterns:
- "wasm-bindgen*"

View File

@ -1,32 +0,0 @@
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

File diff suppressed because one or more lines are too long

View File

@ -17,8 +17,8 @@ push(array: [KclValue], elem: KclValue) -> KclValue
| Name | Type | Description | Required | | Name | Type | Description | Required |
|----------|------|-------------|----------| |----------|------|-------------|----------|
| `array` | [`[KclValue]`](/docs/kcl/types/KclValue) | | Yes | | `array` | [`[KclValue]`](/docs/kcl/types/KclValue) | The array to push to. | Yes |
| `elem` | [`KclValue`](/docs/kcl/types/KclValue) | Any KCL value. | Yes | | `elem` | [`KclValue`](/docs/kcl/types/KclValue) | The element to push to the array. | Yes |
### Returns ### Returns
@ -29,7 +29,7 @@ push(array: [KclValue], elem: KclValue) -> KclValue
```js ```js
arr = [1, 2, 3] arr = [1, 2, 3]
new_arr = push(arr, 4) new_arr = push(arr, elem = 4)
assertEqual(new_arr[3], 4, 0.00001, "4 was added to the end of the array") assertEqual(new_arr[3], 4, 0.00001, "4 was added to the end of the array")
``` ```

View File

@ -17,9 +17,9 @@ reduce(array: [KclValue], start: KclValue, reduce_fn: FunctionParam) -> KclValue
| Name | Type | Description | Required | | Name | Type | Description | Required |
|----------|------|-------------|----------| |----------|------|-------------|----------|
| `array` | [`[KclValue]`](/docs/kcl/types/KclValue) | | Yes | | `array` | [`[KclValue]`](/docs/kcl/types/KclValue) | The array to reduce. | Yes |
| `start` | [`KclValue`](/docs/kcl/types/KclValue) | Any KCL value. | Yes | | `start` | [`KclValue`](/docs/kcl/types/KclValue) | The starting value for the reduction. | Yes |
| `reduce_fn` | `FunctionParam` | | Yes | | `reduce_fn` | `FunctionParam` | The function to reduce the array with. | Yes |
### Returns ### Returns
@ -38,7 +38,7 @@ fn add(a, b) {
// It uses the `reduce` function, to call the `add` function on every // It uses the `reduce` function, to call the `add` function on every
// element of the `arr` parameter. The starting value is 0. // element of the `arr` parameter. The starting value is 0.
fn sum(arr) { fn sum(arr) {
return reduce(arr, 0, add) return reduce(arr, start = 0, reduce_fn = add)
} }
/* The above is basically like this pseudo-code: /* The above is basically like this pseudo-code:
@ -61,7 +61,7 @@ assertEqual(sum([1, 2, 3]), 6, 0.00001, "1 + 2 + 3 summed is 6")
// an anonymous `add` function as its parameter, instead of declaring a // an anonymous `add` function as its parameter, instead of declaring a
// named function outside. // named function outside.
arr = [1, 2, 3] arr = [1, 2, 3]
sum = reduce(arr, 0, fn(i, result_so_far) { sum = reduce(arr, start = 0, reduce_fn = fn(i, result_so_far) {
return i + result_so_far return i + result_so_far
}) })
@ -85,7 +85,7 @@ fn decagon(radius) {
// Use a `reduce` to draw the remaining decagon sides. // Use a `reduce` to draw the remaining decagon sides.
// For each number in the array 1..10, run the given function, // For each number in the array 1..10, run the given function,
// which takes a partially-sketched decagon and adds one more edge to it. // which takes a partially-sketched decagon and adds one more edge to it.
fullDecagon = reduce([1..10], startOfDecagonSketch, fn(i, partialDecagon) { fullDecagon = reduce([1..10], start = startOfDecagonSketch, reduce_fn = fn(i, partialDecagon) {
// Draw one edge of the decagon. // Draw one edge of the decagon.
x = cos(stepAngle * i) * radius x = cos(stepAngle * i) * radius
y = sin(stepAngle * i) * radius y = sin(stepAngle * i) * radius

View File

@ -75843,6 +75843,7 @@
"required": [ "required": [
"angleStart", "angleStart",
"axis", "axis",
"length",
"radius", "radius",
"revolutions" "revolutions"
], ],
@ -75863,10 +75864,9 @@
"type": "boolean" "type": "boolean"
}, },
"length": { "length": {
"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.", "description": "Length of the helix.",
"type": "number", "type": "number",
"format": "double", "format": "double"
"nullable": true
}, },
"radius": { "radius": {
"description": "Radius of the helix.", "description": "Radius of the helix.",
@ -76962,7 +76962,7 @@
"deprecated": false, "deprecated": false,
"examples": [ "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 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 }, %)" ""
] ]
}, },
{ {
@ -106563,7 +106563,7 @@
"summary": "Apply a function to every element of a list.", "summary": "Apply a function to every element of a list.",
"description": "Given a list like `[a, b, c]`, and a function like `f`, returns `[f(a), f(b), f(c)]`", "description": "Given a list like `[a, b, c]`, and a function like `f`, returns `[f(a), f(b), f(c)]`",
"tags": [], "tags": [],
"keywordArguments": false, "keywordArguments": true,
"args": [ "args": [
{ {
"name": "array", "name": "array",
@ -108641,7 +108641,8 @@
} }
}, },
"required": true, "required": true,
"labelRequired": true "description": "The array to map.",
"labelRequired": false
}, },
{ {
"name": "map_fn", "name": "map_fn",
@ -110716,6 +110717,7 @@
} }
}, },
"required": true, "required": true,
"description": "The function to map the array with.",
"labelRequired": true "labelRequired": true
} }
], ],
@ -112800,8 +112802,8 @@
"unpublished": false, "unpublished": false,
"deprecated": false, "deprecated": false,
"examples": [ "examples": [
"r = 10 // radius\nfn drawCircle(id) {\n return startSketchOn(\"XY\")\n |> circle({ center = [id * 2 * r, 0], radius = r }, %)\n}\n\n// Call `drawCircle`, passing in each element of the array.\n// The outputs from each `drawCircle` form a new array,\n// which is the return value from `map`.\ncircles = map([1..3], drawCircle)", "r = 10 // radius\nfn drawCircle(id) {\n return startSketchOn(\"XY\")\n |> circle({ center = [id * 2 * r, 0], radius = r }, %)\n}\n\n// Call `drawCircle`, passing in each element of the array.\n// The outputs from each `drawCircle` form a new array,\n// which is the return value from `map`.\ncircles = map([1..3], map_fn = drawCircle)",
"r = 10 // radius\n// Call `map`, using an anonymous function instead of a named one.\ncircles = map([1..3], fn(id) {\n return startSketchOn(\"XY\")\n |> circle({ center = [id * 2 * r, 0], radius = r }, %)\n})" "r = 10 // radius\n// Call `map`, using an anonymous function instead of a named one.\ncircles = map([1..3], map_fn = fn(id) {\n return startSketchOn(\"XY\")\n |> circle({ center = [id * 2 * r, 0], radius = r }, %)\n})"
] ]
}, },
{ {
@ -146967,7 +146969,7 @@
"summary": "Append an element to the end of an array.", "summary": "Append an element to the end of an array.",
"description": "Returns a new array with the element appended.", "description": "Returns a new array with the element appended.",
"tags": [], "tags": [],
"keywordArguments": false, "keywordArguments": true,
"args": [ "args": [
{ {
"name": "array", "name": "array",
@ -149045,7 +149047,8 @@
} }
}, },
"required": true, "required": true,
"labelRequired": true "description": "The array to push to.",
"labelRequired": false
}, },
{ {
"name": "elem", "name": "elem",
@ -151802,6 +151805,7 @@
} }
}, },
"required": true, "required": true,
"description": "The element to push to the array.",
"labelRequired": true "labelRequired": true
} }
], ],
@ -154565,7 +154569,7 @@
"unpublished": false, "unpublished": false,
"deprecated": false, "deprecated": false,
"examples": [ "examples": [
"arr = [1, 2, 3]\nnew_arr = push(arr, 4)\nassertEqual(new_arr[3], 4, 0.00001, \"4 was added to the end of the array\")" "arr = [1, 2, 3]\nnew_arr = push(arr, elem = 4)\nassertEqual(new_arr[3], 4, 0.00001, \"4 was added to the end of the array\")"
] ]
}, },
{ {
@ -154573,7 +154577,7 @@
"summary": "Take a starting value. Then, for each element of an array, calculate the next value,", "summary": "Take a starting value. Then, for each element of an array, calculate the next value,",
"description": "using the previous value and the element.", "description": "using the previous value and the element.",
"tags": [], "tags": [],
"keywordArguments": false, "keywordArguments": true,
"args": [ "args": [
{ {
"name": "array", "name": "array",
@ -156651,7 +156655,8 @@
} }
}, },
"required": true, "required": true,
"labelRequired": true "description": "The array to reduce.",
"labelRequired": false
}, },
{ {
"name": "start", "name": "start",
@ -159408,6 +159413,7 @@
} }
}, },
"required": true, "required": true,
"description": "The starting value for the reduction.",
"labelRequired": true "labelRequired": true
}, },
{ {
@ -161483,6 +161489,7 @@
} }
}, },
"required": true, "required": true,
"description": "The function to reduce the array with.",
"labelRequired": true "labelRequired": true
} }
], ],
@ -164246,9 +164253,9 @@
"unpublished": false, "unpublished": false,
"deprecated": false, "deprecated": false,
"examples": [ "examples": [
"// This function adds two numbers.\nfn add(a, b) {\n return a + b\n}\n\n// This function adds an array of numbers.\n// It uses the `reduce` function, to call the `add` function on every\n// element of the `arr` parameter. The starting value is 0.\nfn sum(arr) {\n return reduce(arr, 0, add)\n}\n\n/* The above is basically like this pseudo-code:\nfn sum(arr):\n sumSoFar = 0\n for i in arr:\n sumSoFar = add(sumSoFar, i)\n return sumSoFar */\n\n\n// We use `assertEqual` to check that our `sum` function gives the\n// expected result. It's good to check your work!\nassertEqual(sum([1, 2, 3]), 6, 0.00001, \"1 + 2 + 3 summed is 6\")", "// This function adds two numbers.\nfn add(a, b) {\n return a + b\n}\n\n// This function adds an array of numbers.\n// It uses the `reduce` function, to call the `add` function on every\n// element of the `arr` parameter. The starting value is 0.\nfn sum(arr) {\n return reduce(arr, start = 0, reduce_fn = add)\n}\n\n/* The above is basically like this pseudo-code:\nfn sum(arr):\n sumSoFar = 0\n for i in arr:\n sumSoFar = add(sumSoFar, i)\n return sumSoFar */\n\n\n// We use `assertEqual` to check that our `sum` function gives the\n// expected result. It's good to check your work!\nassertEqual(sum([1, 2, 3]), 6, 0.00001, \"1 + 2 + 3 summed is 6\")",
"// This example works just like the previous example above, but it uses\n// an anonymous `add` function as its parameter, instead of declaring a\n// named function outside.\narr = [1, 2, 3]\nsum = reduce(arr, 0, fn(i, result_so_far) {\n return i + result_so_far\n})\n\n// We use `assertEqual` to check that our `sum` function gives the\n// expected result. It's good to check your work!\nassertEqual(sum, 6, 0.00001, \"1 + 2 + 3 summed is 6\")", "// This example works just like the previous example above, but it uses\n// an anonymous `add` function as its parameter, instead of declaring a\n// named function outside.\narr = [1, 2, 3]\nsum = reduce(arr, start = 0, reduce_fn = fn(i, result_so_far) {\n return i + result_so_far\n})\n\n// We use `assertEqual` to check that our `sum` function gives the\n// expected result. It's good to check your work!\nassertEqual(sum, 6, 0.00001, \"1 + 2 + 3 summed is 6\")",
"// Declare a function that sketches a decagon.\nfn decagon(radius) {\n // Each side of the decagon is turned this many degrees from the previous angle.\n stepAngle = 1 / 10 * tau()\n\n // Start the decagon sketch at this point.\n startOfDecagonSketch = startSketchOn('XY')\n |> startProfileAt([cos(0) * radius, sin(0) * radius], %)\n\n // Use a `reduce` to draw the remaining decagon sides.\n // For each number in the array 1..10, run the given function,\n // which takes a partially-sketched decagon and adds one more edge to it.\n fullDecagon = reduce([1..10], startOfDecagonSketch, fn(i, partialDecagon) {\n // Draw one edge of the decagon.\n x = cos(stepAngle * i) * radius\n y = sin(stepAngle * i) * radius\n return lineTo([x, y], partialDecagon)\n })\n\n return fullDecagon\n}\n\n/* The `decagon` above is basically like this pseudo-code:\nfn decagon(radius):\n stepAngle = (1/10) * tau()\n plane = startSketchOn('XY')\n startOfDecagonSketch = startProfileAt([(cos(0)*radius), (sin(0) * radius)], plane)\n\n // Here's the reduce part.\n partialDecagon = startOfDecagonSketch\n for i in [1..10]:\n x = cos(stepAngle * i) * radius\n y = sin(stepAngle * i) * radius\n partialDecagon = lineTo([x, y], partialDecagon)\n fullDecagon = partialDecagon // it's now full\n return fullDecagon */\n\n\n// Use the `decagon` function declared above, to sketch a decagon with radius 5.\ndecagon(5.0)\n |> close(%)" "// Declare a function that sketches a decagon.\nfn decagon(radius) {\n // Each side of the decagon is turned this many degrees from the previous angle.\n stepAngle = 1 / 10 * tau()\n\n // Start the decagon sketch at this point.\n startOfDecagonSketch = startSketchOn('XY')\n |> startProfileAt([cos(0) * radius, sin(0) * radius], %)\n\n // Use a `reduce` to draw the remaining decagon sides.\n // For each number in the array 1..10, run the given function,\n // which takes a partially-sketched decagon and adds one more edge to it.\n fullDecagon = reduce([1..10], start = startOfDecagonSketch, reduce_fn = fn(i, partialDecagon) {\n // Draw one edge of the decagon.\n x = cos(stepAngle * i) * radius\n y = sin(stepAngle * i) * radius\n return lineTo([x, y], partialDecagon)\n })\n\n return fullDecagon\n}\n\n/* The `decagon` above is basically like this pseudo-code:\nfn decagon(radius):\n stepAngle = (1/10) * tau()\n plane = startSketchOn('XY')\n startOfDecagonSketch = startProfileAt([(cos(0)*radius), (sin(0) * radius)], plane)\n\n // Here's the reduce part.\n partialDecagon = startOfDecagonSketch\n for i in [1..10]:\n x = cos(stepAngle * i) * radius\n y = sin(stepAngle * i) * radius\n partialDecagon = lineTo([x, y], partialDecagon)\n fullDecagon = partialDecagon // it's now full\n return fullDecagon */\n\n\n// Use the `decagon` function declared above, to sketch a decagon with radius 5.\ndecagon(5.0)\n |> close(%)"
] ]
}, },
{ {

View File

@ -19,7 +19,7 @@ Data for a helix.
| `revolutions` |`number`| Number of revolutions. | No | | `revolutions` |`number`| Number of revolutions. | No |
| `angleStart` |`number`| Start angle (in degrees). | No | | `angleStart` |`number`| Start angle (in degrees). | No |
| `ccw` |`boolean`| Is the helix rotation counter clockwise? The default is `false`. | No | | `ccw` |`boolean`| Is the helix rotation counter clockwise? The default is `false`. | 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 | | `length` |`number`| Length of the helix. | No |
| `radius` |`number`| Radius of the helix. | No | | `radius` |`number`| Radius of the helix. | No |
| `axis` |[`Axis3dOrEdgeReference`](/docs/kcl/types/Axis3dOrEdgeReference)| Axis to use as mirror. | No | | `axis` |[`Axis3dOrEdgeReference`](/docs/kcl/types/Axis3dOrEdgeReference)| Axis to use as mirror. | No |

View File

@ -121,23 +121,18 @@ export class AuthenticatedTronApp {
export const fixtures = { export const fixtures = {
cmdBar: async ({ page }: { page: Page }, use: any) => { cmdBar: async ({ page }: { page: Page }, use: any) => {
// eslint-disable-next-line react-hooks/rules-of-hooks
await use(new CmdBarFixture(page)) await use(new CmdBarFixture(page))
}, },
editor: async ({ page }: { page: Page }, use: any) => { editor: async ({ page }: { page: Page }, use: any) => {
// eslint-disable-next-line react-hooks/rules-of-hooks
await use(new EditorFixture(page)) await use(new EditorFixture(page))
}, },
toolbar: async ({ page }: { page: Page }, use: any) => { toolbar: async ({ page }: { page: Page }, use: any) => {
// eslint-disable-next-line react-hooks/rules-of-hooks
await use(new ToolbarFixture(page)) await use(new ToolbarFixture(page))
}, },
scene: async ({ page }: { page: Page }, use: any) => { scene: async ({ page }: { page: Page }, use: any) => {
// eslint-disable-next-line react-hooks/rules-of-hooks
await use(new SceneFixture(page)) await use(new SceneFixture(page))
}, },
homePage: async ({ page }: { page: Page }, use: any) => { homePage: async ({ page }: { page: Page }, use: any) => {
// eslint-disable-next-line react-hooks/rules-of-hooks
await use(new HomePageFixture(page)) await use(new HomePageFixture(page))
}, },
} }

View File

@ -14,7 +14,6 @@ export class ToolbarFixture {
extrudeButton!: Locator extrudeButton!: Locator
loftButton!: Locator loftButton!: Locator
sweepButton!: Locator
shellButton!: Locator shellButton!: Locator
offsetPlaneButton!: Locator offsetPlaneButton!: Locator
startSketchBtn!: Locator startSketchBtn!: Locator
@ -41,7 +40,6 @@ export class ToolbarFixture {
this.page = page this.page = page
this.extrudeButton = page.getByTestId('extrude') this.extrudeButton = page.getByTestId('extrude')
this.loftButton = page.getByTestId('loft') this.loftButton = page.getByTestId('loft')
this.sweepButton = page.getByTestId('sweep')
this.shellButton = page.getByTestId('shell') this.shellButton = page.getByTestId('shell')
this.offsetPlaneButton = page.getByTestId('plane-offset') this.offsetPlaneButton = page.getByTestId('plane-offset')
this.startSketchBtn = page.getByTestId('sketch') this.startSketchBtn = page.getByTestId('sketch')

View File

@ -756,17 +756,6 @@ test(`Offset plane point-and-click`, async ({
}) })
await scene.expectPixelColor([74, 74, 74], testPoint, 15) 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 = [ const loftPointAndClickCases = [
@ -862,173 +851,6 @@ loftPointAndClickCases.forEach(({ shouldPreselect }) => {
}) })
await scene.expectPixelColor([89, 89, 89], testPoint, 15) 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)
}) })
}) })
@ -1208,104 +1030,4 @@ extrude001 = extrude(40, sketch001)
}) })
await scene.expectPixelColor([49, 49, 49], testPoint, 15) 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)
})
})
}) })

View File

@ -8,8 +8,8 @@ import { UnitLength_type } from '@kittycad/lib/dist/types/src/models'
test.describe('Testing in-app sample loading', () => { test.describe('Testing in-app sample loading', () => {
/** /**
* Note this test implicitly depends on the KCL sample "a-parametric-bearing-pillow-block", * Note this test implicitly depends on the KCL sample "car-wheel.kcl",
* its title, and its units settings. https://github.com/KittyCAD/kcl-samples/blob/main/a-parametric-bearing-pillow-block/main.kcl * its title, and its units settings. https://github.com/KittyCAD/kcl-samples/blob/main/car-wheel/car-wheel.kcl
*/ */
test('Web: should overwrite current code, cannot create new file', async ({ test('Web: should overwrite current code, cannot create new file', async ({
editor, editor,
@ -29,8 +29,8 @@ test.describe('Testing in-app sample loading', () => {
// Locators and constants // Locators and constants
const newSample = { const newSample = {
file: 'a-parametric-bearing-pillow-block' + FILE_EXT, file: 'car-wheel' + FILE_EXT,
title: 'A Parametric Bearing Pillow Block', title: 'Car Wheel',
} }
const commandBarButton = page.getByRole('button', { name: 'Commands' }) const commandBarButton = page.getByRole('button', { name: 'Commands' })
const samplesCommandOption = page.getByRole('option', { const samplesCommandOption = page.getByRole('option', {
@ -75,8 +75,8 @@ test.describe('Testing in-app sample loading', () => {
/** /**
* Note this test implicitly depends on the KCL samples: * Note this test implicitly depends on the KCL samples:
* "a-parametric-bearing-pillow-block": https://github.com/KittyCAD/kcl-samples/blob/main/a-parametric-bearing-pillow-block/main.kcl * "car-wheel.kcl": https://github.com/KittyCAD/kcl-samples/blob/main/car-wheel/car-wheel.kcl
* "gear-rack": https://github.com/KittyCAD/kcl-samples/blob/main/gear-rack/main.kcl * "gear-rack.kcl": https://github.com/KittyCAD/kcl-samples/blob/main/gear-rack/gear-rack.kcl
*/ */
test( test(
'Desktop: should create new file by default, optionally overwrite', 'Desktop: should create new file by default, optionally overwrite',
@ -93,8 +93,8 @@ test.describe('Testing in-app sample loading', () => {
// Locators and constants // Locators and constants
const sampleOne = { const sampleOne = {
file: 'a-parametric-bearing-pillow-block' + FILE_EXT, file: 'car-wheel' + FILE_EXT,
title: 'A Parametric Bearing Pillow Block', title: 'Car Wheel',
} }
const sampleTwo = { const sampleTwo = {
file: 'gear-rack' + FILE_EXT, file: 'gear-rack' + FILE_EXT,

18
flake.lock generated
View File

@ -2,11 +2,11 @@
"nodes": { "nodes": {
"nixpkgs": { "nixpkgs": {
"locked": { "locked": {
"lastModified": 1736320768, "lastModified": 1721933792,
"narHash": "sha256-nIYdTAiKIGnFNugbomgBJR+Xv5F1ZQU+HfaBqJKroC0=", "narHash": "sha256-zYVwABlQnxpbaHMfX6Wt9jhyQstFYwN2XjleOJV3VVg=",
"owner": "NixOS", "owner": "NixOS",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "4bc9c909d9ac828a039f288cf872d16d38185db8", "rev": "2122a9b35b35719ad9a395fe783eabb092df01b1",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -18,11 +18,11 @@
}, },
"nixpkgs_2": { "nixpkgs_2": {
"locked": { "locked": {
"lastModified": 1728538411, "lastModified": 1718428119,
"narHash": "sha256-f0SBJz1eZ2yOuKUr5CA9BHULGXVSn6miBuUWdTyhUhU=", "narHash": "sha256-WdWDpNaq6u1IPtxtYHHWpl5BmabtpmLnMAx0RdJ/vo8=",
"owner": "NixOS", "owner": "NixOS",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "b69de56fac8c2b6f8fd27f2eca01dcda8e0a4221", "rev": "e6cea36f83499eb4e9cd184c8a8e823296b50ad5",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -43,11 +43,11 @@
"nixpkgs": "nixpkgs_2" "nixpkgs": "nixpkgs_2"
}, },
"locked": { "locked": {
"lastModified": 1736476219, "lastModified": 1721960387,
"narHash": "sha256-+qyv3QqdZCdZ3cSO/cbpEY6tntyYjfe1bB12mdpNFaY=", "narHash": "sha256-o21ax+745ETGXrcgc/yUuLw1SI77ymp3xEpJt+w/kks=",
"owner": "oxalica", "owner": "oxalica",
"repo": "rust-overlay", "repo": "rust-overlay",
"rev": "de30cc5963da22e9742bbbbb9a3344570ed237b9", "rev": "9cbf831c5b20a53354fc12758abd05966f9f1699",
"type": "github" "type": "github"
}, },
"original": { "original": {

View File

@ -26,7 +26,7 @@
"@fortawesome/react-fontawesome": "^0.2.0", "@fortawesome/react-fontawesome": "^0.2.0",
"@headlessui/react": "^1.7.19", "@headlessui/react": "^1.7.19",
"@headlessui/tailwindcss": "^0.2.0", "@headlessui/tailwindcss": "^0.2.0",
"@kittycad/lib": "2.0.13", "@kittycad/lib": "2.0.12",
"@lezer/highlight": "^1.2.1", "@lezer/highlight": "^1.2.1",
"@lezer/lr": "^1.4.1", "@lezer/lr": "^1.4.1",
"@react-hook/resize-observer": "^2.0.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", "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\"", "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", "wasm-prep": "rimraf src/wasm-lib/pkg && mkdirp src/wasm-lib/pkg && rimraf src/wasm-lib/kcl/bindings",
"lint-fix": "eslint --fix --ext .ts --ext .tsx src e2e packages/codemirror-lsp-client/src", "lint-fix": "eslint --fix src e2e packages/codemirror-lsp-client",
"lint": "eslint --max-warnings 0 --ext .ts --ext .tsx src e2e packages/codemirror-lsp-client/src", "lint": "eslint --max-warnings 0 src e2e packages/codemirror-lsp-client",
"files:set-version": "echo \"$(jq --arg v \"$VERSION\" '.version=$v' package.json --indent 2)\" > package.json", "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:set-notes": "./scripts/set-files-notes.sh",
"files:flip-to-nightly": "./scripts/flip-files-to-nightly.sh", "files:flip-to-nightly": "./scripts/flip-files-to-nightly.sh",
@ -149,7 +149,7 @@
"@electron-forge/plugin-vite": "7.4.0", "@electron-forge/plugin-vite": "7.4.0",
"@electron/fuses": "1.8.0", "@electron/fuses": "1.8.0",
"@iarna/toml": "^2.2.5", "@iarna/toml": "^2.2.5",
"@lezer/generator": "^1.7.2", "@lezer/generator": "^1.7.1",
"@nabla/vite-plugin-eslint": "^2.0.5", "@nabla/vite-plugin-eslint": "^2.0.5",
"@playwright/test": "^1.49.0", "@playwright/test": "^1.49.0",
"@testing-library/jest-dom": "^5.14.1", "@testing-library/jest-dom": "^5.14.1",
@ -171,6 +171,8 @@
"@types/uuid": "^9.0.8", "@types/uuid": "^9.0.8",
"@types/wicg-file-system-access": "^2023.10.5", "@types/wicg-file-system-access": "^2023.10.5",
"@types/ws": "^8.5.13", "@types/ws": "^8.5.13",
"@typescript-eslint/eslint-plugin": "^5.0.0",
"@typescript-eslint/parser": "^5.0.0",
"@vitejs/plugin-react": "^4.3.0", "@vitejs/plugin-react": "^4.3.0",
"@vitest/web-worker": "^1.5.0", "@vitest/web-worker": "^1.5.0",
"@xstate/cli": "^0.5.17", "@xstate/cli": "^0.5.17",
@ -180,14 +182,10 @@
"electron-builder": "24.13.3", "electron-builder": "24.13.3",
"electron-notarize": "1.2.2", "electron-notarize": "1.2.2",
"eslint": "^8.0.1", "eslint": "^8.0.1",
"eslint-config-react-app": "^7.0.1",
"eslint-plugin-css-modules": "^2.12.0", "eslint-plugin-css-modules": "^2.12.0",
"eslint-plugin-import": "^2.30.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-suggest-no-throw": "^1.0.0",
"eslint-plugin-testing-library": "^7.1.1",
"happy-dom": "^16.3.0", "happy-dom": "^16.3.0",
"http-server": "^14.1.1", "http-server": "^14.1.1",
"husky": "^9.1.5", "husky": "^9.1.5",
@ -202,7 +200,6 @@
"tailwindcss": "^3.4.1", "tailwindcss": "^3.4.1",
"ts-node": "^10.0.0", "ts-node": "^10.0.0",
"typescript": "^5.7.2", "typescript": "^5.7.2",
"typescript-eslint": "^8.19.1",
"vite": "^5.4.6", "vite": "^5.4.6",
"vite-plugin-package-version": "^1.1.0", "vite-plugin-package-version": "^1.1.0",
"vite-tsconfig-paths": "^4.3.2", "vite-tsconfig-paths": "^4.3.2",

View File

@ -4,5 +4,4 @@ dist
tsconfig.tsbuildinfo tsconfig.tsbuildinfo
*.d.ts *.d.ts
*.js *.js
!postcss.config.js
!rollup.config.js !rollup.config.js

View File

@ -28,7 +28,6 @@
"@rollup/plugin-typescript": "^12.1.2", "@rollup/plugin-typescript": "^12.1.2",
"rollup": "^4.29.1", "rollup": "^4.29.1",
"rollup-plugin-dts": "^6.1.1", "rollup-plugin-dts": "^6.1.1",
"vite-tsconfig-paths": "^4.3.2",
"vitest": "^2.1.8" "vitest": "^2.1.8"
}, },
"files": [ "files": [

View File

@ -1 +0,0 @@
// This is here to prevent using the one in the root of the project.

View File

@ -398,7 +398,7 @@ check-error@^2.1.1:
resolved "https://registry.yarnpkg.com/check-error/-/check-error-2.1.1.tgz#87eb876ae71ee388fa0471fe423f494be1d96ccc" resolved "https://registry.yarnpkg.com/check-error/-/check-error-2.1.1.tgz#87eb876ae71ee388fa0471fe423f494be1d96ccc"
integrity sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw== integrity sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==
debug@^4.1.1, debug@^4.3.7: debug@^4.3.7:
version "4.4.0" version "4.4.0"
resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.0.tgz#2b3f2aea2ffeb776477460267377dc8710faba8a" resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.0.tgz#2b3f2aea2ffeb776477460267377dc8710faba8a"
integrity sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA== integrity sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==
@ -471,11 +471,6 @@ function-bind@^1.1.2:
resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c" resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c"
integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== 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: hasown@^2.0.2:
version "2.0.2" version "2.0.2"
resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.2.tgz#003eaf91be7adc372e84ec59dc37252cedb80003" resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.2.tgz#003eaf91be7adc372e84ec59dc37252cedb80003"
@ -652,11 +647,6 @@ tinyspy@^3.0.2:
resolved "https://registry.yarnpkg.com/tinyspy/-/tinyspy-3.0.2.tgz#86dd3cf3d737b15adcf17d7887c84a75201df20a" resolved "https://registry.yarnpkg.com/tinyspy/-/tinyspy-3.0.2.tgz#86dd3cf3d737b15adcf17d7887c84a75201df20a"
integrity sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q== 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: typescript@^5.7.2:
version "5.7.2" version "5.7.2"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.7.2.tgz#3169cf8c4c8a828cde53ba9ecb3d2b1d5dd67be6" resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.7.2.tgz#3169cf8c4c8a828cde53ba9ecb3d2b1d5dd67be6"
@ -673,15 +663,6 @@ vite-node@2.1.8:
pathe "^1.1.2" pathe "^1.1.2"
vite "^5.0.0" 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: vite@^5.0.0:
version "5.4.11" version "5.4.11"
resolved "https://registry.yarnpkg.com/vite/-/vite-5.4.11.tgz#3b415cd4aed781a356c1de5a9ebafb837715f6e5" resolved "https://registry.yarnpkg.com/vite/-/vite-5.4.11.tgz#3b415cd4aed781a356c1de5a9ebafb837715f6e5"

View File

@ -42,7 +42,7 @@ export default class StreamDemuxer extends Queue<Uint8Array> {
// try to parse the content-length from the headers // try to parse the content-length from the headers
const length = parseInt(match[1]) const length = parseInt(match[1])
if (Number.isNaN(length)) if (isNaN(length))
return Promise.reject(new Error('invalid content length')) return Promise.reject(new Error('invalid content length'))
// slice the headers since we now have the content length // slice the headers since we now have the content length

View File

@ -32,9 +32,10 @@ export default defineConfig({
}, },
projects: [ projects: [
{ {
name: 'chromium', name: 'Google Chrome',
use: { use: {
...devices['Desktop Chrome'], ...devices['Desktop Chrome'],
channel: 'chrome',
contextOptions: { contextOptions: {
/* Chromium is the only one with these permission types */ /* Chromium is the only one with these permission types */
permissions: ['clipboard-write', 'clipboard-read'], permissions: ['clipboard-write', 'clipboard-read'],

View File

@ -1,211 +1,166 @@
[ [
{ {
"file": "main.kcl", "file": "80-20-rail.kcl",
"pathFromProjectDirectoryToFirstFile": "80-20-rail/main.kcl",
"multipleFiles": false,
"title": "80/20 Rail", "title": "80/20 Rail",
"description": "An 80/20 extruded aluminum linear rail. T-slot profile adjustable by profile height, rail length, and origin position" "description": "An 80/20 extruded aluminum linear rail. T-slot profile adjustable by profile height, rail length, and origin position"
}, },
{ {
"file": "main.kcl", "file": "a-parametric-bearing-pillow-block.kcl",
"pathFromProjectDirectoryToFirstFile": "a-parametric-bearing-pillow-block/main.kcl",
"multipleFiles": false,
"title": "A Parametric Bearing Pillow Block", "title": "A Parametric Bearing Pillow Block",
"description": "A bearing pillow block, also known as a plummer block or pillow block bearing, is a pedestal used to provide support for a rotating shaft with the help of compatible bearings and various accessories. Housing a bearing, the pillow block provides a secure and stable foundation that allows the shaft to rotate smoothly within its machinery setup. These components are essential in a wide range of mechanical systems and machinery, playing a key role in reducing friction and supporting radial and axial loads." "description": "A bearing pillow block, also known as a plummer block or pillow block bearing, is a pedestal used to provide support for a rotating shaft with the help of compatible bearings and various accessories. Housing a bearing, the pillow block provides a secure and stable foundation that allows the shaft to rotate smoothly within its machinery setup. These components are essential in a wide range of mechanical systems and machinery, playing a key role in reducing friction and supporting radial and axial loads."
}, },
{ {
"file": "main.kcl", "file": "ball-bearing.kcl",
"pathFromProjectDirectoryToFirstFile": "ball-bearing/main.kcl",
"multipleFiles": false,
"title": "Ball Bearing", "title": "Ball Bearing",
"description": "A ball bearing is a type of rolling-element bearing that uses balls to maintain the separation between the bearing races. The primary purpose of a ball bearing is to reduce rotational friction and support radial and axial loads." "description": "A ball bearing is a type of rolling-element bearing that uses balls to maintain the separation between the bearing races. The primary purpose of a ball bearing is to reduce rotational friction and support radial and axial loads."
}, },
{ {
"file": "main.kcl", "file": "bracket.kcl",
"pathFromProjectDirectoryToFirstFile": "bracket/main.kcl",
"multipleFiles": false,
"title": "Shelf Bracket", "title": "Shelf Bracket",
"description": "This is a bracket that holds a shelf. It is made of aluminum and is designed to hold a force of 300 lbs. The bracket is 6 inches wide and the force is applied at the end of the shelf, 12 inches from the wall. The bracket has a factor of safety of 1.2. The legs of the bracket are 5 inches and 2 inches long. The thickness of the bracket is calculated from the constraints provided." "description": "This is a bracket that holds a shelf. It is made of aluminum and is designed to hold a force of 300 lbs. The bracket is 6 inches wide and the force is applied at the end of the shelf, 12 inches from the wall. The bracket has a factor of safety of 1.2. The legs of the bracket are 5 inches and 2 inches long. The thickness of the bracket is calculated from the constraints provided."
}, },
{ {
"file": "main.kcl", "file": "brake-caliper.kcl",
"pathFromProjectDirectoryToFirstFile": "car-wheel-assembly/main.kcl", "title": "Brake Caliper",
"multipleFiles": true, "description": "Brake calipers are used to squeeze the brake pads against the rotor, causing larger and larger amounts of friction depending on how hard the brakes are pressed."
},
{
"file": "car-wheel.kcl",
"title": "Car Wheel",
"description": "A sports car wheel with a circular lug pattern and spokes."
},
{
"file": "car-wheel-assembly.kcl",
"title": "Car Wheel Assembly", "title": "Car Wheel Assembly",
"description": "A car wheel assembly with a rotor, tire, and lug nuts." "description": "A car wheel assembly with a rotor, tire, and lug nuts."
}, },
{ {
"file": "main.kcl", "file": "dodecahedron.kcl",
"pathFromProjectDirectoryToFirstFile": "dodecahedron/main.kcl",
"multipleFiles": false,
"title": "Hollow Dodecahedron", "title": "Hollow Dodecahedron",
"description": "A regular dodecahedron or pentagonal dodecahedron is a dodecahedron composed of regular pentagonal faces, three meeting at each vertex. This example shows constructing the individual faces of the dodecahedron and extruding inwards." "description": "A regular dodecahedron or pentagonal dodecahedron is a dodecahedron composed of regular pentagonal faces, three meeting at each vertex. This example shows constructing the individual faces of the dodecahedron and extruding inwards."
}, },
{ {
"file": "main.kcl", "file": "enclosure.kcl",
"pathFromProjectDirectoryToFirstFile": "enclosure/main.kcl",
"multipleFiles": false,
"title": "Enclosure", "title": "Enclosure",
"description": "An enclosure body and sealing lid for storing items" "description": "An enclosure body and sealing lid for storing items"
}, },
{ {
"file": "main.kcl", "file": "flange-with-patterns.kcl",
"pathFromProjectDirectoryToFirstFile": "flange-with-patterns/main.kcl",
"multipleFiles": false,
"title": "Flange", "title": "Flange",
"description": "A flange is a flat rim, collar, or rib, typically forged or cast, that is used to strengthen an object, guide it, or attach it to another object. Flanges are known for their use in various applications, including piping, plumbing, and mechanical engineering, among others." "description": "A flange is a flat rim, collar, or rib, typically forged or cast, that is used to strengthen an object, guide it, or attach it to another object. Flanges are known for their use in various applications, including piping, plumbing, and mechanical engineering, among others."
}, },
{ {
"file": "main.kcl", "file": "flange-xy.kcl",
"pathFromProjectDirectoryToFirstFile": "flange-xy/main.kcl",
"multipleFiles": false,
"title": "Flange with XY coordinates", "title": "Flange with XY coordinates",
"description": "A flange is a flat rim, collar, or rib, typically forged or cast, that is used to strengthen an object, guide it, or attach it to another object. Flanges are known for their use in various applications, including piping, plumbing, and mechanical engineering, among others." "description": "A flange is a flat rim, collar, or rib, typically forged or cast, that is used to strengthen an object, guide it, or attach it to another object. Flanges are known for their use in various applications, including piping, plumbing, and mechanical engineering, among others."
}, },
{ {
"file": "main.kcl", "file": "focusrite-scarlett-mounting-bracket.kcl",
"pathFromProjectDirectoryToFirstFile": "focusrite-scarlett-mounting-bracket/main.kcl",
"multipleFiles": false,
"title": "A mounting bracket for the Focusrite Scarlett Solo audio interface", "title": "A mounting bracket for the Focusrite Scarlett Solo audio interface",
"description": "This is a bracket that holds an audio device underneath a desk or shelf. The audio device has dimensions of 144mm wide, 80mm length and 45mm depth with fillets of 6mm. This mounting bracket is designed to be 3D printed with PLA material" "description": "This is a bracket that holds an audio device underneath a desk or shelf. The audio device has dimensions of 144mm wide, 80mm length and 45mm depth with fillets of 6mm. This mounting bracket is designed to be 3D printed with PLA material"
}, },
{ {
"file": "main.kcl", "file": "food-service-spatula.kcl",
"pathFromProjectDirectoryToFirstFile": "food-service-spatula/main.kcl",
"multipleFiles": false,
"title": "Food Service Spatula", "title": "Food Service Spatula",
"description": "Use these spatulas for mixing, flipping, and scraping." "description": "Use these spatulas for mixing, flipping, and scraping."
}, },
{ {
"file": "main.kcl", "file": "french-press.kcl",
"pathFromProjectDirectoryToFirstFile": "french-press/main.kcl",
"multipleFiles": false,
"title": "French Press", "title": "French Press",
"description": "A french press immersion coffee maker" "description": "A french press immersion coffee maker"
}, },
{ {
"file": "main.kcl", "file": "gear.kcl",
"pathFromProjectDirectoryToFirstFile": "gear/main.kcl",
"multipleFiles": false,
"title": "Spur Gear", "title": "Spur Gear",
"description": "A rotating machine part having cut teeth or, in the case of a cogwheel, inserted teeth (called cogs), which mesh with another toothed part to transmit torque. Geared devices can change the speed, torque, and direction of a power source. The two elements that define a gear are its circular shape and the teeth that are integrated into its outer edge, which are designed to fit into the teeth of another gear." "description": "A rotating machine part having cut teeth or, in the case of a cogwheel, inserted teeth (called cogs), which mesh with another toothed part to transmit torque. Geared devices can change the speed, torque, and direction of a power source. The two elements that define a gear are its circular shape and the teeth that are integrated into its outer edge, which are designed to fit into the teeth of another gear."
}, },
{ {
"file": "main.kcl", "file": "gear-rack.kcl",
"pathFromProjectDirectoryToFirstFile": "gear-rack/main.kcl",
"multipleFiles": false,
"title": "100mm Gear Rack", "title": "100mm Gear Rack",
"description": "A flat bar or rail that is engraved with teeth along its length. These teeth are designed to mesh with the teeth of a gear, known as a pinion. When the pinion, a small cylindrical gear, rotates, its teeth engage with the teeth on the rack, causing the rack to move linearly. Conversely, linear motion applied to the rack will cause the pinion to rotate." "description": "A flat bar or rail that is engraved with teeth along its length. These teeth are designed to mesh with the teeth of a gear, known as a pinion. When the pinion, a small cylindrical gear, rotates, its teeth engage with the teeth on the rack, causing the rack to move linearly. Conversely, linear motion applied to the rack will cause the pinion to rotate."
}, },
{ {
"file": "main.kcl", "file": "hex-nut.kcl",
"pathFromProjectDirectoryToFirstFile": "hex-nut/main.kcl",
"multipleFiles": false,
"title": "Hex nut", "title": "Hex nut",
"description": "A hex nut is a type of fastener with a threaded hole and a hexagonal outer shape, used in a wide variety of applications to secure parts together. The hexagonal shape allows for a greater torque to be applied with wrenches or tools, making it one of the most common nut types in hardware." "description": "A hex nut is a type of fastener with a threaded hole and a hexagonal outer shape, used in a wide variety of applications to secure parts together. The hexagonal shape allows for a greater torque to be applied with wrenches or tools, making it one of the most common nut types in hardware."
}, },
{ {
"file": "main.kcl", "file": "i-beam.kcl",
"pathFromProjectDirectoryToFirstFile": "i-beam/main.kcl",
"multipleFiles": false,
"title": "I-beam", "title": "I-beam",
"description": "A structural metal beam with an I shaped cross section. Often used in construction" "description": "A structural metal beam with an I shaped cross section. Often used in construction"
}, },
{ {
"file": "main.kcl", "file": "kitt.kcl",
"pathFromProjectDirectoryToFirstFile": "kitt/main.kcl",
"multipleFiles": false,
"title": "Kitt", "title": "Kitt",
"description": "The beloved KittyCAD mascot in a voxelized style." "description": "The beloved KittyCAD mascot in a voxelized style."
}, },
{ {
"file": "main.kcl", "file": "lego.kcl",
"pathFromProjectDirectoryToFirstFile": "lego/main.kcl",
"multipleFiles": false,
"title": "Lego Brick", "title": "Lego Brick",
"description": "A standard Lego brick. This is a small, plastic construction block toy that can be interlocked with other blocks to build various structures, models, and figures. There are a lot of hacks used in this code." "description": "A standard Lego brick. This is a small, plastic construction block toy that can be interlocked with other blocks to build various structures, models, and figures. There are a lot of hacks used in this code."
}, },
{ {
"file": "main.kcl", "file": "lug-nut.kcl",
"pathFromProjectDirectoryToFirstFile": "mounting-plate/main.kcl", "title": "Lug Nut",
"multipleFiles": false, "description": "lug Nuts are essential components used to create secure connections, whether for electrical purposes, like terminating wires or grounding, or for mechanical purposes, such as providing mounting points or reinforcing structural joints."
},
{
"file": "mounting-plate.kcl",
"title": "Mounting Plate", "title": "Mounting Plate",
"description": "A flat piece of material, often metal or plastic, that serves as a support or base for attaching, securing, or mounting various types of equipment, devices, or components." "description": "A flat piece of material, often metal or plastic, that serves as a support or base for attaching, securing, or mounting various types of equipment, devices, or components."
}, },
{ {
"file": "main.kcl", "file": "multi-axis-robot.kcl",
"pathFromProjectDirectoryToFirstFile": "multi-axis-robot/main.kcl",
"multipleFiles": true,
"title": "Robot Arm", "title": "Robot Arm",
"description": "A 4 axis robotic arm for industrial use. These machines can be used for assembly, packaging, organization of goods, and quality inspection processes" "description": "A 4 axis robotic arm for industrial use. These machines can be used for assembly, packaging, organization of goods, and quality inspection processes"
}, },
{ {
"file": "main.kcl", "file": "pipe.kcl",
"pathFromProjectDirectoryToFirstFile": "pipe/main.kcl",
"multipleFiles": false,
"title": "Pipe", "title": "Pipe",
"description": "A tubular section or hollow cylinder, usually but not necessarily of circular cross-section, used mainly to convey substances that can flow." "description": "A tubular section or hollow cylinder, usually but not necessarily of circular cross-section, used mainly to convey substances that can flow."
}, },
{ {
"file": "main.kcl", "file": "pipe-flange-assembly.kcl",
"pathFromProjectDirectoryToFirstFile": "pipe-flange-assembly/main.kcl",
"multipleFiles": false,
"title": "Pipe and Flange Assembly", "title": "Pipe and Flange Assembly",
"description": "A crucial component in various piping systems, designed to facilitate the connection, disconnection, and access to piping for inspection, cleaning, and modifications. This assembly combines pipes (long cylindrical conduits) with flanges (plate-like fittings) to create a secure yet detachable joint." "description": "A crucial component in various piping systems, designed to facilitate the connection, disconnection, and access to piping for inspection, cleaning, and modifications. This assembly combines pipes (long cylindrical conduits) with flanges (plate-like fittings) to create a secure yet detachable joint."
}, },
{ {
"file": "main.kcl", "file": "pipe-with-bend.kcl",
"pathFromProjectDirectoryToFirstFile": "pipe-with-bend/main.kcl",
"multipleFiles": false,
"title": "Pipe with bend", "title": "Pipe with bend",
"description": "A tubular section or hollow cylinder, usually but not necessarily of circular cross-section, used mainly to convey substances that can flow." "description": "A tubular section or hollow cylinder, usually but not necessarily of circular cross-section, used mainly to convey substances that can flow."
}, },
{ {
"file": "main.kcl", "file": "poopy-shoe.kcl",
"pathFromProjectDirectoryToFirstFile": "poopy-shoe/main.kcl",
"multipleFiles": false,
"title": "Poopy Shoe", "title": "Poopy Shoe",
"description": "poop shute for bambu labs printer - optimized for printing." "description": "poop shute for bambu labs printer - optimized for printing."
}, },
{ {
"file": "main.kcl", "file": "router-template-cross-bar.kcl",
"pathFromProjectDirectoryToFirstFile": "router-template-cross-bar/main.kcl",
"multipleFiles": false,
"title": "Router template for a cross bar", "title": "Router template for a cross bar",
"description": "A guide for routing a notch into a cross bar." "description": "A guide for routing a notch into a cross bar."
}, },
{ {
"file": "main.kcl", "file": "router-template-slate.kcl",
"pathFromProjectDirectoryToFirstFile": "router-template-slate/main.kcl",
"multipleFiles": false,
"title": "Router template for a slate", "title": "Router template for a slate",
"description": "A guide for routing a slate for a cross bar." "description": "A guide for routing a slate for a cross bar."
}, },
{ {
"file": "main.kcl", "file": "sheet-metal-bracket.kcl",
"pathFromProjectDirectoryToFirstFile": "sheet-metal-bracket/main.kcl",
"multipleFiles": false,
"title": "Sheet Metal Bracket", "title": "Sheet Metal Bracket",
"description": "A component typically made from flat sheet metal through various manufacturing processes such as bending, punching, cutting, and forming. These brackets are used to support, attach, or mount other hardware components, often providing a structural or functional base for assembly." "description": "A component typically made from flat sheet metal through various manufacturing processes such as bending, punching, cutting, and forming. These brackets are used to support, attach, or mount other hardware components, often providing a structural or functional base for assembly."
}, },
{ {
"file": "main.kcl", "file": "socket-head-cap-screw.kcl",
"pathFromProjectDirectoryToFirstFile": "socket-head-cap-screw/main.kcl",
"multipleFiles": false,
"title": "Socket Head Cap Screw", "title": "Socket Head Cap Screw",
"description": "This is for a #10-24 screw that is 1.00 inches long. A socket head cap screw is a type of fastener that is widely used in a variety of applications requiring a high strength fastening solution. It is characterized by its cylindrical head and internal hexagonal drive, which allows for tightening with an Allen wrench or hex key." "description": "This is for a #10-24 screw that is 1.00 inches long. A socket head cap screw is a type of fastener that is widely used in a variety of applications requiring a high strength fastening solution. It is characterized by its cylindrical head and internal hexagonal drive, which allows for tightening with an Allen wrench or hex key."
}, },
{ {
"file": "main.kcl", "file": "tire.kcl",
"pathFromProjectDirectoryToFirstFile": "walkie-talkie/main.kcl", "title": "Tire",
"multipleFiles": true, "description": "A tire is a critical component of a vehicle that provides the necessary traction and grip between the car and the road. It supports the vehicle's weight and absorbs shocks from road irregularities."
"title": "Walkie Talkie",
"description": "A portable, handheld two-way radio device that allows users to communicate wirelessly over short to medium distances. It operates on specific radio frequencies and features a push-to-talk button for transmitting messages, making it ideal for quick and reliable communication in outdoor, work, or emergency settings."
}, },
{ {
"file": "main.kcl", "file": "washer.kcl",
"pathFromProjectDirectoryToFirstFile": "washer/main.kcl",
"multipleFiles": false,
"title": "Washer", "title": "Washer",
"description": "A small, typically disk-shaped component with a hole in the middle, used in a wide range of applications, primarily in conjunction with fasteners like bolts and screws. Washers distribute the load of a fastener across a broader area. This is especially important when the fastening surface is soft or uneven, as it helps to prevent damage to the surface and ensures the load is evenly distributed, reducing the risk of the fastener becoming loose over time." "description": "A small, typically disk-shaped component with a hole in the middle, used in a wide range of applications, primarily in conjunction with fasteners like bolts and screws. Washers distribute the load of a fastener across a broader area. This is especially important when the fastening surface is soft or uneven, as it helps to prevent damage to the surface and ensures the load is evenly distributed, reducing the risk of the fastener becoming loose over time."
}, },

View File

@ -57,9 +57,7 @@ export const FileMachineProvider = ({
useEffect(() => { useEffect(() => {
markOnce('code/didLoadFile') markOnce('code/didLoadFile')
async function fetchKclSamples() { async function fetchKclSamples() {
const manifest = await getKclSamplesManifest() setKclSamples(await getKclSamplesManifest())
const filteredFiles = manifest.filter((file) => !file.multipleFiles)
setKclSamples(filteredFiles)
} }
fetchKclSamples().catch(reportError) fetchKclSamples().catch(reportError)
}, []) }, [])
@ -326,7 +324,7 @@ export const FileMachineProvider = ({
} }
}, },
kclSamples.map((sample) => ({ kclSamples.map((sample) => ({
value: sample.pathFromProjectDirectoryToFirstFile, value: sample.file,
name: sample.title, name: sample.title,
})) }))
).filter( ).filter(

View File

@ -148,7 +148,6 @@ function HelpMenuItem({
return ( return (
<li className="p-0 m-0"> <li className="p-0 m-0">
{as === 'a' ? ( {as === 'a' ? (
// eslint-disable-next-line jsx-a11y/no-static-element-interactions
<a <a
{...(props as React.ComponentProps<'a'>)} {...(props as React.ComponentProps<'a'>)}
onClick={openExternalBrowserIfDesktop( onClick={openExternalBrowserIfDesktop(

View File

@ -157,38 +157,39 @@ export const ModelingMachineProvider = ({
'enable copilot': () => { 'enable copilot': () => {
editorManager.setCopilotEnabled(true) editorManager.setCopilotEnabled(true)
}, },
'sketch exit execute': ({ context: { store } }) => { // tsc reports this typing as perfectly fine, but eslint is complaining.
// TODO: Remove this async callback. For some reason eslint wouldn't // It's actually nonsensical, so I'm quieting.
// let me disable @typescript-eslint/no-misused-promises for the line. // eslint-disable-next-line @typescript-eslint/no-misused-promises
;(async () => { 'sketch exit execute': async ({
// When cancelling the sketch mode we should disable sketch mode within the engine. context: { store },
await engineCommandManager.sendSceneCommand({ }): Promise<void> => {
type: 'modeling_cmd_req', // When cancelling the sketch mode we should disable sketch mode within the engine.
cmd_id: uuidv4(), await engineCommandManager.sendSceneCommand({
cmd: { type: 'sketch_mode_disable' }, type: 'modeling_cmd_req',
}) cmd_id: uuidv4(),
cmd: { type: 'sketch_mode_disable' },
})
sceneInfra.camControls.syncDirection = 'clientToEngine' sceneInfra.camControls.syncDirection = 'clientToEngine'
if (cameraProjection.current === 'perspective') { if (cameraProjection.current === 'perspective') {
await sceneInfra.camControls.snapToPerspectiveBeforeHandingBackControlToEngine() await sceneInfra.camControls.snapToPerspectiveBeforeHandingBackControlToEngine()
} }
sceneInfra.camControls.syncDirection = 'engineToClient' sceneInfra.camControls.syncDirection = 'engineToClient'
store.videoElement?.pause() store.videoElement?.pause()
return kclManager return kclManager
.executeCode() .executeCode()
.then(() => { .then(() => {
if (engineCommandManager.engineConnection?.idleMode) return if (engineCommandManager.engineConnection?.idleMode) return
store.videoElement?.play().catch((e) => { store.videoElement?.play().catch((e) => {
console.warn('Video playing was prevented', e) console.warn('Video playing was prevented', e)
})
}) })
.catch(reportRejection) })
})().catch(reportRejection) .catch(reportRejection)
}, },
'Set mouse state': assign(({ context, event }) => { 'Set mouse state': assign(({ context, event }) => {
if (event.type !== 'Set mouse state') return {} if (event.type !== 'Set mouse state') return {}
@ -270,7 +271,6 @@ export const ModelingMachineProvider = ({
cmd_id: uuidv4(), cmd_id: uuidv4(),
cmd: { cmd: {
type: 'default_camera_center_to_selection', type: 'default_camera_center_to_selection',
camera_movement: 'vantage',
}, },
}) })
.catch(reportRejection) .catch(reportRejection)

View File

@ -18,7 +18,6 @@ export const KclEditorMenu = ({ children }: PropsWithChildren) => {
return ( return (
<Menu> <Menu>
{/* eslint-disable-next-line jsx-a11y/no-static-element-interactions */}
<div <div
className="relative" className="relative"
onClick={(e) => { onClick={(e) => {

View File

@ -313,7 +313,6 @@ export const Stream = () => {
} }
return ( return (
// eslint-disable-next-line jsx-a11y/no-static-element-interactions
<div <div
ref={videoWrapperRef} ref={videoWrapperRef}
className="absolute inset-0 z-0" className="absolute inset-0 z-0"

View File

@ -1,81 +1,79 @@
import { assertParse, initPromise, programMemoryInit } from './wasm' import { assertParse, initPromise, programMemoryInit } from './wasm'
import { enginelessExecutor } from '../lib/testHelpers' import { enginelessExecutor } from '../lib/testHelpers'
// These unit tests makes web requests to a public github repository.
import path from 'node:path'
import fs from 'node:fs/promises'
import child_process from 'node:child_process'
// The purpose of these tests is to act as a first line of defense
// if something gets real screwy with our KCL ecosystem.
// THESE TESTS ONLY RUN UNDER A NODEJS ENVIRONMENT. They DO NOT
// test under our application.
const DIR_KCL_SAMPLES = 'kcl-samples'
const URL_GIT_KCL_SAMPLES = 'https://github.com/KittyCAD/kcl-samples.git'
interface KclSampleFile { interface KclSampleFile {
file: string file: string
pathFromProjectDirectoryToFirstFile: string
title: string title: string
filename: string filename: string
description: string description: string
} }
try {
// @ts-expect-error
await fs.rm(DIR_KCL_SAMPLES, { recursive: true })
} catch (e) {
console.log(e)
}
child_process.spawnSync('git', ['clone', URL_GIT_KCL_SAMPLES, DIR_KCL_SAMPLES])
// @ts-expect-error
let files = await fs.readdir(DIR_KCL_SAMPLES)
// @ts-expect-error
const manifestJsonStr = await fs.readFile(
path.resolve(DIR_KCL_SAMPLES, 'manifest.json'),
'utf-8'
)
const manifest = JSON.parse(manifestJsonStr)
process.chdir(DIR_KCL_SAMPLES)
beforeAll(async () => { beforeAll(async () => {
await initPromise await initPromise
}) })
afterAll(async () => { // Only used to actually fetch an older version of KCL code that will break in the parser.
try { /* eslint-disable @typescript-eslint/no-unused-vars */
process.chdir('..') async function getBrokenSampleCodeForLocalTesting() {
await fs.rm(DIR_KCL_SAMPLES, { recursive: true }) const result = await fetch(
} catch (e) {} 'https://raw.githubusercontent.com/KittyCAD/kcl-samples/5ccd04a1773ebdbfd02684057917ce5dbe0eaab3/80-20-rail.kcl'
}) )
const text = await result.text()
return text
}
afterEach(() => { async function getKclSampleCodeFromGithub(file: string): Promise<string> {
process.chdir('..') const result = await fetch(
}) `https://raw.githubusercontent.com/KittyCAD/kcl-samples/refs/heads/main/${file}/${file}.kcl`
)
const text = await result.text()
return text
}
// The tests have to be sequential because we need to change directories async function getFileNamesFromManifestJSON(): Promise<KclSampleFile[]> {
// to support `import` working properly. const result = await fetch(
// @ts-expect-error 'https://raw.githubusercontent.com/KittyCAD/kcl-samples/refs/heads/main/manifest.json'
describe.sequential('Test KCL Samples from public Github repository', () => { )
// @ts-expect-error const json = await result.json()
describe.sequential('when performing enginelessExecutor', () => { json.forEach((file: KclSampleFile) => {
manifest.forEach((file: KclSampleFile) => { const filenameWithoutExtension = file.file.split('.')[0]
// @ts-expect-error file.filename = filenameWithoutExtension
it.sequential( })
`should execute ${file.title} (${file.file}) successfully`, return json
async () => { }
const [dirProject, fileKcl] =
file.pathFromProjectDirectoryToFirstFile.split('/') // Value to use across all tests!
process.chdir(dirProject) let files: KclSampleFile[] = []
const code = await fs.readFile(fileKcl, 'utf-8')
const ast = assertParse(code) describe('Test KCL Samples from public Github repository', () => {
await enginelessExecutor(ast, programMemoryInit()) describe('When parsing source code', () => {
}, // THIS RUNS ACROSS OTHER TESTS!
files.length * 1000 it('should fetch files', async () => {
) files = await getFileNamesFromManifestJSON()
})
// Run through all of the files in the manifest json. This will allow us to be automatically updated
// with the latest changes in github. We won't be hard coding the filenames
files.forEach((file: KclSampleFile) => {
it(`should parse ${file.filename} without errors`, async () => {
const code = await getKclSampleCodeFromGithub(file.filename)
assertParse(code)
}, 1000)
}) })
}) })
describe('when performing enginelessExecutor', () => {
it(
'should run through all the files',
async () => {
for (let i = 0; i < files.length; i++) {
const file: KclSampleFile = files[i]
const code = await getKclSampleCodeFromGithub(file.filename)
const ast = assertParse(code)
await enginelessExecutor(ast, programMemoryInit())
}
},
files.length * 1000
)
})
}) })

View File

@ -374,37 +374,6 @@ 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( export function revolveSketch(
node: Node<Program>, node: Node<Program>,
pathToNode: PathToNode, pathToNode: PathToNode,
@ -1180,17 +1149,11 @@ export async function deleteFromSelection(
((selection?.artifact?.type === 'wall' || ((selection?.artifact?.type === 'wall' ||
selection?.artifact?.type === 'cap') && selection?.artifact?.type === 'cap') &&
varDec.node.init.type === 'PipeExpression') || 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 extrudeNameToDelete = ''
let pathToNode: PathToNode | null = null let pathToNode: PathToNode | null = null
if ( if (selection.artifact?.type !== 'sweep') {
selection.artifact &&
selection.artifact.type !== 'sweep' &&
selection.artifact.type !== 'plane'
) {
const varDecName = varDec.node.id.name const varDecName = varDec.node.id.name
traverse(astClone, { traverse(astClone, {
enter: (node, path) => { enter: (node, path) => {
@ -1206,17 +1169,6 @@ export async function deleteFromSelection(
pathToNode = path pathToNode = path
extrudeNameToDelete = dec.id.name 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
}
} }
}, },
}) })

View File

@ -29,9 +29,7 @@ export function revolveSketch(
pathToSketchNode: PathToNode, pathToSketchNode: PathToNode,
shouldPipe = false, shouldPipe = false,
angle: Expr = createLiteral(4), angle: Expr = createLiteral(4),
axisOrEdge: string, axis: Selections
axis: string,
edge: Selections
): ):
| { | {
modifiedAst: Node<Program> modifiedAst: Node<Program>
@ -43,34 +41,31 @@ export function revolveSketch(
const sketchNode = getNodeFromPath(clonedAst, pathToSketchNode) const sketchNode = getNodeFromPath(clonedAst, pathToSketchNode)
if (err(sketchNode)) return sketchNode if (err(sketchNode)) return sketchNode
let generatedAxis // testing code
const pathToAxisSelection = getNodePathFromSourceRange(
clonedAst,
axis.graphSelections[0]?.codeRef.range
)
if (axisOrEdge === 'Edge') { const lineNode = getNodeFromPath<CallExpression>(
const pathToAxisSelection = getNodePathFromSourceRange( clonedAst,
clonedAst, pathToAxisSelection,
edge.graphSelections[0]?.codeRef.range 'CallExpression'
) )
const lineNode = getNodeFromPath<CallExpression>( if (err(lineNode)) return lineNode
clonedAst,
pathToAxisSelection,
'CallExpression'
)
if (err(lineNode)) return lineNode
const tagResult = mutateAstWithTagForSketchSegment( // TODO Kevin: What if |> close(%)?
clonedAst, // TODO Kevin: What if opposite edge
pathToAxisSelection // TODO Kevin: What if the edge isn't planar to the sketch?
) // TODO Kevin: add a tag.
const tagResult = mutateAstWithTagForSketchSegment(
clonedAst,
pathToAxisSelection
)
// Have the tag whether it is already created or a new one is generated // Have the tag whether it is already created or a new one is generated
if (err(tagResult)) return tagResult if (err(tagResult)) return tagResult
const { tag } = 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 */ /* Original Code */
const { node: sketchExpression } = sketchNode const { node: sketchExpression } = sketchNode
@ -96,12 +91,14 @@ export function revolveSketch(
shallowPath: sketchPathToDecleration, shallowPath: sketchPathToDecleration,
} = sketchVariableDeclaratorNode } = sketchVariableDeclaratorNode
if (!generatedAxis) return new Error('Generated axis selection is missing.') const axisSelection = axis?.graphSelections[0]?.artifact
if (!axisSelection) return new Error('Axis selection is missing.')
const revolveCall = createCallExpressionStdLib('revolve', [ const revolveCall = createCallExpressionStdLib('revolve', [
createObjectExpression({ createObjectExpression({
angle: angle, angle: angle,
axis: generatedAxis, axis: getEdgeTagCall(tag, axisSelection),
}), }),
createIdentifier(sketchVariableDeclarator.id.name), createIdentifier(sketchVariableDeclarator.id.name),
]) ])

View File

@ -49,27 +49,17 @@ export function addShell({
return new Error("Couldn't find extrude") 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. // 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 // We must find a technique for these situations that is robust to intermediate declarations
const extrudeNode = getNodeFromPath<VariableDeclarator>( const sketchNode = getNodeFromPath<VariableDeclarator>(
modifiedAst, modifiedAst,
extrudeLookupResult.pathToExtrudeNode, graphSelection.codeRef.pathToNode,
'VariableDeclarator' 'VariableDeclarator'
) )
const segmentNode = getNodeFromPath<VariableDeclarator>( if (err(sketchNode)) {
modifiedAst, return sketchNode
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 const selectedArtifact = graphSelection.artifact

View File

@ -705,7 +705,7 @@ describe('testing getArtifactsToUpdate', () => {
segIds: [], segIds: [],
id: expect.any(String), id: expect.any(String),
planeId: 'UUID-1', planeId: 'UUID-1',
sweepId: undefined, sweepId: '',
codeRef: { codeRef: {
pathToNode: [['body', '']], pathToNode: [['body', '']],
range: [37, 64, true], range: [37, 64, true],
@ -743,7 +743,7 @@ describe('testing getArtifactsToUpdate', () => {
type: 'segment', type: 'segment',
id: expect.any(String), id: expect.any(String),
pathId: expect.any(String), pathId: expect.any(String),
surfaceId: undefined, surfaceId: '',
edgeIds: [], edgeIds: [],
codeRef: { codeRef: {
range: [70, 86, true], range: [70, 86, true],
@ -770,7 +770,7 @@ describe('testing getArtifactsToUpdate', () => {
id: expect.any(String), id: expect.any(String),
consumedEdgeId: expect.any(String), consumedEdgeId: expect.any(String),
edgeIds: [], edgeIds: [],
surfaceId: undefined, surfaceId: '',
codeRef: { codeRef: {
range: [260, 299, true], range: [260, 299, true],
pathToNode: [['body', '']], pathToNode: [['body', '']],

View File

@ -37,7 +37,7 @@ export interface PathArtifact extends BaseArtifact {
type: 'path' type: 'path'
planeId: ArtifactId planeId: ArtifactId
segIds: Array<ArtifactId> segIds: Array<ArtifactId>
sweepId?: ArtifactId sweepId: ArtifactId
solid2dId?: ArtifactId solid2dId?: ArtifactId
codeRef: CodeRef codeRef: CodeRef
} }
@ -60,7 +60,7 @@ export interface PathArtifactRich extends BaseArtifact {
export interface SegmentArtifact extends BaseArtifact { export interface SegmentArtifact extends BaseArtifact {
type: 'segment' type: 'segment'
pathId: ArtifactId pathId: ArtifactId
surfaceId?: ArtifactId surfaceId: ArtifactId
edgeIds: Array<ArtifactId> edgeIds: Array<ArtifactId>
edgeCutId?: ArtifactId edgeCutId?: ArtifactId
codeRef: CodeRef codeRef: CodeRef
@ -68,7 +68,7 @@ export interface SegmentArtifact extends BaseArtifact {
interface SegmentArtifactRich extends BaseArtifact { interface SegmentArtifactRich extends BaseArtifact {
type: 'segment' type: 'segment'
path: PathArtifact path: PathArtifact
surf?: WallArtifact surf: WallArtifact
edges: Array<SweepEdge> edges: Array<SweepEdge>
edgeCut?: EdgeCut edgeCut?: EdgeCut
codeRef: CodeRef codeRef: CodeRef
@ -77,7 +77,7 @@ interface SegmentArtifactRich extends BaseArtifact {
/** A Sweep is a more generic term for extrude, revolve, loft and sweep*/ /** A Sweep is a more generic term for extrude, revolve, loft and sweep*/
interface SweepArtifact extends BaseArtifact { interface SweepArtifact extends BaseArtifact {
type: 'sweep' type: 'sweep'
subType: 'extrusion' | 'revolve' | 'loft' | 'sweep' subType: 'extrusion' | 'revolve'
pathId: string pathId: string
surfaceIds: Array<string> surfaceIds: Array<string>
edgeIds: Array<string> edgeIds: Array<string>
@ -85,7 +85,7 @@ interface SweepArtifact extends BaseArtifact {
} }
interface SweepArtifactRich extends BaseArtifact { interface SweepArtifactRich extends BaseArtifact {
type: 'sweep' type: 'sweep'
subType: 'extrusion' | 'revolve' | 'loft' | 'sweep' subType: 'extrusion' | 'revolve'
path: PathArtifact path: PathArtifact
surfaces: Array<WallArtifact | CapArtifact> surfaces: Array<WallArtifact | CapArtifact>
edges: Array<SweepEdge> edges: Array<SweepEdge>
@ -120,7 +120,7 @@ interface EdgeCut extends BaseArtifact {
subType: 'fillet' | 'chamfer' subType: 'fillet' | 'chamfer'
consumedEdgeId: ArtifactId consumedEdgeId: ArtifactId
edgeIds: Array<ArtifactId> edgeIds: Array<ArtifactId>
surfaceId?: ArtifactId surfaceId: ArtifactId
codeRef: CodeRef codeRef: CodeRef
} }
@ -308,7 +308,7 @@ export function getArtifactsToUpdate({
id, id,
segIds: [], segIds: [],
planeId: currentPlaneId, planeId: currentPlaneId,
sweepId: undefined, sweepId: '',
codeRef: { range, pathToNode }, codeRef: { range, pathToNode },
}, },
}) })
@ -343,7 +343,7 @@ export function getArtifactsToUpdate({
type: 'segment', type: 'segment',
id, id,
pathId, pathId,
surfaceId: undefined, surfaceId: '',
edgeIds: [], edgeIds: [],
codeRef: { range, pathToNode }, codeRef: { range, pathToNode },
}, },
@ -377,11 +377,7 @@ export function getArtifactsToUpdate({
}) })
} }
return returnArr return returnArr
} else if ( } else if (cmd.type === 'extrude' || cmd.type === 'revolve') {
cmd.type === 'extrude' ||
cmd.type === 'revolve' ||
cmd.type === 'sweep'
) {
const subType = cmd.type === 'extrude' ? 'extrusion' : cmd.type const subType = cmd.type === 'extrude' ? 'extrusion' : cmd.type
returnArr.push({ returnArr.push({
id, id,
@ -402,33 +398,6 @@ export function getArtifactsToUpdate({
artifact: { ...path, sweepId: id }, artifact: { ...path, sweepId: id },
}) })
return returnArr 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 ( } else if (
cmd.type === 'solid3d_get_extrusion_face_info' && cmd.type === 'solid3d_get_extrusion_face_info' &&
response?.type === 'modeling' && response?.type === 'modeling' &&
@ -450,8 +419,7 @@ export function getArtifactsToUpdate({
id: face_id, id: face_id,
segId: curve_id, segId: curve_id,
edgeCutEdgeIds: [], edgeCutEdgeIds: [],
// TODO: Add explicit check for sweepId. Should never use '' sweepId: path.sweepId,
sweepId: path.sweepId ?? '',
pathIds: [], pathIds: [],
}, },
}) })
@ -459,17 +427,15 @@ export function getArtifactsToUpdate({
id: curve_id, id: curve_id,
artifact: { ...seg, surfaceId: face_id }, artifact: { ...seg, surfaceId: face_id },
}) })
if (path.sweepId) { const sweep = getArtifact(path.sweepId)
const sweep = getArtifact(path.sweepId) if (sweep?.type === 'sweep') {
if (sweep?.type === 'sweep') { returnArr.push({
returnArr.push({ id: path.sweepId,
id: path.sweepId, artifact: {
artifact: { ...sweep,
...sweep, surfaceIds: [face_id],
surfaceIds: [face_id], },
}, })
})
}
} }
} }
} }
@ -486,22 +452,19 @@ export function getArtifactsToUpdate({
id: face_id, id: face_id,
subType: cap === 'bottom' ? 'start' : 'end', subType: cap === 'bottom' ? 'start' : 'end',
edgeCutEdgeIds: [], edgeCutEdgeIds: [],
// TODO: Add explicit check for sweepId. Should never use '' sweepId: path.sweepId,
sweepId: path.sweepId ?? '',
pathIds: [], pathIds: [],
}, },
}) })
if (path.sweepId) { const sweep = getArtifact(path.sweepId)
const sweep = getArtifact(path.sweepId) if (sweep?.type !== 'sweep') return
if (sweep?.type !== 'sweep') return returnArr.push({
returnArr.push({ id: path.sweepId,
id: path.sweepId, artifact: {
artifact: { ...sweep,
...sweep, surfaceIds: [face_id],
surfaceIds: [face_id], },
}, })
})
}
} }
} }
}) })
@ -539,8 +502,7 @@ export function getArtifactsToUpdate({
? 'adjacent' ? 'adjacent'
: 'opposite', : 'opposite',
segId: cmd.edge_id, segId: cmd.edge_id,
// TODO: Add explicit check for sweepId. Should never use '' sweepId: path.sweepId,
sweepId: path.sweepId ?? '',
}, },
}, },
{ {
@ -551,7 +513,7 @@ export function getArtifactsToUpdate({
}, },
}, },
{ {
id: sweep.id, id: path.sweepId,
artifact: { artifact: {
...sweep, ...sweep,
edgeIds: [response.data.modeling_response.data.edge], edgeIds: [response.data.modeling_response.data.edge],
@ -567,7 +529,7 @@ export function getArtifactsToUpdate({
subType: cmd.cut_type, subType: cmd.cut_type,
consumedEdgeId: cmd.edge_id, consumedEdgeId: cmd.edge_id,
edgeIds: [], edgeIds: [],
surfaceId: undefined, surfaceId: '',
codeRef: { range, pathToNode }, codeRef: { range, pathToNode },
}, },
}) })
@ -729,12 +691,10 @@ export function expandSegment(
{ key: segment.pathId, types: ['path'] }, { key: segment.pathId, types: ['path'] },
artifactGraph artifactGraph
) )
const surf = segment.surfaceId const surf = getArtifactOfTypes(
? getArtifactOfTypes( { key: segment.surfaceId, types: ['wall'] },
{ key: segment.surfaceId, types: ['wall'] }, artifactGraph
artifactGraph )
)
: undefined
const edges = getArtifactsOfTypes( const edges = getArtifactsOfTypes(
{ keys: segment.edgeIds, types: ['sweepEdge'] }, { keys: segment.edgeIds, types: ['sweepEdge'] },
artifactGraph artifactGraph
@ -851,7 +811,6 @@ export function getSweepFromSuspectedSweepSurface(
artifactGraph artifactGraph
) )
if (err(path)) return path if (err(path)) return path
if (!path.sweepId) return new Error('Path does not have a sweepId')
return getArtifactOfTypes( return getArtifactOfTypes(
{ key: path.sweepId, types: ['sweep'] }, { key: path.sweepId, types: ['sweep'] },
artifactGraph artifactGraph
@ -869,7 +828,6 @@ export function getSweepFromSuspectedPath(
): SweepArtifact | Error { ): SweepArtifact | Error {
const path = getArtifactOfTypes({ key: id, types: ['path'] }, artifactGraph) const path = getArtifactOfTypes({ key: id, types: ['path'] }, artifactGraph)
if (err(path)) return path if (err(path)) return path
if (!path.sweepId) return new Error('Path does not have a sweepId')
return getArtifactOfTypes( return getArtifactOfTypes(
{ key: path.sweepId, types: ['sweep'] }, { key: path.sweepId, types: ['sweep'] },
artifactGraph artifactGraph

View File

@ -1,21 +1,5 @@
import { isDesktop } from 'lib/isDesktop' import { isDesktop } from 'lib/isDesktop'
// Polyfill window.electron fs functions as needed when in a nodejs context
// (INTENDED FOR VITEST SHINANGANS.)
if (process.env.NODE_ENV === 'test' && process.env.VITEST) {
const fs = require('node:fs/promises')
const path = require('node:path')
Object.assign(window, {
electron: {
readFile: fs.readFile,
stat: fs.stat,
readdir: fs.readdir,
path,
process: {},
},
})
}
/// FileSystemManager is a class that provides a way to read files from the local file system. /// FileSystemManager is a class that provides a way to read files from the local file system.
/// It assumes that you are in a project since it is solely used by the std lib /// It assumes that you are in a project since it is solely used by the std lib
/// when executing code. /// when executing code.
@ -35,9 +19,13 @@ class FileSystemManager {
} }
async readFile(path: string): Promise<Uint8Array> { async readFile(path: string): Promise<Uint8Array> {
// Using local file system only works from desktop and nodejs // Using local file system only works from desktop.
if (!window?.electron?.readFile) { if (!isDesktop()) {
return Promise.reject(new Error('No polyfill found for this function')) return Promise.reject(
new Error(
'This function can only be called from the desktop application'
)
)
} }
return this.join(this.dir, path).then((filePath) => { return this.join(this.dir, path).then((filePath) => {
@ -47,8 +35,12 @@ class FileSystemManager {
async exists(path: string): Promise<boolean | void> { async exists(path: string): Promise<boolean | void> {
// Using local file system only works from desktop. // Using local file system only works from desktop.
if (!window?.electron?.stat) { if (!isDesktop()) {
return Promise.reject(new Error('No polyfill found for this function')) return Promise.reject(
new Error(
'This function can only be called from the desktop application'
)
)
} }
return this.join(this.dir, path).then(async (file) => { return this.join(this.dir, path).then(async (file) => {
@ -65,8 +57,12 @@ class FileSystemManager {
async getAllFiles(path: string): Promise<string[] | void> { async getAllFiles(path: string): Promise<string[] | void> {
// Using local file system only works from desktop. // Using local file system only works from desktop.
if (!window?.electron?.readdir) { if (!isDesktop()) {
return Promise.reject(new Error('No polyfill found for this function')) return Promise.reject(
new Error(
'This function can only be called from the desktop application'
)
)
} }
return this.join(this.dir, path).then((filepath) => { return this.join(this.dir, path).then((filepath) => {

View File

@ -37,10 +37,6 @@ export type ModelingCommandSchema = {
// result: (typeof EXTRUSION_RESULTS)[number] // result: (typeof EXTRUSION_RESULTS)[number]
distance: KclCommandValue distance: KclCommandValue
} }
Sweep: {
path: Selections
profile: Selections
}
Loft: { Loft: {
selection: Selections selection: Selections
} }
@ -51,9 +47,7 @@ export type ModelingCommandSchema = {
Revolve: { Revolve: {
selection: Selections selection: Selections
angle: KclCommandValue angle: KclCommandValue
axisOrEdge: string axis: Selections
axis: string
edge: Selections
} }
Fillet: { Fillet: {
// todo // todo
@ -296,33 +290,6 @@ 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: { Loft: {
description: 'Create a 3D body by blending between two or more sketches', description: 'Create a 3D body by blending between two or more sketches',
icon: 'loft', icon: 'loft',
@ -357,10 +324,10 @@ export const modelingMachineCommandConfig: StateMachineCommandSetConfig<
}, },
}, },
}, },
// TODO: Update this configuration, copied from extrude for MVP of revolve, specifically the args.selection
Revolve: { Revolve: {
description: 'Create a 3D body by rotating a sketch region about an axis.', description: 'Create a 3D body by rotating a sketch region about an axis.',
icon: 'revolve', icon: 'revolve',
status: 'development',
needsReview: true, needsReview: true,
args: { args: {
selection: { selection: {
@ -369,34 +336,9 @@ export const modelingMachineCommandConfig: StateMachineCommandSetConfig<
multiple: false, // TODO: multiple selection multiple: false, // TODO: multiple selection
required: true, required: true,
skip: true, skip: true,
warningMessage:
'The revolve workflow is new and under tested. Please break it and report issues.',
},
axisOrEdge: {
inputType: 'options',
required: true,
defaultValue: 'Axis',
options: [
{ name: 'Axis', isCurrent: true, value: 'Axis' },
{ name: 'Edge', isCurrent: false, value: 'Edge' },
],
}, },
axis: { axis: {
required: (commandContext) => required: true,
['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', inputType: 'selection',
selectionTypes: ['segment', 'sweepEdge', 'edgeCutEdge'], selectionTypes: ['segment', 'sweepEdge', 'edgeCutEdge'],
multiple: false, multiple: false,

View File

@ -68,7 +68,7 @@ export const revolveAxisValidator = async ({
} }
const sketchSelection = artifact.pathId const sketchSelection = artifact.pathId
let edgeSelection = data.edge.graphSelections[0].artifact?.id let edgeSelection = data.axis.graphSelections[0].artifact?.id
if (!sketchSelection) { if (!sketchSelection) {
return 'Unable to revolve, sketch is missing' return 'Unable to revolve, sketch is missing'
@ -101,7 +101,7 @@ export const revolveAxisValidator = async ({
return true return true
} else { } else {
// return error message for the toast // return error message for the toast
return 'Unable to revolve with selected edge' return 'Unable to revolve with selected axis'
} }
} }

View File

@ -53,7 +53,6 @@ export const KCL_DEFAULT_CONSTANT_PREFIXES = {
SKETCH: 'sketch', SKETCH: 'sketch',
EXTRUDE: 'extrude', EXTRUDE: 'extrude',
LOFT: 'loft', LOFT: 'loft',
SWEEP: 'sweep',
SHELL: 'shell', SHELL: 'shell',
SEGMENT: 'seg', SEGMENT: 'seg',
REVOLVE: 'revolve', REVOLVE: 'revolve',

View File

@ -15,7 +15,6 @@ import {
StateMachineCommandSetSchema, StateMachineCommandSetSchema,
} from './commandTypes' } from './commandTypes'
import { DEV } from 'env' import { DEV } from 'env'
import { IS_NIGHTLY_OR_DEBUG } from 'routes/Settings'
interface CreateMachineCommandProps< interface CreateMachineCommandProps<
T extends AnyStateMachine, T extends AnyStateMachine,
@ -85,7 +84,7 @@ export function createMachineCommand<
} else if ('status' in commandConfig) { } else if ('status' in commandConfig) {
const { status } = commandConfig const { status } = commandConfig
if (status === 'inactive') return null if (status === 'inactive') return null
if (status === 'development' && !(DEV || IS_NIGHTLY_OR_DEBUG)) return null if (status === 'development' && !DEV) return null
} }
const icon = ('icon' in commandConfig && commandConfig.icon) || undefined const icon = ('icon' in commandConfig && commandConfig.icon) || undefined

View File

@ -3,8 +3,6 @@ import { isDesktop } from './isDesktop'
export type KclSamplesManifestItem = { export type KclSamplesManifestItem = {
file: string file: string
pathFromProjectDirectoryToFirstFile: string
multipleFiles: boolean
title: string title: string
description: string description: string
} }

View File

@ -49,30 +49,20 @@ export function kclCommands(
if (!data?.sample) { if (!data?.sample) {
return return
} }
const pathParts = data.sample.split('/')
const projectPathPart = pathParts[0]
const primaryKclFile = pathParts[1]
const sampleCodeUrl = `https://raw.githubusercontent.com/KittyCAD/kcl-samples/main/${encodeURIComponent( const sampleCodeUrl = `https://raw.githubusercontent.com/KittyCAD/kcl-samples/main/${encodeURIComponent(
projectPathPart data.sample.replace(FILE_EXT, '')
)}/${encodeURIComponent(primaryKclFile)}` )}/${encodeURIComponent(data.sample)}`
const sampleSettingsFileUrl = `https://raw.githubusercontent.com/KittyCAD/kcl-samples/main/${encodeURIComponent( const sampleSettingsFileUrl = `https://raw.githubusercontent.com/KittyCAD/kcl-samples/main/${encodeURIComponent(
projectPathPart data.sample.replace(FILE_EXT, '')
)}/${PROJECT_SETTINGS_FILE_NAME}` )}/${PROJECT_SETTINGS_FILE_NAME}`
Promise.allSettled([fetch(sampleCodeUrl), fetch(sampleSettingsFileUrl)]) Promise.all([fetch(sampleCodeUrl), fetch(sampleSettingsFileUrl)])
.then((results) => {
const a =
'value' in results[0] ? results[0].value : results[0].reason
const b =
'value' in results[1] ? results[1].value : results[1].reason
return [a, b]
})
.then( .then(
async ([ async ([
codeResponse, codeResponse,
settingsResponse, settingsResponse,
]): Promise<OnSubmitProps> => { ]): Promise<OnSubmitProps> => {
if (!codeResponse.ok) { if (!(codeResponse.ok && settingsResponse.ok)) {
console.error( console.error(
'Failed to fetch sample code:', 'Failed to fetch sample code:',
codeResponse.statusText codeResponse.statusText
@ -80,24 +70,20 @@ export function kclCommands(
return Promise.reject(new Error('Failed to fetch sample code')) return Promise.reject(new Error('Failed to fetch sample code'))
} }
const code = await codeResponse.text() const code = await codeResponse.text()
const parsedProjectSettings = parseProjectSettings(
// It's possible that a sample doesn't have a project.toml await settingsResponse.text()
// associated with it. )
let projectSettingsPayload: ReturnType< let projectSettingsPayload: ReturnType<
typeof projectConfigurationToSettingsPayload typeof projectConfigurationToSettingsPayload
> = {} > = {}
if (settingsResponse.ok) { if (!err(parsedProjectSettings)) {
const parsedProjectSettings = parseProjectSettings( projectSettingsPayload = projectConfigurationToSettingsPayload(
await settingsResponse.text() parsedProjectSettings
) )
if (!err(parsedProjectSettings)) {
projectSettingsPayload =
projectConfigurationToSettingsPayload(parsedProjectSettings)
}
} }
return { return {
sampleName: data.sample.split('/')[0] + FILE_EXT, sampleName: data.sample,
code, code,
method: data.method, method: data.method,
sampleUnits: sampleUnits:

View File

@ -137,7 +137,7 @@ See later source ranges for more context. about the sweep`,
{ key: artifact.pathId, types: ['path'] }, { key: artifact.pathId, types: ['path'] },
artifactGraph artifactGraph
) )
if (!err(path) && path.sweepId) { if (!err(path)) {
const sweep = getArtifactOfTypes( const sweep = getArtifactOfTypes(
{ key: path.sweepId, types: ['sweep'] }, { key: path.sweepId, types: ['sweep'] },
artifactGraph artifactGraph

View File

@ -670,7 +670,6 @@ export function codeToIdSelections(
} }
} }
if (type === 'extrude-wall' && entry.artifact.type === 'segment') { if (type === 'extrude-wall' && entry.artifact.type === 'segment') {
if (!entry.artifact.surfaceId) return
const wall = engineCommandManager.artifactGraph.get( const wall = engineCommandManager.artifactGraph.get(
entry.artifact.surfaceId entry.artifact.surfaceId
) )
@ -715,7 +714,6 @@ export function codeToIdSelections(
(type === 'end-cap' || type === 'start-cap') && (type === 'end-cap' || type === 'start-cap') &&
entry.artifact.type === 'path' entry.artifact.type === 'path'
) { ) {
if (!entry.artifact.sweepId) return
const extrusion = getArtifactOfTypes( const extrusion = getArtifactOfTypes(
{ {
key: entry.artifact.sweepId, key: entry.artifact.sweepId,

View File

@ -8,7 +8,6 @@ import {
modelingMachine, modelingMachine,
pipeHasCircle, pipeHasCircle,
} from 'machines/modelingMachine' } from 'machines/modelingMachine'
import { IS_NIGHTLY_OR_DEBUG } from 'routes/Settings'
import { EventFrom, StateFrom } from 'xstate' import { EventFrom, StateFrom } from 'xstate'
export type ToolbarModeName = 'modeling' | 'sketching' export type ToolbarModeName = 'modeling' | 'sketching'
@ -104,7 +103,7 @@ export const toolbarConfig: Record<ToolbarModeName, ToolbarMode> = {
data: { name: 'Revolve', groupId: 'modeling' }, data: { name: 'Revolve', groupId: 'modeling' },
}), }),
icon: 'revolve', icon: 'revolve',
status: DEV || IS_NIGHTLY_OR_DEBUG ? 'available' : 'kcl-only', status: DEV ? 'available' : 'kcl-only',
title: 'Revolve', title: 'Revolve',
hotkey: 'R', hotkey: 'R',
description: description:
@ -119,21 +118,17 @@ export const toolbarConfig: Record<ToolbarModeName, ToolbarMode> = {
}, },
{ {
id: 'sweep', id: 'sweep',
onClick: ({ commandBarSend }) => onClick: () => console.error('Sweep not yet implemented'),
commandBarSend({
type: 'Find and select command',
data: { name: 'Sweep', groupId: 'modeling' },
}),
icon: 'sweep', icon: 'sweep',
status: DEV || IS_NIGHTLY_OR_DEBUG ? 'available' : 'kcl-only', status: 'unavailable',
title: 'Sweep', title: 'Sweep',
hotkey: 'W', hotkey: 'W',
description: description:
'Create a 3D body by moving a sketch region along an arbitrary path.', 'Create a 3D body by moving a sketch region along an arbitrary path.',
links: [ links: [
{ {
label: 'KCL docs', label: 'GitHub discussion',
url: 'https://zoo.dev/docs/kcl/sweep', url: 'https://github.com/KittyCAD/modeling-app/discussions/498',
}, },
], ],
}, },
@ -166,7 +161,7 @@ export const toolbarConfig: Record<ToolbarModeName, ToolbarMode> = {
data: { name: 'Fillet', groupId: 'modeling' }, data: { name: 'Fillet', groupId: 'modeling' },
}), }),
icon: 'fillet3d', icon: 'fillet3d',
status: DEV || IS_NIGHTLY_OR_DEBUG ? 'available' : 'kcl-only', status: DEV ? 'available' : 'kcl-only',
title: 'Fillet', title: 'Fillet',
hotkey: 'F', hotkey: 'F',
description: 'Round the edges of a 3D solid.', description: 'Round the edges of a 3D solid.',

View File

@ -345,7 +345,7 @@ export function onDragNumberCalculation(text: string, e: MouseEvent) {
) )
const newVal = roundOff(addition, precision) const newVal = roundOff(addition, precision)
if (Number.isNaN(newVal)) { if (isNaN(newVal)) {
return return
} }

View File

@ -45,7 +45,6 @@ import {
import { revolveSketch } from 'lang/modifyAst/addRevolve' import { revolveSketch } from 'lang/modifyAst/addRevolve'
import { import {
addOffsetPlane, addOffsetPlane,
addSweep,
deleteFromSelection, deleteFromSelection,
extrudeSketch, extrudeSketch,
loftSketches, loftSketches,
@ -267,7 +266,6 @@ export type ModelingMachineEvent =
| { type: 'Export'; data: ModelingCommandSchema['Export'] } | { type: 'Export'; data: ModelingCommandSchema['Export'] }
| { type: 'Make'; data: ModelingCommandSchema['Make'] } | { type: 'Make'; data: ModelingCommandSchema['Make'] }
| { type: 'Extrude'; data?: ModelingCommandSchema['Extrude'] } | { type: 'Extrude'; data?: ModelingCommandSchema['Extrude'] }
| { type: 'Sweep'; data?: ModelingCommandSchema['Sweep'] }
| { type: 'Loft'; data?: ModelingCommandSchema['Loft'] } | { type: 'Loft'; data?: ModelingCommandSchema['Loft'] }
| { type: 'Shell'; data?: ModelingCommandSchema['Shell'] } | { type: 'Shell'; data?: ModelingCommandSchema['Shell'] }
| { type: 'Revolve'; data?: ModelingCommandSchema['Revolve'] } | { type: 'Revolve'; data?: ModelingCommandSchema['Revolve'] }
@ -687,7 +685,7 @@ export const modelingMachine = setup({
if (event.type !== 'Revolve') return if (event.type !== 'Revolve') return
;(async () => { ;(async () => {
if (!event.data) return if (!event.data) return
const { selection, angle, axis, edge, axisOrEdge } = event.data const { selection, angle, axis } = event.data
let ast = kclManager.ast let ast = kclManager.ast
if ( if (
'variableName' in angle && 'variableName' in angle &&
@ -712,9 +710,7 @@ export const modelingMachine = setup({
'variableName' in angle 'variableName' in angle
? angle.variableIdentifierAst ? angle.variableIdentifierAst
: angle.valueAst, : angle.valueAst,
axisOrEdge, axis
axis,
edge
) )
if (trap(revolveSketchRes)) return if (trap(revolveSketchRes)) return
const { modifiedAst, pathToRevolveArg } = revolveSketchRes const { modifiedAst, pathToRevolveArg } = revolveSketchRes
@ -1546,66 +1542,6 @@ 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( loftAstMod: fromPromise(
async ({ async ({
input, input,
@ -1801,11 +1737,6 @@ export const modelingMachine = setup({
reenter: false, reenter: false,
}, },
Sweep: {
target: 'Applying sweep',
reenter: true,
},
Loft: { Loft: {
target: 'Applying loft', target: 'Applying loft',
reenter: true, reenter: true,
@ -2598,19 +2529,6 @@ 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': { 'Applying loft': {
invoke: { invoke: {
src: 'loftAstMod', src: 'loftAstMod',

View File

@ -320,11 +320,6 @@ export function getAutoUpdater(): AppUpdater {
} }
app.on('ready', () => { app.on('ready', () => {
// Disable auto updater on non-versioned builds
if (packageJSON.version === '0.0.0') {
return
}
const autoUpdater = getAutoUpdater() const autoUpdater = getAutoUpdater()
// TODO: we're getting `Error: Response ends without calling any handlers` with our setup, // TODO: we're getting `Error: Response ends without calling any handlers` with our setup,
// so at the moment this isn't worth enabling // so at the moment this isn't worth enabling

View File

@ -41,13 +41,13 @@ export default function Export() {
export to almost any CAD software. export to almost any CAD software.
</p> </p>
<p className="my-4"> <p className="my-4">
Our teammate Katie is working on the file format, check out{' '} Our teammate David is working on the file format, check out{' '}
<a <a
href="https://github.com/KhronosGroup/glTF/pull/2343" href="https://www.youtube.com/watch?v=8SuW0qkYCZo"
target="_blank" target="_blank"
rel="noreferrer noopener" rel="noreferrer noopener"
> >
her standards proposal on GitHub his talk with the Metaverse Standards Forum
</a> </a>
! !
</p> </p>

View File

@ -32,8 +32,6 @@ export const PACKAGE_NAME = isDesktop()
export const IS_NIGHTLY = PACKAGE_NAME.indexOf('-nightly') > -1 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) { export function getReleaseUrl(version: string = APP_VERSION) {
return `https://github.com/KittyCAD/modeling-app/releases/tag/${ return `https://github.com/KittyCAD/modeling-app/releases/tag/${
IS_NIGHTLY ? 'nightly-' : '' IS_NIGHTLY ? 'nightly-' : ''

146
src/wasm-lib/Cargo.lock generated
View File

@ -176,7 +176,7 @@ checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.96", "syn 2.0.95",
] ]
[[package]] [[package]]
@ -187,7 +187,7 @@ checksum = "3f934833b4b7233644e5848f235df3f57ed8c80f1528a26c3dfa13d2147fa056"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.96", "syn 2.0.95",
] ]
[[package]] [[package]]
@ -204,7 +204,7 @@ checksum = "3c87f3f15e7794432337fc718554eaa4dc8f04c9677a950ffe366f20a162ae42"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.96", "syn 2.0.95",
] ]
[[package]] [[package]]
@ -474,7 +474,7 @@ dependencies = [
"heck 0.5.0", "heck 0.5.0",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.96", "syn 2.0.95",
] ]
[[package]] [[package]]
@ -665,7 +665,7 @@ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"strsim", "strsim",
"syn 2.0.96", "syn 2.0.95",
] ]
[[package]] [[package]]
@ -676,7 +676,7 @@ checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806"
dependencies = [ dependencies = [
"darling_core", "darling_core",
"quote", "quote",
"syn 2.0.96", "syn 2.0.95",
] ]
[[package]] [[package]]
@ -723,7 +723,7 @@ dependencies = [
[[package]] [[package]]
name = "derive-docs" name = "derive-docs"
version = "0.1.34" version = "0.1.33"
dependencies = [ dependencies = [
"Inflector", "Inflector",
"anyhow", "anyhow",
@ -737,7 +737,7 @@ dependencies = [
"rustfmt-wrapper", "rustfmt-wrapper",
"serde", "serde",
"serde_tokenstream", "serde_tokenstream",
"syn 2.0.96", "syn 2.0.95",
] ]
[[package]] [[package]]
@ -748,38 +748,7 @@ checksum = "67e77553c4162a157adbf834ebae5b415acbecbeafc7a74b0e886657506a7611"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.96", "syn 2.0.95",
]
[[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]] [[package]]
@ -822,7 +791,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.96", "syn 2.0.95",
] ]
[[package]] [[package]]
@ -860,7 +829,7 @@ checksum = "a1ab991c1362ac86c61ab6f556cff143daa22e5a15e4e189df818b2fd19fe65b"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.96", "syn 2.0.95",
] ]
[[package]] [[package]]
@ -1021,7 +990,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.96", "syn 2.0.95",
] ]
[[package]] [[package]]
@ -1117,7 +1086,7 @@ dependencies = [
"inflections", "inflections",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.96", "syn 2.0.95",
] ]
[[package]] [[package]]
@ -1163,18 +1132,17 @@ dependencies = [
[[package]] [[package]]
name = "handlebars" name = "handlebars"
version = "6.3.0" version = "6.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d6b224b95c1e668ac0270325ad563b2eef1469fbbb8959bc7c692c844b813d9" checksum = "fd4ccde012831f9a071a637b0d4e31df31c0f6c525784b35ae76a9ac6bc1e315"
dependencies = [ dependencies = [
"derive_builder",
"log", "log",
"num-order", "num-order",
"pest", "pest",
"pest_derive", "pest_derive",
"serde", "serde",
"serde_json", "serde_json",
"thiserror 2.0.0", "thiserror 1.0.68",
] ]
[[package]] [[package]]
@ -1526,7 +1494,7 @@ checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.96", "syn 2.0.95",
] ]
[[package]] [[package]]
@ -1716,7 +1684,7 @@ dependencies = [
[[package]] [[package]]
name = "kcl-lib" name = "kcl-lib"
version = "0.2.30" version = "0.2.29"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"approx 0.5.1", "approx 0.5.1",
@ -1784,7 +1752,7 @@ dependencies = [
[[package]] [[package]]
name = "kcl-test-server" name = "kcl-test-server"
version = "0.1.20" version = "0.1.19"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"hyper 0.14.30", "hyper 0.14.30",
@ -1851,9 +1819,9 @@ dependencies = [
[[package]] [[package]]
name = "kittycad-modeling-cmds" name = "kittycad-modeling-cmds"
version = "0.2.89" version = "0.2.86"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ce9e58b34645facea36bc9f4868877bbe6fcac01b92896825e8d4f2f7c71dbd6" checksum = "65e34a8eeb4fff5167666d1f2bc36c95d08ab3a0f736a02c8d33a8cde21cfd8d"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"chrono", "chrono",
@ -1884,7 +1852,7 @@ dependencies = [
"kittycad-modeling-cmds-macros-impl", "kittycad-modeling-cmds-macros-impl",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.96", "syn 2.0.95",
] ]
[[package]] [[package]]
@ -1895,7 +1863,7 @@ checksum = "fdb4ee23cc996aa2dca7584d410e8826e08161e1ac4335bb646d5ede33f37cb3"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.96", "syn 2.0.95",
] ]
[[package]] [[package]]
@ -2045,7 +2013,7 @@ checksum = "dcf09caffaac8068c346b6df2a7fc27a177fd20b39421a39ce0a211bde679a6c"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.96", "syn 2.0.95",
] ]
[[package]] [[package]]
@ -2344,7 +2312,7 @@ dependencies = [
"regex", "regex",
"regex-syntax 0.8.5", "regex-syntax 0.8.5",
"structmeta", "structmeta",
"syn 2.0.96", "syn 2.0.95",
] ]
[[package]] [[package]]
@ -2358,7 +2326,7 @@ dependencies = [
"regex", "regex",
"regex-syntax 0.8.5", "regex-syntax 0.8.5",
"structmeta", "structmeta",
"syn 2.0.96", "syn 2.0.95",
] ]
[[package]] [[package]]
@ -2398,7 +2366,7 @@ dependencies = [
"pest_meta", "pest_meta",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.96", "syn 2.0.95",
] ]
[[package]] [[package]]
@ -2456,7 +2424,7 @@ checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.96", "syn 2.0.95",
] ]
[[package]] [[package]]
@ -2586,7 +2554,7 @@ dependencies = [
"proc-macro-error-attr2", "proc-macro-error-attr2",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.96", "syn 2.0.95",
] ]
[[package]] [[package]]
@ -2645,7 +2613,7 @@ dependencies = [
"proc-macro2", "proc-macro2",
"pyo3-macros-backend", "pyo3-macros-backend",
"quote", "quote",
"syn 2.0.96", "syn 2.0.95",
] ]
[[package]] [[package]]
@ -2658,7 +2626,7 @@ dependencies = [
"proc-macro2", "proc-macro2",
"pyo3-build-config", "pyo3-build-config",
"quote", "quote",
"syn 2.0.96", "syn 2.0.95",
] ]
[[package]] [[package]]
@ -3193,7 +3161,7 @@ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"serde_derive_internals", "serde_derive_internals",
"syn 2.0.96", "syn 2.0.95",
] ]
[[package]] [[package]]
@ -3257,7 +3225,7 @@ checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.96", "syn 2.0.95",
] ]
[[package]] [[package]]
@ -3268,7 +3236,7 @@ checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.96", "syn 2.0.95",
] ]
[[package]] [[package]]
@ -3292,7 +3260,7 @@ checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.96", "syn 2.0.95",
] ]
[[package]] [[package]]
@ -3313,7 +3281,7 @@ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"serde", "serde",
"syn 2.0.96", "syn 2.0.95",
] ]
[[package]] [[package]]
@ -3462,7 +3430,7 @@ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"structmeta-derive", "structmeta-derive",
"syn 2.0.96", "syn 2.0.95",
] ]
[[package]] [[package]]
@ -3473,7 +3441,7 @@ checksum = "152a0b65a590ff6c3da95cabe2353ee04e6167c896b28e3b14478c2636c922fc"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.96", "syn 2.0.95",
] ]
[[package]] [[package]]
@ -3495,7 +3463,7 @@ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"rustversion", "rustversion",
"syn 2.0.96", "syn 2.0.95",
] ]
[[package]] [[package]]
@ -3538,9 +3506,9 @@ dependencies = [
[[package]] [[package]]
name = "syn" name = "syn"
version = "2.0.96" version = "2.0.95"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d5d0adab1ae378d7f53bdebc67a39f1f151407ef230f0ce2883572f5d8985c80" checksum = "46f71c0377baf4ef1cc3e3402ded576dccc315800fbc62dfc7fe04b009773b4a"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -3564,7 +3532,7 @@ checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.96", "syn 2.0.95",
] ]
[[package]] [[package]]
@ -3672,7 +3640,7 @@ checksum = "a7c61ec9a6f64d2793d8a45faba21efbe3ced62a886d44c36a009b2b519b4c7e"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.96", "syn 2.0.95",
] ]
[[package]] [[package]]
@ -3683,7 +3651,7 @@ checksum = "22efd00f33f93fa62848a7cab956c3d38c8d43095efda1decfc2b3a5dc0b8972"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.96", "syn 2.0.95",
] ]
[[package]] [[package]]
@ -3795,7 +3763,7 @@ checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.96", "syn 2.0.95",
] ]
[[package]] [[package]]
@ -3937,7 +3905,7 @@ checksum = "84fd902d4e0b9a4b27f2f440108dc034e1758628a9b702f8ec61ad66355422fa"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.96", "syn 2.0.95",
] ]
[[package]] [[package]]
@ -3965,7 +3933,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.96", "syn 2.0.95",
] ]
[[package]] [[package]]
@ -4048,7 +4016,7 @@ checksum = "0e9d8656589772eeec2cf7a8264d9cda40fb28b9bc53118ceb9e8c07f8f38730"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.96", "syn 2.0.95",
"termcolor", "termcolor",
] ]
@ -4191,9 +4159,9 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
[[package]] [[package]]
name = "uuid" name = "uuid"
version = "1.11.1" version = "1.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b913a3b5fe84142e269d63cc62b64319ccaf89b748fc31fe025177f767a756c4" checksum = "f8c5f0a0af699448548ad1a2fbf920fb4bee257eae39953ba95cb84891a0446a"
dependencies = [ dependencies = [
"getrandom", "getrandom",
"serde", "serde",
@ -4227,7 +4195,7 @@ dependencies = [
"proc-macro-error2", "proc-macro-error2",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.96", "syn 2.0.95",
] ]
[[package]] [[package]]
@ -4288,7 +4256,7 @@ dependencies = [
"log", "log",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.96", "syn 2.0.95",
"wasm-bindgen-shared", "wasm-bindgen-shared",
] ]
@ -4324,7 +4292,7 @@ checksum = "30d7a95b763d3c45903ed6c81f156801839e5ee968bb07e534c44df0fcd330c2"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.96", "syn 2.0.95",
"wasm-bindgen-backend", "wasm-bindgen-backend",
"wasm-bindgen-shared", "wasm-bindgen-shared",
] ]
@ -4705,7 +4673,7 @@ checksum = "28cc31741b18cb6f1d5ff12f5b7523e3d6eb0852bbbad19d73905511d9849b95"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.96", "syn 2.0.95",
"synstructure", "synstructure",
] ]
@ -4727,7 +4695,7 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.96", "syn 2.0.95",
] ]
[[package]] [[package]]
@ -4747,7 +4715,7 @@ checksum = "0ea7b4a3637ea8669cedf0f1fd5c286a17f3de97b8dd5a70a6c167a1730e63a5"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.96", "syn 2.0.95",
"synstructure", "synstructure",
] ]
@ -4776,7 +4744,7 @@ checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.96", "syn 2.0.95",
] ]
[[package]] [[package]]

View File

@ -76,7 +76,7 @@ members = [
[workspace.dependencies] [workspace.dependencies]
http = "1" http = "1"
kittycad = { version = "0.3.28", default-features = false, features = ["js", "requests"] } kittycad = { version = "0.3.28", default-features = false, features = ["js", "requests"] }
kittycad-modeling-cmds = { version = "0.2.89", features = [ kittycad-modeling-cmds = { version = "0.2.86", features = [
"ts-rs", "ts-rs",
"websocket", "websocket",
] } ] }

View File

@ -1,7 +1,7 @@
[package] [package]
name = "derive-docs" name = "derive-docs"
description = "A tool for generating documentation from Rust derive macros" description = "A tool for generating documentation from Rust derive macros"
version = "0.1.34" version = "0.1.33"
edition = "2021" edition = "2021"
license = "MIT" license = "MIT"
repository = "https://github.com/KittyCAD/modeling-app" repository = "https://github.com/KittyCAD/modeling-app"
@ -20,7 +20,7 @@ quote = "1"
regex = "1.11" regex = "1.11"
serde = { version = "1.0.217", features = ["derive"] } serde = { version = "1.0.217", features = ["derive"] }
serde_tokenstream = "0.2" serde_tokenstream = "0.2"
syn = { version = "2.0.96", features = ["full"] } syn = { version = "2.0.95", features = ["full"] }
[dev-dependencies] [dev-dependencies]
anyhow = "1.0.95" anyhow = "1.0.95"

View File

@ -1,7 +1,7 @@
[package] [package]
name = "kcl-test-server" name = "kcl-test-server"
description = "A test server for KCL" description = "A test server for KCL"
version = "0.1.20" version = "0.1.19"
edition = "2021" edition = "2021"
license = "MIT" license = "MIT"

View File

@ -502,6 +502,4 @@ impl kcl_lib::EngineManager for EngineConnection {
})), })),
} }
} }
async fn close(&self) {}
} }

View File

@ -1,7 +1,7 @@
[package] [package]
name = "kcl-lib" name = "kcl-lib"
description = "KittyCAD Language implementation and tools" description = "KittyCAD Language implementation and tools"
version = "0.2.30" version = "0.2.29"
edition = "2021" edition = "2021"
license = "MIT" license = "MIT"
repository = "https://github.com/KittyCAD/modeling-app" 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" convert_case = "0.6.0"
dashmap = "6.1.0" dashmap = "6.1.0"
derive-docs = { version = "0.1.34", path = "../derive-docs" } derive-docs = { version = "0.1.33", path = "../derive-docs" }
dhat = { version = "0.3", optional = true } dhat = { version = "0.3", optional = true }
fnv = "1.0.7" fnv = "1.0.7"
form_urlencoded = "1.2.1" form_urlencoded = "1.2.1"
@ -112,7 +112,7 @@ tabled = ["dep:tabled"]
base64 = "0.22.1" base64 = "0.22.1"
criterion = { version = "0.5.1", features = ["async_tokio"] } criterion = { version = "0.5.1", features = ["async_tokio"] }
expectorate = "1.1.0" expectorate = "1.1.0"
handlebars = "6.3.0" handlebars = "6.2.0"
iai = "0.1" iai = "0.1"
image = { version = "0.25.5", default-features = false, features = ["png"] } image = { version = "0.25.5", default-features = false, features = ["png"] }
insta = { version = "1.41.1", features = ["json", "filters", "redactions"] } insta = { version = "1.41.1", features = ["json", "filters", "redactions"] }

View File

@ -1024,36 +1024,6 @@ mod tests {
assert_eq!(snippet, r#"hole(${0:holeSketch}, ${1:%})${}"#); 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. // We want to test the snippets we compile at lsp start.
#[test] #[test]
fn get_all_stdlib_autocomplete_snippets() { fn get_all_stdlib_autocomplete_snippets() {

View File

@ -37,10 +37,9 @@ enum SocketHealth {
} }
type WebSocketTcpWrite = futures::stream::SplitSink<tokio_tungstenite::WebSocketStream<reqwest::Upgraded>, WsMsg>; type WebSocketTcpWrite = futures::stream::SplitSink<tokio_tungstenite::WebSocketStream<reqwest::Upgraded>, WsMsg>;
#[derive(Debug)] #[derive(Debug, Clone)]
pub struct EngineConnection { pub struct EngineConnection {
engine_req_tx: mpsc::Sender<ToEngineReq>, engine_req_tx: mpsc::Sender<ToEngineReq>,
shutdown_tx: mpsc::Sender<()>,
responses: Arc<DashMap<uuid::Uuid, WebSocketResponse>>, responses: Arc<DashMap<uuid::Uuid, WebSocketResponse>>,
pending_errors: Arc<Mutex<Vec<String>>>, pending_errors: Arc<Mutex<Vec<String>>>,
#[allow(dead_code)] #[allow(dead_code)]
@ -131,49 +130,21 @@ struct ToEngineReq {
impl EngineConnection { impl EngineConnection {
/// Start waiting for incoming engine requests, and send each one over the WebSocket to the engine. /// Start waiting for incoming engine requests, and send each one over the WebSocket to the engine.
async fn start_write_actor( async fn start_write_actor(mut tcp_write: WebSocketTcpWrite, mut engine_req_rx: mpsc::Receiver<ToEngineReq>) {
mut tcp_write: WebSocketTcpWrite, while let Some(req) = engine_req_rx.recv().await {
mut engine_req_rx: mpsc::Receiver<ToEngineReq>, let ToEngineReq { req, request_sent } = req;
mut shutdown_rx: mpsc::Receiver<()>, let res = if let WebSocketRequest::ModelingCmdReq(ModelingCmdReq {
) { cmd: ModelingCmd::ImportFiles { .. },
loop { cmd_id: _,
tokio::select! { }) = &req
maybe_req = engine_req_rx.recv() => { {
match maybe_req { // Send it as binary.
Some(ToEngineReq { req, request_sent }) => { Self::inner_send_to_engine_binary(req, &mut tcp_write).await
// Decide whether to send as binary or text, } else {
// then send to the engine. Self::inner_send_to_engine(req, &mut tcp_write).await
let res = if let WebSocketRequest::ModelingCmdReq(ModelingCmdReq { };
cmd: ModelingCmd::ImportFiles { .. }, let _ = request_sent.send(res);
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 weve 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; let _ = Self::inner_close_engine(&mut tcp_write).await;
} }
@ -223,8 +194,7 @@ impl EngineConnection {
let (tcp_write, tcp_read) = ws_stream.split(); let (tcp_write, tcp_read) = ws_stream.split();
let (engine_req_tx, engine_req_rx) = mpsc::channel(10); let (engine_req_tx, engine_req_rx) = mpsc::channel(10);
let (shutdown_tx, shutdown_rx) = mpsc::channel(1); tokio::task::spawn(Self::start_write_actor(tcp_write, engine_req_rx));
tokio::task::spawn(Self::start_write_actor(tcp_write, engine_req_rx, shutdown_rx));
let mut tcp_read = TcpRead { stream: tcp_read }; let mut tcp_read = TcpRead { stream: tcp_read };
@ -334,7 +304,6 @@ impl EngineConnection {
Ok(EngineConnection { Ok(EngineConnection {
engine_req_tx, engine_req_tx,
shutdown_tx,
tcp_read_handle: Arc::new(TcpReadHandle { tcp_read_handle: Arc::new(TcpReadHandle {
handle: Arc::new(tcp_read_handle), handle: Arc::new(tcp_read_handle),
}), }),
@ -515,15 +484,4 @@ impl EngineManager for EngineConnection {
fn get_session_data(&self) -> Option<ModelingSessionData> { fn get_session_data(&self) -> Option<ModelingSessionData> {
self.session_data.lock().unwrap().clone() 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;
}
}
}
}
} }

View File

@ -160,6 +160,4 @@ impl crate::engine::EngineManager for EngineConnection {
})), })),
} }
} }
async fn close(&self) {}
} }

View File

@ -267,7 +267,4 @@ impl crate::engine::EngineManager for EngineConnection {
Ok(ws_result) Ok(ws_result)
} }
// maybe we can actually impl this here? not sure how atm.
async fn close(&self) {}
} }

View File

@ -600,9 +600,6 @@ pub trait EngineManager: std::fmt::Debug + Send + Sync + 'static {
fn get_session_data(&self) -> Option<ModelingSessionData> { fn get_session_data(&self) -> Option<ModelingSessionData> {
None 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)] #[derive(Debug, Hash, Eq, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]

View File

@ -2013,13 +2013,10 @@ impl ExecutorContext {
// AND if we aren't in wasm it doesn't really matter. // AND if we aren't in wasm it doesn't really matter.
Ok(()) Ok(())
} }
/// Given an old ast, old program memory and new ast, find the parts of the code that need to be // Given an old ast, old program memory and new ast, find the parts of the code that need to be
/// re-executed. // re-executed.
/// This function should never error, because in the case of any internal error, we should just pop // This function should never error, because in the case of any internal error, we should just pop
/// the cache. // 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> { pub async fn get_changed_program(&self, info: CacheInformation) -> Option<CacheResult> {
let Some(old) = info.old else { let Some(old) = info.old else {
// We have no old info, we need to re-execute the whole thing. // We have no old info, we need to re-execute the whole thing.
@ -2140,7 +2137,7 @@ impl ExecutorContext {
} }
} }
std::cmp::Ordering::Equal => { std::cmp::Ordering::Equal => {
// currently unreachable, but let's pretend like the code // currently unreachable, but lets pretend like the code
// above can do something meaningful here for when we get // above can do something meaningful here for when we get
// to diffing and yanking chunks of the program apart. // to diffing and yanking chunks of the program apart.
@ -2239,10 +2236,7 @@ impl ExecutorContext {
) )
})?; })?;
// Move the artifact commands to simplify cache management. // Move the artifact commands to simplify cache management.
exec_state exec_state.global.artifact_commands = self.engine.take_artifact_commands();
.global
.artifact_commands
.extend(self.engine.take_artifact_commands());
let session_data = self.engine.get_session_data(); let session_data = self.engine.get_session_data();
Ok(session_data) Ok(session_data)
} }
@ -2632,10 +2626,6 @@ impl ExecutorContext {
self.prepare_snapshot().await self.prepare_snapshot().await
} }
pub async fn close(&self) {
self.engine.close().await;
}
} }
/// For each argument given, /// For each argument given,

View File

@ -1108,7 +1108,7 @@ impl<'a> FromKclValue<'a> for super::helix::HelixData {
fn from_kcl_val(arg: &'a KclValue) -> Option<Self> { fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
let obj = arg.as_object()?; let obj = arg.as_object()?;
let_field_of!(obj, revolutions); let_field_of!(obj, revolutions);
let_field_of!(obj, length?); let_field_of!(obj, length);
let_field_of!(obj, ccw?); let_field_of!(obj, ccw?);
let_field_of!(obj, radius); let_field_of!(obj, radius);
let_field_of!(obj, axis); let_field_of!(obj, axis);

View File

@ -1,9 +1,6 @@
use derive_docs::stdlib; use derive_docs::stdlib;
use super::{ use super::{args::Arg, Args, FnAsArg};
args::{Arg, FromArgs},
Args, FnAsArg,
};
use crate::{ use crate::{
errors::{KclError, KclErrorDetails}, errors::{KclError, KclErrorDetails},
execution::{ExecState, FunctionParam, KclValue}, execution::{ExecState, FunctionParam, KclValue},
@ -12,14 +9,47 @@ use crate::{
/// Apply a function to each element of an array. /// Apply a function to each element of an array.
pub async fn map(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> { pub async fn map(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
let (array, f): (Vec<KclValue>, FnAsArg<'_>) = FromArgs::from_args(&args, 0)?; let array = args.get_unlabeled_kw_arg("array")?;
// Check that the array is an array
let array: Vec<KclValue> = match array {
KclValue::Array { value, meta: _ } => value,
_ => {
return Err(KclError::Semantic(KclErrorDetails {
source_ranges: vec![args.source_range],
message: format!(
"Expected an array to map, but got a value of type {}",
array.human_friendly_type()
),
}))
}
};
// Check that the map_fn is a function
let map_fn_kclvalue: KclValue = args.get_kw_arg("map_fn")?;
match map_fn_kclvalue {
KclValue::Function { .. } => (),
_ => {
return Err(KclError::Semantic(KclErrorDetails {
source_ranges: vec![args.source_range],
message: format!(
"Expected map_fn to be a function, but got a value of type {}",
map_fn_kclvalue.human_friendly_type()
),
}))
}
}
// Extract the function from the KclValue
let map_fn: FnAsArg<'_> = args.get_kw_arg("map_fn")?;
let meta = vec![args.source_range.into()]; let meta = vec![args.source_range.into()];
let map_fn = FunctionParam { let map_fn = FunctionParam {
inner: f.func, inner: map_fn.func,
fn_expr: f.expr, fn_expr: map_fn.expr,
meta: meta.clone(), meta: meta.clone(),
ctx: args.ctx.clone(), ctx: args.ctx.clone(),
memory: *f.memory, memory: *map_fn.memory,
}; };
let new_array = inner_map(array, map_fn, exec_state, &args).await?; let new_array = inner_map(array, map_fn, exec_state, &args).await?;
Ok(KclValue::Array { value: new_array, meta }) Ok(KclValue::Array { value: new_array, meta })
@ -41,7 +71,7 @@ pub async fn map(exec_state: &mut ExecState, args: Args) -> Result<KclValue, Kcl
/// // which is the return value from `map`. /// // which is the return value from `map`.
/// circles = map( /// circles = map(
/// [1..3], /// [1..3],
/// drawCircle /// map_fn = drawCircle
/// ) /// )
/// ``` /// ```
/// ```no_run /// ```no_run
@ -49,7 +79,7 @@ pub async fn map(exec_state: &mut ExecState, args: Args) -> Result<KclValue, Kcl
/// // Call `map`, using an anonymous function instead of a named one. /// // Call `map`, using an anonymous function instead of a named one.
/// circles = map( /// circles = map(
/// [1..3], /// [1..3],
/// fn(id) { /// map_fn = fn(id) {
/// return startSketchOn("XY") /// return startSketchOn("XY")
/// |> circle({ center: [id * 2 * r, 0], radius: r}, %) /// |> circle({ center: [id * 2 * r, 0], radius: r}, %)
/// } /// }
@ -57,6 +87,12 @@ pub async fn map(exec_state: &mut ExecState, args: Args) -> Result<KclValue, Kcl
/// ``` /// ```
#[stdlib { #[stdlib {
name = "map", name = "map",
keywords = true,
unlabeled_first = true,
arg_docs = {
array = "The array to map.",
map_fn = "The function to map the array with.",
}
}] }]
async fn inner_map<'a>( async fn inner_map<'a>(
array: Vec<KclValue>, array: Vec<KclValue>,
@ -91,13 +127,43 @@ async fn call_map_closure<'a>(
/// For each item in an array, update a value. /// For each item in an array, update a value.
pub async fn reduce(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> { pub async fn reduce(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
let (array, start, f): (Vec<KclValue>, KclValue, FnAsArg<'_>) = FromArgs::from_args(&args, 0)?; let array_val = args.get_unlabeled_kw_arg("array")?;
let start = args.get_kw_arg("start")?;
let reduce_fn_kclvalue: KclValue = args.get_kw_arg("reduce_fn")?;
let array: Vec<KclValue> = match array_val {
KclValue::Array { value, meta: _ } => value,
_ => {
return Err(KclError::Semantic(KclErrorDetails {
source_ranges: vec![args.source_range],
message: format!("You can't reduce a value of type {}", array_val.human_friendly_type()),
}))
}
};
// Check that the reduce_fn is a function
match reduce_fn_kclvalue {
KclValue::Function { .. } => (),
_ => {
return Err(KclError::Semantic(KclErrorDetails {
source_ranges: vec![args.source_range],
message: format!(
"Expected reduce_fn to be a function, but got a value of type {}",
reduce_fn_kclvalue.human_friendly_type()
),
}))
}
}
// Extract the function from the KclValue
let reduce_fn: FnAsArg<'_> = args.get_kw_arg("reduce_fn")?;
let reduce_fn = FunctionParam { let reduce_fn = FunctionParam {
inner: f.func, inner: reduce_fn.func,
fn_expr: f.expr, fn_expr: reduce_fn.expr,
meta: vec![args.source_range.into()], meta: vec![args.source_range.into()],
ctx: args.ctx.clone(), ctx: args.ctx.clone(),
memory: *f.memory, memory: *reduce_fn.memory,
}; };
inner_reduce(array, start, reduce_fn, exec_state, &args).await inner_reduce(array, start, reduce_fn, exec_state, &args).await
} }
@ -111,7 +177,7 @@ pub async fn reduce(exec_state: &mut ExecState, args: Args) -> Result<KclValue,
/// // This function adds an array of numbers. /// // This function adds an array of numbers.
/// // It uses the `reduce` function, to call the `add` function on every /// // It uses the `reduce` function, to call the `add` function on every
/// // element of the `arr` parameter. The starting value is 0. /// // element of the `arr` parameter. The starting value is 0.
/// fn sum(arr) { return reduce(arr, 0, add) } /// fn sum(arr) { return reduce(arr, start = 0, reduce_fn = add) }
/// ///
/// /* /// /*
/// The above is basically like this pseudo-code: /// The above is basically like this pseudo-code:
@ -131,7 +197,7 @@ pub async fn reduce(exec_state: &mut ExecState, args: Args) -> Result<KclValue,
/// // an anonymous `add` function as its parameter, instead of declaring a /// // an anonymous `add` function as its parameter, instead of declaring a
/// // named function outside. /// // named function outside.
/// arr = [1, 2, 3] /// arr = [1, 2, 3]
/// sum = reduce(arr, 0, (i, result_so_far) => { return i + result_so_far }) /// sum = reduce(arr, start = 0, reduce_fn = (i, result_so_far) => { return i + result_so_far })
/// ///
/// // We use `assertEqual` to check that our `sum` function gives the /// // We use `assertEqual` to check that our `sum` function gives the
/// // expected result. It's good to check your work! /// // expected result. It's good to check your work!
@ -150,7 +216,7 @@ pub async fn reduce(exec_state: &mut ExecState, args: Args) -> Result<KclValue,
/// // Use a `reduce` to draw the remaining decagon sides. /// // Use a `reduce` to draw the remaining decagon sides.
/// // For each number in the array 1..10, run the given function, /// // For each number in the array 1..10, run the given function,
/// // which takes a partially-sketched decagon and adds one more edge to it. /// // which takes a partially-sketched decagon and adds one more edge to it.
/// fullDecagon = reduce([1..10], startOfDecagonSketch, fn(i, partialDecagon) { /// fullDecagon = reduce([1..10], start = startOfDecagonSketch, reduce_fn = fn(i, partialDecagon) {
/// // Draw one edge of the decagon. /// // Draw one edge of the decagon.
/// x = cos(stepAngle * i) * radius /// x = cos(stepAngle * i) * radius
/// y = sin(stepAngle * i) * radius /// y = sin(stepAngle * i) * radius
@ -183,6 +249,13 @@ pub async fn reduce(exec_state: &mut ExecState, args: Args) -> Result<KclValue,
/// ``` /// ```
#[stdlib { #[stdlib {
name = "reduce", name = "reduce",
keywords = true,
unlabeled_first = true,
arg_docs = {
array = "The array to reduce.",
start = "The starting value for the reduction.",
reduce_fn = "The function to reduce the array with.",
}
}] }]
async fn inner_reduce<'a>( async fn inner_reduce<'a>(
array: Vec<KclValue>, array: Vec<KclValue>,
@ -227,11 +300,17 @@ async fn call_reduce_closure<'a>(
/// ///
/// ```no_run /// ```no_run
/// arr = [1, 2, 3] /// arr = [1, 2, 3]
/// new_arr = push(arr, 4) /// new_arr = push(arr, elem = 4)
/// assertEqual(new_arr[3], 4, 0.00001, "4 was added to the end of the array") /// assertEqual(new_arr[3], 4, 0.00001, "4 was added to the end of the array")
/// ``` /// ```
#[stdlib { #[stdlib {
name = "push", name = "push",
keywords = true,
unlabeled_first = true,
arg_docs = {
array = "The array to push to.",
elem = "The element to push to the array.",
}
}] }]
async fn inner_push(mut array: Vec<KclValue>, elem: KclValue, args: &Args) -> Result<KclValue, KclError> { async fn inner_push(mut array: Vec<KclValue>, elem: KclValue, args: &Args) -> Result<KclValue, KclError> {
// Unwrap the KclValues to JValues for manipulation // Unwrap the KclValues to JValues for manipulation
@ -244,7 +323,8 @@ async fn inner_push(mut array: Vec<KclValue>, elem: KclValue, args: &Args) -> Re
pub async fn push(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> { pub async fn push(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
// Extract the array and the element from the arguments // Extract the array and the element from the arguments
let (val, elem): (KclValue, KclValue) = FromArgs::from_args(&args, 0)?; let val = args.get_unlabeled_kw_arg("array")?;
let elem = args.get_kw_arg("elem")?;
let meta = vec![args.source_range]; let meta = vec![args.source_range];
let KclValue::Array { value: array, meta: _ } = val else { let KclValue::Array { value: array, meta: _ } = val else {

View File

@ -26,9 +26,8 @@ pub struct HelixData {
/// The default is `false`. /// The default is `false`.
#[serde(default)] #[serde(default)]
pub ccw: bool, pub ccw: bool,
/// Length of the helix. This is not necessary if the helix is created around an edge. If not /// Length of the helix.
/// given the length of the edge is used. pub length: f64,
pub length: Option<f64>,
/// Radius of the helix. /// Radius of the helix.
pub radius: f64, pub radius: f64,
/// Axis to use as mirror. /// Axis to use as mirror.
@ -65,7 +64,7 @@ pub async fn helix(exec_state: &mut ExecState, args: Args) -> Result<KclValue, K
/// ///
/// ```no_run /// ```no_run
/// // Create a helix around an edge. /// // Create a helix around an edge.
/// helper001 = startSketchOn('XZ') /// /*helper001 = startSketchOn('XZ')
/// |> startProfileAt([0, 0], %) /// |> startProfileAt([0, 0], %)
/// |> line([0, 10], %, $edge001) /// |> line([0, 10], %, $edge001)
/// ///
@ -81,7 +80,7 @@ pub async fn helix(exec_state: &mut ExecState, args: Args) -> Result<KclValue, K
/// // Create a spring by sweeping around the helix path. /// // Create a spring by sweeping around the helix path.
/// springSketch = startSketchOn('XY') /// springSketch = startSketchOn('XY')
/// |> circle({ center = [0, 0], radius = 1 }, %) /// |> circle({ center = [0, 0], radius = 1 }, %)
/// //|> sweep({ path = helixPath }, %) /// |> sweep({ path = helixPath }, %)*/
/// ``` /// ```
#[stdlib { #[stdlib {
name = "helix", name = "helix",
@ -106,20 +105,12 @@ async fn inner_helix(data: HelixData, exec_state: &mut ExecState, args: Args) ->
Axis3dOrEdgeReference::Axis(axis) => { Axis3dOrEdgeReference::Axis(axis) => {
let (axis, origin) = axis.axis_and_origin()?; 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( args.batch_modeling_cmd(
exec_state.next_uuid(), exec_state.next_uuid(),
ModelingCmd::from(mcmd::EntityMakeHelixFromParams { ModelingCmd::from(mcmd::EntityMakeHelixFromParams {
radius: data.radius, radius: data.radius,
is_clockwise: !data.ccw, is_clockwise: !data.ccw,
length: LengthUnit(length), length: LengthUnit(data.length),
revolutions: data.revolutions, revolutions: data.revolutions,
start_angle: Angle::from_degrees(data.angle_start), start_angle: Angle::from_degrees(data.angle_start),
axis, axis,
@ -128,21 +119,25 @@ async fn inner_helix(data: HelixData, exec_state: &mut ExecState, args: Args) ->
) )
.await?; .await?;
} }
Axis3dOrEdgeReference::Edge(edge) => { Axis3dOrEdgeReference::Edge(_edge) => {
let edge_id = edge.get_engine_id(exec_state, &args)?; /*let edge_id = edge.get_engine_id(exec_state, &args)?;
args.batch_modeling_cmd( args.batch_modeling_cmd(
exec_state.next_uuid(), exec_state.next_uuid(),
ModelingCmd::from(mcmd::EntityMakeHelixFromEdge { ModelingCmd::from(mcmd::EntityMakeHelixFromEdge {
radius: data.radius, radius: data.radius,
is_clockwise: !data.ccw, is_clockwise: !data.ccw,
length: data.length.map(LengthUnit), length: LengthUnit(data.length),
revolutions: data.revolutions, revolutions: data.revolutions,
start_angle: Angle::from_degrees(data.angle_start), start_angle: Angle::from_degrees(data.angle_start),
edge_id, edge_id,
}), }),
) )
.await?; .await?;*/
return Err(KclError::Unimplemented(crate::errors::KclErrorDetails {
message: "Helix around edge is not yet implemented".to_string(),
source_ranges: vec![args.source_range],
}));
} }
}; };

View File

@ -156,8 +156,5 @@ async fn inner_loft(
.await?; .await?;
// Using the first sketch as the base curve, idk we might want to change this later. // Using the first sketch as the base curve, idk we might want to change this later.
let mut sketch = sketches[0].clone(); do_post_extrude(sketches[0].clone(), 0.0, exec_state, args).await
// 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
} }

View File

@ -25,12 +25,10 @@ pub async fn execute_and_snapshot(
) -> Result<image::DynamicImage, ExecError> { ) -> Result<image::DynamicImage, ExecError> {
let ctx = new_context(units, true, project_directory).await?; let ctx = new_context(units, true, project_directory).await?;
let program = Program::parse_no_errs(code).map_err(KclErrorWithOutputs::no_outputs)?; let program = Program::parse_no_errs(code).map_err(KclErrorWithOutputs::no_outputs)?;
let res = do_execute_and_snapshot(&ctx, program) do_execute_and_snapshot(&ctx, program)
.await .await
.map(|(_state, snap)| snap) .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. /// Executes a kcl program and takes a snapshot of the result.
@ -41,16 +39,14 @@ pub async fn execute_and_snapshot_ast(
project_directory: Option<PathBuf>, project_directory: Option<PathBuf>,
) -> Result<(ProgramMemory, Vec<Operation>, Vec<ArtifactCommand>, image::DynamicImage), ExecErrorWithState> { ) -> Result<(ProgramMemory, Vec<Operation>, Vec<ArtifactCommand>, image::DynamicImage), ExecErrorWithState> {
let ctx = new_context(units, true, project_directory).await?; let ctx = new_context(units, true, project_directory).await?;
let res = do_execute_and_snapshot(&ctx, ast).await.map(|(state, snap)| { do_execute_and_snapshot(&ctx, ast).await.map(|(state, snap)| {
( (
state.mod_local.memory, state.mod_local.memory,
state.mod_local.operations, state.mod_local.operations,
state.global.artifact_commands, state.global.artifact_commands,
snap, snap,
) )
}); })
ctx.close().await;
res
} }
pub async fn execute_and_snapshot_no_auth( pub async fn execute_and_snapshot_no_auth(
@ -60,12 +56,10 @@ pub async fn execute_and_snapshot_no_auth(
) -> Result<image::DynamicImage, ExecError> { ) -> Result<image::DynamicImage, ExecError> {
let ctx = new_context(units, false, project_directory).await?; let ctx = new_context(units, false, project_directory).await?;
let program = Program::parse_no_errs(code).map_err(KclErrorWithOutputs::no_outputs)?; let program = Program::parse_no_errs(code).map_err(KclErrorWithOutputs::no_outputs)?;
let res = do_execute_and_snapshot(&ctx, program) do_execute_and_snapshot(&ctx, program)
.await .await
.map(|(_state, snap)| snap) .map(|(_state, snap)| snap)
.map_err(|err| err.error); .map_err(|err| err.error)
ctx.close().await;
res
} }
async fn do_execute_and_snapshot( async fn do_execute_and_snapshot(
@ -86,9 +80,6 @@ async fn do_execute_and_snapshot(
.map_err(|e| ExecError::BadPng(e.to_string())) .map_err(|e| ExecError::BadPng(e.to_string()))
.and_then(|x| x.decode().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()))?; .map_err(|err| ExecErrorWithState::new(err, exec_state.clone()))?;
ctx.close().await;
Ok((exec_state, img)) Ok((exec_state, img))
} }

View File

@ -1,6 +1,8 @@
--- ---
source: kcl/src/simulation_tests.rs source: kcl/src/simulation_tests.rs
assertion_line: 55
description: Result of parsing argument_error.kcl description: Result of parsing argument_error.kcl
snapshot_kind: text
--- ---
{ {
"Ok": { "Ok": {
@ -61,39 +63,39 @@ description: Result of parsing argument_error.kcl
"type": "VariableDeclaration" "type": "VariableDeclaration"
}, },
{ {
"end": 38, "end": 47,
"expression": { "expression": {
"arguments": [ "arguments": [
{ {
"end": 29, "type": "LabeledArg",
"name": "f", "label": {
"start": 28, "type": "Identifier",
"type": "Identifier", "name": "map_fn"
"type": "Identifier" },
}, "arg": {
{ "elements": [
"elements": [ {
{ "end": 42,
"end": 33, "raw": "0",
"raw": "0", "start": 41,
"start": 32, "type": "Literal",
"type": "Literal", "type": "Literal",
"type": "Literal", "value": 0.0
"value": 0.0 },
}, {
{ "end": 45,
"end": 36, "raw": "1",
"raw": "1", "start": 44,
"start": 35, "type": "Literal",
"type": "Literal", "type": "Literal",
"type": "Literal", "value": 1.0
"value": 1.0 }
} ],
], "end": 46,
"end": 37, "start": 40,
"start": 31, "type": "ArrayExpression",
"type": "ArrayExpression", "type": "ArrayExpression"
"type": "ArrayExpression" }
} }
], ],
"callee": { "callee": {
@ -102,17 +104,24 @@ description: Result of parsing argument_error.kcl
"start": 24, "start": 24,
"type": "Identifier" "type": "Identifier"
}, },
"end": 38, "end": 47,
"start": 24, "start": 24,
"type": "CallExpression", "type": "CallExpressionKw",
"type": "CallExpression" "type": "CallExpressionKw",
"unlabeled": {
"end": 29,
"name": "f",
"start": 28,
"type": "Identifier",
"type": "Identifier"
}
}, },
"start": 24, "start": 24,
"type": "ExpressionStatement", "type": "ExpressionStatement",
"type": "ExpressionStatement" "type": "ExpressionStatement"
} }
], ],
"end": 39, "end": 48,
"nonCodeMeta": { "nonCodeMeta": {
"nonCodeNodes": { "nonCodeNodes": {
"0": [ "0": [

View File

@ -1,12 +1,14 @@
--- ---
source: kcl/src/simulation_tests.rs source: kcl/src/simulation_tests.rs
assertion_line: 133
description: Error from executing argument_error.kcl description: Error from executing argument_error.kcl
snapshot_kind: text
--- ---
KCL Type error KCL Semantic error
× type: Expected an array but found Function × semantic: Expected an array to map, but got a value of type Function
╭─[5:5] ╭─[5:1]
4 │ 4 │
5 │ map(f, [0, 1]) 5 │ map(f, map_fn = [0, 1])
· · ──────────────────────
╰──── ╰────

View File

@ -2,4 +2,4 @@ fn f(i) {
return 5 return 5
} }
map(f, [0, 1]) map(f, map_fn = [0, 1])

View File

@ -1,6 +1,8 @@
--- ---
source: kcl/src/simulation_tests.rs source: kcl/src/simulation_tests.rs
assertion_line: 55
description: Result of parsing array_elem_push.kcl description: Result of parsing array_elem_push.kcl
snapshot_kind: text
--- ---
{ {
"Ok": { "Ok": {
@ -57,46 +59,53 @@ description: Result of parsing array_elem_push.kcl
}, },
{ {
"declaration": { "declaration": {
"end": 39, "end": 45,
"id": { "id": {
"end": 24, "end": 23,
"name": "new_arr1", "name": "newArr1",
"start": 16, "start": 16,
"type": "Identifier" "type": "Identifier"
}, },
"init": { "init": {
"arguments": [ "arguments": [
{ {
"end": 35, "type": "LabeledArg",
"name": "arr", "label": {
"start": 32, "type": "Identifier",
"type": "Identifier", "name": "elem"
"type": "Identifier" },
}, "arg": {
{ "end": 44,
"end": 38, "raw": "4",
"raw": "4", "start": 43,
"start": 37, "type": "Literal",
"type": "Literal", "type": "Literal",
"type": "Literal", "value": 4.0
"value": 4.0 }
} }
], ],
"callee": { "callee": {
"end": 31, "end": 30,
"name": "push", "name": "push",
"start": 27, "start": 26,
"type": "Identifier" "type": "Identifier"
}, },
"end": 39, "end": 45,
"start": 27, "start": 26,
"type": "CallExpression", "type": "CallExpressionKw",
"type": "CallExpression" "type": "CallExpressionKw",
"unlabeled": {
"end": 34,
"name": "arr",
"start": 31,
"type": "Identifier",
"type": "Identifier"
}
}, },
"start": 16, "start": 16,
"type": "VariableDeclarator" "type": "VariableDeclarator"
}, },
"end": 39, "end": 45,
"kind": "const", "kind": "const",
"start": 16, "start": 16,
"type": "VariableDeclaration", "type": "VariableDeclaration",
@ -104,647 +113,654 @@ description: Result of parsing array_elem_push.kcl
}, },
{ {
"declaration": { "declaration": {
"end": 68, "end": 79,
"id": { "id": {
"end": 48, "end": 53,
"name": "new_arr2", "name": "newArr2",
"start": 40, "start": 46,
"type": "Identifier" "type": "Identifier"
}, },
"init": { "init": {
"arguments": [ "arguments": [
{ {
"end": 64, "type": "LabeledArg",
"name": "new_arr1", "label": {
"start": 56, "type": "Identifier",
"type": "Identifier", "name": "elem"
"type": "Identifier" },
}, "arg": {
{ "end": 78,
"end": 67, "raw": "5",
"raw": "5", "start": 77,
"start": 66, "type": "Literal",
"type": "Literal", "type": "Literal",
"type": "Literal", "value": 5.0
"value": 5.0 }
} }
], ],
"callee": { "callee": {
"end": 55, "end": 60,
"name": "push", "name": "push",
"start": 51, "start": 56,
"type": "Identifier" "type": "Identifier"
}, },
"end": 68, "end": 79,
"start": 51, "start": 56,
"type": "CallExpression", "type": "CallExpressionKw",
"type": "CallExpression" "type": "CallExpressionKw",
"unlabeled": {
"end": 68,
"name": "newArr1",
"start": 61,
"type": "Identifier",
"type": "Identifier"
}
}, },
"start": 40, "start": 46,
"type": "VariableDeclarator" "type": "VariableDeclarator"
}, },
"end": 68, "end": 79,
"kind": "const", "kind": "const",
"start": 40, "start": 46,
"type": "VariableDeclaration", "type": "VariableDeclaration",
"type": "VariableDeclaration" "type": "VariableDeclaration"
}, },
{ {
"end": 142, "end": 152,
"expression": { "expression": {
"arguments": [ "arguments": [
{ {
"computed": false, "computed": false,
"end": 92, "end": 102,
"object": { "object": {
"end": 89, "end": 99,
"name": "new_arr1", "name": "newArr1",
"start": 81, "start": 92,
"type": "Identifier", "type": "Identifier",
"type": "Identifier" "type": "Identifier"
}, },
"property": { "property": {
"end": 91, "end": 101,
"raw": "0", "raw": "0",
"start": 90, "start": 100,
"type": "Literal", "type": "Literal",
"type": "Literal", "type": "Literal",
"value": 0.0 "value": 0.0
}, },
"start": 81, "start": 92,
"type": "MemberExpression", "type": "MemberExpression",
"type": "MemberExpression" "type": "MemberExpression"
}, },
{ {
"end": 95, "end": 105,
"raw": "1", "raw": "1",
"start": 94, "start": 104,
"type": "Literal", "type": "Literal",
"type": "Literal", "type": "Literal",
"value": 1.0 "value": 1.0
}, },
{ {
"end": 104, "end": 114,
"raw": "0.00001", "raw": "0.00001",
"start": 97, "start": 107,
"type": "Literal", "type": "Literal",
"type": "Literal", "type": "Literal",
"value": 0.00001 "value": 0.00001
}, },
{ {
"end": 141, "end": 151,
"raw": "\"element 0 should not have changed\"", "raw": "\"element 0 should not have changed\"",
"start": 106, "start": 116,
"type": "Literal", "type": "Literal",
"type": "Literal", "type": "Literal",
"value": "element 0 should not have changed" "value": "element 0 should not have changed"
} }
], ],
"callee": { "callee": {
"end": 80, "end": 91,
"name": "assertEqual", "name": "assertEqual",
"start": 69, "start": 80,
"type": "Identifier" "type": "Identifier"
}, },
"end": 142, "end": 152,
"start": 69, "start": 80,
"type": "CallExpression", "type": "CallExpression",
"type": "CallExpression" "type": "CallExpression"
}, },
"start": 69, "start": 80,
"type": "ExpressionStatement", "type": "ExpressionStatement",
"type": "ExpressionStatement" "type": "ExpressionStatement"
}, },
{ {
"end": 216, "end": 225,
"expression": { "expression": {
"arguments": [ "arguments": [
{ {
"computed": false, "computed": false,
"end": 166, "end": 175,
"object": { "object": {
"end": 163, "end": 172,
"name": "new_arr1", "name": "newArr1",
"start": 155, "start": 165,
"type": "Identifier", "type": "Identifier",
"type": "Identifier" "type": "Identifier"
}, },
"property": { "property": {
"end": 165, "end": 174,
"raw": "1", "raw": "1",
"start": 164, "start": 173,
"type": "Literal", "type": "Literal",
"type": "Literal", "type": "Literal",
"value": 1.0 "value": 1.0
}, },
"start": 155, "start": 165,
"type": "MemberExpression", "type": "MemberExpression",
"type": "MemberExpression" "type": "MemberExpression"
}, },
{
"end": 169,
"raw": "2",
"start": 168,
"type": "Literal",
"type": "Literal",
"value": 2.0
},
{ {
"end": 178, "end": 178,
"raw": "2",
"start": 177,
"type": "Literal",
"type": "Literal",
"value": 2.0
},
{
"end": 187,
"raw": "0.00001", "raw": "0.00001",
"start": 171, "start": 180,
"type": "Literal", "type": "Literal",
"type": "Literal", "type": "Literal",
"value": 0.00001 "value": 0.00001
}, },
{ {
"end": 215, "end": 224,
"raw": "\"element 1 should not have changed\"", "raw": "\"element 1 should not have changed\"",
"start": 180, "start": 189,
"type": "Literal", "type": "Literal",
"type": "Literal", "type": "Literal",
"value": "element 1 should not have changed" "value": "element 1 should not have changed"
} }
], ],
"callee": { "callee": {
"end": 154, "end": 164,
"name": "assertEqual", "name": "assertEqual",
"start": 143, "start": 153,
"type": "Identifier" "type": "Identifier"
}, },
"end": 216, "end": 225,
"start": 143, "start": 153,
"type": "CallExpression", "type": "CallExpression",
"type": "CallExpression" "type": "CallExpression"
}, },
"start": 143, "start": 153,
"type": "ExpressionStatement", "type": "ExpressionStatement",
"type": "ExpressionStatement" "type": "ExpressionStatement"
}, },
{ {
"end": 290, "end": 298,
"expression": { "expression": {
"arguments": [ "arguments": [
{ {
"computed": false, "computed": false,
"end": 240, "end": 248,
"object": { "object": {
"end": 237, "end": 245,
"name": "new_arr1", "name": "newArr1",
"start": 229, "start": 238,
"type": "Identifier", "type": "Identifier",
"type": "Identifier" "type": "Identifier"
}, },
"property": { "property": {
"end": 239, "end": 247,
"raw": "2", "raw": "2",
"start": 238, "start": 246,
"type": "Literal", "type": "Literal",
"type": "Literal", "type": "Literal",
"value": 2.0 "value": 2.0
}, },
"start": 229, "start": 238,
"type": "MemberExpression", "type": "MemberExpression",
"type": "MemberExpression" "type": "MemberExpression"
}, },
{ {
"end": 243, "end": 251,
"raw": "3", "raw": "3",
"start": 242, "start": 250,
"type": "Literal", "type": "Literal",
"type": "Literal", "type": "Literal",
"value": 3.0 "value": 3.0
}, },
{ {
"end": 252, "end": 260,
"raw": "0.00001", "raw": "0.00001",
"start": 245, "start": 253,
"type": "Literal", "type": "Literal",
"type": "Literal", "type": "Literal",
"value": 0.00001 "value": 0.00001
}, },
{ {
"end": 289, "end": 297,
"raw": "\"element 2 should not have changed\"", "raw": "\"element 2 should not have changed\"",
"start": 254, "start": 262,
"type": "Literal", "type": "Literal",
"type": "Literal", "type": "Literal",
"value": "element 2 should not have changed" "value": "element 2 should not have changed"
} }
], ],
"callee": { "callee": {
"end": 228, "end": 237,
"name": "assertEqual", "name": "assertEqual",
"start": 217, "start": 226,
"type": "Identifier" "type": "Identifier"
}, },
"end": 290, "end": 298,
"start": 217, "start": 226,
"type": "CallExpression", "type": "CallExpression",
"type": "CallExpression" "type": "CallExpression"
}, },
"start": 217, "start": 226,
"type": "ExpressionStatement", "type": "ExpressionStatement",
"type": "ExpressionStatement" "type": "ExpressionStatement"
}, },
{ {
"end": 366, "end": 373,
"expression": { "expression": {
"arguments": [ "arguments": [
{ {
"computed": false, "computed": false,
"end": 314, "end": 321,
"object": { "object": {
"end": 311, "end": 318,
"name": "new_arr1", "name": "newArr1",
"start": 303, "start": 311,
"type": "Identifier", "type": "Identifier",
"type": "Identifier" "type": "Identifier"
}, },
"property": { "property": {
"end": 313, "end": 320,
"raw": "3", "raw": "3",
"start": 312, "start": 319,
"type": "Literal", "type": "Literal",
"type": "Literal", "type": "Literal",
"value": 3.0 "value": 3.0
}, },
"start": 303, "start": 311,
"type": "MemberExpression", "type": "MemberExpression",
"type": "MemberExpression" "type": "MemberExpression"
}, },
{ {
"end": 317, "end": 324,
"raw": "4", "raw": "4",
"start": 316, "start": 323,
"type": "Literal", "type": "Literal",
"type": "Literal", "type": "Literal",
"value": 4.0 "value": 4.0
}, },
{ {
"end": 326, "end": 333,
"raw": "0.00001", "raw": "0.00001",
"start": 319, "start": 326,
"type": "Literal", "type": "Literal",
"type": "Literal", "type": "Literal",
"value": 0.00001 "value": 0.00001
}, },
{ {
"end": 365, "end": 372,
"raw": "\"4 was added to the end of the array\"", "raw": "\"4 was added to the end of the array\"",
"start": 328, "start": 335,
"type": "Literal", "type": "Literal",
"type": "Literal", "type": "Literal",
"value": "4 was added to the end of the array" "value": "4 was added to the end of the array"
} }
], ],
"callee": { "callee": {
"end": 302, "end": 310,
"name": "assertEqual", "name": "assertEqual",
"start": 291, "start": 299,
"type": "Identifier" "type": "Identifier"
}, },
"end": 366, "end": 373,
"start": 291, "start": 299,
"type": "CallExpression", "type": "CallExpression",
"type": "CallExpression" "type": "CallExpression"
}, },
"start": 291, "start": 299,
"type": "ExpressionStatement", "type": "ExpressionStatement",
"type": "ExpressionStatement" "type": "ExpressionStatement"
}, },
{ {
"end": 440, "end": 446,
"expression": { "expression": {
"arguments": [ "arguments": [
{ {
"computed": false, "computed": false,
"end": 390, "end": 396,
"object": { "object": {
"end": 387, "end": 393,
"name": "new_arr2", "name": "newArr2",
"start": 379, "start": 386,
"type": "Identifier", "type": "Identifier",
"type": "Identifier" "type": "Identifier"
}, },
"property": { "property": {
"end": 389, "end": 395,
"raw": "0", "raw": "0",
"start": 388, "start": 394,
"type": "Literal", "type": "Literal",
"type": "Literal", "type": "Literal",
"value": 0.0 "value": 0.0
}, },
"start": 379, "start": 386,
"type": "MemberExpression", "type": "MemberExpression",
"type": "MemberExpression" "type": "MemberExpression"
}, },
{ {
"end": 393, "end": 399,
"raw": "1", "raw": "1",
"start": 392, "start": 398,
"type": "Literal", "type": "Literal",
"type": "Literal", "type": "Literal",
"value": 1.0 "value": 1.0
}, },
{ {
"end": 402, "end": 408,
"raw": "0.00001", "raw": "0.00001",
"start": 395, "start": 401,
"type": "Literal", "type": "Literal",
"type": "Literal", "type": "Literal",
"value": 0.00001 "value": 0.00001
}, },
{ {
"end": 439, "end": 445,
"raw": "\"element 0 should not have changed\"", "raw": "\"element 0 should not have changed\"",
"start": 404, "start": 410,
"type": "Literal", "type": "Literal",
"type": "Literal", "type": "Literal",
"value": "element 0 should not have changed" "value": "element 0 should not have changed"
} }
], ],
"callee": { "callee": {
"end": 378, "end": 385,
"name": "assertEqual", "name": "assertEqual",
"start": 367, "start": 374,
"type": "Identifier" "type": "Identifier"
}, },
"end": 440, "end": 446,
"start": 367, "start": 374,
"type": "CallExpression", "type": "CallExpression",
"type": "CallExpression" "type": "CallExpression"
}, },
"start": 367, "start": 374,
"type": "ExpressionStatement", "type": "ExpressionStatement",
"type": "ExpressionStatement" "type": "ExpressionStatement"
}, },
{ {
"end": 514, "end": 519,
"expression": { "expression": {
"arguments": [ "arguments": [
{ {
"computed": false, "computed": false,
"end": 464, "end": 469,
"object": { "object": {
"end": 461, "end": 466,
"name": "new_arr2", "name": "newArr2",
"start": 453, "start": 459,
"type": "Identifier", "type": "Identifier",
"type": "Identifier" "type": "Identifier"
}, },
"property": { "property": {
"end": 463, "end": 468,
"raw": "1", "raw": "1",
"start": 462, "start": 467,
"type": "Literal", "type": "Literal",
"type": "Literal", "type": "Literal",
"value": 1.0 "value": 1.0
}, },
"start": 453, "start": 459,
"type": "MemberExpression", "type": "MemberExpression",
"type": "MemberExpression" "type": "MemberExpression"
}, },
{ {
"end": 467, "end": 472,
"raw": "2", "raw": "2",
"start": 466, "start": 471,
"type": "Literal", "type": "Literal",
"type": "Literal", "type": "Literal",
"value": 2.0 "value": 2.0
}, },
{ {
"end": 476, "end": 481,
"raw": "0.00001", "raw": "0.00001",
"start": 469, "start": 474,
"type": "Literal", "type": "Literal",
"type": "Literal", "type": "Literal",
"value": 0.00001 "value": 0.00001
}, },
{ {
"end": 513, "end": 518,
"raw": "\"element 1 should not have changed\"", "raw": "\"element 1 should not have changed\"",
"start": 478, "start": 483,
"type": "Literal", "type": "Literal",
"type": "Literal", "type": "Literal",
"value": "element 1 should not have changed" "value": "element 1 should not have changed"
} }
], ],
"callee": { "callee": {
"end": 452, "end": 458,
"name": "assertEqual", "name": "assertEqual",
"start": 441, "start": 447,
"type": "Identifier" "type": "Identifier"
}, },
"end": 514, "end": 519,
"start": 441, "start": 447,
"type": "CallExpression", "type": "CallExpression",
"type": "CallExpression" "type": "CallExpression"
}, },
"start": 441, "start": 447,
"type": "ExpressionStatement", "type": "ExpressionStatement",
"type": "ExpressionStatement" "type": "ExpressionStatement"
}, },
{ {
"end": 588, "end": 592,
"expression": { "expression": {
"arguments": [ "arguments": [
{ {
"computed": false, "computed": false,
"end": 538, "end": 542,
"object": { "object": {
"end": 535, "end": 539,
"name": "new_arr2", "name": "newArr2",
"start": 527, "start": 532,
"type": "Identifier", "type": "Identifier",
"type": "Identifier" "type": "Identifier"
}, },
"property": { "property": {
"end": 537, "end": 541,
"raw": "2", "raw": "2",
"start": 536, "start": 540,
"type": "Literal", "type": "Literal",
"type": "Literal", "type": "Literal",
"value": 2.0 "value": 2.0
}, },
"start": 527, "start": 532,
"type": "MemberExpression", "type": "MemberExpression",
"type": "MemberExpression" "type": "MemberExpression"
}, },
{ {
"end": 541, "end": 545,
"raw": "3", "raw": "3",
"start": 540, "start": 544,
"type": "Literal", "type": "Literal",
"type": "Literal", "type": "Literal",
"value": 3.0 "value": 3.0
}, },
{ {
"end": 550, "end": 554,
"raw": "0.00001", "raw": "0.00001",
"start": 543, "start": 547,
"type": "Literal", "type": "Literal",
"type": "Literal", "type": "Literal",
"value": 0.00001 "value": 0.00001
}, },
{ {
"end": 587, "end": 591,
"raw": "\"element 2 should not have changed\"", "raw": "\"element 2 should not have changed\"",
"start": 552, "start": 556,
"type": "Literal", "type": "Literal",
"type": "Literal", "type": "Literal",
"value": "element 2 should not have changed" "value": "element 2 should not have changed"
} }
], ],
"callee": { "callee": {
"end": 526, "end": 531,
"name": "assertEqual", "name": "assertEqual",
"start": 515, "start": 520,
"type": "Identifier" "type": "Identifier"
}, },
"end": 588, "end": 592,
"start": 515, "start": 520,
"type": "CallExpression", "type": "CallExpression",
"type": "CallExpression" "type": "CallExpression"
}, },
"start": 515, "start": 520,
"type": "ExpressionStatement", "type": "ExpressionStatement",
"type": "ExpressionStatement" "type": "ExpressionStatement"
}, },
{ {
"end": 664, "end": 667,
"expression": { "expression": {
"arguments": [ "arguments": [
{ {
"computed": false, "computed": false,
"end": 612, "end": 615,
"object": { "object": {
"end": 609, "end": 612,
"name": "new_arr2", "name": "newArr2",
"start": 601, "start": 605,
"type": "Identifier", "type": "Identifier",
"type": "Identifier" "type": "Identifier"
}, },
"property": { "property": {
"end": 611, "end": 614,
"raw": "3", "raw": "3",
"start": 610, "start": 613,
"type": "Literal", "type": "Literal",
"type": "Literal", "type": "Literal",
"value": 3.0 "value": 3.0
}, },
"start": 601, "start": 605,
"type": "MemberExpression", "type": "MemberExpression",
"type": "MemberExpression" "type": "MemberExpression"
}, },
{ {
"end": 615, "end": 618,
"raw": "4", "raw": "4",
"start": 614, "start": 617,
"type": "Literal", "type": "Literal",
"type": "Literal", "type": "Literal",
"value": 4.0 "value": 4.0
}, },
{ {
"end": 624, "end": 627,
"raw": "0.00001", "raw": "0.00001",
"start": 617, "start": 620,
"type": "Literal", "type": "Literal",
"type": "Literal", "type": "Literal",
"value": 0.00001 "value": 0.00001
}, },
{ {
"end": 663, "end": 666,
"raw": "\"4 was added to the end of the array\"", "raw": "\"4 was added to the end of the array\"",
"start": 626, "start": 629,
"type": "Literal", "type": "Literal",
"type": "Literal", "type": "Literal",
"value": "4 was added to the end of the array" "value": "4 was added to the end of the array"
} }
], ],
"callee": { "callee": {
"end": 600, "end": 604,
"name": "assertEqual", "name": "assertEqual",
"start": 589, "start": 593,
"type": "Identifier" "type": "Identifier"
}, },
"end": 664, "end": 667,
"start": 589, "start": 593,
"type": "CallExpression", "type": "CallExpression",
"type": "CallExpression" "type": "CallExpression"
}, },
"start": 589, "start": 593,
"type": "ExpressionStatement", "type": "ExpressionStatement",
"type": "ExpressionStatement" "type": "ExpressionStatement"
}, },
{ {
"end": 740, "end": 742,
"expression": { "expression": {
"arguments": [ "arguments": [
{ {
"computed": false, "computed": false,
"end": 688, "end": 690,
"object": { "object": {
"end": 685, "end": 687,
"name": "new_arr2", "name": "newArr2",
"start": 677, "start": 680,
"type": "Identifier", "type": "Identifier",
"type": "Identifier" "type": "Identifier"
}, },
"property": { "property": {
"end": 687, "end": 689,
"raw": "4", "raw": "4",
"start": 686, "start": 688,
"type": "Literal", "type": "Literal",
"type": "Literal", "type": "Literal",
"value": 4.0 "value": 4.0
}, },
"start": 677, "start": 680,
"type": "MemberExpression", "type": "MemberExpression",
"type": "MemberExpression" "type": "MemberExpression"
}, },
{ {
"end": 691, "end": 693,
"raw": "5", "raw": "5",
"start": 690, "start": 692,
"type": "Literal", "type": "Literal",
"type": "Literal", "type": "Literal",
"value": 5.0 "value": 5.0
}, },
{ {
"end": 700, "end": 702,
"raw": "0.00001", "raw": "0.00001",
"start": 693, "start": 695,
"type": "Literal", "type": "Literal",
"type": "Literal", "type": "Literal",
"value": 0.00001 "value": 0.00001
}, },
{ {
"end": 739, "end": 741,
"raw": "\"5 was added to the end of the array\"", "raw": "\"5 was added to the end of the array\"",
"start": 702, "start": 704,
"type": "Literal", "type": "Literal",
"type": "Literal", "type": "Literal",
"value": "5 was added to the end of the array" "value": "5 was added to the end of the array"
} }
], ],
"callee": { "callee": {
"end": 676, "end": 679,
"name": "assertEqual", "name": "assertEqual",
"start": 665, "start": 668,
"type": "Identifier" "type": "Identifier"
}, },
"end": 740, "end": 742,
"start": 665, "start": 668,
"type": "CallExpression", "type": "CallExpression",
"type": "CallExpression" "type": "CallExpression"
}, },
"start": 665, "start": 668,
"type": "ExpressionStatement", "type": "ExpressionStatement",
"type": "ExpressionStatement" "type": "ExpressionStatement"
} }
], ],
"end": 741, "end": 743,
"start": 0 "start": 0
} }
} }

View File

@ -1,12 +1,12 @@
arr = [1, 2, 3] arr = [1, 2, 3]
new_arr1 = push(arr, 4) newArr1 = push(arr, elem = 4)
new_arr2 = push(new_arr1, 5) newArr2 = push(newArr1, elem = 5)
assertEqual(new_arr1[0], 1, 0.00001, "element 0 should not have changed") assertEqual(newArr1[0], 1, 0.00001, "element 0 should not have changed")
assertEqual(new_arr1[1], 2, 0.00001, "element 1 should not have changed") assertEqual(newArr1[1], 2, 0.00001, "element 1 should not have changed")
assertEqual(new_arr1[2], 3, 0.00001, "element 2 should not have changed") assertEqual(newArr1[2], 3, 0.00001, "element 2 should not have changed")
assertEqual(new_arr1[3], 4, 0.00001, "4 was added to the end of the array") assertEqual(newArr1[3], 4, 0.00001, "4 was added to the end of the array")
assertEqual(new_arr2[0], 1, 0.00001, "element 0 should not have changed") assertEqual(newArr2[0], 1, 0.00001, "element 0 should not have changed")
assertEqual(new_arr2[1], 2, 0.00001, "element 1 should not have changed") assertEqual(newArr2[1], 2, 0.00001, "element 1 should not have changed")
assertEqual(new_arr2[2], 3, 0.00001, "element 2 should not have changed") assertEqual(newArr2[2], 3, 0.00001, "element 2 should not have changed")
assertEqual(new_arr2[3], 4, 0.00001, "4 was added to the end of the array") assertEqual(newArr2[3], 4, 0.00001, "4 was added to the end of the array")
assertEqual(new_arr2[4], 5, 0.00001, "5 was added to the end of the array") assertEqual(newArr2[4], 5, 0.00001, "5 was added to the end of the array")

View File

@ -1,6 +1,6 @@
--- ---
source: kcl/src/simulation_tests.rs source: kcl/src/simulation_tests.rs
assertion_line: 92 assertion_line: 99
description: Program memory after executing array_elem_push.kcl description: Program memory after executing array_elem_push.kcl
snapshot_kind: text snapshot_kind: text
--- ---
@ -81,7 +81,7 @@ snapshot_kind: text
} }
] ]
}, },
"new_arr1": { "newArr1": {
"type": "Array", "type": "Array",
"value": [ "value": [
{ {
@ -129,8 +129,8 @@ snapshot_kind: text
"__meta": [ "__meta": [
{ {
"sourceRange": [ "sourceRange": [
37, 43,
38, 44,
0 0
] ]
} }
@ -140,14 +140,14 @@ snapshot_kind: text
"__meta": [ "__meta": [
{ {
"sourceRange": [ "sourceRange": [
27, 26,
39, 45,
0 0
] ]
} }
] ]
}, },
"new_arr2": { "newArr2": {
"type": "Array", "type": "Array",
"value": [ "value": [
{ {
@ -195,8 +195,8 @@ snapshot_kind: text
"__meta": [ "__meta": [
{ {
"sourceRange": [ "sourceRange": [
37, 43,
38, 44,
0 0
] ]
} }
@ -208,8 +208,8 @@ snapshot_kind: text
"__meta": [ "__meta": [
{ {
"sourceRange": [ "sourceRange": [
66, 77,
67, 78,
0 0
] ]
} }
@ -219,8 +219,8 @@ snapshot_kind: text
"__meta": [ "__meta": [
{ {
"sourceRange": [ "sourceRange": [
51, 56,
68, 79,
0 0
] ]
} }

View File

@ -1,6 +1,8 @@
--- ---
source: kcl/src/simulation_tests.rs source: kcl/src/simulation_tests.rs
assertion_line: 55
description: Result of parsing array_elem_push_fail.kcl description: Result of parsing array_elem_push_fail.kcl
snapshot_kind: text
--- ---
{ {
"Ok": { "Ok": {
@ -57,7 +59,7 @@ description: Result of parsing array_elem_push_fail.kcl
}, },
{ {
"declaration": { "declaration": {
"end": 40, "end": 47,
"id": { "id": {
"end": 25, "end": 25,
"name": "pushedArr", "name": "pushedArr",
@ -67,19 +69,19 @@ description: Result of parsing array_elem_push_fail.kcl
"init": { "init": {
"arguments": [ "arguments": [
{ {
"end": 36, "type": "LabeledArg",
"name": "arr", "label": {
"start": 33, "type": "Identifier",
"type": "Identifier", "name": "elem"
"type": "Identifier" },
}, "arg": {
{ "end": 46,
"end": 39, "raw": "4",
"raw": "4", "start": 45,
"start": 38, "type": "Literal",
"type": "Literal", "type": "Literal",
"type": "Literal", "value": 4.0
"value": 4.0 }
} }
], ],
"callee": { "callee": {
@ -88,15 +90,22 @@ description: Result of parsing array_elem_push_fail.kcl
"start": 28, "start": 28,
"type": "Identifier" "type": "Identifier"
}, },
"end": 40, "end": 47,
"start": 28, "start": 28,
"type": "CallExpression", "type": "CallExpressionKw",
"type": "CallExpression" "type": "CallExpressionKw",
"unlabeled": {
"end": 36,
"name": "arr",
"start": 33,
"type": "Identifier",
"type": "Identifier"
}
}, },
"start": 16, "start": 16,
"type": "VariableDeclarator" "type": "VariableDeclarator"
}, },
"end": 40, "end": 47,
"kind": "const", "kind": "const",
"start": 16, "start": 16,
"type": "VariableDeclaration", "type": "VariableDeclaration",
@ -104,46 +113,46 @@ description: Result of parsing array_elem_push_fail.kcl
}, },
{ {
"declaration": { "declaration": {
"end": 54, "end": 61,
"id": { "id": {
"end": 45, "end": 52,
"name": "fail", "name": "fail",
"start": 41, "start": 48,
"type": "Identifier" "type": "Identifier"
}, },
"init": { "init": {
"computed": false, "computed": false,
"end": 54, "end": 61,
"object": { "object": {
"end": 51, "end": 58,
"name": "arr", "name": "arr",
"start": 48, "start": 55,
"type": "Identifier", "type": "Identifier",
"type": "Identifier" "type": "Identifier"
}, },
"property": { "property": {
"end": 53, "end": 60,
"raw": "3", "raw": "3",
"start": 52, "start": 59,
"type": "Literal", "type": "Literal",
"type": "Literal", "type": "Literal",
"value": 3.0 "value": 3.0
}, },
"start": 48, "start": 55,
"type": "MemberExpression", "type": "MemberExpression",
"type": "MemberExpression" "type": "MemberExpression"
}, },
"start": 41, "start": 48,
"type": "VariableDeclarator" "type": "VariableDeclarator"
}, },
"end": 54, "end": 61,
"kind": "const", "kind": "const",
"start": 41, "start": 48,
"type": "VariableDeclaration", "type": "VariableDeclaration",
"type": "VariableDeclaration" "type": "VariableDeclaration"
} }
], ],
"end": 55, "end": 62,
"start": 0 "start": 0
} }
} }

View File

@ -1,12 +1,14 @@
--- ---
source: kcl/src/simulation_tests.rs source: kcl/src/simulation_tests.rs
assertion_line: 133
description: Error from executing array_elem_push_fail.kcl description: Error from executing array_elem_push_fail.kcl
snapshot_kind: text
--- ---
KCL UndefinedValue error KCL UndefinedValue error
× undefined value: The array doesn't have any item at index 3 × undefined value: The array doesn't have any item at index 3
╭─[3:8] ╭─[3:8]
2 │ pushedArr = push(arr, 4) 2 │ pushedArr = push(arr, elem = 4)
3 │ fail = arr[3] 3 │ fail = arr[3]
· ────── · ──────
╰──── ╰────

View File

@ -1,3 +1,3 @@
arr = [1, 2, 3] arr = [1, 2, 3]
pushedArr = push(arr, 4) pushedArr = push(arr, elem = 4)
fail = arr[3] fail = arr[3]

View File

@ -1,6 +1,8 @@
--- ---
source: kcl/src/simulation_tests.rs source: kcl/src/simulation_tests.rs
assertion_line: 55
description: Result of parsing double_map_fn.kcl description: Result of parsing double_map_fn.kcl
snapshot_kind: text
--- ---
{ {
"Ok": { "Ok": {
@ -117,7 +119,7 @@ description: Result of parsing double_map_fn.kcl
}, },
{ {
"declaration": { "declaration": {
"end": 101, "end": 119,
"id": { "id": {
"end": 50, "end": 50,
"name": "ys", "name": "ys",
@ -136,17 +138,18 @@ description: Result of parsing double_map_fn.kcl
{ {
"arguments": [ "arguments": [
{ {
"end": 66, "type": "LabeledArg",
"start": 65, "label": {
"type": "PipeSubstitution", "type": "Identifier",
"type": "PipeSubstitution" "name": "map_fn"
}, },
{ "arg": {
"end": 77, "end": 86,
"name": "increment", "name": "increment",
"start": 68, "start": 77,
"type": "Identifier", "type": "Identifier",
"type": "Identifier" "type": "Identifier"
}
} }
], ],
"callee": { "callee": {
@ -155,40 +158,53 @@ description: Result of parsing double_map_fn.kcl
"start": 61, "start": 61,
"type": "Identifier" "type": "Identifier"
}, },
"end": 78, "end": 87,
"start": 61, "start": 61,
"type": "CallExpression", "type": "CallExpressionKw",
"type": "CallExpression" "type": "CallExpressionKw",
"unlabeled": {
"end": 66,
"start": 65,
"type": "PipeSubstitution",
"type": "PipeSubstitution"
}
}, },
{ {
"arguments": [ "arguments": [
{ {
"end": 89, "type": "LabeledArg",
"start": 88, "label": {
"type": "PipeSubstitution", "type": "Identifier",
"type": "PipeSubstitution" "name": "map_fn"
}, },
{ "arg": {
"end": 100, "end": 118,
"name": "increment", "name": "increment",
"start": 91, "start": 109,
"type": "Identifier", "type": "Identifier",
"type": "Identifier" "type": "Identifier"
}
} }
], ],
"callee": { "callee": {
"end": 87, "end": 96,
"name": "map", "name": "map",
"start": 84, "start": 93,
"type": "Identifier" "type": "Identifier"
}, },
"end": 101, "end": 119,
"start": 84, "start": 93,
"type": "CallExpression", "type": "CallExpressionKw",
"type": "CallExpression" "type": "CallExpressionKw",
"unlabeled": {
"end": 98,
"start": 97,
"type": "PipeSubstitution",
"type": "PipeSubstitution"
}
} }
], ],
"end": 101, "end": 119,
"start": 53, "start": 53,
"type": "PipeExpression", "type": "PipeExpression",
"type": "PipeExpression" "type": "PipeExpression"
@ -196,14 +212,14 @@ description: Result of parsing double_map_fn.kcl
"start": 48, "start": 48,
"type": "VariableDeclarator" "type": "VariableDeclarator"
}, },
"end": 101, "end": 119,
"kind": "const", "kind": "const",
"start": 48, "start": 48,
"type": "VariableDeclaration", "type": "VariableDeclaration",
"type": "VariableDeclaration" "type": "VariableDeclaration"
} }
], ],
"end": 102, "end": 120,
"nonCodeMeta": { "nonCodeMeta": {
"nonCodeNodes": { "nonCodeNodes": {
"0": [ "0": [

View File

@ -4,5 +4,5 @@ fn increment(i) {
xs = [0..2] xs = [0..2]
ys = xs ys = xs
|> map(%, increment) |> map(%, map_fn = increment)
|> map(%, increment) |> map(%, map_fn = increment)

View File

@ -1,5 +1,6 @@
--- ---
source: kcl/src/simulation_tests.rs source: kcl/src/simulation_tests.rs
assertion_line: 99
description: Program memory after executing double_map_fn.kcl description: Program memory after executing double_map_fn.kcl
snapshot_kind: text snapshot_kind: text
--- ---
@ -261,8 +262,8 @@ snapshot_kind: text
"__meta": [ "__meta": [
{ {
"sourceRange": [ "sourceRange": [
84, 93,
101, 119,
0 0
] ]
} }

Binary file not shown.

Before

Width:  |  Height:  |  Size: 198 KiB

After

Width:  |  Height:  |  Size: 19 KiB

View File

@ -1,18 +1,14 @@
//! Cache testing framework. //! Cache testing framework.
use anyhow::Result; use anyhow::Result;
use kcl_lib::{ExecError, ExecState}; use kcl_lib::ExecError;
#[derive(Debug)]
struct Variation<'a> { struct Variation<'a> {
code: &'a str, code: &'a str,
settings: &'a kcl_lib::ExecutorSettings, settings: &'a kcl_lib::ExecutorSettings,
} }
async fn cache_test( async fn cache_test(test_name: &str, variations: Vec<Variation<'_>>) -> Result<Vec<(String, image::DynamicImage)>> {
test_name: &str,
variations: Vec<Variation<'_>>,
) -> Result<Vec<(String, image::DynamicImage, ExecState)>> {
let first = variations let first = variations
.first() .first()
.ok_or_else(|| anyhow::anyhow!("No variations provided for test '{}'", test_name))?; .ok_or_else(|| anyhow::anyhow!("No variations provided for test '{}'", test_name))?;
@ -46,7 +42,7 @@ async fn cache_test(
// Save the snapshot. // Save the snapshot.
let path = crate::assert_out(&format!("cache_{}_{}", test_name, index), &img); let path = crate::assert_out(&format!("cache_{}_{}", test_name, index), &img);
img_results.push((path, img, exec_state.clone())); img_results.push((path, img));
// Prepare the last state. // Prepare the last state.
old_ast_state = Some(kcl_lib::OldAstState { old_ast_state = Some(kcl_lib::OldAstState {
@ -56,8 +52,6 @@ async fn cache_test(
}); });
} }
ctx.close().await;
Ok(img_results) Ok(img_results)
} }
@ -220,47 +214,3 @@ async fn kcl_test_cache_change_highlight_edges_changes_visual() {
assert!(first.1 != second.1); 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.

Before

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 47 KiB

View File

@ -1,10 +0,0 @@
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',
])

1530
yarn.lock

File diff suppressed because it is too large Load Diff