Compare commits
30 Commits
nightly-v2
...
guptaarnav
Author | SHA1 | Date | |
---|---|---|---|
8b9580b178 | |||
34032b6cb7 | |||
4a654523d2 | |||
73a7e2bfd6 | |||
eb0850fea9 | |||
029f76f273 | |||
28b5f7080c | |||
5b1dcfecd6 | |||
f89d191425 | |||
2f4e4b62a8 | |||
5ebd5c8dbb | |||
a9ceaf2678 | |||
c8afd3399b | |||
5dda4828c6 | |||
72acab752c | |||
81df38ad1c | |||
0576a2bef1 | |||
4b2f6b4647 | |||
69edaa4183 | |||
2eb7c382bf | |||
38913ecb98 | |||
debd06129f | |||
d38bd342a0 | |||
f026f10335 | |||
895d7ebc6d | |||
65edf17a44 | |||
0c2a0a8c07 | |||
97cef4d16c | |||
d4ebddf75f | |||
b7d729a4b4 |
@ -1,3 +1,3 @@
|
|||||||
[codespell]
|
[codespell]
|
||||||
ignore-words-list: crate,everytime,inout,co-ordinate,ot,nwo,atleast,ue,afterall
|
ignore-words-list: crate,everytime,inout,co-ordinate,ot,nwo,atleast,ue,afterall
|
||||||
skip: **/target,node_modules,build,**/Cargo.lock,./docs/kcl/*.md,.yarn.lock,**/yarn.lock,./openapi/*.json,./src/lib/machine-api.d.ts,./packages/codemirror-lang-kcl/test/all.test.ts
|
skip: **/target,node_modules,build,dist,./out,**/Cargo.lock,./docs/kcl/*.md,.yarn.lock,**/yarn.lock,./openapi/*.json,./packages/codemirror-lang-kcl/test/all.test.ts,tsconfig.tsbuildinfo
|
||||||
|
@ -24,3 +24,5 @@ once fixed in engine will just start working here with no language changes.
|
|||||||
chamfer cases work currently.
|
chamfer cases work currently.
|
||||||
|
|
||||||
- **Appearance**: Changing the appearance on a loft does not work.
|
- **Appearance**: Changing the appearance on a loft does not work.
|
||||||
|
|
||||||
|
- **Helix**: Currently sweeping a helix does not work.
|
||||||
|
43
docs/kcl/helixRevolutions.md
Normal file
@ -48,6 +48,7 @@ layout: manual
|
|||||||
* [`getOppositeEdge`](kcl/getOppositeEdge)
|
* [`getOppositeEdge`](kcl/getOppositeEdge)
|
||||||
* [`getPreviousAdjacentEdge`](kcl/getPreviousAdjacentEdge)
|
* [`getPreviousAdjacentEdge`](kcl/getPreviousAdjacentEdge)
|
||||||
* [`helix`](kcl/helix)
|
* [`helix`](kcl/helix)
|
||||||
|
* [`helixRevolutions`](kcl/helixRevolutions)
|
||||||
* [`hole`](kcl/hole)
|
* [`hole`](kcl/hole)
|
||||||
* [`hollow`](kcl/hollow)
|
* [`hollow`](kcl/hollow)
|
||||||
* [`import`](kcl/import)
|
* [`import`](kcl/import)
|
||||||
@ -58,6 +59,7 @@ layout: manual
|
|||||||
* [`legAngX`](kcl/legAngX)
|
* [`legAngX`](kcl/legAngX)
|
||||||
* [`legAngY`](kcl/legAngY)
|
* [`legAngY`](kcl/legAngY)
|
||||||
* [`legLen`](kcl/legLen)
|
* [`legLen`](kcl/legLen)
|
||||||
|
* [`len`](kcl/len)
|
||||||
* [`line`](kcl/line)
|
* [`line`](kcl/line)
|
||||||
* [`lineTo`](kcl/lineTo)
|
* [`lineTo`](kcl/lineTo)
|
||||||
* [`ln`](kcl/ln)
|
* [`ln`](kcl/ln)
|
||||||
@ -81,6 +83,7 @@ layout: manual
|
|||||||
* [`pi`](kcl/pi)
|
* [`pi`](kcl/pi)
|
||||||
* [`polar`](kcl/polar)
|
* [`polar`](kcl/polar)
|
||||||
* [`polygon`](kcl/polygon)
|
* [`polygon`](kcl/polygon)
|
||||||
|
* [`pop`](kcl/pop)
|
||||||
* [`pow`](kcl/pow)
|
* [`pow`](kcl/pow)
|
||||||
* [`profileStart`](kcl/profileStart)
|
* [`profileStart`](kcl/profileStart)
|
||||||
* [`profileStartX`](kcl/profileStartX)
|
* [`profileStartX`](kcl/profileStartX)
|
||||||
|
37
docs/kcl/len.md
Normal file
39
docs/kcl/pop.md
Normal file
11631
docs/kcl/std.json
@ -1,19 +1,19 @@
|
|||||||
---
|
---
|
||||||
title: "AxisOrEdgeReference"
|
title: "Axis2dOrEdgeReference"
|
||||||
excerpt: "Axis or tagged edge."
|
excerpt: "A 2D axis or tagged edge."
|
||||||
layout: manual
|
layout: manual
|
||||||
---
|
---
|
||||||
|
|
||||||
Axis or tagged edge.
|
A 2D axis or tagged edge.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
**This schema accepts any of the following:**
|
**This schema accepts any of the following:**
|
||||||
|
|
||||||
Axis and origin.
|
2D axis and origin.
|
||||||
|
|
||||||
[`AxisAndOrigin`](/docs/kcl/types/AxisAndOrigin)
|
[`AxisAndOrigin2d`](/docs/kcl/types/AxisAndOrigin2d)
|
||||||
|
|
||||||
|
|
||||||
|
|
42
docs/kcl/types/Axis3dOrEdgeReference.md
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
---
|
||||||
|
title: "Axis3dOrEdgeReference"
|
||||||
|
excerpt: "A 3D axis or tagged edge."
|
||||||
|
layout: manual
|
||||||
|
---
|
||||||
|
|
||||||
|
A 3D axis or tagged edge.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
**This schema accepts any of the following:**
|
||||||
|
|
||||||
|
3D axis and origin.
|
||||||
|
|
||||||
|
[`AxisAndOrigin3d`](/docs/kcl/types/AxisAndOrigin3d)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
Tagged edge.
|
||||||
|
|
||||||
|
[`EdgeReference`](/docs/kcl/types/EdgeReference)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1,10 +1,10 @@
|
|||||||
---
|
---
|
||||||
title: "AxisAndOrigin"
|
title: "AxisAndOrigin2d"
|
||||||
excerpt: "Axis and origin."
|
excerpt: "A 2D axis and origin."
|
||||||
layout: manual
|
layout: manual
|
||||||
---
|
---
|
||||||
|
|
||||||
Axis and origin.
|
A 2D axis and origin.
|
||||||
|
|
||||||
|
|
||||||
|
|
105
docs/kcl/types/AxisAndOrigin3d.md
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
---
|
||||||
|
title: "AxisAndOrigin3d"
|
||||||
|
excerpt: "A 3D axis and origin."
|
||||||
|
layout: manual
|
||||||
|
---
|
||||||
|
|
||||||
|
A 3D axis and origin.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
**This schema accepts exactly one of the following:**
|
||||||
|
|
||||||
|
X-axis.
|
||||||
|
|
||||||
|
**enum:** `X`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
Y-axis.
|
||||||
|
|
||||||
|
**enum:** `Y`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
Z-axis.
|
||||||
|
|
||||||
|
**enum:** `Z`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
Flip the X-axis.
|
||||||
|
|
||||||
|
**enum:** `-X`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
Flip the Y-axis.
|
||||||
|
|
||||||
|
**enum:** `-Y`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
Flip the Z-axis.
|
||||||
|
|
||||||
|
**enum:** `-Z`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
**Type:** `object`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
| Property | Type | Description | Required |
|
||||||
|
|----------|------|-------------|----------|
|
||||||
|
| `custom` |`object`| | No |
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
25
docs/kcl/types/Helix.md
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
---
|
||||||
|
title: "Helix"
|
||||||
|
excerpt: "A helix."
|
||||||
|
layout: manual
|
||||||
|
---
|
||||||
|
|
||||||
|
A helix.
|
||||||
|
|
||||||
|
**Type:** `object`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
| Property | Type | Description | Required |
|
||||||
|
|----------|------|-------------|----------|
|
||||||
|
| `value` |`string`| The id of the helix. | No |
|
||||||
|
| `revolutions` |`number`| Number of revolutions. | No |
|
||||||
|
| `angleStart` |`number`| Start angle (in degrees). | No |
|
||||||
|
| `ccw` |`boolean`| Is the helix rotation counter clockwise? | No |
|
||||||
|
| `__meta` |`[` [`Metadata`](/docs/kcl/types/Metadata) `]`| | No |
|
||||||
|
|
||||||
|
|
@ -1,10 +1,10 @@
|
|||||||
---
|
---
|
||||||
title: "HelixData"
|
title: "HelixData"
|
||||||
excerpt: "Data for helices."
|
excerpt: "Data for a helix."
|
||||||
layout: manual
|
layout: manual
|
||||||
---
|
---
|
||||||
|
|
||||||
Data for helices.
|
Data for a helix.
|
||||||
|
|
||||||
**Type:** `object`
|
**Type:** `object`
|
||||||
|
|
||||||
@ -19,6 +19,8 @@ Data for helices.
|
|||||||
| `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. If this argument is not provided, the height of the solid is used. | No |
|
| `length` |`number`| Length of the helix. | No |
|
||||||
|
| `radius` |`number`| Radius of the helix. | No |
|
||||||
|
| `axis` |[`Axis3dOrEdgeReference`](/docs/kcl/types/Axis3dOrEdgeReference)| Axis to use as mirror. | No |
|
||||||
|
|
||||||
|
|
||||||
|
24
docs/kcl/types/HelixRevolutionsData.md
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
---
|
||||||
|
title: "HelixRevolutionsData"
|
||||||
|
excerpt: "Data for helix revolutions."
|
||||||
|
layout: manual
|
||||||
|
---
|
||||||
|
|
||||||
|
Data for helix revolutions.
|
||||||
|
|
||||||
|
**Type:** `object`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
| Property | Type | Description | Required |
|
||||||
|
|----------|------|-------------|----------|
|
||||||
|
| `revolutions` |`number`| Number of revolutions. | No |
|
||||||
|
| `angleStart` |`number`| Start angle (in degrees). | No |
|
||||||
|
| `ccw` |`boolean`| Is the helix rotation counter clockwise? The default is `false`. | No |
|
||||||
|
| `length` |`number`| Length of the helix. If this argument is not provided, the height of the solid is used. | No |
|
||||||
|
|
||||||
|
|
25
docs/kcl/types/HelixValue.md
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
---
|
||||||
|
title: "HelixValue"
|
||||||
|
excerpt: "A helix."
|
||||||
|
layout: manual
|
||||||
|
---
|
||||||
|
|
||||||
|
A helix.
|
||||||
|
|
||||||
|
**Type:** `object`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
| Property | Type | Description | Required |
|
||||||
|
|----------|------|-------------|----------|
|
||||||
|
| `value` |`string`| The id of the helix. | No |
|
||||||
|
| `revolutions` |`number`| Number of revolutions. | No |
|
||||||
|
| `angleStart` |`number`| Start angle (in degrees). | No |
|
||||||
|
| `ccw` |`boolean`| Is the helix rotation counter clockwise? | No |
|
||||||
|
| `__meta` |`[` [`Metadata`](/docs/kcl/types/Metadata) `]`| | No |
|
||||||
|
|
||||||
|
|
@ -285,6 +285,27 @@ An solid is a collection of extrude surfaces.
|
|||||||
| `value` |`[` [`Solid`](/docs/kcl/types/Solid) `]`| | No |
|
| `value` |`[` [`Solid`](/docs/kcl/types/Solid) `]`| | No |
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
A helix.
|
||||||
|
|
||||||
|
**Type:** `object`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
| Property | Type | Description | Required |
|
||||||
|
|----------|------|-------------|----------|
|
||||||
|
| `type` |enum: [`Helix`](/docs/kcl/types/Helix)| | No |
|
||||||
|
| `value` |`string`| The id of the helix. | No |
|
||||||
|
| `revolutions` |`number`| Number of revolutions. | No |
|
||||||
|
| `angleStart` |`number`| Start angle (in degrees). | No |
|
||||||
|
| `ccw` |`boolean`| Is the helix rotation counter clockwise? | No |
|
||||||
|
| `__meta` |`[` [`Metadata`](/docs/kcl/types/Metadata) `]`| | No |
|
||||||
|
|
||||||
|
|
||||||
----
|
----
|
||||||
Data for an imported geometry.
|
Data for an imported geometry.
|
||||||
|
|
||||||
|
@ -16,6 +16,6 @@ Data for a mirror.
|
|||||||
|
|
||||||
| Property | Type | Description | Required |
|
| Property | Type | Description | Required |
|
||||||
|----------|------|-------------|----------|
|
|----------|------|-------------|----------|
|
||||||
| `axis` |[`AxisOrEdgeReference`](/docs/kcl/types/AxisOrEdgeReference)| Axis to use as mirror. | No |
|
| `axis` |[`Axis2dOrEdgeReference`](/docs/kcl/types/Axis2dOrEdgeReference)| Axis to use as mirror. | No |
|
||||||
|
|
||||||
|
|
||||||
|
@ -17,7 +17,7 @@ Data for revolution surfaces.
|
|||||||
| Property | Type | Description | Required |
|
| Property | Type | Description | Required |
|
||||||
|----------|------|-------------|----------|
|
|----------|------|-------------|----------|
|
||||||
| `angle` |`number` (**maximum:** 360.0) (**minimum:** -360.0)| Angle to revolve (in degrees). Default is 360. | No |
|
| `angle` |`number` (**maximum:** 360.0) (**minimum:** -360.0)| Angle to revolve (in degrees). Default is 360. | No |
|
||||||
| `axis` |[`AxisOrEdgeReference`](/docs/kcl/types/AxisOrEdgeReference)| Axis of revolution. | No |
|
| `axis` |[`Axis2dOrEdgeReference`](/docs/kcl/types/Axis2dOrEdgeReference)| Axis of revolution. | No |
|
||||||
| `tolerance` |`number`| Tolerance for the revolve operation. | No |
|
| `tolerance` |`number`| Tolerance for the revolve operation. | No |
|
||||||
|
|
||||||
|
|
||||||
|
@ -16,7 +16,7 @@ Data for a sweep.
|
|||||||
|
|
||||||
| Property | Type | Description | Required |
|
| Property | Type | Description | Required |
|
||||||
|----------|------|-------------|----------|
|
|----------|------|-------------|----------|
|
||||||
| `path` |[`Sketch`](/docs/kcl/types/Sketch)| The path to sweep along. | No |
|
| `path` |[`SweepPath`](/docs/kcl/types/SweepPath)| The path to sweep along. | No |
|
||||||
| `sectional` |`boolean`| If true, the sweep will be broken up into sub-sweeps (extrusions, revolves, sweeps) based on the trajectory path components. | No |
|
| `sectional` |`boolean`| If true, the sweep will be broken up into sub-sweeps (extrusions, revolves, sweeps) based on the trajectory path components. | No |
|
||||||
| `tolerance` |`number`| Tolerance for the sweep operation. | No |
|
| `tolerance` |`number`| Tolerance for the sweep operation. | No |
|
||||||
|
|
||||||
|
42
docs/kcl/types/SweepPath.md
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
---
|
||||||
|
title: "SweepPath"
|
||||||
|
excerpt: "A path to sweep along."
|
||||||
|
layout: manual
|
||||||
|
---
|
||||||
|
|
||||||
|
A path to sweep along.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
**This schema accepts any of the following:**
|
||||||
|
|
||||||
|
A path to sweep along.
|
||||||
|
|
||||||
|
[`Sketch`](/docs/kcl/types/Sketch)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
A path to sweep along.
|
||||||
|
|
||||||
|
[`Helix`](/docs/kcl/types/Helix)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -36,7 +36,8 @@ type DragFromHandler = (
|
|||||||
|
|
||||||
export class SceneFixture {
|
export class SceneFixture {
|
||||||
public page: Page
|
public page: Page
|
||||||
|
public streamWrapper!: Locator
|
||||||
|
public loadingIndicator!: Locator
|
||||||
private exeIndicator!: Locator
|
private exeIndicator!: Locator
|
||||||
|
|
||||||
constructor(page: Page) {
|
constructor(page: Page) {
|
||||||
@ -64,6 +65,8 @@ export class SceneFixture {
|
|||||||
this.page = page
|
this.page = page
|
||||||
|
|
||||||
this.exeIndicator = page.getByTestId('model-state-indicator-execution-done')
|
this.exeIndicator = page.getByTestId('model-state-indicator-execution-done')
|
||||||
|
this.streamWrapper = page.getByTestId('stream')
|
||||||
|
this.loadingIndicator = this.streamWrapper.getByTestId('loading')
|
||||||
}
|
}
|
||||||
|
|
||||||
makeMouseHelpers = (
|
makeMouseHelpers = (
|
||||||
|
@ -115,7 +115,7 @@ test(
|
|||||||
)
|
)
|
||||||
|
|
||||||
test(
|
test(
|
||||||
'yyyyyyyyy open a file in a project works and renders, open another file in different project with errors, it should clear the scene',
|
'open a file in a project works and renders, open another file in different project with errors, it should clear the scene',
|
||||||
{ tag: '@electron' },
|
{ tag: '@electron' },
|
||||||
async ({ context, page }, testInfo) => {
|
async ({ context, page }, testInfo) => {
|
||||||
await context.folderSetupFn(async (dir) => {
|
await context.folderSetupFn(async (dir) => {
|
||||||
@ -199,7 +199,7 @@ test(
|
|||||||
)
|
)
|
||||||
|
|
||||||
test(
|
test(
|
||||||
'aaayyyyyyyy open a file in a project works and renders, open another file in different project that is empty, it should clear the scene',
|
'open a file in a project works and renders, open another file in different project that is empty, it should clear the scene',
|
||||||
{ tag: '@electron' },
|
{ tag: '@electron' },
|
||||||
async ({ context, page }, testInfo) => {
|
async ({ context, page }, testInfo) => {
|
||||||
await context.folderSetupFn(async (dir) => {
|
await context.folderSetupFn(async (dir) => {
|
||||||
@ -276,7 +276,7 @@ test(
|
|||||||
)
|
)
|
||||||
|
|
||||||
test(
|
test(
|
||||||
'nooooooooooooo open a file in a project works and renders, open empty file, it should clear the scene',
|
'open a file in a project works and renders, open empty file, it should clear the scene',
|
||||||
{ tag: '@electron' },
|
{ tag: '@electron' },
|
||||||
async ({ context, page }, testInfo) => {
|
async ({ context, page }, testInfo) => {
|
||||||
await context.folderSetupFn(async (dir) => {
|
await context.folderSetupFn(async (dir) => {
|
||||||
@ -1885,3 +1885,48 @@ test.fixme(
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
test(
|
||||||
|
'project name with foreign characters should open',
|
||||||
|
{ tag: '@electron' },
|
||||||
|
async ({ context, page }, testInfo) => {
|
||||||
|
await context.folderSetupFn(async (dir) => {
|
||||||
|
const bracketDir = path.join(dir, 'اَلْعَرَبِيَّةُ')
|
||||||
|
await fsp.mkdir(bracketDir, { recursive: true })
|
||||||
|
await fsp.copyFile(
|
||||||
|
executorInputPath('focusrite_scarlett_mounting_braket.kcl'),
|
||||||
|
path.join(bracketDir, 'main.kcl')
|
||||||
|
)
|
||||||
|
|
||||||
|
await fsp.writeFile(path.join(bracketDir, 'empty.kcl'), '')
|
||||||
|
})
|
||||||
|
|
||||||
|
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||||
|
const u = await getUtils(page)
|
||||||
|
|
||||||
|
page.on('console', console.log)
|
||||||
|
|
||||||
|
const pointOnModel = { x: 630, y: 280 }
|
||||||
|
|
||||||
|
await test.step('Opening the اَلْعَرَبِيَّةُ project should load the stream', async () => {
|
||||||
|
// expect to see the text bracket
|
||||||
|
await expect(page.getByText('اَلْعَرَبِيَّةُ')).toBeVisible()
|
||||||
|
|
||||||
|
await page.getByText('اَلْعَرَبِيَّةُ').click()
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
page.getByRole('button', { name: 'Start Sketch' })
|
||||||
|
).toBeEnabled({
|
||||||
|
timeout: 20_000,
|
||||||
|
})
|
||||||
|
|
||||||
|
// gray at this pixel means the stream has loaded in the most
|
||||||
|
// user way we can verify it (pixel color)
|
||||||
|
await expect
|
||||||
|
.poll(() => u.getGreatestPixDiff(pointOnModel, [85, 85, 85]), {
|
||||||
|
timeout: 10_000,
|
||||||
|
})
|
||||||
|
.toBeLessThan(15)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
)
|
||||||
|
@ -614,6 +614,38 @@ extrude001 = extrude(50, sketch001)
|
|||||||
await expect(gizmo).toBeVisible()
|
await expect(gizmo).toBeVisible()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test(`Refreshing the app doesn't cause the stream to pause on long-executing files`, async ({
|
||||||
|
context,
|
||||||
|
homePage,
|
||||||
|
scene,
|
||||||
|
toolbar,
|
||||||
|
viewport,
|
||||||
|
}) => {
|
||||||
|
await context.folderSetupFn(async (dir) => {
|
||||||
|
const legoDir = path.join(dir, 'lego')
|
||||||
|
await fsp.mkdir(legoDir, { recursive: true })
|
||||||
|
await fsp.copyFile(
|
||||||
|
executorInputPath('lego.kcl'),
|
||||||
|
path.join(legoDir, 'main.kcl')
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
await test.step(`Test setup`, async () => {
|
||||||
|
await homePage.openProject('lego')
|
||||||
|
await toolbar.closePane('code')
|
||||||
|
})
|
||||||
|
await test.step(`Waiting for the loading spinner to disappear`, async () => {
|
||||||
|
await scene.loadingIndicator.waitFor({ state: 'detached' })
|
||||||
|
})
|
||||||
|
await test.step(`The part should start loading quickly, not waiting until execution is complete`, async () => {
|
||||||
|
await scene.expectPixelColor(
|
||||||
|
[143, 143, 143],
|
||||||
|
{ x: (viewport?.width ?? 1200) / 2, y: (viewport?.height ?? 500) / 2 },
|
||||||
|
15
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
async function clickExportButton(page: Page) {
|
async function clickExportButton(page: Page) {
|
||||||
|
@ -39,8 +39,8 @@ test.describe('Sketch tests', () => {
|
|||||||
${startProfileAt1}
|
${startProfileAt1}
|
||||||
|> arc({
|
|> arc({
|
||||||
radius = screwRadius,
|
radius = screwRadius,
|
||||||
angle_start = 0,
|
angleStart = 0,
|
||||||
angle_end = 360
|
angleEnd = 360
|
||||||
}, %)
|
}, %)
|
||||||
|
|
||||||
part001 = startSketchOn('XY')
|
part001 = startSketchOn('XY')
|
||||||
@ -60,8 +60,8 @@ test.describe('Sketch tests', () => {
|
|||||||
|> yLine(wireOffset, %)
|
|> yLine(wireOffset, %)
|
||||||
|> arc({
|
|> arc({
|
||||||
radius = wireRadius,
|
radius = wireRadius,
|
||||||
angle_start = 0,
|
angleStart = 0,
|
||||||
angle_end = 180
|
angleEnd = 180
|
||||||
}, %)
|
}, %)
|
||||||
|> yLine(-wireOffset, %)
|
|> yLine(-wireOffset, %)
|
||||||
|> xLine(-width / 4, %)
|
|> xLine(-width / 4, %)
|
||||||
|
Before Width: | Height: | Size: 51 KiB After Width: | Height: | Size: 52 KiB |
Before Width: | Height: | Size: 53 KiB After Width: | Height: | Size: 53 KiB |
Before Width: | Height: | Size: 52 KiB After Width: | Height: | Size: 52 KiB |
Before Width: | Height: | Size: 74 KiB After Width: | Height: | Size: 74 KiB |
Before Width: | Height: | Size: 50 KiB After Width: | Height: | Size: 50 KiB |
Before Width: | Height: | Size: 65 KiB After Width: | Height: | Size: 66 KiB |
Before Width: | Height: | Size: 144 KiB After Width: | Height: | Size: 144 KiB |
Before Width: | Height: | Size: 128 KiB After Width: | Height: | Size: 128 KiB |
Before Width: | Height: | Size: 47 KiB After Width: | Height: | Size: 48 KiB |
Before Width: | Height: | Size: 53 KiB After Width: | Height: | Size: 53 KiB |
Before Width: | Height: | Size: 48 KiB After Width: | Height: | Size: 48 KiB |
Before Width: | Height: | Size: 52 KiB After Width: | Height: | Size: 52 KiB |
Before Width: | Height: | Size: 53 KiB After Width: | Height: | Size: 53 KiB |
Before Width: | Height: | Size: 47 KiB After Width: | Height: | Size: 47 KiB |
@ -389,25 +389,25 @@ test.describe('Testing selections', () => {
|
|||||||
await expect(u.codeLocator).toContainText(`sketch005 = startSketchOn({
|
await expect(u.codeLocator).toContainText(`sketch005 = startSketchOn({
|
||||||
plane = {
|
plane = {
|
||||||
origin = { x = 0, y = -50, z = 0 },
|
origin = { x = 0, y = -50, z = 0 },
|
||||||
x_axis = { x = 1, y = 0, z = 0 },
|
xAxis = { x = 1, y = 0, z = 0 },
|
||||||
y_axis = { x = 0, y = 0, z = 1 },
|
yAxis = { x = 0, y = 0, z = 1 },
|
||||||
z_axis = { x = 0, y = -1, z = 0 }
|
zAxis = { x = 0, y = -1, z = 0 }
|
||||||
}
|
}
|
||||||
})`)
|
})`)
|
||||||
await expect(u.codeLocator).toContainText(`sketch003 = startSketchOn({
|
await expect(u.codeLocator).toContainText(`sketch003 = startSketchOn({
|
||||||
plane = {
|
plane = {
|
||||||
origin = { x = 116.53, y = 0, z = 163.25 },
|
origin = { x = 116.53, y = 0, z = 163.25 },
|
||||||
x_axis = { x = -0.81, y = 0, z = 0.58 },
|
xAxis = { x = -0.81, y = 0, z = 0.58 },
|
||||||
y_axis = { x = 0, y = -1, z = 0 },
|
yAxis = { x = 0, y = -1, z = 0 },
|
||||||
z_axis = { x = 0.58, y = 0, z = 0.81 }
|
zAxis = { x = 0.58, y = 0, z = 0.81 }
|
||||||
}
|
}
|
||||||
})`)
|
})`)
|
||||||
await expect(u.codeLocator).toContainText(`sketch002 = startSketchOn({
|
await expect(u.codeLocator).toContainText(`sketch002 = startSketchOn({
|
||||||
plane = {
|
plane = {
|
||||||
origin = { x = -91.74, y = 0, z = 80.89 },
|
origin = { x = -91.74, y = 0, z = 80.89 },
|
||||||
x_axis = { x = -0.66, y = 0, z = -0.75 },
|
xAxis = { x = -0.66, y = 0, z = -0.75 },
|
||||||
y_axis = { x = 0, y = -1, z = 0 },
|
yAxis = { x = 0, y = -1, z = 0 },
|
||||||
z_axis = { x = -0.75, y = 0, z = 0.66 }
|
zAxis = { x = -0.75, y = 0, z = 0.66 }
|
||||||
}
|
}
|
||||||
})`)
|
})`)
|
||||||
|
|
||||||
|
@ -156,13 +156,13 @@ test.describe('Text-to-CAD tests', () => {
|
|||||||
const cmdSearchBar = page.getByPlaceholder('Search commands')
|
const cmdSearchBar = page.getByPlaceholder('Search commands')
|
||||||
await expect(cmdSearchBar).toBeVisible()
|
await expect(cmdSearchBar).toBeVisible()
|
||||||
|
|
||||||
const textToCadCommand = page.getByText('Text-to-CAD')
|
const textToCadCommand = page.getByRole('option', { name: 'Text-to-CAD' })
|
||||||
await expect(textToCadCommand.first()).toBeVisible()
|
await expect(textToCadCommand.first()).toBeVisible()
|
||||||
// Click the Text-to-CAD command
|
// Click the Text-to-CAD command
|
||||||
await textToCadCommand.first().click()
|
await textToCadCommand.first().click()
|
||||||
|
|
||||||
// Enter the prompt.
|
// Enter the prompt.
|
||||||
const prompt = page.getByText('Prompt')
|
const prompt = page.getByRole('textbox', { name: 'Prompt' })
|
||||||
await expect(prompt.first()).toBeVisible()
|
await expect(prompt.first()).toBeVisible()
|
||||||
|
|
||||||
// Type the prompt.
|
// Type the prompt.
|
||||||
@ -224,13 +224,13 @@ test.describe('Text-to-CAD tests', () => {
|
|||||||
const cmdSearchBar = page.getByPlaceholder('Search commands')
|
const cmdSearchBar = page.getByPlaceholder('Search commands')
|
||||||
await expect(cmdSearchBar).toBeVisible()
|
await expect(cmdSearchBar).toBeVisible()
|
||||||
|
|
||||||
const textToCadCommand = page.getByText('Text-to-CAD')
|
const textToCadCommand = page.getByRole('option', { name: 'Text-to-CAD' })
|
||||||
await expect(textToCadCommand.first()).toBeVisible()
|
await expect(textToCadCommand.first()).toBeVisible()
|
||||||
// Click the Text-to-CAD command
|
// Click the Text-to-CAD command
|
||||||
await textToCadCommand.first().click()
|
await textToCadCommand.first().click()
|
||||||
|
|
||||||
// Enter the prompt.
|
// Enter the prompt.
|
||||||
const prompt = page.getByText('Prompt')
|
const prompt = page.getByRole('textbox', { name: 'Prompt' })
|
||||||
await expect(prompt.first()).toBeVisible()
|
await expect(prompt.first()).toBeVisible()
|
||||||
|
|
||||||
const badPrompt = 'akjsndladf lajbhflauweyfaaaljhr472iouafyvsssssss'
|
const badPrompt = 'akjsndladf lajbhflauweyfaaaljhr472iouafyvsssssss'
|
||||||
@ -314,13 +314,13 @@ test.describe('Text-to-CAD tests', () => {
|
|||||||
const cmdSearchBar = page.getByPlaceholder('Search commands')
|
const cmdSearchBar = page.getByPlaceholder('Search commands')
|
||||||
await expect(cmdSearchBar).toBeVisible()
|
await expect(cmdSearchBar).toBeVisible()
|
||||||
|
|
||||||
const textToCadCommand = page.getByText('Text-to-CAD')
|
const textToCadCommand = page.getByRole('option', { name: 'Text-to-CAD' })
|
||||||
await expect(textToCadCommand.first()).toBeVisible()
|
await expect(textToCadCommand.first()).toBeVisible()
|
||||||
// Click the Text-to-CAD command
|
// Click the Text-to-CAD command
|
||||||
await textToCadCommand.first().click()
|
await textToCadCommand.first().click()
|
||||||
|
|
||||||
// Enter the prompt.
|
// Enter the prompt.
|
||||||
const prompt = page.getByText('Prompt')
|
const prompt = page.getByRole('textbox', { name: 'Prompt' })
|
||||||
await expect(prompt.first()).toBeVisible()
|
await expect(prompt.first()).toBeVisible()
|
||||||
|
|
||||||
const badPrompt = 'akjsndladflajbhflauweyf15;'
|
const badPrompt = 'akjsndladflajbhflauweyf15;'
|
||||||
@ -392,13 +392,13 @@ test.describe('Text-to-CAD tests', () => {
|
|||||||
const cmdSearchBar = page.getByPlaceholder('Search commands')
|
const cmdSearchBar = page.getByPlaceholder('Search commands')
|
||||||
await expect(cmdSearchBar).toBeVisible()
|
await expect(cmdSearchBar).toBeVisible()
|
||||||
|
|
||||||
const textToCadCommand = page.getByText('Text-to-CAD')
|
const textToCadCommand = page.getByRole('option', { name: 'Text-to-CAD' })
|
||||||
await expect(textToCadCommand.first()).toBeVisible()
|
await expect(textToCadCommand.first()).toBeVisible()
|
||||||
// Click the Text-to-CAD command
|
// Click the Text-to-CAD command
|
||||||
await textToCadCommand.first().click()
|
await textToCadCommand.first().click()
|
||||||
|
|
||||||
// Enter the prompt.
|
// Enter the prompt.
|
||||||
const prompt = page.getByText('Prompt')
|
const prompt = page.getByRole('textbox', { name: 'Prompt' })
|
||||||
await expect(prompt.first()).toBeVisible()
|
await expect(prompt.first()).toBeVisible()
|
||||||
|
|
||||||
// Type the prompt.
|
// Type the prompt.
|
||||||
@ -604,7 +604,7 @@ async function sendPromptFromCommandBar(page: Page, promptStr: string) {
|
|||||||
await page.waitForTimeout(1000)
|
await page.waitForTimeout(1000)
|
||||||
|
|
||||||
// Enter the prompt.
|
// Enter the prompt.
|
||||||
const prompt = page.getByText('Prompt')
|
const prompt = page.getByRole('textbox', { name: 'Prompt' })
|
||||||
await expect(prompt.first()).toBeVisible()
|
await expect(prompt.first()).toBeVisible()
|
||||||
|
|
||||||
// Type the prompt.
|
// Type the prompt.
|
||||||
|
1
interface.d.ts
vendored
@ -93,5 +93,6 @@ export interface IElectronAPI {
|
|||||||
declare global {
|
declare global {
|
||||||
interface Window {
|
interface Window {
|
||||||
electron: IElectronAPI
|
electron: IElectronAPI
|
||||||
|
openExternalLink: (e: React.MouseEvent<HTMLAnchorElement>) => void
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
12
package.json
@ -15,7 +15,7 @@
|
|||||||
"@codemirror/autocomplete": "^6.17.0",
|
"@codemirror/autocomplete": "^6.17.0",
|
||||||
"@codemirror/commands": "^6.6.0",
|
"@codemirror/commands": "^6.6.0",
|
||||||
"@codemirror/language": "^6.10.3",
|
"@codemirror/language": "^6.10.3",
|
||||||
"@codemirror/lint": "^6.8.1",
|
"@codemirror/lint": "^6.8.4",
|
||||||
"@codemirror/search": "^6.5.6",
|
"@codemirror/search": "^6.5.6",
|
||||||
"@codemirror/state": "^6.4.1",
|
"@codemirror/state": "^6.4.1",
|
||||||
"@codemirror/theme-one-dark": "^6.1.2",
|
"@codemirror/theme-one-dark": "^6.1.2",
|
||||||
@ -52,13 +52,13 @@
|
|||||||
"react": "^18.3.1",
|
"react": "^18.3.1",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
"react-hot-toast": "^2.4.1",
|
"react-hot-toast": "^2.4.1",
|
||||||
"react-hotkeys-hook": "^4.5.1",
|
"react-hotkeys-hook": "^4.6.1",
|
||||||
"react-json-view": "^1.21.3",
|
"react-json-view": "^1.21.3",
|
||||||
"react-modal": "^3.16.1",
|
"react-modal": "^3.16.3",
|
||||||
"react-modal-promise": "^1.0.2",
|
"react-modal-promise": "^1.0.2",
|
||||||
"react-router-dom": "^6.28.0",
|
"react-router-dom": "^6.28.0",
|
||||||
"sketch-helpers": "^0.0.4",
|
"sketch-helpers": "^0.0.4",
|
||||||
"three": "^0.166.1",
|
"three": "^0.172.0",
|
||||||
"ua-parser-js": "^1.0.37",
|
"ua-parser-js": "^1.0.37",
|
||||||
"uuid": "^11.0.2",
|
"uuid": "^11.0.2",
|
||||||
"vscode-jsonrpc": "^8.2.1",
|
"vscode-jsonrpc": "^8.2.1",
|
||||||
@ -166,7 +166,7 @@
|
|||||||
"@types/react": "^18.3.4",
|
"@types/react": "^18.3.4",
|
||||||
"@types/react-dom": "^18.3.1",
|
"@types/react-dom": "^18.3.1",
|
||||||
"@types/react-modal": "^3.16.3",
|
"@types/react-modal": "^3.16.3",
|
||||||
"@types/three": "^0.163.0",
|
"@types/three": "^0.172.0",
|
||||||
"@types/ua-parser-js": "^0.7.39",
|
"@types/ua-parser-js": "^0.7.39",
|
||||||
"@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",
|
||||||
@ -186,7 +186,7 @@
|
|||||||
"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-suggest-no-throw": "^1.0.0",
|
"eslint-plugin-suggest-no-throw": "^1.0.0",
|
||||||
"happy-dom": "^15.11.7",
|
"happy-dom": "^16.3.0",
|
||||||
"http-server": "^14.1.1",
|
"http-server": "^14.1.1",
|
||||||
"husky": "^9.1.5",
|
"husky": "^9.1.5",
|
||||||
"kill-port": "^2.0.1",
|
"kill-port": "^2.0.1",
|
||||||
|
@ -85,7 +85,7 @@ commaSep1NoTrailingComma<term> { term ("," term)* }
|
|||||||
@tokens {
|
@tokens {
|
||||||
String[isolate] { "'" ("\\" _ | !['\\])* "'" | '"' ("\\" _ | !["\\])* '"' }
|
String[isolate] { "'" ("\\" _ | !['\\])* "'" | '"' ("\\" _ | !["\\])* '"' }
|
||||||
|
|
||||||
Number { "." @digit+ | @digit+ ("." @digit*)? }
|
Number { "." @digit+ | @digit+ ("." @digit+)? }
|
||||||
@precedence { Number, "." }
|
@precedence { Number, "." }
|
||||||
|
|
||||||
AddOp { "+" | "-" }
|
AddOp { "+" | "-" }
|
||||||
|
43
packages/codemirror-lang-kcl/test/range.txt
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
# spaced
|
||||||
|
|
||||||
|
a = [0 .. 1]
|
||||||
|
|
||||||
|
==>
|
||||||
|
Program(VariableDeclaration(VariableDefinition,
|
||||||
|
Equals,
|
||||||
|
ArrayExpression(IntegerRange(Number,
|
||||||
|
Number))))
|
||||||
|
|
||||||
|
# compact
|
||||||
|
|
||||||
|
a = [0..1]
|
||||||
|
|
||||||
|
==>
|
||||||
|
Program(VariableDeclaration(VariableDefinition,
|
||||||
|
Equals,
|
||||||
|
ArrayExpression(IntegerRange(Number,
|
||||||
|
Number))))
|
||||||
|
|
||||||
|
# expr spaced
|
||||||
|
|
||||||
|
a = [start .. start + 10]
|
||||||
|
|
||||||
|
==>
|
||||||
|
Program(VariableDeclaration(VariableDefinition,
|
||||||
|
Equals,
|
||||||
|
ArrayExpression(IntegerRange(VariableName,
|
||||||
|
BinaryExpression(VariableName,
|
||||||
|
AddOp,
|
||||||
|
Number)))))
|
||||||
|
|
||||||
|
# expr compact
|
||||||
|
|
||||||
|
a = [start..start + 10]
|
||||||
|
|
||||||
|
==>
|
||||||
|
Program(VariableDeclaration(VariableDefinition,
|
||||||
|
Equals,
|
||||||
|
ArrayExpression(IntegerRange(VariableName,
|
||||||
|
BinaryExpression(VariableName,
|
||||||
|
AddOp,
|
||||||
|
Number)))))
|
@ -75,6 +75,7 @@ function CommandBarTextareaInput({
|
|||||||
target.selectionStart = selectionStart + 1
|
target.selectionStart = selectionStart + 1
|
||||||
target.selectionEnd = selectionStart + 1
|
target.selectionEnd = selectionStart + 1
|
||||||
} else if (event.key === 'Enter') {
|
} else if (event.key === 'Enter') {
|
||||||
|
event.preventDefault()
|
||||||
formRef.current?.dispatchEvent(
|
formRef.current?.dispatchEvent(
|
||||||
new Event('submit', { bubbles: true })
|
new Event('submit', { bubbles: true })
|
||||||
)
|
)
|
||||||
|
@ -218,20 +218,6 @@ export const Stream = () => {
|
|||||||
}
|
}
|
||||||
}, [IDLE, streamState])
|
}, [IDLE, streamState])
|
||||||
|
|
||||||
/**
|
|
||||||
* Play the vid
|
|
||||||
*/
|
|
||||||
useEffect(() => {
|
|
||||||
if (!kclManager.isExecuting) {
|
|
||||||
setTimeout(() => {
|
|
||||||
// execute in the next event loop
|
|
||||||
videoRef.current?.play().catch((e) => {
|
|
||||||
console.warn('Video playing was prevented', e, videoRef.current)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}, [kclManager.isExecuting])
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (
|
if (
|
||||||
typeof window === 'undefined' ||
|
typeof window === 'undefined' ||
|
||||||
@ -243,9 +229,15 @@ export const Stream = () => {
|
|||||||
|
|
||||||
// The browser complains if we try to load a new stream without pausing first.
|
// The browser complains if we try to load a new stream without pausing first.
|
||||||
// Do not immediately play the stream!
|
// Do not immediately play the stream!
|
||||||
|
// we instead use a setTimeout to play the stream in the next event loop
|
||||||
try {
|
try {
|
||||||
videoRef.current.srcObject = mediaStream
|
videoRef.current.srcObject = mediaStream
|
||||||
videoRef.current.pause()
|
videoRef.current.pause()
|
||||||
|
setTimeout(() => {
|
||||||
|
videoRef.current?.play().catch((e) => {
|
||||||
|
console.warn('Video playing was prevented', e, videoRef.current)
|
||||||
|
})
|
||||||
|
})
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.warn('Attempted to pause stream while play was still loading', e)
|
console.warn('Attempted to pause stream while play was still loading', e)
|
||||||
}
|
}
|
||||||
|
@ -150,4 +150,31 @@ describe('ToastUpdate tests', () => {
|
|||||||
expect(restartButton).toBeEnabled()
|
expect(restartButton).toBeEnabled()
|
||||||
expect(dismissButton).toBeEnabled()
|
expect(dismissButton).toBeEnabled()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('Happy path: external links render correctly', () => {
|
||||||
|
const releaseNotesWithBreakingChanges = `
|
||||||
|
## Some markdown release notes
|
||||||
|
- [Zoo](https://zoo.dev/)
|
||||||
|
`
|
||||||
|
const onRestart = vi.fn()
|
||||||
|
const onDismiss = vi.fn()
|
||||||
|
|
||||||
|
render(
|
||||||
|
<ToastUpdate
|
||||||
|
onRestart={onRestart}
|
||||||
|
onDismiss={onDismiss}
|
||||||
|
version={testData.version}
|
||||||
|
releaseNotes={releaseNotesWithBreakingChanges}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
|
||||||
|
// Locators and other constants
|
||||||
|
const zooDev = screen.getByText('Zoo', {
|
||||||
|
selector: 'a',
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(zooDev).toHaveAttribute('href', 'https://zoo.dev/')
|
||||||
|
expect(zooDev).toHaveAttribute('target', '_blank')
|
||||||
|
expect(zooDev).toHaveAttribute('onClick')
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
import toast from 'react-hot-toast'
|
import toast from 'react-hot-toast'
|
||||||
import { ActionButton } from './ActionButton'
|
import { ActionButton } from './ActionButton'
|
||||||
import { openExternalBrowserIfDesktop } from 'lib/openWindow'
|
import { openExternalBrowserIfDesktop } from 'lib/openWindow'
|
||||||
import { Marked } from '@ts-stack/markdown'
|
import { escape, Marked, MarkedOptions, unescape } from '@ts-stack/markdown'
|
||||||
import { getReleaseUrl } from 'routes/Settings'
|
import { getReleaseUrl } from 'routes/Settings'
|
||||||
|
import { SafeRenderer } from 'lib/markdown'
|
||||||
|
|
||||||
export function ToastUpdate({
|
export function ToastUpdate({
|
||||||
version,
|
version,
|
||||||
@ -19,6 +20,14 @@ export function ToastUpdate({
|
|||||||
?.toLocaleLowerCase()
|
?.toLocaleLowerCase()
|
||||||
.includes('breaking')
|
.includes('breaking')
|
||||||
|
|
||||||
|
const markedOptions: MarkedOptions = {
|
||||||
|
gfm: true,
|
||||||
|
breaks: true,
|
||||||
|
sanitize: true,
|
||||||
|
unescape,
|
||||||
|
escape,
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="inset-0 z-50 grid place-content-center rounded bg-chalkboard-110/50 shadow-md">
|
<div className="inset-0 z-50 grid place-content-center rounded bg-chalkboard-110/50 shadow-md">
|
||||||
<div className="max-w-3xl min-w-[35rem] p-8 rounded bg-chalkboard-10 dark:bg-chalkboard-90">
|
<div className="max-w-3xl min-w-[35rem] p-8 rounded bg-chalkboard-10 dark:bg-chalkboard-90">
|
||||||
@ -58,9 +67,8 @@ export function ToastUpdate({
|
|||||||
className="parsed-markdown py-2 px-4 mt-2 border-t border-chalkboard-30 dark:border-chalkboard-60 max-h-60 overflow-y-auto"
|
className="parsed-markdown py-2 px-4 mt-2 border-t border-chalkboard-30 dark:border-chalkboard-60 max-h-60 overflow-y-auto"
|
||||||
dangerouslySetInnerHTML={{
|
dangerouslySetInnerHTML={{
|
||||||
__html: Marked.parse(releaseNotes, {
|
__html: Marked.parse(releaseNotes, {
|
||||||
gfm: true,
|
renderer: new SafeRenderer(markedOptions),
|
||||||
breaks: true,
|
...markedOptions,
|
||||||
sanitize: true,
|
|
||||||
}),
|
}),
|
||||||
}}
|
}}
|
||||||
></div>
|
></div>
|
||||||
|
@ -1,42 +0,0 @@
|
|||||||
import { fireEvent, render, screen } from '@testing-library/react'
|
|
||||||
import { vi } from 'vitest'
|
|
||||||
import { UpdaterModal } from './UpdaterModal'
|
|
||||||
|
|
||||||
describe('UpdaterModal tests', () => {
|
|
||||||
test('Renders the modal', () => {
|
|
||||||
const callback = vi.fn()
|
|
||||||
const data = {
|
|
||||||
version: '1.2.3',
|
|
||||||
date: '2021-22-23T21:22:23Z',
|
|
||||||
body: 'This is the body.',
|
|
||||||
}
|
|
||||||
|
|
||||||
render(
|
|
||||||
<UpdaterModal
|
|
||||||
isOpen={true}
|
|
||||||
onReject={() => {}}
|
|
||||||
onResolve={callback}
|
|
||||||
instanceId=""
|
|
||||||
open={false}
|
|
||||||
close={(res) => {}}
|
|
||||||
version={data.version}
|
|
||||||
date={data.date}
|
|
||||||
body={data.body}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
|
|
||||||
expect(screen.getByTestId('update-version')).toHaveTextContent(data.version)
|
|
||||||
|
|
||||||
const updateButton = screen.getByTestId('update-button-update')
|
|
||||||
expect(updateButton).toBeEnabled()
|
|
||||||
fireEvent.click(updateButton)
|
|
||||||
expect(callback.mock.calls).toHaveLength(1)
|
|
||||||
expect(callback.mock.lastCall[0]).toEqual({ wantUpdate: true })
|
|
||||||
|
|
||||||
const cancelButton = screen.getByTestId('update-button-cancel')
|
|
||||||
expect(cancelButton).toBeEnabled()
|
|
||||||
fireEvent.click(cancelButton)
|
|
||||||
expect(callback.mock.calls).toHaveLength(2)
|
|
||||||
expect(callback.mock.lastCall[0]).toEqual({ wantUpdate: false })
|
|
||||||
})
|
|
||||||
})
|
|
@ -1,87 +0,0 @@
|
|||||||
import { create, InstanceProps } from 'react-modal-promise'
|
|
||||||
import { ActionButton } from './ActionButton'
|
|
||||||
import { Logo } from './Logo'
|
|
||||||
import { Marked } from '@ts-stack/markdown'
|
|
||||||
|
|
||||||
type ModalResolve = {
|
|
||||||
wantUpdate: boolean
|
|
||||||
}
|
|
||||||
|
|
||||||
type ModalReject = boolean
|
|
||||||
|
|
||||||
type UpdaterModalProps = InstanceProps<ModalResolve, ModalReject> & {
|
|
||||||
version: string
|
|
||||||
date?: string
|
|
||||||
body?: string
|
|
||||||
}
|
|
||||||
|
|
||||||
export const createUpdaterModal = create<
|
|
||||||
UpdaterModalProps,
|
|
||||||
ModalResolve,
|
|
||||||
ModalReject
|
|
||||||
>
|
|
||||||
|
|
||||||
export const UpdaterModal = ({
|
|
||||||
onResolve,
|
|
||||||
version,
|
|
||||||
date,
|
|
||||||
body,
|
|
||||||
}: UpdaterModalProps) => (
|
|
||||||
<div className="fixed inset-0 z-50 grid place-content-center bg-chalkboard-110/50">
|
|
||||||
<div className="max-w-3xl min-w-[45rem] p-8 rounded bg-chalkboard-10 dark:bg-chalkboard-90">
|
|
||||||
<div className="flex items-center">
|
|
||||||
<h1 className="flex-grow text-3xl font-bold">New version available!</h1>
|
|
||||||
<Logo className="h-9" />
|
|
||||||
</div>
|
|
||||||
<div className="my-4 flex items-baseline">
|
|
||||||
<span
|
|
||||||
className="px-3 py-1 text-xl rounded-full bg-energy-10 text-energy-80"
|
|
||||||
data-testid="update-version"
|
|
||||||
>
|
|
||||||
v{version}
|
|
||||||
</span>
|
|
||||||
<span className="ml-4 text-sm text-gray-400">Published on {date}</span>
|
|
||||||
</div>
|
|
||||||
{/* TODO: fix list bullets */}
|
|
||||||
{body && (
|
|
||||||
<div
|
|
||||||
className="my-4 max-h-60 overflow-y-auto"
|
|
||||||
dangerouslySetInnerHTML={{
|
|
||||||
__html: Marked.parse(body, {
|
|
||||||
gfm: true,
|
|
||||||
breaks: true,
|
|
||||||
sanitize: true,
|
|
||||||
}),
|
|
||||||
}}
|
|
||||||
></div>
|
|
||||||
)}
|
|
||||||
<div className="flex justify-between">
|
|
||||||
<ActionButton
|
|
||||||
Element="button"
|
|
||||||
onClick={() => onResolve({ wantUpdate: false })}
|
|
||||||
iconStart={{
|
|
||||||
icon: 'close',
|
|
||||||
bgClassName: 'bg-destroy-80',
|
|
||||||
iconClassName: 'text-destroy-20 group-hover:text-destroy-10',
|
|
||||||
}}
|
|
||||||
className="hover:border-destroy-40 hover:bg-destroy-10/50 dark:hover:bg-destroy-80/50"
|
|
||||||
data-testid="update-button-cancel"
|
|
||||||
>
|
|
||||||
Not now
|
|
||||||
</ActionButton>
|
|
||||||
<ActionButton
|
|
||||||
Element="button"
|
|
||||||
onClick={() => onResolve({ wantUpdate: true })}
|
|
||||||
iconStart={{
|
|
||||||
icon: 'arrowRight',
|
|
||||||
bgClassName: 'dark:bg-chalkboard-80',
|
|
||||||
}}
|
|
||||||
className="dark:hover:bg-chalkboard-80/50"
|
|
||||||
data-testid="update-button-update"
|
|
||||||
>
|
|
||||||
Update
|
|
||||||
</ActionButton>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
@ -10,8 +10,11 @@ import { AppStreamProvider } from 'AppState'
|
|||||||
import { ToastUpdate } from 'components/ToastUpdate'
|
import { ToastUpdate } from 'components/ToastUpdate'
|
||||||
import { markOnce } from 'lib/performance'
|
import { markOnce } from 'lib/performance'
|
||||||
import { AUTO_UPDATER_TOAST_ID } from 'lib/constants'
|
import { AUTO_UPDATER_TOAST_ID } from 'lib/constants'
|
||||||
|
import { initializeWindowExceptionHandler } from 'lib/exceptions'
|
||||||
|
|
||||||
markOnce('code/willAuth')
|
markOnce('code/willAuth')
|
||||||
|
initializeWindowExceptionHandler()
|
||||||
|
|
||||||
// uncomment for xstate inspector
|
// uncomment for xstate inspector
|
||||||
// import { DEV } from 'env'
|
// import { DEV } from 'env'
|
||||||
// import { inspect } from '@xstate/inspect'
|
// import { inspect } from '@xstate/inspect'
|
||||||
|
@ -376,7 +376,11 @@ export class KclManager {
|
|||||||
}
|
}
|
||||||
this.ast = { ...ast }
|
this.ast = { ...ast }
|
||||||
// updateArtifactGraph relies on updated executeState/programMemory
|
// updateArtifactGraph relies on updated executeState/programMemory
|
||||||
await this.engineCommandManager.updateArtifactGraph(this.ast)
|
await this.engineCommandManager.updateArtifactGraph(
|
||||||
|
this.ast,
|
||||||
|
execState.artifactCommands,
|
||||||
|
execState.artifacts
|
||||||
|
)
|
||||||
this._executeCallback()
|
this._executeCallback()
|
||||||
if (!isInterrupted) {
|
if (!isInterrupted) {
|
||||||
sceneInfra.modelingSend({ type: 'code edit during sketch' })
|
sceneInfra.modelingSend({ type: 'code edit during sketch' })
|
||||||
@ -390,6 +394,24 @@ export class KclManager {
|
|||||||
this._cancelTokens.delete(currentExecutionId)
|
this._cancelTokens.delete(currentExecutionId)
|
||||||
markOnce('code/endExecuteAst')
|
markOnce('code/endExecuteAst')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This cleanup function is external and internal to the KclSingleton class.
|
||||||
|
* Since the WASM runtime can panic and the error cannot be caught in executeAst
|
||||||
|
* we need a global exception handler in exceptions.ts
|
||||||
|
* This file will interface with this cleanup as if it caught the original error
|
||||||
|
* to properly restore the TS application state.
|
||||||
|
*/
|
||||||
|
executeAstCleanUp() {
|
||||||
|
this.isExecuting = false
|
||||||
|
this.executeIsStale = null
|
||||||
|
this.engineCommandManager.addCommandLog({
|
||||||
|
type: 'execution-done',
|
||||||
|
data: null,
|
||||||
|
})
|
||||||
|
markOnce('code/endExecuteAst')
|
||||||
|
}
|
||||||
|
|
||||||
// NOTE: this always updates the code state and editor.
|
// NOTE: this always updates the code state and editor.
|
||||||
// DO NOT CALL THIS from codemirror ever.
|
// DO NOT CALL THIS from codemirror ever.
|
||||||
async executeAstMock(
|
async executeAstMock(
|
||||||
|
@ -47,7 +47,7 @@ describe('parsing errors', () => {
|
|||||||
const result = parse(code)
|
const result = parse(code)
|
||||||
if (err(result)) throw result
|
if (err(result)) throw result
|
||||||
const error = result.errors[0]
|
const error = result.errors[0]
|
||||||
expect(error.message).toBe('Unexpected token: (')
|
expect(error.message).toBe('Array is missing a closing bracket(`]`)')
|
||||||
expect(error.sourceRange).toEqual([27, 28, 0])
|
expect(error.sourceRange).toEqual([28, 29, 0])
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -10,6 +10,7 @@ describe('test kclErrToDiagnostic', () => {
|
|||||||
msg: 'Semantic error',
|
msg: 'Semantic error',
|
||||||
sourceRange: [0, 1, true],
|
sourceRange: [0, 1, true],
|
||||||
operations: [],
|
operations: [],
|
||||||
|
artifactCommands: [],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: '',
|
name: '',
|
||||||
@ -18,6 +19,7 @@ describe('test kclErrToDiagnostic', () => {
|
|||||||
msg: 'Type error',
|
msg: 'Type error',
|
||||||
sourceRange: [4, 5, true],
|
sourceRange: [4, 5, true],
|
||||||
operations: [],
|
operations: [],
|
||||||
|
artifactCommands: [],
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
const diagnostics = kclErrorsToDiagnostics(errors)
|
const diagnostics = kclErrorsToDiagnostics(errors)
|
||||||
|
@ -5,7 +5,7 @@ import { posToOffset } from '@kittycad/codemirror-lsp-client'
|
|||||||
import { Diagnostic as LspDiagnostic } from 'vscode-languageserver-protocol'
|
import { Diagnostic as LspDiagnostic } from 'vscode-languageserver-protocol'
|
||||||
import { Text } from '@codemirror/state'
|
import { Text } from '@codemirror/state'
|
||||||
import { EditorView } from 'codemirror'
|
import { EditorView } from 'codemirror'
|
||||||
import { SourceRange } from 'lang/wasm'
|
import { ArtifactCommand, SourceRange } from 'lang/wasm'
|
||||||
import { Operation } from 'wasm-lib/kcl/bindings/Operation'
|
import { Operation } from 'wasm-lib/kcl/bindings/Operation'
|
||||||
|
|
||||||
type ExtractKind<T> = T extends { kind: infer K } ? K : never
|
type ExtractKind<T> = T extends { kind: infer K } ? K : never
|
||||||
@ -14,86 +14,141 @@ export class KCLError extends Error {
|
|||||||
sourceRange: SourceRange
|
sourceRange: SourceRange
|
||||||
msg: string
|
msg: string
|
||||||
operations: Operation[]
|
operations: Operation[]
|
||||||
|
artifactCommands: ArtifactCommand[]
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
kind: ExtractKind<RustKclError> | 'name',
|
kind: ExtractKind<RustKclError> | 'name',
|
||||||
msg: string,
|
msg: string,
|
||||||
sourceRange: SourceRange,
|
sourceRange: SourceRange,
|
||||||
operations: Operation[]
|
operations: Operation[],
|
||||||
|
artifactCommands: ArtifactCommand[]
|
||||||
) {
|
) {
|
||||||
super()
|
super()
|
||||||
this.kind = kind
|
this.kind = kind
|
||||||
this.msg = msg
|
this.msg = msg
|
||||||
this.sourceRange = sourceRange
|
this.sourceRange = sourceRange
|
||||||
this.operations = operations
|
this.operations = operations
|
||||||
|
this.artifactCommands = artifactCommands
|
||||||
Object.setPrototypeOf(this, KCLError.prototype)
|
Object.setPrototypeOf(this, KCLError.prototype)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class KCLLexicalError extends KCLError {
|
export class KCLLexicalError extends KCLError {
|
||||||
constructor(msg: string, sourceRange: SourceRange, operations: Operation[]) {
|
constructor(
|
||||||
super('lexical', msg, sourceRange, operations)
|
msg: string,
|
||||||
|
sourceRange: SourceRange,
|
||||||
|
operations: Operation[],
|
||||||
|
artifactCommands: ArtifactCommand[]
|
||||||
|
) {
|
||||||
|
super('lexical', msg, sourceRange, operations, artifactCommands)
|
||||||
Object.setPrototypeOf(this, KCLSyntaxError.prototype)
|
Object.setPrototypeOf(this, KCLSyntaxError.prototype)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class KCLInternalError extends KCLError {
|
export class KCLInternalError extends KCLError {
|
||||||
constructor(msg: string, sourceRange: SourceRange, operations: Operation[]) {
|
constructor(
|
||||||
super('internal', msg, sourceRange, operations)
|
msg: string,
|
||||||
|
sourceRange: SourceRange,
|
||||||
|
operations: Operation[],
|
||||||
|
artifactCommands: ArtifactCommand[]
|
||||||
|
) {
|
||||||
|
super('internal', msg, sourceRange, operations, artifactCommands)
|
||||||
Object.setPrototypeOf(this, KCLSyntaxError.prototype)
|
Object.setPrototypeOf(this, KCLSyntaxError.prototype)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class KCLSyntaxError extends KCLError {
|
export class KCLSyntaxError extends KCLError {
|
||||||
constructor(msg: string, sourceRange: SourceRange, operations: Operation[]) {
|
constructor(
|
||||||
super('syntax', msg, sourceRange, operations)
|
msg: string,
|
||||||
|
sourceRange: SourceRange,
|
||||||
|
operations: Operation[],
|
||||||
|
artifactCommands: ArtifactCommand[]
|
||||||
|
) {
|
||||||
|
super('syntax', msg, sourceRange, operations, artifactCommands)
|
||||||
Object.setPrototypeOf(this, KCLSyntaxError.prototype)
|
Object.setPrototypeOf(this, KCLSyntaxError.prototype)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class KCLSemanticError extends KCLError {
|
export class KCLSemanticError extends KCLError {
|
||||||
constructor(msg: string, sourceRange: SourceRange, operations: Operation[]) {
|
constructor(
|
||||||
super('semantic', msg, sourceRange, operations)
|
msg: string,
|
||||||
|
sourceRange: SourceRange,
|
||||||
|
operations: Operation[],
|
||||||
|
artifactCommands: ArtifactCommand[]
|
||||||
|
) {
|
||||||
|
super('semantic', msg, sourceRange, operations, artifactCommands)
|
||||||
Object.setPrototypeOf(this, KCLSemanticError.prototype)
|
Object.setPrototypeOf(this, KCLSemanticError.prototype)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class KCLTypeError extends KCLError {
|
export class KCLTypeError extends KCLError {
|
||||||
constructor(msg: string, sourceRange: SourceRange, operations: Operation[]) {
|
constructor(
|
||||||
super('type', msg, sourceRange, operations)
|
msg: string,
|
||||||
|
sourceRange: SourceRange,
|
||||||
|
operations: Operation[],
|
||||||
|
artifactCommands: ArtifactCommand[]
|
||||||
|
) {
|
||||||
|
super('type', msg, sourceRange, operations, artifactCommands)
|
||||||
Object.setPrototypeOf(this, KCLTypeError.prototype)
|
Object.setPrototypeOf(this, KCLTypeError.prototype)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class KCLUnimplementedError extends KCLError {
|
export class KCLUnimplementedError extends KCLError {
|
||||||
constructor(msg: string, sourceRange: SourceRange, operations: Operation[]) {
|
constructor(
|
||||||
super('unimplemented', msg, sourceRange, operations)
|
msg: string,
|
||||||
|
sourceRange: SourceRange,
|
||||||
|
operations: Operation[],
|
||||||
|
artifactCommands: ArtifactCommand[]
|
||||||
|
) {
|
||||||
|
super('unimplemented', msg, sourceRange, operations, artifactCommands)
|
||||||
Object.setPrototypeOf(this, KCLUnimplementedError.prototype)
|
Object.setPrototypeOf(this, KCLUnimplementedError.prototype)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class KCLUnexpectedError extends KCLError {
|
export class KCLUnexpectedError extends KCLError {
|
||||||
constructor(msg: string, sourceRange: SourceRange, operations: Operation[]) {
|
constructor(
|
||||||
super('unexpected', msg, sourceRange, operations)
|
msg: string,
|
||||||
|
sourceRange: SourceRange,
|
||||||
|
operations: Operation[],
|
||||||
|
artifactCommands: ArtifactCommand[]
|
||||||
|
) {
|
||||||
|
super('unexpected', msg, sourceRange, operations, artifactCommands)
|
||||||
Object.setPrototypeOf(this, KCLUnexpectedError.prototype)
|
Object.setPrototypeOf(this, KCLUnexpectedError.prototype)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class KCLValueAlreadyDefined extends KCLError {
|
export class KCLValueAlreadyDefined extends KCLError {
|
||||||
constructor(key: string, sourceRange: SourceRange, operations: Operation[]) {
|
constructor(
|
||||||
|
key: string,
|
||||||
|
sourceRange: SourceRange,
|
||||||
|
operations: Operation[],
|
||||||
|
artifactCommands: ArtifactCommand[]
|
||||||
|
) {
|
||||||
super(
|
super(
|
||||||
'name',
|
'name',
|
||||||
`Key ${key} was already defined elsewhere`,
|
`Key ${key} was already defined elsewhere`,
|
||||||
sourceRange,
|
sourceRange,
|
||||||
operations
|
operations,
|
||||||
|
artifactCommands
|
||||||
)
|
)
|
||||||
Object.setPrototypeOf(this, KCLValueAlreadyDefined.prototype)
|
Object.setPrototypeOf(this, KCLValueAlreadyDefined.prototype)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class KCLUndefinedValueError extends KCLError {
|
export class KCLUndefinedValueError extends KCLError {
|
||||||
constructor(key: string, sourceRange: SourceRange, operations: Operation[]) {
|
constructor(
|
||||||
super('name', `Key ${key} has not been defined`, sourceRange, operations)
|
key: string,
|
||||||
|
sourceRange: SourceRange,
|
||||||
|
operations: Operation[],
|
||||||
|
artifactCommands: ArtifactCommand[]
|
||||||
|
) {
|
||||||
|
super(
|
||||||
|
'name',
|
||||||
|
`Key ${key} has not been defined`,
|
||||||
|
sourceRange,
|
||||||
|
operations,
|
||||||
|
artifactCommands
|
||||||
|
)
|
||||||
Object.setPrototypeOf(this, KCLUndefinedValueError.prototype)
|
Object.setPrototypeOf(this, KCLUndefinedValueError.prototype)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -113,6 +168,7 @@ export function lspDiagnosticsToKclErrors(
|
|||||||
'unexpected',
|
'unexpected',
|
||||||
message,
|
message,
|
||||||
[posToOffset(doc, range.start)!, posToOffset(doc, range.end)!, true],
|
[posToOffset(doc, range.start)!, posToOffset(doc, range.end)!, true],
|
||||||
|
[],
|
||||||
[]
|
[]
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
@ -481,6 +481,7 @@ const theExtrude = startSketchOn('XY')
|
|||||||
'undefined_value',
|
'undefined_value',
|
||||||
'memory item key `myVarZ` is not defined',
|
'memory item key `myVarZ` is not defined',
|
||||||
[129, 135, true],
|
[129, 135, true],
|
||||||
|
[],
|
||||||
[]
|
[]
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import {
|
import {
|
||||||
Program,
|
Program,
|
||||||
_executor,
|
executor,
|
||||||
ProgramMemory,
|
ProgramMemory,
|
||||||
kclLint,
|
kclLint,
|
||||||
emptyExecState,
|
emptyExecState,
|
||||||
@ -64,7 +64,7 @@ export async function executeAst({
|
|||||||
try {
|
try {
|
||||||
const execState = await (programMemoryOverride
|
const execState = await (programMemoryOverride
|
||||||
? enginelessExecutor(ast, programMemoryOverride)
|
? enginelessExecutor(ast, programMemoryOverride)
|
||||||
: _executor(ast, engineCommandManager))
|
: executor(ast, engineCommandManager))
|
||||||
|
|
||||||
await engineCommandManager.waitForAllCommands()
|
await engineCommandManager.waitForAllCommands()
|
||||||
|
|
||||||
|
@ -806,9 +806,9 @@ sketch001 = startSketchOn('XZ')
|
|||||||
sketch002 = startSketchOn({
|
sketch002 = startSketchOn({
|
||||||
plane = {
|
plane = {
|
||||||
origin = { x = 1, y = 2, z = 3 },
|
origin = { x = 1, y = 2, z = 3 },
|
||||||
x_axis = { x = 4, y = 5, z = 6 },
|
xAxis = { x = 4, y = 5, z = 6 },
|
||||||
y_axis = { x = 7, y = 8, z = 9 },
|
yAxis = { x = 7, y = 8, z = 9 },
|
||||||
z_axis = { x = 10, y = 11, z = 12 }
|
zAxis = { x = 10, y = 11, z = 12 }
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|> startProfileAt([-12.55, 2.89], %)
|
|> startProfileAt([-12.55, 2.89], %)
|
||||||
@ -862,9 +862,9 @@ sketch001 = startSketchOn('XZ')
|
|||||||
sketch002 = startSketchOn({
|
sketch002 = startSketchOn({
|
||||||
plane = {
|
plane = {
|
||||||
origin = { x = 1, y = 2, z = 3 },
|
origin = { x = 1, y = 2, z = 3 },
|
||||||
x_axis = { x = 4, y = 5, z = 6 },
|
xAxis = { x = 4, y = 5, z = 6 },
|
||||||
y_axis = { x = 7, y = 8, z = 9 },
|
yAxis = { x = 7, y = 8, z = 9 },
|
||||||
z_axis = { x = 10, y = 11, z = 12 }
|
zAxis = { x = 10, y = 11, z = 12 }
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|> startProfileAt([-12.55, 2.89], %)
|
|> startProfileAt([-12.55, 2.89], %)
|
||||||
|
@ -1278,17 +1278,17 @@ export async function deleteFromSelection(
|
|||||||
y: roundLiteral(faceDetails.origin.y),
|
y: roundLiteral(faceDetails.origin.y),
|
||||||
z: roundLiteral(faceDetails.origin.z),
|
z: roundLiteral(faceDetails.origin.z),
|
||||||
}),
|
}),
|
||||||
x_axis: createObjectExpression({
|
xAxis: createObjectExpression({
|
||||||
x: roundLiteral(faceDetails.x_axis.x),
|
x: roundLiteral(faceDetails.x_axis.x),
|
||||||
y: roundLiteral(faceDetails.x_axis.y),
|
y: roundLiteral(faceDetails.x_axis.y),
|
||||||
z: roundLiteral(faceDetails.x_axis.z),
|
z: roundLiteral(faceDetails.x_axis.z),
|
||||||
}),
|
}),
|
||||||
y_axis: createObjectExpression({
|
yAxis: createObjectExpression({
|
||||||
x: roundLiteral(faceDetails.y_axis.x),
|
x: roundLiteral(faceDetails.y_axis.x),
|
||||||
y: roundLiteral(faceDetails.y_axis.y),
|
y: roundLiteral(faceDetails.y_axis.y),
|
||||||
z: roundLiteral(faceDetails.y_axis.z),
|
z: roundLiteral(faceDetails.y_axis.z),
|
||||||
}),
|
}),
|
||||||
z_axis: createObjectExpression({
|
zAxis: createObjectExpression({
|
||||||
x: roundLiteral(faceDetails.z_axis.x),
|
x: roundLiteral(faceDetails.z_axis.x),
|
||||||
y: roundLiteral(faceDetails.z_axis.y),
|
y: roundLiteral(faceDetails.z_axis.y),
|
||||||
z: roundLiteral(faceDetails.z_axis.z),
|
z: roundLiteral(faceDetails.z_axis.z),
|
||||||
|
@ -61,19 +61,18 @@ export interface FilletParameters {
|
|||||||
export type EdgeTreatmentParameters = ChamferParameters | FilletParameters
|
export type EdgeTreatmentParameters = ChamferParameters | FilletParameters
|
||||||
|
|
||||||
// Apply Edge Treatment (Fillet or Chamfer) To Selection
|
// Apply Edge Treatment (Fillet or Chamfer) To Selection
|
||||||
export function applyEdgeTreatmentToSelection(
|
export async function applyEdgeTreatmentToSelection(
|
||||||
ast: Node<Program>,
|
ast: Node<Program>,
|
||||||
selection: Selections,
|
selection: Selections,
|
||||||
parameters: EdgeTreatmentParameters
|
parameters: EdgeTreatmentParameters
|
||||||
): void | Error {
|
): Promise<void | Error> {
|
||||||
// 1. clone and modify with edge treatment and tag
|
// 1. clone and modify with edge treatment and tag
|
||||||
const result = modifyAstWithEdgeTreatmentAndTag(ast, selection, parameters)
|
const result = modifyAstWithEdgeTreatmentAndTag(ast, selection, parameters)
|
||||||
if (err(result)) return result
|
if (err(result)) return result
|
||||||
const { modifiedAst, pathToEdgeTreatmentNode } = result
|
const { modifiedAst, pathToEdgeTreatmentNode } = result
|
||||||
|
|
||||||
// 2. update ast
|
// 2. update ast
|
||||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
await updateAstAndFocus(modifiedAst, pathToEdgeTreatmentNode)
|
||||||
updateAstAndFocus(modifiedAst, pathToEdgeTreatmentNode)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function modifyAstWithEdgeTreatmentAndTag(
|
export function modifyAstWithEdgeTreatmentAndTag(
|
||||||
@ -291,7 +290,7 @@ export function getPathToExtrudeForSegmentSelection(
|
|||||||
async function updateAstAndFocus(
|
async function updateAstAndFocus(
|
||||||
modifiedAst: Node<Program>,
|
modifiedAst: Node<Program>,
|
||||||
pathToEdgeTreatmentNode: Array<PathToNode>
|
pathToEdgeTreatmentNode: Array<PathToNode>
|
||||||
) {
|
): Promise<void> {
|
||||||
const updatedAst = await kclManager.updateAst(modifiedAst, true, {
|
const updatedAst = await kclManager.updateAst(modifiedAst, true, {
|
||||||
focusPath: pathToEdgeTreatmentNode,
|
focusPath: pathToEdgeTreatmentNode,
|
||||||
})
|
})
|
||||||
|
@ -1,7 +1,13 @@
|
|||||||
import { makeDefaultPlanes, assertParse, initPromise, Program } from 'lang/wasm'
|
import {
|
||||||
|
makeDefaultPlanes,
|
||||||
|
assertParse,
|
||||||
|
initPromise,
|
||||||
|
Program,
|
||||||
|
ArtifactCommand,
|
||||||
|
ExecState,
|
||||||
|
} from 'lang/wasm'
|
||||||
import { Models } from '@kittycad/lib'
|
import { Models } from '@kittycad/lib'
|
||||||
import {
|
import {
|
||||||
OrderedCommand,
|
|
||||||
ResponseMap,
|
ResponseMap,
|
||||||
createArtifactGraph,
|
createArtifactGraph,
|
||||||
filterArtifacts,
|
filterArtifacts,
|
||||||
@ -22,6 +28,7 @@ import * as d3 from 'd3-force'
|
|||||||
import path from 'path'
|
import path from 'path'
|
||||||
import pixelmatch from 'pixelmatch'
|
import pixelmatch from 'pixelmatch'
|
||||||
import { PNG } from 'pngjs'
|
import { PNG } from 'pngjs'
|
||||||
|
import { Node } from 'wasm-lib/kcl/bindings/Node'
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Note this is an integration test, these tests connect to our real dev server and make websocket commands.
|
Note this is an integration test, these tests connect to our real dev server and make websocket commands.
|
||||||
@ -108,7 +115,7 @@ sketch002 = startSketchOn(offsetPlane001)
|
|||||||
|> line([6.78, 15.01], %)
|
|> line([6.78, 15.01], %)
|
||||||
`
|
`
|
||||||
|
|
||||||
// add more code snippets here and use `getCommands` to get the orderedCommands and responseMap for more tests
|
// add more code snippets here and use `getCommands` to get the artifactCommands and responseMap for more tests
|
||||||
const codeToWriteCacheFor = {
|
const codeToWriteCacheFor = {
|
||||||
exampleCode1,
|
exampleCode1,
|
||||||
sketchOnFaceOnFaceEtc,
|
sketchOnFaceOnFaceEtc,
|
||||||
@ -120,8 +127,9 @@ type CodeKey = keyof typeof codeToWriteCacheFor
|
|||||||
|
|
||||||
type CacheShape = {
|
type CacheShape = {
|
||||||
[key in CodeKey]: {
|
[key in CodeKey]: {
|
||||||
orderedCommands: OrderedCommand[]
|
artifactCommands: ArtifactCommand[]
|
||||||
responseMap: ResponseMap
|
responseMap: ResponseMap
|
||||||
|
execStateArtifacts: ExecState['artifacts']
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -151,8 +159,9 @@ beforeAll(async () => {
|
|||||||
await kclManager.executeAst({ ast })
|
await kclManager.executeAst({ ast })
|
||||||
|
|
||||||
cacheToWriteToFileTemp[codeKey] = {
|
cacheToWriteToFileTemp[codeKey] = {
|
||||||
orderedCommands: engineCommandManager.orderedCommands,
|
artifactCommands: kclManager.execState.artifactCommands,
|
||||||
responseMap: engineCommandManager.responseMap,
|
responseMap: engineCommandManager.responseMap,
|
||||||
|
execStateArtifacts: kclManager.execState.artifacts,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const cache = JSON.stringify(cacheToWriteToFileTemp)
|
const cache = JSON.stringify(cacheToWriteToFileTemp)
|
||||||
@ -171,18 +180,24 @@ afterAll(() => {
|
|||||||
|
|
||||||
describe('testing createArtifactGraph', () => {
|
describe('testing createArtifactGraph', () => {
|
||||||
describe('code with offset planes and a sketch:', () => {
|
describe('code with offset planes and a sketch:', () => {
|
||||||
let ast: Program
|
let ast: Node<Program>
|
||||||
let theMap: ReturnType<typeof createArtifactGraph>
|
let theMap: ReturnType<typeof createArtifactGraph>
|
||||||
|
|
||||||
it('setup', () => {
|
it('setup', () => {
|
||||||
// putting this logic in here because describe blocks runs before beforeAll has finished
|
// putting this logic in here because describe blocks runs before beforeAll has finished
|
||||||
const {
|
const {
|
||||||
orderedCommands,
|
artifactCommands,
|
||||||
responseMap,
|
responseMap,
|
||||||
ast: _ast,
|
ast: _ast,
|
||||||
|
execStateArtifacts,
|
||||||
} = getCommands('exampleCodeOffsetPlanes')
|
} = getCommands('exampleCodeOffsetPlanes')
|
||||||
ast = _ast
|
ast = _ast
|
||||||
theMap = createArtifactGraph({ orderedCommands, responseMap, ast })
|
theMap = createArtifactGraph({
|
||||||
|
artifactCommands,
|
||||||
|
responseMap,
|
||||||
|
ast,
|
||||||
|
execStateArtifacts,
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
it(`there should be one sketch`, () => {
|
it(`there should be one sketch`, () => {
|
||||||
@ -217,17 +232,23 @@ describe('testing createArtifactGraph', () => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
describe('code with an extrusion, fillet and sketch of face:', () => {
|
describe('code with an extrusion, fillet and sketch of face:', () => {
|
||||||
let ast: Program
|
let ast: Node<Program>
|
||||||
let theMap: ReturnType<typeof createArtifactGraph>
|
let theMap: ReturnType<typeof createArtifactGraph>
|
||||||
it('setup', () => {
|
it('setup', () => {
|
||||||
// putting this logic in here because describe blocks runs before beforeAll has finished
|
// putting this logic in here because describe blocks runs before beforeAll has finished
|
||||||
const {
|
const {
|
||||||
orderedCommands,
|
artifactCommands,
|
||||||
responseMap,
|
responseMap,
|
||||||
ast: _ast,
|
ast: _ast,
|
||||||
|
execStateArtifacts,
|
||||||
} = getCommands('exampleCode1')
|
} = getCommands('exampleCode1')
|
||||||
ast = _ast
|
ast = _ast
|
||||||
theMap = createArtifactGraph({ orderedCommands, responseMap, ast })
|
theMap = createArtifactGraph({
|
||||||
|
artifactCommands,
|
||||||
|
responseMap,
|
||||||
|
ast,
|
||||||
|
execStateArtifacts,
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
it('there should be two planes for the extrusion and the sketch on face', () => {
|
it('there should be two planes for the extrusion and the sketch on face', () => {
|
||||||
@ -312,17 +333,23 @@ describe('testing createArtifactGraph', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
describe(`code with sketches but no extrusions or other 3D elements`, () => {
|
describe(`code with sketches but no extrusions or other 3D elements`, () => {
|
||||||
let ast: Program
|
let ast: Node<Program>
|
||||||
let theMap: ReturnType<typeof createArtifactGraph>
|
let theMap: ReturnType<typeof createArtifactGraph>
|
||||||
it(`setup`, () => {
|
it(`setup`, () => {
|
||||||
// putting this logic in here because describe blocks runs before beforeAll has finished
|
// putting this logic in here because describe blocks runs before beforeAll has finished
|
||||||
const {
|
const {
|
||||||
orderedCommands,
|
artifactCommands,
|
||||||
responseMap,
|
responseMap,
|
||||||
ast: _ast,
|
ast: _ast,
|
||||||
|
execStateArtifacts,
|
||||||
} = getCommands('exampleCodeNo3D')
|
} = getCommands('exampleCodeNo3D')
|
||||||
ast = _ast
|
ast = _ast
|
||||||
theMap = createArtifactGraph({ orderedCommands, responseMap, ast })
|
theMap = createArtifactGraph({
|
||||||
|
artifactCommands,
|
||||||
|
responseMap,
|
||||||
|
ast,
|
||||||
|
execStateArtifacts,
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
it('there should be two planes, one for each sketch path', () => {
|
it('there should be two planes, one for each sketch path', () => {
|
||||||
@ -377,17 +404,23 @@ describe('testing createArtifactGraph', () => {
|
|||||||
|
|
||||||
describe('capture graph of sketchOnFaceOnFace...', () => {
|
describe('capture graph of sketchOnFaceOnFace...', () => {
|
||||||
describe('code with an extrusion, fillet and sketch of face:', () => {
|
describe('code with an extrusion, fillet and sketch of face:', () => {
|
||||||
let ast: Program
|
let ast: Node<Program>
|
||||||
let theMap: ReturnType<typeof createArtifactGraph>
|
let theMap: ReturnType<typeof createArtifactGraph>
|
||||||
it('setup', async () => {
|
it('setup', async () => {
|
||||||
// putting this logic in here because describe blocks runs before beforeAll has finished
|
// putting this logic in here because describe blocks runs before beforeAll has finished
|
||||||
const {
|
const {
|
||||||
orderedCommands,
|
artifactCommands,
|
||||||
responseMap,
|
responseMap,
|
||||||
ast: _ast,
|
ast: _ast,
|
||||||
|
execStateArtifacts,
|
||||||
} = getCommands('sketchOnFaceOnFaceEtc')
|
} = getCommands('sketchOnFaceOnFaceEtc')
|
||||||
ast = _ast
|
ast = _ast
|
||||||
theMap = createArtifactGraph({ orderedCommands, responseMap, ast })
|
theMap = createArtifactGraph({
|
||||||
|
artifactCommands,
|
||||||
|
responseMap,
|
||||||
|
ast,
|
||||||
|
execStateArtifacts,
|
||||||
|
})
|
||||||
|
|
||||||
// Ostensibly this takes a screen shot of the graph of the artifactGraph
|
// Ostensibly this takes a screen shot of the graph of the artifactGraph
|
||||||
// but it's it also tests that all of the id links are correct because if one
|
// but it's it also tests that all of the id links are correct because if one
|
||||||
@ -399,17 +432,21 @@ describe('capture graph of sketchOnFaceOnFace...', () => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
function getCommands(codeKey: CodeKey): CacheShape[CodeKey] & { ast: Program } {
|
function getCommands(
|
||||||
|
codeKey: CodeKey
|
||||||
|
): CacheShape[CodeKey] & { ast: Node<Program> } {
|
||||||
const ast = assertParse(codeKey)
|
const ast = assertParse(codeKey)
|
||||||
const file = fs.readFileSync(fullPath, 'utf-8')
|
const file = fs.readFileSync(fullPath, 'utf-8')
|
||||||
const parsed: CacheShape = JSON.parse(file)
|
const parsed: CacheShape = JSON.parse(file)
|
||||||
// these either already exist from the last run, or were created in
|
// these either already exist from the last run, or were created in
|
||||||
const orderedCommands = parsed[codeKey].orderedCommands
|
const artifactCommands = parsed[codeKey].artifactCommands
|
||||||
const responseMap = parsed[codeKey].responseMap
|
const responseMap = parsed[codeKey].responseMap
|
||||||
|
const execStateArtifacts = parsed[codeKey].execStateArtifacts
|
||||||
return {
|
return {
|
||||||
orderedCommands,
|
artifactCommands,
|
||||||
responseMap,
|
responseMap,
|
||||||
ast,
|
ast,
|
||||||
|
execStateArtifacts,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -635,20 +672,30 @@ async function GraphTheGraph(
|
|||||||
|
|
||||||
describe('testing getArtifactsToUpdate', () => {
|
describe('testing getArtifactsToUpdate', () => {
|
||||||
it('should return an array of artifacts to update', () => {
|
it('should return an array of artifacts to update', () => {
|
||||||
const { orderedCommands, responseMap, ast } = getCommands('exampleCode1')
|
const { artifactCommands, responseMap, ast, execStateArtifacts } =
|
||||||
const map = createArtifactGraph({ orderedCommands, responseMap, ast })
|
getCommands('exampleCode1')
|
||||||
|
const map = createArtifactGraph({
|
||||||
|
artifactCommands,
|
||||||
|
responseMap,
|
||||||
|
ast,
|
||||||
|
execStateArtifacts,
|
||||||
|
})
|
||||||
const getArtifact = (id: string) => map.get(id)
|
const getArtifact = (id: string) => map.get(id)
|
||||||
const currentPlaneId = 'UUID-1'
|
const currentPlaneId = 'UUID-1'
|
||||||
const getUpdateObjects = (type: Models['ModelingCmd_type']['type']) => {
|
const getUpdateObjects = (type: Models['ModelingCmd_type']['type']) => {
|
||||||
|
const artifactCommand = artifactCommands.find(
|
||||||
|
(a) => a.command.type === type
|
||||||
|
)
|
||||||
|
if (!artifactCommand) {
|
||||||
|
throw new Error(`No artifactCommand found for ${type}`)
|
||||||
|
}
|
||||||
const artifactsToUpdate = getArtifactsToUpdate({
|
const artifactsToUpdate = getArtifactsToUpdate({
|
||||||
orderedCommand: orderedCommands.find(
|
artifactCommand,
|
||||||
(a) =>
|
|
||||||
a.command.type === 'modeling_cmd_req' && a.command.cmd.type === type
|
|
||||||
)!,
|
|
||||||
responseMap,
|
responseMap,
|
||||||
getArtifact,
|
getArtifact,
|
||||||
currentPlaneId,
|
currentPlaneId,
|
||||||
ast,
|
ast,
|
||||||
|
execStateArtifacts,
|
||||||
})
|
})
|
||||||
return artifactsToUpdate.map(({ artifact }) => artifact)
|
return artifactsToUpdate.map(({ artifact }) => artifact)
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,15 @@
|
|||||||
import { PathToNode, Program, SourceRange } from 'lang/wasm'
|
import {
|
||||||
|
ArtifactCommand,
|
||||||
|
ExecState,
|
||||||
|
PathToNode,
|
||||||
|
Program,
|
||||||
|
SourceRange,
|
||||||
|
sourceRangeFromRust,
|
||||||
|
} from 'lang/wasm'
|
||||||
import { Models } from '@kittycad/lib'
|
import { Models } from '@kittycad/lib'
|
||||||
import { getNodePathFromSourceRange } from 'lang/queryAst'
|
import { getNodePathFromSourceRange } from 'lang/queryAst'
|
||||||
import { err } from 'lib/trap'
|
import { err } from 'lib/trap'
|
||||||
|
import { Node } from 'wasm-lib/kcl/bindings/Node'
|
||||||
|
|
||||||
export type ArtifactId = string
|
export type ArtifactId = string
|
||||||
|
|
||||||
@ -143,50 +151,47 @@ type OkWebSocketResponseData = Models['OkWebSocketResponseData_type']
|
|||||||
export interface ResponseMap {
|
export interface ResponseMap {
|
||||||
[commandId: string]: OkWebSocketResponseData
|
[commandId: string]: OkWebSocketResponseData
|
||||||
}
|
}
|
||||||
export interface OrderedCommand {
|
|
||||||
command: EngineCommand
|
|
||||||
range: SourceRange
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Creates a graph of artifacts from a list of ordered commands and their responses
|
/** Creates a graph of artifacts from a list of ordered commands and their responses
|
||||||
* muting the Map should happen entirely this function, other functions called within
|
* muting the Map should happen entirely this function, other functions called within
|
||||||
* should return data on how to update the map, and not do so directly.
|
* should return data on how to update the map, and not do so directly.
|
||||||
*/
|
*/
|
||||||
export function createArtifactGraph({
|
export function createArtifactGraph({
|
||||||
orderedCommands,
|
artifactCommands,
|
||||||
responseMap,
|
responseMap,
|
||||||
ast,
|
ast,
|
||||||
|
execStateArtifacts,
|
||||||
}: {
|
}: {
|
||||||
orderedCommands: Array<OrderedCommand>
|
artifactCommands: Array<ArtifactCommand>
|
||||||
responseMap: ResponseMap
|
responseMap: ResponseMap
|
||||||
ast: Program
|
ast: Node<Program>
|
||||||
|
execStateArtifacts: ExecState['artifacts']
|
||||||
}) {
|
}) {
|
||||||
const myMap = new Map<ArtifactId, Artifact>()
|
const myMap = new Map<ArtifactId, Artifact>()
|
||||||
|
|
||||||
/** see docstring for {@link getArtifactsToUpdate} as to why this is needed */
|
/** see docstring for {@link getArtifactsToUpdate} as to why this is needed */
|
||||||
let currentPlaneId = ''
|
let currentPlaneId = ''
|
||||||
|
|
||||||
orderedCommands.forEach((orderedCommand) => {
|
for (const artifactCommand of artifactCommands) {
|
||||||
if (orderedCommand.command?.type === 'modeling_cmd_req') {
|
if (artifactCommand.command.type === 'enable_sketch_mode') {
|
||||||
if (orderedCommand.command.cmd.type === 'enable_sketch_mode') {
|
currentPlaneId = artifactCommand.command.entity_id
|
||||||
currentPlaneId = orderedCommand.command.cmd.entity_id
|
|
||||||
}
|
}
|
||||||
if (orderedCommand.command.cmd.type === 'sketch_mode_disable') {
|
if (artifactCommand.command.type === 'sketch_mode_disable') {
|
||||||
currentPlaneId = ''
|
currentPlaneId = ''
|
||||||
}
|
}
|
||||||
}
|
|
||||||
const artifactsToUpdate = getArtifactsToUpdate({
|
const artifactsToUpdate = getArtifactsToUpdate({
|
||||||
orderedCommand,
|
artifactCommand,
|
||||||
responseMap,
|
responseMap,
|
||||||
getArtifact: (id: ArtifactId) => myMap.get(id),
|
getArtifact: (id: ArtifactId) => myMap.get(id),
|
||||||
currentPlaneId,
|
currentPlaneId,
|
||||||
ast,
|
ast,
|
||||||
|
execStateArtifacts,
|
||||||
})
|
})
|
||||||
artifactsToUpdate.forEach(({ id, artifact }) => {
|
artifactsToUpdate.forEach(({ id, artifact }) => {
|
||||||
const mergedArtifact = mergeArtifacts(myMap.get(id), artifact)
|
const mergedArtifact = mergeArtifacts(myMap.get(id), artifact)
|
||||||
myMap.set(id, mergedArtifact)
|
myMap.set(id, mergedArtifact)
|
||||||
})
|
})
|
||||||
})
|
}
|
||||||
return myMap
|
return myMap
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -227,30 +232,30 @@ function mergeArtifacts(
|
|||||||
* can remove this.
|
* can remove this.
|
||||||
*/
|
*/
|
||||||
export function getArtifactsToUpdate({
|
export function getArtifactsToUpdate({
|
||||||
orderedCommand: { command, range },
|
artifactCommand,
|
||||||
getArtifact,
|
getArtifact,
|
||||||
responseMap,
|
responseMap,
|
||||||
currentPlaneId,
|
currentPlaneId,
|
||||||
ast,
|
ast,
|
||||||
|
execStateArtifacts,
|
||||||
}: {
|
}: {
|
||||||
orderedCommand: OrderedCommand
|
artifactCommand: ArtifactCommand
|
||||||
responseMap: ResponseMap
|
responseMap: ResponseMap
|
||||||
/** Passing in a getter because we don't wan this function to update the map directly */
|
/** Passing in a getter because we don't wan this function to update the map directly */
|
||||||
getArtifact: (id: ArtifactId) => Artifact | undefined
|
getArtifact: (id: ArtifactId) => Artifact | undefined
|
||||||
currentPlaneId: ArtifactId
|
currentPlaneId: ArtifactId
|
||||||
ast: Program
|
ast: Node<Program>
|
||||||
|
execStateArtifacts: ExecState['artifacts']
|
||||||
}): Array<{
|
}): Array<{
|
||||||
id: ArtifactId
|
id: ArtifactId
|
||||||
artifact: Artifact
|
artifact: Artifact
|
||||||
}> {
|
}> {
|
||||||
|
const range = sourceRangeFromRust(artifactCommand.range)
|
||||||
const pathToNode = getNodePathFromSourceRange(ast, range)
|
const pathToNode = getNodePathFromSourceRange(ast, range)
|
||||||
|
|
||||||
// expect all to be `modeling_cmd_req` as batch commands have
|
const id = artifactCommand.cmdId
|
||||||
// already been expanded before being added to orderedCommands
|
|
||||||
if (command.type !== 'modeling_cmd_req') return []
|
|
||||||
const id = command.cmd_id
|
|
||||||
const response = responseMap[id]
|
const response = responseMap[id]
|
||||||
const cmd = command.cmd
|
const cmd = artifactCommand.command
|
||||||
const returnArr: ReturnType<typeof getArtifactsToUpdate> = []
|
const returnArr: ReturnType<typeof getArtifactsToUpdate> = []
|
||||||
if (!response) return returnArr
|
if (!response) return returnArr
|
||||||
if (cmd.type === 'make_plane' && range[1] !== 0) {
|
if (cmd.type === 'make_plane' && range[1] !== 0) {
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
import {
|
import {
|
||||||
|
ArtifactCommand,
|
||||||
defaultRustSourceRange,
|
defaultRustSourceRange,
|
||||||
defaultSourceRange,
|
ExecState,
|
||||||
Program,
|
Program,
|
||||||
RustSourceRange,
|
RustSourceRange,
|
||||||
SourceRange,
|
SourceRange,
|
||||||
sourceRangeFromRust,
|
|
||||||
} from 'lang/wasm'
|
} from 'lang/wasm'
|
||||||
import { VITE_KC_API_WS_MODELING_URL, VITE_KC_DEV_TOKEN } from 'env'
|
import { VITE_KC_API_WS_MODELING_URL, VITE_KC_DEV_TOKEN } from 'env'
|
||||||
import { Models } from '@kittycad/lib'
|
import { Models } from '@kittycad/lib'
|
||||||
@ -20,7 +20,6 @@ import { DefaultPlanes } from 'wasm-lib/kcl/bindings/DefaultPlanes'
|
|||||||
import {
|
import {
|
||||||
ArtifactGraph,
|
ArtifactGraph,
|
||||||
EngineCommand,
|
EngineCommand,
|
||||||
OrderedCommand,
|
|
||||||
ResponseMap,
|
ResponseMap,
|
||||||
createArtifactGraph,
|
createArtifactGraph,
|
||||||
} from 'lang/std/artifactGraph'
|
} from 'lang/std/artifactGraph'
|
||||||
@ -37,6 +36,7 @@ import { KclManager } from 'lang/KclSingleton'
|
|||||||
import { reportRejection } from 'lib/trap'
|
import { reportRejection } from 'lib/trap'
|
||||||
import { markOnce } from 'lib/performance'
|
import { markOnce } from 'lib/performance'
|
||||||
import { MachineManager } from 'components/MachineManagerProvider'
|
import { MachineManager } from 'components/MachineManagerProvider'
|
||||||
|
import { Node } from 'wasm-lib/kcl/bindings/Node'
|
||||||
|
|
||||||
// TODO(paultag): This ought to be tweakable.
|
// TODO(paultag): This ought to be tweakable.
|
||||||
const pingIntervalMs = 5_000
|
const pingIntervalMs = 5_000
|
||||||
@ -1303,7 +1303,7 @@ export enum EngineCommandManagerEvents {
|
|||||||
*
|
*
|
||||||
* As commands are send their state is tracked in {@link pendingCommands} and clear as soon as we receive a response.
|
* As commands are send their state is tracked in {@link pendingCommands} and clear as soon as we receive a response.
|
||||||
*
|
*
|
||||||
* Also all commands that are sent are kept track of in {@link orderedCommands} and their responses are kept in {@link responseMap}
|
* Also all commands that are sent are kept track of in WASM artifactCommands and their responses are kept in {@link responseMap}
|
||||||
* Both of these data structures are used to process the {@link artifactGraph}.
|
* Both of these data structures are used to process the {@link artifactGraph}.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@ -1329,12 +1329,7 @@ export class EngineCommandManager extends EventTarget {
|
|||||||
[commandId: string]: PendingMessage
|
[commandId: string]: PendingMessage
|
||||||
} = {}
|
} = {}
|
||||||
/**
|
/**
|
||||||
* The orderedCommands array of all the the commands sent to the engine, un-folded from batches, and made into one long
|
* A map of the responses to the WASM artifactCommands, when processing the commands into the artifactGraph, this response map allow
|
||||||
* list of the individual commands, this is used to process all the commands into the artifactGraph
|
|
||||||
*/
|
|
||||||
orderedCommands: Array<OrderedCommand> = []
|
|
||||||
/**
|
|
||||||
* A map of the responses to the {@link orderedCommands}, when processing the commands into the artifactGraph, this response map allow
|
|
||||||
* us to look up the response by command id
|
* us to look up the response by command id
|
||||||
*/
|
*/
|
||||||
responseMap: ResponseMap = {}
|
responseMap: ResponseMap = {}
|
||||||
@ -1830,7 +1825,6 @@ export class EngineCommandManager extends EventTarget {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
async startNewSession() {
|
async startNewSession() {
|
||||||
this.orderedCommands = []
|
|
||||||
this.responseMap = {}
|
this.responseMap = {}
|
||||||
await this.initPlanes()
|
await this.initPlanes()
|
||||||
}
|
}
|
||||||
@ -2073,28 +2067,6 @@ export class EngineCommandManager extends EventTarget {
|
|||||||
isSceneCommand,
|
isSceneCommand,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (message.command.type === 'modeling_cmd_req') {
|
|
||||||
this.orderedCommands.push({
|
|
||||||
command: message.command,
|
|
||||||
range: sourceRangeFromRust(message.range),
|
|
||||||
})
|
|
||||||
} else if (message.command.type === 'modeling_cmd_batch_req') {
|
|
||||||
message.command.requests.forEach((req) => {
|
|
||||||
const cmdId = req.cmd_id || ''
|
|
||||||
const range = cmdId
|
|
||||||
? sourceRangeFromRust(message.idToRangeMap[cmdId])
|
|
||||||
: defaultSourceRange()
|
|
||||||
const cmd: EngineCommand = {
|
|
||||||
type: 'modeling_cmd_req',
|
|
||||||
cmd_id: req.cmd_id,
|
|
||||||
cmd: req.cmd,
|
|
||||||
}
|
|
||||||
this.orderedCommands.push({
|
|
||||||
command: cmd,
|
|
||||||
range,
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
this.engineConnection?.send(message.command)
|
this.engineConnection?.send(message.command)
|
||||||
return promise
|
return promise
|
||||||
}
|
}
|
||||||
@ -2115,11 +2087,16 @@ export class EngineCommandManager extends EventTarget {
|
|||||||
Object.values(this.pendingCommands).map((a) => a.promise)
|
Object.values(this.pendingCommands).map((a) => a.promise)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
updateArtifactGraph(ast: Program) {
|
updateArtifactGraph(
|
||||||
|
ast: Node<Program>,
|
||||||
|
artifactCommands: ArtifactCommand[],
|
||||||
|
execStateArtifacts: ExecState['artifacts']
|
||||||
|
) {
|
||||||
this.artifactGraph = createArtifactGraph({
|
this.artifactGraph = createArtifactGraph({
|
||||||
orderedCommands: this.orderedCommands,
|
artifactCommands,
|
||||||
responseMap: this.responseMap,
|
responseMap: this.responseMap,
|
||||||
ast,
|
ast,
|
||||||
|
execStateArtifacts,
|
||||||
})
|
})
|
||||||
// TODO check if these still need to be deferred once e2e tests are working again.
|
// TODO check if these still need to be deferred once e2e tests are working again.
|
||||||
if (this.artifactGraph.size) {
|
if (this.artifactGraph.size) {
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import init, {
|
import {
|
||||||
|
init,
|
||||||
parse_wasm,
|
parse_wasm,
|
||||||
recast_wasm,
|
recast_wasm,
|
||||||
execute,
|
execute,
|
||||||
@ -16,7 +17,9 @@ import init, {
|
|||||||
default_project_settings,
|
default_project_settings,
|
||||||
base64_decode,
|
base64_decode,
|
||||||
clear_scene_and_bust_cache,
|
clear_scene_and_bust_cache,
|
||||||
} from '../wasm-lib/pkg/wasm_lib'
|
reloadModule,
|
||||||
|
} from 'lib/wasm_lib_wrapper'
|
||||||
|
|
||||||
import { KCLError } from './errors'
|
import { KCLError } from './errors'
|
||||||
import { KclError as RustKclError } from '../wasm-lib/kcl/bindings/KclError'
|
import { KclError as RustKclError } from '../wasm-lib/kcl/bindings/KclError'
|
||||||
import { EngineCommandManager } from './std/engineConnection'
|
import { EngineCommandManager } from './std/engineConnection'
|
||||||
@ -45,7 +48,13 @@ import { SourceRange as RustSourceRange } from 'wasm-lib/kcl/bindings/SourceRang
|
|||||||
import { getAllCurrentSettings } from 'lib/settings/settingsUtils'
|
import { getAllCurrentSettings } from 'lib/settings/settingsUtils'
|
||||||
import { Operation } from 'wasm-lib/kcl/bindings/Operation'
|
import { Operation } from 'wasm-lib/kcl/bindings/Operation'
|
||||||
import { KclErrorWithOutputs } from 'wasm-lib/kcl/bindings/KclErrorWithOutputs'
|
import { KclErrorWithOutputs } from 'wasm-lib/kcl/bindings/KclErrorWithOutputs'
|
||||||
|
import { Artifact } from 'wasm-lib/kcl/bindings/Artifact'
|
||||||
|
import { ArtifactId } from 'wasm-lib/kcl/bindings/ArtifactId'
|
||||||
|
import { ArtifactCommand } from 'wasm-lib/kcl/bindings/ArtifactCommand'
|
||||||
|
|
||||||
|
export type { Artifact } from 'wasm-lib/kcl/bindings/Artifact'
|
||||||
|
export type { ArtifactCommand } from 'wasm-lib/kcl/bindings/ArtifactCommand'
|
||||||
|
export type { ArtifactId } from 'wasm-lib/kcl/bindings/ArtifactId'
|
||||||
export type { Configuration } from 'wasm-lib/kcl/bindings/Configuration'
|
export type { Configuration } from 'wasm-lib/kcl/bindings/Configuration'
|
||||||
export type { Program } from '../wasm-lib/kcl/bindings/Program'
|
export type { Program } from '../wasm-lib/kcl/bindings/Program'
|
||||||
export type { Expr } from '../wasm-lib/kcl/bindings/Expr'
|
export type { Expr } from '../wasm-lib/kcl/bindings/Expr'
|
||||||
@ -144,6 +153,7 @@ export const wasmUrl = () => {
|
|||||||
// Initialise the wasm module.
|
// Initialise the wasm module.
|
||||||
const initialise = async () => {
|
const initialise = async () => {
|
||||||
try {
|
try {
|
||||||
|
await reloadModule()
|
||||||
const fullUrl = wasmUrl()
|
const fullUrl = wasmUrl()
|
||||||
const input = await fetch(fullUrl)
|
const input = await fetch(fullUrl)
|
||||||
const buffer = await input.arrayBuffer()
|
const buffer = await input.arrayBuffer()
|
||||||
@ -223,6 +233,7 @@ export const parse = (code: string | Error): ParseResult | Error => {
|
|||||||
parsed.kind,
|
parsed.kind,
|
||||||
parsed.msg,
|
parsed.msg,
|
||||||
sourceRangeFromRust(parsed.sourceRanges[0]),
|
sourceRangeFromRust(parsed.sourceRanges[0]),
|
||||||
|
[],
|
||||||
[]
|
[]
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -247,6 +258,8 @@ export const isPathToNodeNumber = (
|
|||||||
export interface ExecState {
|
export interface ExecState {
|
||||||
memory: ProgramMemory
|
memory: ProgramMemory
|
||||||
operations: Operation[]
|
operations: Operation[]
|
||||||
|
artifacts: { [key in ArtifactId]?: Artifact }
|
||||||
|
artifactCommands: ArtifactCommand[]
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -257,6 +270,8 @@ export function emptyExecState(): ExecState {
|
|||||||
return {
|
return {
|
||||||
memory: ProgramMemory.empty(),
|
memory: ProgramMemory.empty(),
|
||||||
operations: [],
|
operations: [],
|
||||||
|
artifacts: {},
|
||||||
|
artifactCommands: [],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -264,6 +279,8 @@ function execStateFromRust(execOutcome: RustExecOutcome): ExecState {
|
|||||||
return {
|
return {
|
||||||
memory: ProgramMemory.fromRaw(execOutcome.memory),
|
memory: ProgramMemory.fromRaw(execOutcome.memory),
|
||||||
operations: execOutcome.operations,
|
operations: execOutcome.operations,
|
||||||
|
artifacts: execOutcome.artifacts,
|
||||||
|
artifactCommands: execOutcome.artifactCommands,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -506,22 +523,6 @@ export const executor = async (
|
|||||||
return Promise.reject(programMemoryOverride)
|
return Promise.reject(programMemoryOverride)
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||||
engineCommandManager.startNewSession()
|
|
||||||
const _programMemory = await _executor(
|
|
||||||
node,
|
|
||||||
engineCommandManager,
|
|
||||||
programMemoryOverride
|
|
||||||
)
|
|
||||||
await engineCommandManager.waitForAllCommands()
|
|
||||||
|
|
||||||
return _programMemory
|
|
||||||
}
|
|
||||||
|
|
||||||
export const _executor = async (
|
|
||||||
node: Node<Program>,
|
|
||||||
engineCommandManager: EngineCommandManager,
|
|
||||||
programMemoryOverride: ProgramMemory | Error | null = null
|
|
||||||
): Promise<ExecState> => {
|
|
||||||
if (programMemoryOverride !== null && err(programMemoryOverride))
|
if (programMemoryOverride !== null && err(programMemoryOverride))
|
||||||
return Promise.reject(programMemoryOverride)
|
return Promise.reject(programMemoryOverride)
|
||||||
|
|
||||||
@ -550,7 +551,8 @@ export const _executor = async (
|
|||||||
parsed.error.kind,
|
parsed.error.kind,
|
||||||
parsed.error.msg,
|
parsed.error.msg,
|
||||||
sourceRangeFromRust(parsed.error.sourceRanges[0]),
|
sourceRangeFromRust(parsed.error.sourceRanges[0]),
|
||||||
parsed.operations
|
parsed.operations,
|
||||||
|
parsed.artifactCommands
|
||||||
)
|
)
|
||||||
|
|
||||||
return Promise.reject(kclError)
|
return Promise.reject(kclError)
|
||||||
@ -610,6 +612,7 @@ export const modifyAstForSketch = async (
|
|||||||
parsed.kind,
|
parsed.kind,
|
||||||
parsed.msg,
|
parsed.msg,
|
||||||
sourceRangeFromRust(parsed.sourceRanges[0]),
|
sourceRangeFromRust(parsed.sourceRanges[0]),
|
||||||
|
[],
|
||||||
[]
|
[]
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -679,6 +682,7 @@ export function programMemoryInit(): ProgramMemory | Error {
|
|||||||
parsed.kind,
|
parsed.kind,
|
||||||
parsed.msg,
|
parsed.msg,
|
||||||
sourceRangeFromRust(parsed.sourceRanges[0]),
|
sourceRangeFromRust(parsed.sourceRanges[0]),
|
||||||
|
[],
|
||||||
[]
|
[]
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
51
src/lib/exceptions.ts
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
import { kclManager } from 'lib/singletons'
|
||||||
|
import { reloadModule, getModule } from 'lib/wasm_lib_wrapper'
|
||||||
|
import toast from 'react-hot-toast'
|
||||||
|
import { reportRejection } from './trap'
|
||||||
|
|
||||||
|
let initialized = false
|
||||||
|
|
||||||
|
/**
|
||||||
|
* WASM/Rust runtime can panic and the original try/catch/finally blocks will not trigger
|
||||||
|
* on the await promise. The interface will killed. This means we need to catch the error at
|
||||||
|
* the global/DOM level. This will have to interface with whatever controlflow that needs to be picked up
|
||||||
|
* within the error branch in the typescript to cover the application state.
|
||||||
|
*/
|
||||||
|
export const initializeWindowExceptionHandler = () => {
|
||||||
|
if (window && !initialized) {
|
||||||
|
window.addEventListener('error', (event) => {
|
||||||
|
void (async () => {
|
||||||
|
if (matchImportExportErrorCrash(event.message)) {
|
||||||
|
// do global singleton cleanup
|
||||||
|
kclManager.executeAstCleanUp()
|
||||||
|
toast.error(
|
||||||
|
'You have hit a KCL execution bug! Put your KCL code in a github issue to help us resolve this bug.'
|
||||||
|
)
|
||||||
|
try {
|
||||||
|
await reloadModule()
|
||||||
|
await getModule().default()
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Failed to initialize wasm_lib')
|
||||||
|
console.error(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})().catch(reportRejection)
|
||||||
|
})
|
||||||
|
// Make sure we only initialize this event listener once
|
||||||
|
initialized = true
|
||||||
|
} else {
|
||||||
|
console.error(
|
||||||
|
`Failed to initialize, window: ${window}, initialized:${initialized}`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specifically match a substring of the message error to detect an import export runtime issue
|
||||||
|
* when the WASM runtime panics
|
||||||
|
*/
|
||||||
|
const matchImportExportErrorCrash = (message: string): boolean => {
|
||||||
|
// called `Result::unwrap_throw()` on an `Err` value
|
||||||
|
const substringError = '`Result::unwrap_throw()` on an `Err` value'
|
||||||
|
return message.indexOf(substringError) !== -1 ? true : false
|
||||||
|
}
|
2
src/lib/machine-api.d.ts
vendored
@ -155,7 +155,7 @@ export interface components {
|
|||||||
color?: string | null
|
color?: string | null
|
||||||
/** @description The material that the filament is made of. */
|
/** @description The material that the filament is made of. */
|
||||||
material: components['schemas']['FilamentMaterial']
|
material: components['schemas']['FilamentMaterial']
|
||||||
/** @description The name of the filament, this is likely specfic to the manufacturer. */
|
/** @description The name of the filament, this is likely specific to the manufacturer. */
|
||||||
name?: string | null
|
name?: string | null
|
||||||
}
|
}
|
||||||
/** @description The material that the filament is made of. */
|
/** @description The material that the filament is made of. */
|
||||||
|
52
src/lib/markdown.ts
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
import { MarkedOptions, Renderer, unescape } from '@ts-stack/markdown'
|
||||||
|
import { openExternalBrowserIfDesktop } from './openWindow'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Main goal of this custom renderer is to prevent links from changing the current location
|
||||||
|
* this is specially important for the desktop app.
|
||||||
|
*/
|
||||||
|
export class SafeRenderer extends Renderer {
|
||||||
|
constructor(options: MarkedOptions) {
|
||||||
|
super(options)
|
||||||
|
|
||||||
|
// Attach a global function for non-react anchor elements that need safe navigation
|
||||||
|
window.openExternalLink = (e: React.MouseEvent<HTMLAnchorElement>) => {
|
||||||
|
openExternalBrowserIfDesktop()(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extended from https://github.com/ts-stack/markdown/blob/c5c1925c1153ca2fe9051c356ef0ddc60b3e1d6a/packages/markdown/src/renderer.ts#L116
|
||||||
|
link(href: string, title: string, text: string): string {
|
||||||
|
if (this.options.sanitize) {
|
||||||
|
let prot: string
|
||||||
|
|
||||||
|
try {
|
||||||
|
prot = decodeURIComponent(unescape(href))
|
||||||
|
.replace(/[^\w:]/g, '')
|
||||||
|
.toLowerCase()
|
||||||
|
} catch (e) {
|
||||||
|
return text
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
// eslint-disable-next-line no-script-url
|
||||||
|
prot.indexOf('javascript:') === 0 ||
|
||||||
|
prot.indexOf('vbscript:') === 0 ||
|
||||||
|
prot.indexOf('data:') === 0
|
||||||
|
) {
|
||||||
|
return text
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let out =
|
||||||
|
'<a onclick="openExternalLink(event)" target="_blank" href="' + href + '"'
|
||||||
|
|
||||||
|
if (title) {
|
||||||
|
out += ' title="' + title + '"'
|
||||||
|
}
|
||||||
|
|
||||||
|
out += '>' + text + '</a>'
|
||||||
|
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
}
|
@ -1,20 +1,16 @@
|
|||||||
import {
|
import {
|
||||||
Program,
|
Program,
|
||||||
ProgramMemory,
|
ProgramMemory,
|
||||||
_executor,
|
executor,
|
||||||
SourceRange,
|
SourceRange,
|
||||||
ExecState,
|
ExecState,
|
||||||
} from '../lang/wasm'
|
} from '../lang/wasm'
|
||||||
import {
|
import { EngineCommandManager } from 'lang/std/engineConnection'
|
||||||
EngineCommandManager,
|
|
||||||
EngineCommandManagerEvents,
|
|
||||||
} from 'lang/std/engineConnection'
|
|
||||||
import { EngineCommand } from 'lang/std/artifactGraph'
|
import { EngineCommand } from 'lang/std/artifactGraph'
|
||||||
import { Models } from '@kittycad/lib'
|
import { Models } from '@kittycad/lib'
|
||||||
import { v4 as uuidv4 } from 'uuid'
|
import { v4 as uuidv4 } from 'uuid'
|
||||||
import { DefaultPlanes } from 'wasm-lib/kcl/bindings/DefaultPlanes'
|
import { DefaultPlanes } from 'wasm-lib/kcl/bindings/DefaultPlanes'
|
||||||
import { err, reportRejection } from 'lib/trap'
|
import { err } from 'lib/trap'
|
||||||
import { toSync } from './utils'
|
|
||||||
import { Node } from 'wasm-lib/kcl/bindings/Node'
|
import { Node } from 'wasm-lib/kcl/bindings/Node'
|
||||||
|
|
||||||
type WebSocketResponse = Models['WebSocketResponse_type']
|
type WebSocketResponse = Models['WebSocketResponse_type']
|
||||||
@ -94,36 +90,7 @@ export async function enginelessExecutor(
|
|||||||
}) as any as EngineCommandManager
|
}) as any as EngineCommandManager
|
||||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||||
mockEngineCommandManager.startNewSession()
|
mockEngineCommandManager.startNewSession()
|
||||||
const execState = await _executor(ast, mockEngineCommandManager, pmo)
|
const execState = await executor(ast, mockEngineCommandManager, pmo)
|
||||||
await mockEngineCommandManager.waitForAllCommands()
|
await mockEngineCommandManager.waitForAllCommands()
|
||||||
return execState
|
return execState
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function executor(
|
|
||||||
ast: Node<Program>,
|
|
||||||
pmo: ProgramMemory = ProgramMemory.empty()
|
|
||||||
): Promise<ExecState> {
|
|
||||||
const engineCommandManager = new EngineCommandManager()
|
|
||||||
engineCommandManager.start({
|
|
||||||
setIsStreamReady: () => {},
|
|
||||||
setMediaStream: () => {},
|
|
||||||
width: 0,
|
|
||||||
height: 0,
|
|
||||||
makeDefaultPlanes: () => {
|
|
||||||
return new Promise((resolve) => resolve(defaultPlanes))
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
return new Promise((resolve) => {
|
|
||||||
engineCommandManager.addEventListener(
|
|
||||||
EngineCommandManagerEvents.SceneReady,
|
|
||||||
toSync(async () => {
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
||||||
engineCommandManager.startNewSession()
|
|
||||||
const execState = await _executor(ast, engineCommandManager, pmo)
|
|
||||||
await engineCommandManager.waitForAllCommands()
|
|
||||||
resolve(execState)
|
|
||||||
}, reportRejection)
|
|
||||||
)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
@ -274,6 +274,35 @@ export const toolbarConfig: Record<ToolbarModeName, ToolbarMode> = {
|
|||||||
links: [],
|
links: [],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
'break',
|
||||||
|
[
|
||||||
|
{
|
||||||
|
id: 'text-to-cad',
|
||||||
|
onClick: ({ commandBarSend }) =>
|
||||||
|
commandBarSend({
|
||||||
|
type: 'Find and select command',
|
||||||
|
data: { name: 'Text-to-CAD', groupId: 'modeling' },
|
||||||
|
}),
|
||||||
|
icon: 'sparkles',
|
||||||
|
status: 'available',
|
||||||
|
title: 'Text-to-CAD',
|
||||||
|
description: 'Generate geometry from a text prompt.',
|
||||||
|
links: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'prompt-to-edit',
|
||||||
|
onClick: ({ commandBarSend }) =>
|
||||||
|
commandBarSend({
|
||||||
|
type: 'Find and select command',
|
||||||
|
data: { name: 'Prompt-to-edit', groupId: 'modeling' },
|
||||||
|
}),
|
||||||
|
icon: 'sparkles',
|
||||||
|
status: 'available',
|
||||||
|
title: 'Prompt-to-Edit',
|
||||||
|
description: 'Edit geometry based on a text prompt.',
|
||||||
|
links: [],
|
||||||
|
},
|
||||||
|
],
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
sketching: {
|
sketching: {
|
||||||
|
@ -153,7 +153,10 @@ export function toSync<F extends AsyncFn<F>>(
|
|||||||
) => void | PromiseLike<void | null | undefined> | null | undefined
|
) => void | PromiseLike<void | null | undefined> | null | undefined
|
||||||
): (...args: Parameters<F>) => void {
|
): (...args: Parameters<F>) => void {
|
||||||
return (...args: Parameters<F>) => {
|
return (...args: Parameters<F>) => {
|
||||||
fn(...args).catch(onReject)
|
void fn(...args).catch((...args) => {
|
||||||
|
console.error(...args)
|
||||||
|
return onReject(...args)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
108
src/lib/wasm_lib_wrapper.ts
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
/**
|
||||||
|
* This wrapper file is to enable reloading of the wasm_lib.js file.
|
||||||
|
* When the wasm instance bricks there is no API or interface to restart,
|
||||||
|
* restore, or re init the WebAssembly instance. The entire application would need
|
||||||
|
* to restart.
|
||||||
|
* A way to bypass this is by reloading the entire .js file so the global wasm variable
|
||||||
|
* gets reinitialized and we do not use that old reference
|
||||||
|
*/
|
||||||
|
|
||||||
|
import {
|
||||||
|
parse_wasm as ParseWasm,
|
||||||
|
recast_wasm as RecastWasm,
|
||||||
|
execute as Execute,
|
||||||
|
kcl_lint as KclLint,
|
||||||
|
modify_ast_for_sketch_wasm as ModifyAstForSketch,
|
||||||
|
is_points_ccw as IsPointsCcw,
|
||||||
|
get_tangential_arc_to_info as GetTangentialArcToInfo,
|
||||||
|
program_memory_init as ProgramMemoryInit,
|
||||||
|
make_default_planes as MakeDefaultPlanes,
|
||||||
|
coredump as CoreDump,
|
||||||
|
toml_stringify as TomlStringify,
|
||||||
|
default_app_settings as DefaultAppSettings,
|
||||||
|
parse_app_settings as ParseAppSettings,
|
||||||
|
parse_project_settings as ParseProjectSettings,
|
||||||
|
default_project_settings as DefaultProjectSettings,
|
||||||
|
base64_decode as Base64Decode,
|
||||||
|
clear_scene_and_bust_cache as ClearSceneAndBustCache,
|
||||||
|
} from '../wasm-lib/pkg/wasm_lib'
|
||||||
|
|
||||||
|
type ModuleType = typeof import('../wasm-lib/pkg/wasm_lib')
|
||||||
|
|
||||||
|
// Stores the result of the import of the wasm_lib file
|
||||||
|
let data: ModuleType
|
||||||
|
|
||||||
|
// Imports the .js file again which will clear the old import
|
||||||
|
// This allows us to reinitialize the wasm instance
|
||||||
|
export async function reloadModule() {
|
||||||
|
data = await import(`../wasm-lib/pkg/wasm_lib`)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getModule(): ModuleType {
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function init(module_or_path: any) {
|
||||||
|
return await getModule().default(module_or_path)
|
||||||
|
}
|
||||||
|
export const parse_wasm: typeof ParseWasm = (...args) => {
|
||||||
|
return getModule().parse_wasm(...args)
|
||||||
|
}
|
||||||
|
export const recast_wasm: typeof RecastWasm = (...args) => {
|
||||||
|
return getModule().recast_wasm(...args)
|
||||||
|
}
|
||||||
|
export const execute: typeof Execute = (...args) => {
|
||||||
|
return getModule().execute(...args)
|
||||||
|
}
|
||||||
|
export const kcl_lint: typeof KclLint = (...args) => {
|
||||||
|
return getModule().kcl_lint(...args)
|
||||||
|
}
|
||||||
|
export const modify_ast_for_sketch_wasm: typeof ModifyAstForSketch = (
|
||||||
|
...args
|
||||||
|
) => {
|
||||||
|
return getModule().modify_ast_for_sketch_wasm(...args)
|
||||||
|
}
|
||||||
|
export const is_points_ccw: typeof IsPointsCcw = (...args) => {
|
||||||
|
return getModule().is_points_ccw(...args)
|
||||||
|
}
|
||||||
|
export const get_tangential_arc_to_info: typeof GetTangentialArcToInfo = (
|
||||||
|
...args
|
||||||
|
) => {
|
||||||
|
return getModule().get_tangential_arc_to_info(...args)
|
||||||
|
}
|
||||||
|
export const program_memory_init: typeof ProgramMemoryInit = (...args) => {
|
||||||
|
return getModule().program_memory_init(...args)
|
||||||
|
}
|
||||||
|
export const make_default_planes: typeof MakeDefaultPlanes = (...args) => {
|
||||||
|
return getModule().make_default_planes(...args)
|
||||||
|
}
|
||||||
|
export const coredump: typeof CoreDump = (...args) => {
|
||||||
|
return getModule().coredump(...args)
|
||||||
|
}
|
||||||
|
export const toml_stringify: typeof TomlStringify = (...args) => {
|
||||||
|
return getModule().toml_stringify(...args)
|
||||||
|
}
|
||||||
|
export const default_app_settings: typeof DefaultAppSettings = (...args) => {
|
||||||
|
return getModule().default_app_settings(...args)
|
||||||
|
}
|
||||||
|
export const parse_app_settings: typeof ParseAppSettings = (...args) => {
|
||||||
|
return getModule().parse_app_settings(...args)
|
||||||
|
}
|
||||||
|
export const parse_project_settings: typeof ParseProjectSettings = (
|
||||||
|
...args
|
||||||
|
) => {
|
||||||
|
return getModule().parse_project_settings(...args)
|
||||||
|
}
|
||||||
|
export const default_project_settings: typeof DefaultProjectSettings = (
|
||||||
|
...args
|
||||||
|
) => {
|
||||||
|
return getModule().default_project_settings(...args)
|
||||||
|
}
|
||||||
|
export const base64_decode: typeof Base64Decode = (...args) => {
|
||||||
|
return getModule().base64_decode(...args)
|
||||||
|
}
|
||||||
|
export const clear_scene_and_bust_cache: typeof ClearSceneAndBustCache = (
|
||||||
|
...args
|
||||||
|
) => {
|
||||||
|
return getModule().clear_scene_and_bust_cache(...args)
|
||||||
|
}
|
@ -763,30 +763,6 @@ export const modelingMachine = setup({
|
|||||||
await codeManager.updateEditorWithAstAndWriteToFile(modifiedAst)
|
await codeManager.updateEditorWithAstAndWriteToFile(modifiedAst)
|
||||||
})().catch(reportRejection)
|
})().catch(reportRejection)
|
||||||
},
|
},
|
||||||
'AST fillet': ({ event }) => {
|
|
||||||
if (event.type !== 'Fillet') return
|
|
||||||
if (!event.data) return
|
|
||||||
|
|
||||||
// Extract inputs
|
|
||||||
const ast = kclManager.ast
|
|
||||||
const { selection, radius } = event.data
|
|
||||||
const parameters: FilletParameters = {
|
|
||||||
type: EdgeTreatmentType.Fillet,
|
|
||||||
radius,
|
|
||||||
}
|
|
||||||
|
|
||||||
// Apply fillet to selection
|
|
||||||
const applyEdgeTreatmentToSelectionResult = applyEdgeTreatmentToSelection(
|
|
||||||
ast,
|
|
||||||
selection,
|
|
||||||
parameters
|
|
||||||
)
|
|
||||||
if (err(applyEdgeTreatmentToSelectionResult))
|
|
||||||
return applyEdgeTreatmentToSelectionResult
|
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
||||||
codeManager.updateEditorWithAstAndWriteToFile(kclManager.ast)
|
|
||||||
},
|
|
||||||
'set selection filter to curves only': () => {
|
'set selection filter to curves only': () => {
|
||||||
;(async () => {
|
;(async () => {
|
||||||
await engineCommandManager.sendSceneCommand({
|
await engineCommandManager.sendSceneCommand({
|
||||||
@ -1670,6 +1646,33 @@ export const modelingMachine = setup({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
|
filletAstMod: fromPromise(
|
||||||
|
async ({
|
||||||
|
input,
|
||||||
|
}: {
|
||||||
|
input: ModelingCommandSchema['Fillet'] | undefined
|
||||||
|
}) => {
|
||||||
|
if (!input) {
|
||||||
|
return new Error('No input provided')
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract inputs
|
||||||
|
const ast = kclManager.ast
|
||||||
|
const { selection, radius } = input
|
||||||
|
const parameters: FilletParameters = {
|
||||||
|
type: EdgeTreatmentType.Fillet,
|
||||||
|
radius,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply fillet to selection
|
||||||
|
const filletResult = await applyEdgeTreatmentToSelection(
|
||||||
|
ast,
|
||||||
|
selection,
|
||||||
|
parameters
|
||||||
|
)
|
||||||
|
if (err(filletResult)) return filletResult
|
||||||
|
}
|
||||||
|
),
|
||||||
'submit-prompt-edit': fromPromise(
|
'submit-prompt-edit': fromPromise(
|
||||||
async ({ input }: { input: ModelingCommandSchema['Prompt-to-edit'] }) => {
|
async ({ input }: { input: ModelingCommandSchema['Prompt-to-edit'] }) => {
|
||||||
console.log('doing thing', input)
|
console.log('doing thing', input)
|
||||||
@ -1745,9 +1748,8 @@ export const modelingMachine = setup({
|
|||||||
},
|
},
|
||||||
|
|
||||||
Fillet: {
|
Fillet: {
|
||||||
target: 'idle',
|
target: 'Applying fillet',
|
||||||
actions: ['AST fillet'],
|
reenter: true,
|
||||||
reenter: false,
|
|
||||||
},
|
},
|
||||||
|
|
||||||
Export: {
|
Export: {
|
||||||
@ -2553,6 +2555,19 @@ export const modelingMachine = setup({
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
'Applying fillet': {
|
||||||
|
invoke: {
|
||||||
|
src: 'filletAstMod',
|
||||||
|
id: 'filletAstMod',
|
||||||
|
input: ({ event }) => {
|
||||||
|
if (event.type !== 'Fillet') return undefined
|
||||||
|
return event.data
|
||||||
|
},
|
||||||
|
onDone: ['idle'],
|
||||||
|
onError: ['idle'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
'Applying Prompt-to-edit': {
|
'Applying Prompt-to-edit': {
|
||||||
invoke: {
|
invoke: {
|
||||||
src: 'submit-prompt-edit',
|
src: 'submit-prompt-edit',
|
||||||
|
49
src/wasm-lib/Cargo.lock
generated
@ -181,9 +181,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "async-trait"
|
name = "async-trait"
|
||||||
version = "0.1.83"
|
version = "0.1.85"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "721cae7de5c34fbb2acd27e21e6d2cf7b886dce0c27388d46c4e6c47ea4318dd"
|
checksum = "3f934833b4b7233644e5848f235df3f57ed8c80f1528a26c3dfa13d2147fa056"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@ -1819,9 +1819,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "kittycad-modeling-cmds"
|
name = "kittycad-modeling-cmds"
|
||||||
version = "0.2.79"
|
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 = "10a9cab4476455be70ea57643c31444068b056d091bd348cab6044c0d8ad7fcc"
|
checksum = "65e34a8eeb4fff5167666d1f2bc36c95d08ab3a0f736a02c8d33a8cde21cfd8d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"chrono",
|
"chrono",
|
||||||
@ -1839,6 +1839,7 @@ dependencies = [
|
|||||||
"serde",
|
"serde",
|
||||||
"serde_bytes",
|
"serde_bytes",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
|
"ts-rs",
|
||||||
"uuid",
|
"uuid",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -1856,9 +1857,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "kittycad-modeling-cmds-macros-impl"
|
name = "kittycad-modeling-cmds-macros-impl"
|
||||||
version = "0.1.12"
|
version = "0.1.13"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6607507a8a0e4273b943179f0a3ef8e90712308d1d3095246040c29cfdbf985b"
|
checksum = "fdb4ee23cc996aa2dca7584d410e8826e08161e1ac4335bb646d5ede33f37cb3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@ -2687,9 +2688,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "quote"
|
name = "quote"
|
||||||
version = "1.0.37"
|
version = "1.0.38"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af"
|
checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
]
|
]
|
||||||
@ -3200,9 +3201,9 @@ checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde"
|
name = "serde"
|
||||||
version = "1.0.216"
|
version = "1.0.217"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0b9781016e935a97e8beecf0c933758c97a5520d32930e460142b4cd80c6338e"
|
checksum = "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde_derive",
|
"serde_derive",
|
||||||
]
|
]
|
||||||
@ -3218,9 +3219,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_derive"
|
name = "serde_derive"
|
||||||
version = "1.0.216"
|
version = "1.0.217"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "46f859dbbf73865c6627ed570e78961cd3ac92407a2d117204c49232485da55e"
|
checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@ -3240,9 +3241,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_json"
|
name = "serde_json"
|
||||||
version = "1.0.133"
|
version = "1.0.135"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c7fceb2473b9166b2294ef05efcb65a3db80803f0b03ef86a5fc88a2b85ee377"
|
checksum = "2b0d7ba2887406110130a978386c4e1befb98c674b4fba677954e4db976630d9"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"indexmap 2.7.0",
|
"indexmap 2.7.0",
|
||||||
"itoa",
|
"itoa",
|
||||||
@ -3993,15 +3994,15 @@ checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ts-rs"
|
name = "ts-rs"
|
||||||
version = "10.0.0"
|
version = "10.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3a2f31991cee3dce1ca4f929a8a04fdd11fd8801aac0f2030b0fa8a0a3fef6b9"
|
checksum = "e640d9b0964e9d39df633548591090ab92f7a4567bc31d3891af23471a3365c6"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"chrono",
|
"chrono",
|
||||||
"indexmap 2.7.0",
|
"indexmap 2.7.0",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"thiserror 1.0.68",
|
"thiserror 2.0.0",
|
||||||
"ts-rs-macros",
|
"ts-rs-macros",
|
||||||
"url",
|
"url",
|
||||||
"uuid",
|
"uuid",
|
||||||
@ -4009,9 +4010,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ts-rs-macros"
|
name = "ts-rs-macros"
|
||||||
version = "10.0.0"
|
version = "10.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0ea0b99e8ec44abd6f94a18f28f7934437809dd062820797c52401298116f70e"
|
checksum = "0e9d8656589772eeec2cf7a8264d9cda40fb28b9bc53118ceb9e8c07f8f38730"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@ -4609,9 +4610,9 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "winnow"
|
name = "winnow"
|
||||||
version = "0.6.20"
|
version = "0.6.22"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "36c1fec1a2bb5866f07c25f68c26e565c4c200aebb96d7e55710c19d3e8ac49b"
|
checksum = "39281189af81c07ec09db316b302a3e67bf9bd7cbf6c820b50e35fee9c2fa980"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"memchr",
|
"memchr",
|
||||||
]
|
]
|
||||||
@ -4748,9 +4749,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zip"
|
name = "zip"
|
||||||
version = "2.2.0"
|
version = "2.2.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "dc5e4288ea4057ae23afc69a4472434a87a2495cafce6632fd1c4ec9f5cf3494"
|
checksum = "ae9c1ea7b3a5e1f4b922ff856a129881167511563dc219869afe3787fc0c1a45"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"arbitrary",
|
"arbitrary",
|
||||||
"crc32fast",
|
"crc32fast",
|
||||||
@ -4758,5 +4759,5 @@ dependencies = [
|
|||||||
"displaydoc",
|
"displaydoc",
|
||||||
"indexmap 2.7.0",
|
"indexmap 2.7.0",
|
||||||
"memchr",
|
"memchr",
|
||||||
"thiserror 1.0.68",
|
"thiserror 2.0.0",
|
||||||
]
|
]
|
||||||
|
@ -16,7 +16,7 @@ gloo-utils = "0.2.0"
|
|||||||
kcl-lib = { path = "kcl" }
|
kcl-lib = { path = "kcl" }
|
||||||
kittycad.workspace = true
|
kittycad.workspace = true
|
||||||
lazy_static = "1.5.0"
|
lazy_static = "1.5.0"
|
||||||
serde_json = "1.0.128"
|
serde_json = "1.0.135"
|
||||||
tokio = { version = "1.41.1", features = ["sync"] }
|
tokio = { version = "1.41.1", features = ["sync"] }
|
||||||
toml = "0.8.19"
|
toml = "0.8.19"
|
||||||
uuid = { version = "1.11.0", features = ["v4", "js", "serde"] }
|
uuid = { version = "1.11.0", features = ["v4", "js", "serde"] }
|
||||||
@ -76,7 +76,10 @@ 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.79", features = ["websocket"] }
|
kittycad-modeling-cmds = { version = "0.2.86", features = [
|
||||||
|
"ts-rs",
|
||||||
|
"websocket",
|
||||||
|
] }
|
||||||
|
|
||||||
[workspace.lints.clippy]
|
[workspace.lints.clippy]
|
||||||
assertions_on_result_states = "warn"
|
assertions_on_result_states = "warn"
|
||||||
|
@ -18,7 +18,7 @@ once_cell = "1.20.2"
|
|||||||
proc-macro2 = "1"
|
proc-macro2 = "1"
|
||||||
quote = "1"
|
quote = "1"
|
||||||
regex = "1.11"
|
regex = "1.11"
|
||||||
serde = { version = "1.0.214", features = ["derive"] }
|
serde = { version = "1.0.217", features = ["derive"] }
|
||||||
serde_tokenstream = "0.2"
|
serde_tokenstream = "0.2"
|
||||||
syn = { version = "2.0.95", features = ["full"] }
|
syn = { version = "2.0.95", features = ["full"] }
|
||||||
|
|
||||||
|
@ -832,7 +832,7 @@ fn generate_code_block_test(fn_name: &str, code_block: &str, index: usize) -> pr
|
|||||||
let result = match crate::test_server::execute_and_snapshot(code, crate::settings::types::UnitLength::Mm, None).await {
|
let result = match crate::test_server::execute_and_snapshot(code, crate::settings::types::UnitLength::Mm, None).await {
|
||||||
Err(crate::errors::ExecError::Kcl(e)) => {
|
Err(crate::errors::ExecError::Kcl(e)) => {
|
||||||
return Err(miette::Report::new(crate::errors::Report {
|
return Err(miette::Report::new(crate::errors::Report {
|
||||||
error: e,
|
error: e.error,
|
||||||
filename: format!("{}{}", #fn_name, #index),
|
filename: format!("{}{}", #fn_name, #index),
|
||||||
kcl_source: #code_block.to_string(),
|
kcl_source: #code_block.to_string(),
|
||||||
}));
|
}));
|
||||||
|
@ -37,7 +37,7 @@ mod test_examples_someFn {
|
|||||||
{
|
{
|
||||||
Err(crate::errors::ExecError::Kcl(e)) => {
|
Err(crate::errors::ExecError::Kcl(e)) => {
|
||||||
return Err(miette::Report::new(crate::errors::Report {
|
return Err(miette::Report::new(crate::errors::Report {
|
||||||
error: e,
|
error: e.error,
|
||||||
filename: format!("{}{}", "someFn", 0usize),
|
filename: format!("{}{}", "someFn", 0usize),
|
||||||
kcl_source: "someFn()".to_string(),
|
kcl_source: "someFn()".to_string(),
|
||||||
}));
|
}));
|
||||||
|
@ -37,7 +37,7 @@ mod test_examples_someFn {
|
|||||||
{
|
{
|
||||||
Err(crate::errors::ExecError::Kcl(e)) => {
|
Err(crate::errors::ExecError::Kcl(e)) => {
|
||||||
return Err(miette::Report::new(crate::errors::Report {
|
return Err(miette::Report::new(crate::errors::Report {
|
||||||
error: e,
|
error: e.error,
|
||||||
filename: format!("{}{}", "someFn", 0usize),
|
filename: format!("{}{}", "someFn", 0usize),
|
||||||
kcl_source: "someFn()".to_string(),
|
kcl_source: "someFn()".to_string(),
|
||||||
}));
|
}));
|
||||||
|
@ -38,7 +38,7 @@ mod test_examples_show {
|
|||||||
{
|
{
|
||||||
Err(crate::errors::ExecError::Kcl(e)) => {
|
Err(crate::errors::ExecError::Kcl(e)) => {
|
||||||
return Err(miette::Report::new(crate::errors::Report {
|
return Err(miette::Report::new(crate::errors::Report {
|
||||||
error: e,
|
error: e.error,
|
||||||
filename: format!("{}{}", "show", 0usize),
|
filename: format!("{}{}", "show", 0usize),
|
||||||
kcl_source: "This is another code block.\nyes sirrr.\nshow".to_string(),
|
kcl_source: "This is another code block.\nyes sirrr.\nshow".to_string(),
|
||||||
}));
|
}));
|
||||||
@ -92,7 +92,7 @@ mod test_examples_show {
|
|||||||
{
|
{
|
||||||
Err(crate::errors::ExecError::Kcl(e)) => {
|
Err(crate::errors::ExecError::Kcl(e)) => {
|
||||||
return Err(miette::Report::new(crate::errors::Report {
|
return Err(miette::Report::new(crate::errors::Report {
|
||||||
error: e,
|
error: e.error,
|
||||||
filename: format!("{}{}", "show", 1usize),
|
filename: format!("{}{}", "show", 1usize),
|
||||||
kcl_source: "This is code.\nIt does other shit.\nshow".to_string(),
|
kcl_source: "This is code.\nIt does other shit.\nshow".to_string(),
|
||||||
}));
|
}));
|
||||||
|
@ -38,7 +38,7 @@ mod test_examples_show {
|
|||||||
{
|
{
|
||||||
Err(crate::errors::ExecError::Kcl(e)) => {
|
Err(crate::errors::ExecError::Kcl(e)) => {
|
||||||
return Err(miette::Report::new(crate::errors::Report {
|
return Err(miette::Report::new(crate::errors::Report {
|
||||||
error: e,
|
error: e.error,
|
||||||
filename: format!("{}{}", "show", 0usize),
|
filename: format!("{}{}", "show", 0usize),
|
||||||
kcl_source: "This is code.\nIt does other shit.\nshow".to_string(),
|
kcl_source: "This is code.\nIt does other shit.\nshow".to_string(),
|
||||||
}));
|
}));
|
||||||
|
@ -39,7 +39,7 @@ mod test_examples_my_func {
|
|||||||
{
|
{
|
||||||
Err(crate::errors::ExecError::Kcl(e)) => {
|
Err(crate::errors::ExecError::Kcl(e)) => {
|
||||||
return Err(miette::Report::new(crate::errors::Report {
|
return Err(miette::Report::new(crate::errors::Report {
|
||||||
error: e,
|
error: e.error,
|
||||||
filename: format!("{}{}", "my_func", 0usize),
|
filename: format!("{}{}", "my_func", 0usize),
|
||||||
kcl_source: "This is another code block.\nyes sirrr.\nmyFunc".to_string(),
|
kcl_source: "This is another code block.\nyes sirrr.\nmyFunc".to_string(),
|
||||||
}));
|
}));
|
||||||
@ -93,7 +93,7 @@ mod test_examples_my_func {
|
|||||||
{
|
{
|
||||||
Err(crate::errors::ExecError::Kcl(e)) => {
|
Err(crate::errors::ExecError::Kcl(e)) => {
|
||||||
return Err(miette::Report::new(crate::errors::Report {
|
return Err(miette::Report::new(crate::errors::Report {
|
||||||
error: e,
|
error: e.error,
|
||||||
filename: format!("{}{}", "my_func", 1usize),
|
filename: format!("{}{}", "my_func", 1usize),
|
||||||
kcl_source: "This is code.\nIt does other shit.\nmyFunc".to_string(),
|
kcl_source: "This is code.\nIt does other shit.\nmyFunc".to_string(),
|
||||||
}));
|
}));
|
||||||
|
@ -39,7 +39,7 @@ mod test_examples_line_to {
|
|||||||
{
|
{
|
||||||
Err(crate::errors::ExecError::Kcl(e)) => {
|
Err(crate::errors::ExecError::Kcl(e)) => {
|
||||||
return Err(miette::Report::new(crate::errors::Report {
|
return Err(miette::Report::new(crate::errors::Report {
|
||||||
error: e,
|
error: e.error,
|
||||||
filename: format!("{}{}", "line_to", 0usize),
|
filename: format!("{}{}", "line_to", 0usize),
|
||||||
kcl_source: "This is another code block.\nyes sirrr.\nlineTo".to_string(),
|
kcl_source: "This is another code block.\nyes sirrr.\nlineTo".to_string(),
|
||||||
}));
|
}));
|
||||||
@ -93,7 +93,7 @@ mod test_examples_line_to {
|
|||||||
{
|
{
|
||||||
Err(crate::errors::ExecError::Kcl(e)) => {
|
Err(crate::errors::ExecError::Kcl(e)) => {
|
||||||
return Err(miette::Report::new(crate::errors::Report {
|
return Err(miette::Report::new(crate::errors::Report {
|
||||||
error: e,
|
error: e.error,
|
||||||
filename: format!("{}{}", "line_to", 1usize),
|
filename: format!("{}{}", "line_to", 1usize),
|
||||||
kcl_source: "This is code.\nIt does other shit.\nlineTo".to_string(),
|
kcl_source: "This is code.\nIt does other shit.\nlineTo".to_string(),
|
||||||
}));
|
}));
|
||||||
|
@ -38,7 +38,7 @@ mod test_examples_min {
|
|||||||
{
|
{
|
||||||
Err(crate::errors::ExecError::Kcl(e)) => {
|
Err(crate::errors::ExecError::Kcl(e)) => {
|
||||||
return Err(miette::Report::new(crate::errors::Report {
|
return Err(miette::Report::new(crate::errors::Report {
|
||||||
error: e,
|
error: e.error,
|
||||||
filename: format!("{}{}", "min", 0usize),
|
filename: format!("{}{}", "min", 0usize),
|
||||||
kcl_source: "This is another code block.\nyes sirrr.\nmin".to_string(),
|
kcl_source: "This is another code block.\nyes sirrr.\nmin".to_string(),
|
||||||
}));
|
}));
|
||||||
@ -92,7 +92,7 @@ mod test_examples_min {
|
|||||||
{
|
{
|
||||||
Err(crate::errors::ExecError::Kcl(e)) => {
|
Err(crate::errors::ExecError::Kcl(e)) => {
|
||||||
return Err(miette::Report::new(crate::errors::Report {
|
return Err(miette::Report::new(crate::errors::Report {
|
||||||
error: e,
|
error: e.error,
|
||||||
filename: format!("{}{}", "min", 1usize),
|
filename: format!("{}{}", "min", 1usize),
|
||||||
kcl_source: "This is code.\nIt does other shit.\nmin".to_string(),
|
kcl_source: "This is code.\nIt does other shit.\nmin".to_string(),
|
||||||
}));
|
}));
|
||||||
|
@ -38,7 +38,7 @@ mod test_examples_show {
|
|||||||
{
|
{
|
||||||
Err(crate::errors::ExecError::Kcl(e)) => {
|
Err(crate::errors::ExecError::Kcl(e)) => {
|
||||||
return Err(miette::Report::new(crate::errors::Report {
|
return Err(miette::Report::new(crate::errors::Report {
|
||||||
error: e,
|
error: e.error,
|
||||||
filename: format!("{}{}", "show", 0usize),
|
filename: format!("{}{}", "show", 0usize),
|
||||||
kcl_source: "This is code.\nIt does other shit.\nshow".to_string(),
|
kcl_source: "This is code.\nIt does other shit.\nshow".to_string(),
|
||||||
}));
|
}));
|
||||||
|
@ -38,7 +38,7 @@ mod test_examples_import {
|
|||||||
{
|
{
|
||||||
Err(crate::errors::ExecError::Kcl(e)) => {
|
Err(crate::errors::ExecError::Kcl(e)) => {
|
||||||
return Err(miette::Report::new(crate::errors::Report {
|
return Err(miette::Report::new(crate::errors::Report {
|
||||||
error: e,
|
error: e.error,
|
||||||
filename: format!("{}{}", "import", 0usize),
|
filename: format!("{}{}", "import", 0usize),
|
||||||
kcl_source: "This is code.\nIt does other shit.\nimport".to_string(),
|
kcl_source: "This is code.\nIt does other shit.\nimport".to_string(),
|
||||||
}));
|
}));
|
||||||
|
@ -38,7 +38,7 @@ mod test_examples_import {
|
|||||||
{
|
{
|
||||||
Err(crate::errors::ExecError::Kcl(e)) => {
|
Err(crate::errors::ExecError::Kcl(e)) => {
|
||||||
return Err(miette::Report::new(crate::errors::Report {
|
return Err(miette::Report::new(crate::errors::Report {
|
||||||
error: e,
|
error: e.error,
|
||||||
filename: format!("{}{}", "import", 0usize),
|
filename: format!("{}{}", "import", 0usize),
|
||||||
kcl_source: "This is code.\nIt does other shit.\nimport".to_string(),
|
kcl_source: "This is code.\nIt does other shit.\nimport".to_string(),
|
||||||
}));
|
}));
|
||||||
|
@ -38,7 +38,7 @@ mod test_examples_import {
|
|||||||
{
|
{
|
||||||
Err(crate::errors::ExecError::Kcl(e)) => {
|
Err(crate::errors::ExecError::Kcl(e)) => {
|
||||||
return Err(miette::Report::new(crate::errors::Report {
|
return Err(miette::Report::new(crate::errors::Report {
|
||||||
error: e,
|
error: e.error,
|
||||||
filename: format!("{}{}", "import", 0usize),
|
filename: format!("{}{}", "import", 0usize),
|
||||||
kcl_source: "This is code.\nIt does other shit.\nimport".to_string(),
|
kcl_source: "This is code.\nIt does other shit.\nimport".to_string(),
|
||||||
}));
|
}));
|
||||||
|
@ -38,7 +38,7 @@ mod test_examples_show {
|
|||||||
{
|
{
|
||||||
Err(crate::errors::ExecError::Kcl(e)) => {
|
Err(crate::errors::ExecError::Kcl(e)) => {
|
||||||
return Err(miette::Report::new(crate::errors::Report {
|
return Err(miette::Report::new(crate::errors::Report {
|
||||||
error: e,
|
error: e.error,
|
||||||
filename: format!("{}{}", "show", 0usize),
|
filename: format!("{}{}", "show", 0usize),
|
||||||
kcl_source: "This is code.\nIt does other shit.\nshow".to_string(),
|
kcl_source: "This is code.\nIt does other shit.\nshow".to_string(),
|
||||||
}));
|
}));
|
||||||
|
@ -37,7 +37,7 @@ mod test_examples_some_function {
|
|||||||
{
|
{
|
||||||
Err(crate::errors::ExecError::Kcl(e)) => {
|
Err(crate::errors::ExecError::Kcl(e)) => {
|
||||||
return Err(miette::Report::new(crate::errors::Report {
|
return Err(miette::Report::new(crate::errors::Report {
|
||||||
error: e,
|
error: e.error,
|
||||||
filename: format!("{}{}", "some_function", 0usize),
|
filename: format!("{}{}", "some_function", 0usize),
|
||||||
kcl_source: "someFunction()".to_string(),
|
kcl_source: "someFunction()".to_string(),
|
||||||
}));
|
}));
|
||||||
|
@ -10,8 +10,8 @@ anyhow = "1.0.95"
|
|||||||
hyper = { version = "0.14.29", features = ["http1", "server", "tcp"] }
|
hyper = { version = "0.14.29", features = ["http1", "server", "tcp"] }
|
||||||
kcl-lib = { version = "0.2", path = "../kcl" }
|
kcl-lib = { version = "0.2", path = "../kcl" }
|
||||||
pico-args = "0.5.0"
|
pico-args = "0.5.0"
|
||||||
serde = { version = "1.0.214", features = ["derive"] }
|
serde = { version = "1.0.217", features = ["derive"] }
|
||||||
serde_json = "1.0.128"
|
serde_json = "1.0.135"
|
||||||
tokio = { version = "1.41.1", features = ["macros", "rt-multi-thread"] }
|
tokio = { version = "1.41.1", features = ["macros", "rt-multi-thread"] }
|
||||||
|
|
||||||
[lints]
|
[lints]
|
||||||
|
@ -14,7 +14,7 @@ path = "src/tool.rs"
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow = "1"
|
anyhow = "1"
|
||||||
async-trait = "0.1.81"
|
async-trait = "0.1.85"
|
||||||
indexmap = "2.7.0"
|
indexmap = "2.7.0"
|
||||||
kcl-lib = { path = "../kcl" }
|
kcl-lib = { path = "../kcl" }
|
||||||
kittycad = { workspace = true, features = ["clap"] }
|
kittycad = { workspace = true, features = ["clap"] }
|
||||||
|
@ -6,7 +6,7 @@ use std::{
|
|||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use indexmap::IndexMap;
|
use indexmap::IndexMap;
|
||||||
use kcl_lib::{
|
use kcl_lib::{
|
||||||
exec::{DefaultPlanes, IdGenerator},
|
exec::{ArtifactCommand, DefaultPlanes, IdGenerator},
|
||||||
ExecutionKind, KclError,
|
ExecutionKind, KclError,
|
||||||
};
|
};
|
||||||
use kittycad_modeling_cmds::{
|
use kittycad_modeling_cmds::{
|
||||||
@ -76,7 +76,9 @@ impl EngineConnection {
|
|||||||
"".into()
|
"".into()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
kcmc::ModelingCmd::SketchModeDisable(kcmc::SketchModeDisable {}) => "scene->disableSketchMode();".into(),
|
kcmc::ModelingCmd::SketchModeDisable(kcmc::SketchModeDisable { .. }) => {
|
||||||
|
"scene->disableSketchMode();".into()
|
||||||
|
}
|
||||||
kcmc::ModelingCmd::MakePlane(kcmc::MakePlane {
|
kcmc::ModelingCmd::MakePlane(kcmc::MakePlane {
|
||||||
origin,
|
origin,
|
||||||
x_axis,
|
x_axis,
|
||||||
@ -105,7 +107,7 @@ impl EngineConnection {
|
|||||||
size.0
|
size.0
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
kcmc::ModelingCmd::StartPath(kcmc::StartPath {}) => {
|
kcmc::ModelingCmd::StartPath(kcmc::StartPath { .. }) => {
|
||||||
let sketch_id = format!("sketch_{}", cpp_id);
|
let sketch_id = format!("sketch_{}", cpp_id);
|
||||||
let path_id = format!("path_{}", cpp_id);
|
let path_id = format!("path_{}", cpp_id);
|
||||||
format!(
|
format!(
|
||||||
@ -367,6 +369,10 @@ impl kcl_lib::EngineManager for EngineConnection {
|
|||||||
self.batch_end.clone()
|
self.batch_end.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn take_artifact_commands(&self) -> Vec<ArtifactCommand> {
|
||||||
|
Vec::new()
|
||||||
|
}
|
||||||
|
|
||||||
fn execution_kind(&self) -> ExecutionKind {
|
fn execution_kind(&self) -> ExecutionKind {
|
||||||
let guard = self.execution_kind.lock().unwrap();
|
let guard = self.execution_kind.lock().unwrap();
|
||||||
*guard
|
*guard
|
||||||
@ -414,7 +420,7 @@ impl kcl_lib::EngineManager for EngineConnection {
|
|||||||
id: uuid::Uuid,
|
id: uuid::Uuid,
|
||||||
_source_range: kcl_lib::SourceRange,
|
_source_range: kcl_lib::SourceRange,
|
||||||
cmd: WebSocketRequest,
|
cmd: WebSocketRequest,
|
||||||
_id_to_source_range: std::collections::HashMap<uuid::Uuid, kcl_lib::SourceRange>,
|
_id_to_source_range: HashMap<uuid::Uuid, kcl_lib::SourceRange>,
|
||||||
) -> Result<WebSocketResponse, KclError> {
|
) -> Result<WebSocketResponse, KclError> {
|
||||||
match cmd {
|
match cmd {
|
||||||
WebSocketRequest::ModelingCmdBatchReq(ModelingBatch {
|
WebSocketRequest::ModelingCmdBatchReq(ModelingBatch {
|
||||||
|
@ -13,7 +13,7 @@ keywords = ["kcl", "KittyCAD", "CAD"]
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow = { version = "1.0.95", features = ["backtrace"] }
|
anyhow = { version = "1.0.95", features = ["backtrace"] }
|
||||||
async-recursion = "1.1.1"
|
async-recursion = "1.1.1"
|
||||||
async-trait = "0.1.83"
|
async-trait = "0.1.85"
|
||||||
base64 = "0.22.1"
|
base64 = "0.22.1"
|
||||||
chrono = "0.4.38"
|
chrono = "0.4.38"
|
||||||
clap = { version = "4.5.23", default-features = false, optional = true, features = [
|
clap = { version = "4.5.23", default-features = false, optional = true, features = [
|
||||||
@ -54,13 +54,13 @@ schemars = { version = "0.8.17", features = [
|
|||||||
"uuid1",
|
"uuid1",
|
||||||
"preserve_order",
|
"preserve_order",
|
||||||
] }
|
] }
|
||||||
serde = { version = "1.0.214", features = ["derive"] }
|
serde = { version = "1.0.217", features = ["derive"] }
|
||||||
serde_json = "1.0.128"
|
serde_json = "1.0.135"
|
||||||
sha2 = "0.10.8"
|
sha2 = "0.10.8"
|
||||||
tabled = { version = "0.15.0", optional = true }
|
tabled = { version = "0.15.0", optional = true }
|
||||||
thiserror = "2.0.0"
|
thiserror = "2.0.0"
|
||||||
toml = "0.8.19"
|
toml = "0.8.19"
|
||||||
ts-rs = { version = "10.0.0", features = [
|
ts-rs = { version = "10.1.0", features = [
|
||||||
"uuid-impl",
|
"uuid-impl",
|
||||||
"url-impl",
|
"url-impl",
|
||||||
"chrono-impl",
|
"chrono-impl",
|
||||||
@ -73,8 +73,8 @@ urlencoding = "2.1.3"
|
|||||||
uuid = { version = "1.11.0", features = ["v4", "js", "serde"] }
|
uuid = { version = "1.11.0", features = ["v4", "js", "serde"] }
|
||||||
validator = { version = "0.19.0", features = ["derive"] }
|
validator = { version = "0.19.0", features = ["derive"] }
|
||||||
web-time = "1.1"
|
web-time = "1.1"
|
||||||
winnow = "0.6.18"
|
winnow = "0.6.22"
|
||||||
zip = { version = "2.0.0", default-features = false }
|
zip = { version = "2.2.2", default-features = false }
|
||||||
|
|
||||||
[target.'cfg(target_arch = "wasm32")'.dependencies]
|
[target.'cfg(target_arch = "wasm32")'.dependencies]
|
||||||
js-sys = { version = "0.3.72" }
|
js-sys = { version = "0.3.72" }
|
||||||
|
@ -535,7 +535,11 @@ fn generate_type(
|
|||||||
|| name == "CircularPattern3dData"
|
|| name == "CircularPattern3dData"
|
||||||
|| name == "LinearPattern2dData"
|
|| name == "LinearPattern2dData"
|
||||||
|| name == "LinearPattern3dData"
|
|| name == "LinearPattern3dData"
|
||||||
|| name == "Mirror2dData")
|
|| name == "Mirror2dData"
|
||||||
|
|| name == "Axis2dOrEdgeReference"
|
||||||
|
|| name == "Axis3dOrEdgeReference"
|
||||||
|
|| name == "AxisAndOrigin2d"
|
||||||
|
|| name == "AxisAndOrigin3d")
|
||||||
{
|
{
|
||||||
return Err(anyhow::anyhow!("Type name is not pascal cased: {}", name));
|
return Err(anyhow::anyhow!("Type name is not pascal cased: {}", name));
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,10 @@
|
|||||||
//! Functions for setting up our WebSocket and WebRTC connections for communications with the
|
//! Functions for setting up our WebSocket and WebRTC connections for communications with the
|
||||||
//! engine.
|
//! engine.
|
||||||
|
|
||||||
use std::sync::{Arc, Mutex};
|
use std::{
|
||||||
|
collections::HashMap,
|
||||||
|
sync::{Arc, Mutex},
|
||||||
|
};
|
||||||
|
|
||||||
use anyhow::{anyhow, Result};
|
use anyhow::{anyhow, Result};
|
||||||
use dashmap::DashMap;
|
use dashmap::DashMap;
|
||||||
@ -14,15 +17,16 @@ use kcmc::{
|
|||||||
},
|
},
|
||||||
ModelingCmd,
|
ModelingCmd,
|
||||||
};
|
};
|
||||||
use kittycad_modeling_cmds as kcmc;
|
use kittycad_modeling_cmds::{self as kcmc, id::ModelingCmdId, websocket::ModelingBatch};
|
||||||
use tokio::sync::{mpsc, oneshot, RwLock};
|
use tokio::sync::{mpsc, oneshot, RwLock};
|
||||||
use tokio_tungstenite::tungstenite::Message as WsMsg;
|
use tokio_tungstenite::tungstenite::Message as WsMsg;
|
||||||
|
use uuid::Uuid;
|
||||||
|
|
||||||
use super::ExecutionKind;
|
use super::ExecutionKind;
|
||||||
use crate::{
|
use crate::{
|
||||||
engine::EngineManager,
|
engine::EngineManager,
|
||||||
errors::{KclError, KclErrorDetails},
|
errors::{KclError, KclErrorDetails},
|
||||||
execution::{DefaultPlanes, IdGenerator},
|
execution::{ArtifactCommand, DefaultPlanes, IdGenerator},
|
||||||
SourceRange,
|
SourceRange,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -43,6 +47,7 @@ pub struct EngineConnection {
|
|||||||
socket_health: Arc<Mutex<SocketHealth>>,
|
socket_health: Arc<Mutex<SocketHealth>>,
|
||||||
batch: Arc<Mutex<Vec<(WebSocketRequest, SourceRange)>>>,
|
batch: Arc<Mutex<Vec<(WebSocketRequest, SourceRange)>>>,
|
||||||
batch_end: Arc<Mutex<IndexMap<uuid::Uuid, (WebSocketRequest, SourceRange)>>>,
|
batch_end: Arc<Mutex<IndexMap<uuid::Uuid, (WebSocketRequest, SourceRange)>>>,
|
||||||
|
artifact_commands: Arc<Mutex<Vec<ArtifactCommand>>>,
|
||||||
|
|
||||||
/// The default planes for the scene.
|
/// The default planes for the scene.
|
||||||
default_planes: Arc<RwLock<Option<DefaultPlanes>>>,
|
default_planes: Arc<RwLock<Option<DefaultPlanes>>>,
|
||||||
@ -307,11 +312,34 @@ impl EngineConnection {
|
|||||||
socket_health,
|
socket_health,
|
||||||
batch: Arc::new(Mutex::new(Vec::new())),
|
batch: Arc::new(Mutex::new(Vec::new())),
|
||||||
batch_end: Arc::new(Mutex::new(IndexMap::new())),
|
batch_end: Arc::new(Mutex::new(IndexMap::new())),
|
||||||
|
artifact_commands: Arc::new(Mutex::new(Vec::new())),
|
||||||
default_planes: Default::default(),
|
default_planes: Default::default(),
|
||||||
session_data,
|
session_data,
|
||||||
execution_kind: Default::default(),
|
execution_kind: Default::default(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn handle_command(
|
||||||
|
&self,
|
||||||
|
cmd: &ModelingCmd,
|
||||||
|
cmd_id: ModelingCmdId,
|
||||||
|
id_to_source_range: &HashMap<Uuid, SourceRange>,
|
||||||
|
) -> Result<(), KclError> {
|
||||||
|
let cmd_id = *cmd_id.as_ref();
|
||||||
|
let range = id_to_source_range
|
||||||
|
.get(&cmd_id)
|
||||||
|
.copied()
|
||||||
|
.ok_or_else(|| KclError::internal(format!("Failed to get source range for command ID: {:?}", cmd_id)))?;
|
||||||
|
|
||||||
|
// Add artifact command.
|
||||||
|
let mut artifact_commands = self.artifact_commands.lock().unwrap();
|
||||||
|
artifact_commands.push(ArtifactCommand {
|
||||||
|
cmd_id,
|
||||||
|
range,
|
||||||
|
command: cmd.clone(),
|
||||||
|
});
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait::async_trait]
|
#[async_trait::async_trait]
|
||||||
@ -324,6 +352,11 @@ impl EngineManager for EngineConnection {
|
|||||||
self.batch_end.clone()
|
self.batch_end.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn take_artifact_commands(&self) -> Vec<ArtifactCommand> {
|
||||||
|
let mut artifact_commands = self.artifact_commands.lock().unwrap();
|
||||||
|
std::mem::take(&mut *artifact_commands)
|
||||||
|
}
|
||||||
|
|
||||||
fn execution_kind(&self) -> ExecutionKind {
|
fn execution_kind(&self) -> ExecutionKind {
|
||||||
let guard = self.execution_kind.lock().unwrap();
|
let guard = self.execution_kind.lock().unwrap();
|
||||||
*guard
|
*guard
|
||||||
@ -371,8 +404,20 @@ impl EngineManager for EngineConnection {
|
|||||||
id: uuid::Uuid,
|
id: uuid::Uuid,
|
||||||
source_range: SourceRange,
|
source_range: SourceRange,
|
||||||
cmd: WebSocketRequest,
|
cmd: WebSocketRequest,
|
||||||
_id_to_source_range: std::collections::HashMap<uuid::Uuid, SourceRange>,
|
id_to_source_range: HashMap<Uuid, SourceRange>,
|
||||||
) -> Result<WebSocketResponse, KclError> {
|
) -> Result<WebSocketResponse, KclError> {
|
||||||
|
match &cmd {
|
||||||
|
WebSocketRequest::ModelingCmdBatchReq(ModelingBatch { requests, .. }) => {
|
||||||
|
for request in requests {
|
||||||
|
self.handle_command(&request.cmd, request.cmd_id, &id_to_source_range)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
WebSocketRequest::ModelingCmdReq(request) => {
|
||||||
|
self.handle_command(&request.cmd, request.cmd_id, &id_to_source_range)?;
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
|
||||||
let (tx, rx) = oneshot::channel();
|
let (tx, rx) = oneshot::channel();
|
||||||
|
|
||||||
// Send the request to the engine, via the actor.
|
// Send the request to the engine, via the actor.
|
||||||
|
@ -15,12 +15,14 @@ use kcmc::{
|
|||||||
WebSocketResponse,
|
WebSocketResponse,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use kittycad_modeling_cmds::{self as kcmc};
|
use kittycad_modeling_cmds::{self as kcmc, id::ModelingCmdId, ModelingCmd};
|
||||||
|
use uuid::Uuid;
|
||||||
|
|
||||||
use super::ExecutionKind;
|
use super::ExecutionKind;
|
||||||
use crate::{
|
use crate::{
|
||||||
errors::KclError,
|
errors::KclError,
|
||||||
execution::{DefaultPlanes, IdGenerator},
|
exec::DefaultPlanes,
|
||||||
|
execution::{ArtifactCommand, IdGenerator},
|
||||||
SourceRange,
|
SourceRange,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -28,6 +30,7 @@ use crate::{
|
|||||||
pub struct EngineConnection {
|
pub struct EngineConnection {
|
||||||
batch: Arc<Mutex<Vec<(WebSocketRequest, SourceRange)>>>,
|
batch: Arc<Mutex<Vec<(WebSocketRequest, SourceRange)>>>,
|
||||||
batch_end: Arc<Mutex<IndexMap<uuid::Uuid, (WebSocketRequest, SourceRange)>>>,
|
batch_end: Arc<Mutex<IndexMap<uuid::Uuid, (WebSocketRequest, SourceRange)>>>,
|
||||||
|
artifact_commands: Arc<Mutex<Vec<ArtifactCommand>>>,
|
||||||
execution_kind: Arc<Mutex<ExecutionKind>>,
|
execution_kind: Arc<Mutex<ExecutionKind>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -36,9 +39,32 @@ impl EngineConnection {
|
|||||||
Ok(EngineConnection {
|
Ok(EngineConnection {
|
||||||
batch: Arc::new(Mutex::new(Vec::new())),
|
batch: Arc::new(Mutex::new(Vec::new())),
|
||||||
batch_end: Arc::new(Mutex::new(IndexMap::new())),
|
batch_end: Arc::new(Mutex::new(IndexMap::new())),
|
||||||
|
artifact_commands: Arc::new(Mutex::new(Vec::new())),
|
||||||
execution_kind: Default::default(),
|
execution_kind: Default::default(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn handle_command(
|
||||||
|
&self,
|
||||||
|
cmd: &ModelingCmd,
|
||||||
|
cmd_id: ModelingCmdId,
|
||||||
|
id_to_source_range: &HashMap<Uuid, SourceRange>,
|
||||||
|
) -> Result<(), KclError> {
|
||||||
|
let cmd_id = *cmd_id.as_ref();
|
||||||
|
let range = id_to_source_range
|
||||||
|
.get(&cmd_id)
|
||||||
|
.copied()
|
||||||
|
.ok_or_else(|| KclError::internal(format!("Failed to get source range for command ID: {:?}", cmd_id)))?;
|
||||||
|
|
||||||
|
// Add artifact command.
|
||||||
|
let mut artifact_commands = self.artifact_commands.lock().unwrap();
|
||||||
|
artifact_commands.push(ArtifactCommand {
|
||||||
|
cmd_id,
|
||||||
|
range,
|
||||||
|
command: cmd.clone(),
|
||||||
|
});
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait::async_trait]
|
#[async_trait::async_trait]
|
||||||
@ -51,6 +77,11 @@ impl crate::engine::EngineManager for EngineConnection {
|
|||||||
self.batch_end.clone()
|
self.batch_end.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn take_artifact_commands(&self) -> Vec<ArtifactCommand> {
|
||||||
|
let mut artifact_commands = self.artifact_commands.lock().unwrap();
|
||||||
|
std::mem::take(&mut *artifact_commands)
|
||||||
|
}
|
||||||
|
|
||||||
fn execution_kind(&self) -> ExecutionKind {
|
fn execution_kind(&self) -> ExecutionKind {
|
||||||
let guard = self.execution_kind.lock().unwrap();
|
let guard = self.execution_kind.lock().unwrap();
|
||||||
*guard
|
*guard
|
||||||
@ -84,7 +115,7 @@ impl crate::engine::EngineManager for EngineConnection {
|
|||||||
id: uuid::Uuid,
|
id: uuid::Uuid,
|
||||||
_source_range: SourceRange,
|
_source_range: SourceRange,
|
||||||
cmd: WebSocketRequest,
|
cmd: WebSocketRequest,
|
||||||
_id_to_source_range: std::collections::HashMap<uuid::Uuid, SourceRange>,
|
id_to_source_range: HashMap<Uuid, SourceRange>,
|
||||||
) -> Result<WebSocketResponse, KclError> {
|
) -> Result<WebSocketResponse, KclError> {
|
||||||
match cmd {
|
match cmd {
|
||||||
WebSocketRequest::ModelingCmdBatchReq(ModelingBatch {
|
WebSocketRequest::ModelingCmdBatchReq(ModelingBatch {
|
||||||
@ -95,6 +126,7 @@ impl crate::engine::EngineManager for EngineConnection {
|
|||||||
// Create the empty responses.
|
// Create the empty responses.
|
||||||
let mut responses = HashMap::new();
|
let mut responses = HashMap::new();
|
||||||
for request in requests {
|
for request in requests {
|
||||||
|
self.handle_command(&request.cmd, request.cmd_id, &id_to_source_range)?;
|
||||||
responses.insert(
|
responses.insert(
|
||||||
request.cmd_id,
|
request.cmd_id,
|
||||||
BatchResponse::Success {
|
BatchResponse::Success {
|
||||||
@ -108,6 +140,17 @@ impl crate::engine::EngineManager for EngineConnection {
|
|||||||
success: true,
|
success: true,
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
WebSocketRequest::ModelingCmdReq(request) => {
|
||||||
|
self.handle_command(&request.cmd, request.cmd_id, &id_to_source_range)?;
|
||||||
|
|
||||||
|
Ok(WebSocketResponse::Success(SuccessWebSocketResponse {
|
||||||
|
request_id: Some(id),
|
||||||
|
resp: OkWebSocketResponseData::Modeling {
|
||||||
|
modeling_response: OkModelingCmdResponse::Empty {},
|
||||||
|
},
|
||||||
|
success: true,
|
||||||
|
}))
|
||||||
|
}
|
||||||
_ => Ok(WebSocketResponse::Success(SuccessWebSocketResponse {
|
_ => Ok(WebSocketResponse::Success(SuccessWebSocketResponse {
|
||||||
request_id: Some(id),
|
request_id: Some(id),
|
||||||
resp: OkWebSocketResponseData::Modeling {
|
resp: OkWebSocketResponseData::Modeling {
|
||||||
|
@ -1,17 +1,25 @@
|
|||||||
//! Functions for setting up our WebSocket and WebRTC connections for communications with the
|
//! Functions for setting up our WebSocket and WebRTC connections for communications with the
|
||||||
//! engine.
|
//! engine.
|
||||||
use std::sync::{Arc, Mutex};
|
use std::{
|
||||||
|
collections::HashMap,
|
||||||
|
sync::{Arc, Mutex},
|
||||||
|
};
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use indexmap::IndexMap;
|
use indexmap::IndexMap;
|
||||||
use kcmc::websocket::{WebSocketRequest, WebSocketResponse};
|
use kcmc::{
|
||||||
|
id::ModelingCmdId,
|
||||||
|
websocket::{ModelingBatch, WebSocketRequest, WebSocketResponse},
|
||||||
|
ModelingCmd,
|
||||||
|
};
|
||||||
use kittycad_modeling_cmds as kcmc;
|
use kittycad_modeling_cmds as kcmc;
|
||||||
|
use uuid::Uuid;
|
||||||
use wasm_bindgen::prelude::*;
|
use wasm_bindgen::prelude::*;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
engine::ExecutionKind,
|
engine::ExecutionKind,
|
||||||
errors::{KclError, KclErrorDetails},
|
errors::{KclError, KclErrorDetails},
|
||||||
execution::{DefaultPlanes, IdGenerator},
|
execution::{ArtifactCommand, DefaultPlanes, IdGenerator},
|
||||||
SourceRange,
|
SourceRange,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -44,6 +52,7 @@ pub struct EngineConnection {
|
|||||||
manager: Arc<EngineCommandManager>,
|
manager: Arc<EngineCommandManager>,
|
||||||
batch: Arc<Mutex<Vec<(WebSocketRequest, SourceRange)>>>,
|
batch: Arc<Mutex<Vec<(WebSocketRequest, SourceRange)>>>,
|
||||||
batch_end: Arc<Mutex<IndexMap<uuid::Uuid, (WebSocketRequest, SourceRange)>>>,
|
batch_end: Arc<Mutex<IndexMap<uuid::Uuid, (WebSocketRequest, SourceRange)>>>,
|
||||||
|
artifact_commands: Arc<Mutex<Vec<ArtifactCommand>>>,
|
||||||
execution_kind: Arc<Mutex<ExecutionKind>>,
|
execution_kind: Arc<Mutex<ExecutionKind>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -57,11 +66,36 @@ impl EngineConnection {
|
|||||||
manager: Arc::new(manager),
|
manager: Arc::new(manager),
|
||||||
batch: Arc::new(Mutex::new(Vec::new())),
|
batch: Arc::new(Mutex::new(Vec::new())),
|
||||||
batch_end: Arc::new(Mutex::new(IndexMap::new())),
|
batch_end: Arc::new(Mutex::new(IndexMap::new())),
|
||||||
|
artifact_commands: Arc::new(Mutex::new(Vec::new())),
|
||||||
execution_kind: Default::default(),
|
execution_kind: Default::default(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl EngineConnection {
|
||||||
|
fn handle_command(
|
||||||
|
&self,
|
||||||
|
cmd: &ModelingCmd,
|
||||||
|
cmd_id: ModelingCmdId,
|
||||||
|
id_to_source_range: &HashMap<Uuid, SourceRange>,
|
||||||
|
) -> Result<(), KclError> {
|
||||||
|
let cmd_id = *cmd_id.as_ref();
|
||||||
|
let range = id_to_source_range
|
||||||
|
.get(&cmd_id)
|
||||||
|
.copied()
|
||||||
|
.ok_or_else(|| KclError::internal(format!("Failed to get source range for command ID: {:?}", cmd_id)))?;
|
||||||
|
|
||||||
|
// Add artifact command.
|
||||||
|
let mut artifact_commands = self.artifact_commands.lock().unwrap();
|
||||||
|
artifact_commands.push(ArtifactCommand {
|
||||||
|
cmd_id,
|
||||||
|
range,
|
||||||
|
command: cmd.clone(),
|
||||||
|
});
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[async_trait::async_trait]
|
#[async_trait::async_trait]
|
||||||
impl crate::engine::EngineManager for EngineConnection {
|
impl crate::engine::EngineManager for EngineConnection {
|
||||||
fn batch(&self) -> Arc<Mutex<Vec<(WebSocketRequest, SourceRange)>>> {
|
fn batch(&self) -> Arc<Mutex<Vec<(WebSocketRequest, SourceRange)>>> {
|
||||||
@ -72,6 +106,11 @@ impl crate::engine::EngineManager for EngineConnection {
|
|||||||
self.batch_end.clone()
|
self.batch_end.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn take_artifact_commands(&self) -> Vec<ArtifactCommand> {
|
||||||
|
let mut artifact_commands = self.artifact_commands.lock().unwrap();
|
||||||
|
std::mem::take(&mut *artifact_commands)
|
||||||
|
}
|
||||||
|
|
||||||
fn execution_kind(&self) -> ExecutionKind {
|
fn execution_kind(&self) -> ExecutionKind {
|
||||||
let guard = self.execution_kind.lock().unwrap();
|
let guard = self.execution_kind.lock().unwrap();
|
||||||
*guard
|
*guard
|
||||||
@ -161,8 +200,20 @@ impl crate::engine::EngineManager for EngineConnection {
|
|||||||
id: uuid::Uuid,
|
id: uuid::Uuid,
|
||||||
source_range: SourceRange,
|
source_range: SourceRange,
|
||||||
cmd: WebSocketRequest,
|
cmd: WebSocketRequest,
|
||||||
id_to_source_range: std::collections::HashMap<uuid::Uuid, SourceRange>,
|
id_to_source_range: HashMap<uuid::Uuid, SourceRange>,
|
||||||
) -> Result<WebSocketResponse, KclError> {
|
) -> Result<WebSocketResponse, KclError> {
|
||||||
|
match &cmd {
|
||||||
|
WebSocketRequest::ModelingCmdBatchReq(ModelingBatch { requests, .. }) => {
|
||||||
|
for request in requests {
|
||||||
|
self.handle_command(&request.cmd, request.cmd_id, &id_to_source_range)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
WebSocketRequest::ModelingCmdReq(request) => {
|
||||||
|
self.handle_command(&request.cmd, request.cmd_id, &id_to_source_range)?;
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
|
||||||
let source_range_str = serde_json::to_string(&source_range).map_err(|e| {
|
let source_range_str = serde_json::to_string(&source_range).map_err(|e| {
|
||||||
KclError::Engine(KclErrorDetails {
|
KclError::Engine(KclErrorDetails {
|
||||||
message: format!("Failed to serialize source range: {:?}", e),
|
message: format!("Failed to serialize source range: {:?}", e),
|
||||||
|
@ -32,7 +32,7 @@ use uuid::Uuid;
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
errors::{KclError, KclErrorDetails},
|
errors::{KclError, KclErrorDetails},
|
||||||
execution::{DefaultPlanes, IdGenerator, Point3d},
|
execution::{ArtifactCommand, DefaultPlanes, IdGenerator, Point3d},
|
||||||
SourceRange,
|
SourceRange,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -67,6 +67,14 @@ pub trait EngineManager: std::fmt::Debug + Send + Sync + 'static {
|
|||||||
/// Get the batch of end commands to be sent to the engine.
|
/// Get the batch of end commands to be sent to the engine.
|
||||||
fn batch_end(&self) -> Arc<Mutex<IndexMap<uuid::Uuid, (WebSocketRequest, SourceRange)>>>;
|
fn batch_end(&self) -> Arc<Mutex<IndexMap<uuid::Uuid, (WebSocketRequest, SourceRange)>>>;
|
||||||
|
|
||||||
|
/// Take the artifact commands generated up to this point and clear them.
|
||||||
|
fn take_artifact_commands(&self) -> Vec<ArtifactCommand>;
|
||||||
|
|
||||||
|
/// Clear all artifact commands that have accumulated so far.
|
||||||
|
fn clear_artifact_commands(&self) {
|
||||||
|
self.take_artifact_commands();
|
||||||
|
}
|
||||||
|
|
||||||
/// Get the current execution kind.
|
/// Get the current execution kind.
|
||||||
fn execution_kind(&self) -> ExecutionKind;
|
fn execution_kind(&self) -> ExecutionKind;
|
||||||
|
|
||||||
@ -106,7 +114,7 @@ pub trait EngineManager: std::fmt::Debug + Send + Sync + 'static {
|
|||||||
self.batch_modeling_cmd(
|
self.batch_modeling_cmd(
|
||||||
uuid::Uuid::new_v4(),
|
uuid::Uuid::new_v4(),
|
||||||
source_range,
|
source_range,
|
||||||
&ModelingCmd::SceneClearAll(mcmd::SceneClearAll {}),
|
&ModelingCmd::SceneClearAll(mcmd::SceneClearAll::default()),
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
@ -114,6 +122,10 @@ pub trait EngineManager: std::fmt::Debug + Send + Sync + 'static {
|
|||||||
// Otherwise the hooks below won't work.
|
// Otherwise the hooks below won't work.
|
||||||
self.flush_batch(false, source_range).await?;
|
self.flush_batch(false, source_range).await?;
|
||||||
|
|
||||||
|
// Ensure artifact commands are cleared so that we don't accumulate them
|
||||||
|
// across runs.
|
||||||
|
self.clear_artifact_commands();
|
||||||
|
|
||||||
// Do the after clear scene hook.
|
// Do the after clear scene hook.
|
||||||
self.clear_scene_post_hook(id_generator, source_range).await?;
|
self.clear_scene_post_hook(id_generator, source_range).await?;
|
||||||
|
|
||||||
@ -217,15 +229,13 @@ pub trait EngineManager: std::fmt::Debug + Send + Sync + 'static {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Send the modeling cmd and wait for the response.
|
/// Send the modeling cmd and wait for the response.
|
||||||
// TODO: This should only borrow `cmd`.
|
|
||||||
// See https://github.com/KittyCAD/modeling-app/issues/2821
|
|
||||||
async fn send_modeling_cmd(
|
async fn send_modeling_cmd(
|
||||||
&self,
|
&self,
|
||||||
id: uuid::Uuid,
|
id: uuid::Uuid,
|
||||||
source_range: SourceRange,
|
source_range: SourceRange,
|
||||||
cmd: ModelingCmd,
|
cmd: &ModelingCmd,
|
||||||
) -> Result<OkWebSocketResponseData, crate::errors::KclError> {
|
) -> Result<OkWebSocketResponseData, crate::errors::KclError> {
|
||||||
self.batch_modeling_cmd(id, source_range, &cmd).await?;
|
self.batch_modeling_cmd(id, source_range, cmd).await?;
|
||||||
|
|
||||||
// Flush the batch queue.
|
// Flush the batch queue.
|
||||||
self.flush_batch(false, source_range).await
|
self.flush_batch(false, source_range).await
|
||||||
|