Compare commits
13 Commits
franknoiro
...
dependabot
| Author | SHA1 | Date | |
|---|---|---|---|
| 6e2274a6c4 | |||
| 4b9f71c994 | |||
| e86a5622c8 | |||
| a503d1ce50 | |||
| 11a94cc99e | |||
| e295f82495 | |||
| 0b9cf2dc21 | |||
| eb58507e93 | |||
| ca28a5f549 | |||
| 6f4bbdb79e | |||
| 6773dbe7ff | |||
| acfc2b47fa | |||
| 9dc7ff9797 |
@ -253,6 +253,10 @@ const extrusion = extrude(5, sketch001)
|
||||
type: "TangentialArcTo",
|
||||
} |
|
||||
{
|
||||
// arc's direction
|
||||
ccw: string,
|
||||
// the arc's center
|
||||
center: [number, number],
|
||||
// The from point.
|
||||
from: [number, number],
|
||||
// The tag of the path.
|
||||
@ -458,6 +462,10 @@ const extrusion = extrude(5, sketch001)
|
||||
type: "TangentialArcTo",
|
||||
} |
|
||||
{
|
||||
// arc's direction
|
||||
ccw: string,
|
||||
// the arc's center
|
||||
center: [number, number],
|
||||
// The from point.
|
||||
from: [number, number],
|
||||
// The tag of the path.
|
||||
|
||||
@ -257,6 +257,10 @@ const extrusion = extrude(5, sketch001)
|
||||
type: "TangentialArcTo",
|
||||
} |
|
||||
{
|
||||
// arc's direction
|
||||
ccw: string,
|
||||
// the arc's center
|
||||
center: [number, number],
|
||||
// The from point.
|
||||
from: [number, number],
|
||||
// The tag of the path.
|
||||
@ -462,6 +466,10 @@ const extrusion = extrude(5, sketch001)
|
||||
type: "TangentialArcTo",
|
||||
} |
|
||||
{
|
||||
// arc's direction
|
||||
ccw: string,
|
||||
// the arc's center
|
||||
center: [number, number],
|
||||
// The from point.
|
||||
from: [number, number],
|
||||
// The tag of the path.
|
||||
|
||||
@ -172,6 +172,10 @@ const example = extrude(10, exampleSketch)
|
||||
type: "TangentialArcTo",
|
||||
} |
|
||||
{
|
||||
// arc's direction
|
||||
ccw: string,
|
||||
// the arc's center
|
||||
center: [number, number],
|
||||
// The from point.
|
||||
from: [number, number],
|
||||
// The tag of the path.
|
||||
@ -377,6 +381,10 @@ const example = extrude(10, exampleSketch)
|
||||
type: "TangentialArcTo",
|
||||
} |
|
||||
{
|
||||
// arc's direction
|
||||
ccw: string,
|
||||
// the arc's center
|
||||
center: [number, number],
|
||||
// The from point.
|
||||
from: [number, number],
|
||||
// The tag of the path.
|
||||
@ -584,6 +592,10 @@ const example = extrude(10, exampleSketch)
|
||||
type: "TangentialArcTo",
|
||||
} |
|
||||
{
|
||||
// arc's direction
|
||||
ccw: string,
|
||||
// the arc's center
|
||||
center: [number, number],
|
||||
// The from point.
|
||||
from: [number, number],
|
||||
// The tag of the path.
|
||||
@ -789,6 +801,10 @@ const example = extrude(10, exampleSketch)
|
||||
type: "TangentialArcTo",
|
||||
} |
|
||||
{
|
||||
// arc's direction
|
||||
ccw: string,
|
||||
// the arc's center
|
||||
center: [number, number],
|
||||
// The from point.
|
||||
from: [number, number],
|
||||
// The tag of the path.
|
||||
|
||||
@ -171,6 +171,10 @@ const extrusion = extrude(10, sketch001)
|
||||
type: "TangentialArcTo",
|
||||
} |
|
||||
{
|
||||
// arc's direction
|
||||
ccw: string,
|
||||
// the arc's center
|
||||
center: [number, number],
|
||||
// The from point.
|
||||
from: [number, number],
|
||||
// The tag of the path.
|
||||
@ -376,6 +380,10 @@ const extrusion = extrude(10, sketch001)
|
||||
type: "TangentialArcTo",
|
||||
} |
|
||||
{
|
||||
// arc's direction
|
||||
ccw: string,
|
||||
// the arc's center
|
||||
center: [number, number],
|
||||
// The from point.
|
||||
from: [number, number],
|
||||
// The tag of the path.
|
||||
@ -583,6 +591,10 @@ const extrusion = extrude(10, sketch001)
|
||||
type: "TangentialArcTo",
|
||||
} |
|
||||
{
|
||||
// arc's direction
|
||||
ccw: string,
|
||||
// the arc's center
|
||||
center: [number, number],
|
||||
// The from point.
|
||||
from: [number, number],
|
||||
// The tag of the path.
|
||||
@ -788,6 +800,10 @@ const extrusion = extrude(10, sketch001)
|
||||
type: "TangentialArcTo",
|
||||
} |
|
||||
{
|
||||
// arc's direction
|
||||
ccw: string,
|
||||
// the arc's center
|
||||
center: [number, number],
|
||||
// The from point.
|
||||
from: [number, number],
|
||||
// The tag of the path.
|
||||
|
||||
@ -173,6 +173,10 @@ const example = extrude(10, exampleSketch)
|
||||
type: "TangentialArcTo",
|
||||
} |
|
||||
{
|
||||
// arc's direction
|
||||
ccw: string,
|
||||
// the arc's center
|
||||
center: [number, number],
|
||||
// The from point.
|
||||
from: [number, number],
|
||||
// The tag of the path.
|
||||
@ -378,6 +382,10 @@ const example = extrude(10, exampleSketch)
|
||||
type: "TangentialArcTo",
|
||||
} |
|
||||
{
|
||||
// arc's direction
|
||||
ccw: string,
|
||||
// the arc's center
|
||||
center: [number, number],
|
||||
// The from point.
|
||||
from: [number, number],
|
||||
// The tag of the path.
|
||||
@ -585,6 +593,10 @@ const example = extrude(10, exampleSketch)
|
||||
type: "TangentialArcTo",
|
||||
} |
|
||||
{
|
||||
// arc's direction
|
||||
ccw: string,
|
||||
// the arc's center
|
||||
center: [number, number],
|
||||
// The from point.
|
||||
from: [number, number],
|
||||
// The tag of the path.
|
||||
@ -790,6 +802,10 @@ const example = extrude(10, exampleSketch)
|
||||
type: "TangentialArcTo",
|
||||
} |
|
||||
{
|
||||
// arc's direction
|
||||
ccw: string,
|
||||
// the arc's center
|
||||
center: [number, number],
|
||||
// The from point.
|
||||
from: [number, number],
|
||||
// The tag of the path.
|
||||
|
||||
@ -265,6 +265,10 @@ const example = extrude(10, exampleSketch)
|
||||
type: "TangentialArcTo",
|
||||
} |
|
||||
{
|
||||
// arc's direction
|
||||
ccw: string,
|
||||
// the arc's center
|
||||
center: [number, number],
|
||||
// The from point.
|
||||
from: [number, number],
|
||||
// The tag of the path.
|
||||
@ -470,6 +474,10 @@ const example = extrude(10, exampleSketch)
|
||||
type: "TangentialArcTo",
|
||||
} |
|
||||
{
|
||||
// arc's direction
|
||||
ccw: string,
|
||||
// the arc's center
|
||||
center: [number, number],
|
||||
// The from point.
|
||||
from: [number, number],
|
||||
// The tag of the path.
|
||||
@ -677,6 +685,10 @@ const example = extrude(10, exampleSketch)
|
||||
type: "TangentialArcTo",
|
||||
} |
|
||||
{
|
||||
// arc's direction
|
||||
ccw: string,
|
||||
// the arc's center
|
||||
center: [number, number],
|
||||
// The from point.
|
||||
from: [number, number],
|
||||
// The tag of the path.
|
||||
@ -882,6 +894,10 @@ const example = extrude(10, exampleSketch)
|
||||
type: "TangentialArcTo",
|
||||
} |
|
||||
{
|
||||
// arc's direction
|
||||
ccw: string,
|
||||
// the arc's center
|
||||
center: [number, number],
|
||||
// The from point.
|
||||
from: [number, number],
|
||||
// The tag of the path.
|
||||
|
||||
@ -170,6 +170,10 @@ const example = extrude(10, exampleSketch)
|
||||
type: "TangentialArcTo",
|
||||
} |
|
||||
{
|
||||
// arc's direction
|
||||
ccw: string,
|
||||
// the arc's center
|
||||
center: [number, number],
|
||||
// The from point.
|
||||
from: [number, number],
|
||||
// The tag of the path.
|
||||
@ -375,6 +379,10 @@ const example = extrude(10, exampleSketch)
|
||||
type: "TangentialArcTo",
|
||||
} |
|
||||
{
|
||||
// arc's direction
|
||||
ccw: string,
|
||||
// the arc's center
|
||||
center: [number, number],
|
||||
// The from point.
|
||||
from: [number, number],
|
||||
// The tag of the path.
|
||||
@ -582,6 +590,10 @@ const example = extrude(10, exampleSketch)
|
||||
type: "TangentialArcTo",
|
||||
} |
|
||||
{
|
||||
// arc's direction
|
||||
ccw: string,
|
||||
// the arc's center
|
||||
center: [number, number],
|
||||
// The from point.
|
||||
from: [number, number],
|
||||
// The tag of the path.
|
||||
@ -787,6 +799,10 @@ const example = extrude(10, exampleSketch)
|
||||
type: "TangentialArcTo",
|
||||
} |
|
||||
{
|
||||
// arc's direction
|
||||
ccw: string,
|
||||
// the arc's center
|
||||
center: [number, number],
|
||||
// The from point.
|
||||
from: [number, number],
|
||||
// The tag of the path.
|
||||
|
||||
@ -170,6 +170,10 @@ const example = extrude(10, exampleSketch)
|
||||
type: "TangentialArcTo",
|
||||
} |
|
||||
{
|
||||
// arc's direction
|
||||
ccw: string,
|
||||
// the arc's center
|
||||
center: [number, number],
|
||||
// The from point.
|
||||
from: [number, number],
|
||||
// The tag of the path.
|
||||
@ -375,6 +379,10 @@ const example = extrude(10, exampleSketch)
|
||||
type: "TangentialArcTo",
|
||||
} |
|
||||
{
|
||||
// arc's direction
|
||||
ccw: string,
|
||||
// the arc's center
|
||||
center: [number, number],
|
||||
// The from point.
|
||||
from: [number, number],
|
||||
// The tag of the path.
|
||||
@ -582,6 +590,10 @@ const example = extrude(10, exampleSketch)
|
||||
type: "TangentialArcTo",
|
||||
} |
|
||||
{
|
||||
// arc's direction
|
||||
ccw: string,
|
||||
// the arc's center
|
||||
center: [number, number],
|
||||
// The from point.
|
||||
from: [number, number],
|
||||
// The tag of the path.
|
||||
@ -787,6 +799,10 @@ const example = extrude(10, exampleSketch)
|
||||
type: "TangentialArcTo",
|
||||
} |
|
||||
{
|
||||
// arc's direction
|
||||
ccw: string,
|
||||
// the arc's center
|
||||
center: [number, number],
|
||||
// The from point.
|
||||
from: [number, number],
|
||||
// The tag of the path.
|
||||
|
||||
@ -183,6 +183,10 @@ const exampleSketch = startSketchOn('XZ')
|
||||
type: "TangentialArcTo",
|
||||
} |
|
||||
{
|
||||
// arc's direction
|
||||
ccw: string,
|
||||
// the arc's center
|
||||
center: [number, number],
|
||||
// The from point.
|
||||
from: [number, number],
|
||||
// The tag of the path.
|
||||
@ -388,6 +392,10 @@ const exampleSketch = startSketchOn('XZ')
|
||||
type: "TangentialArcTo",
|
||||
} |
|
||||
{
|
||||
// arc's direction
|
||||
ccw: string,
|
||||
// the arc's center
|
||||
center: [number, number],
|
||||
// The from point.
|
||||
from: [number, number],
|
||||
// The tag of the path.
|
||||
@ -595,6 +603,10 @@ const exampleSketch = startSketchOn('XZ')
|
||||
type: "TangentialArcTo",
|
||||
} |
|
||||
{
|
||||
// arc's direction
|
||||
ccw: string,
|
||||
// the arc's center
|
||||
center: [number, number],
|
||||
// The from point.
|
||||
from: [number, number],
|
||||
// The tag of the path.
|
||||
@ -800,6 +812,10 @@ const exampleSketch = startSketchOn('XZ')
|
||||
type: "TangentialArcTo",
|
||||
} |
|
||||
{
|
||||
// arc's direction
|
||||
ccw: string,
|
||||
// the arc's center
|
||||
center: [number, number],
|
||||
// The from point.
|
||||
from: [number, number],
|
||||
// The tag of the path.
|
||||
|
||||
@ -176,6 +176,10 @@ const example = extrude(10, exampleSketch)
|
||||
type: "TangentialArcTo",
|
||||
} |
|
||||
{
|
||||
// arc's direction
|
||||
ccw: string,
|
||||
// the arc's center
|
||||
center: [number, number],
|
||||
// The from point.
|
||||
from: [number, number],
|
||||
// The tag of the path.
|
||||
@ -381,6 +385,10 @@ const example = extrude(10, exampleSketch)
|
||||
type: "TangentialArcTo",
|
||||
} |
|
||||
{
|
||||
// arc's direction
|
||||
ccw: string,
|
||||
// the arc's center
|
||||
center: [number, number],
|
||||
// The from point.
|
||||
from: [number, number],
|
||||
// The tag of the path.
|
||||
@ -588,6 +596,10 @@ const example = extrude(10, exampleSketch)
|
||||
type: "TangentialArcTo",
|
||||
} |
|
||||
{
|
||||
// arc's direction
|
||||
ccw: string,
|
||||
// the arc's center
|
||||
center: [number, number],
|
||||
// The from point.
|
||||
from: [number, number],
|
||||
// The tag of the path.
|
||||
@ -793,6 +805,10 @@ const example = extrude(10, exampleSketch)
|
||||
type: "TangentialArcTo",
|
||||
} |
|
||||
{
|
||||
// arc's direction
|
||||
ccw: string,
|
||||
// the arc's center
|
||||
center: [number, number],
|
||||
// The from point.
|
||||
from: [number, number],
|
||||
// The tag of the path.
|
||||
|
||||
@ -398,6 +398,10 @@ const mountingPlate = extrude(thickness, mountingPlateSketch)
|
||||
type: "TangentialArcTo",
|
||||
} |
|
||||
{
|
||||
// arc's direction
|
||||
ccw: string,
|
||||
// the arc's center
|
||||
center: [number, number],
|
||||
// The from point.
|
||||
from: [number, number],
|
||||
// The tag of the path.
|
||||
@ -798,6 +802,10 @@ const mountingPlate = extrude(thickness, mountingPlateSketch)
|
||||
type: "TangentialArcTo",
|
||||
} |
|
||||
{
|
||||
// arc's direction
|
||||
ccw: string,
|
||||
// the arc's center
|
||||
center: [number, number],
|
||||
// The from point.
|
||||
from: [number, number],
|
||||
// The tag of the path.
|
||||
|
||||
@ -169,6 +169,10 @@ const example = extrude(5, exampleSketch)
|
||||
type: "TangentialArcTo",
|
||||
} |
|
||||
{
|
||||
// arc's direction
|
||||
ccw: string,
|
||||
// the arc's center
|
||||
center: [number, number],
|
||||
// The from point.
|
||||
from: [number, number],
|
||||
// The tag of the path.
|
||||
@ -541,6 +545,10 @@ const example = extrude(5, exampleSketch)
|
||||
type: "TangentialArcTo",
|
||||
} |
|
||||
{
|
||||
// arc's direction
|
||||
ccw: string,
|
||||
// the arc's center
|
||||
center: [number, number],
|
||||
// The from point.
|
||||
from: [number, number],
|
||||
// The tag of the path.
|
||||
@ -748,6 +756,10 @@ const example = extrude(5, exampleSketch)
|
||||
type: "TangentialArcTo",
|
||||
} |
|
||||
{
|
||||
// arc's direction
|
||||
ccw: string,
|
||||
// the arc's center
|
||||
center: [number, number],
|
||||
// The from point.
|
||||
from: [number, number],
|
||||
// The tag of the path.
|
||||
@ -953,6 +965,10 @@ const example = extrude(5, exampleSketch)
|
||||
type: "TangentialArcTo",
|
||||
} |
|
||||
{
|
||||
// arc's direction
|
||||
ccw: string,
|
||||
// the arc's center
|
||||
center: [number, number],
|
||||
// The from point.
|
||||
from: [number, number],
|
||||
// The tag of the path.
|
||||
|
||||
@ -171,6 +171,10 @@ const example = extrude(10, exampleSketch)
|
||||
type: "TangentialArcTo",
|
||||
} |
|
||||
{
|
||||
// arc's direction
|
||||
ccw: string,
|
||||
// the arc's center
|
||||
center: [number, number],
|
||||
// The from point.
|
||||
from: [number, number],
|
||||
// The tag of the path.
|
||||
@ -376,6 +380,10 @@ const example = extrude(10, exampleSketch)
|
||||
type: "TangentialArcTo",
|
||||
} |
|
||||
{
|
||||
// arc's direction
|
||||
ccw: string,
|
||||
// the arc's center
|
||||
center: [number, number],
|
||||
// The from point.
|
||||
from: [number, number],
|
||||
// The tag of the path.
|
||||
@ -583,6 +591,10 @@ const example = extrude(10, exampleSketch)
|
||||
type: "TangentialArcTo",
|
||||
} |
|
||||
{
|
||||
// arc's direction
|
||||
ccw: string,
|
||||
// the arc's center
|
||||
center: [number, number],
|
||||
// The from point.
|
||||
from: [number, number],
|
||||
// The tag of the path.
|
||||
@ -788,6 +800,10 @@ const example = extrude(10, exampleSketch)
|
||||
type: "TangentialArcTo",
|
||||
} |
|
||||
{
|
||||
// arc's direction
|
||||
ccw: string,
|
||||
// the arc's center
|
||||
center: [number, number],
|
||||
// The from point.
|
||||
from: [number, number],
|
||||
// The tag of the path.
|
||||
|
||||
@ -196,6 +196,10 @@ const example = extrude(10, exampleSketch)
|
||||
type: "TangentialArcTo",
|
||||
} |
|
||||
{
|
||||
// arc's direction
|
||||
ccw: string,
|
||||
// the arc's center
|
||||
center: [number, number],
|
||||
// The from point.
|
||||
from: [number, number],
|
||||
// The tag of the path.
|
||||
@ -402,6 +406,10 @@ const example = extrude(10, exampleSketch)
|
||||
type: "TangentialArcTo",
|
||||
} |
|
||||
{
|
||||
// arc's direction
|
||||
ccw: string,
|
||||
// the arc's center
|
||||
center: [number, number],
|
||||
// The from point.
|
||||
from: [number, number],
|
||||
// The tag of the path.
|
||||
@ -728,6 +736,10 @@ const example = extrude(10, exampleSketch)
|
||||
type: "TangentialArcTo",
|
||||
} |
|
||||
{
|
||||
// arc's direction
|
||||
ccw: string,
|
||||
// the arc's center
|
||||
center: [number, number],
|
||||
// The from point.
|
||||
from: [number, number],
|
||||
// The tag of the path.
|
||||
|
||||
@ -398,6 +398,10 @@ const mountingPlate = extrude(thickness, mountingPlateSketch)
|
||||
type: "TangentialArcTo",
|
||||
} |
|
||||
{
|
||||
// arc's direction
|
||||
ccw: string,
|
||||
// the arc's center
|
||||
center: [number, number],
|
||||
// The from point.
|
||||
from: [number, number],
|
||||
// The tag of the path.
|
||||
@ -798,6 +802,10 @@ const mountingPlate = extrude(thickness, mountingPlateSketch)
|
||||
type: "TangentialArcTo",
|
||||
} |
|
||||
{
|
||||
// arc's direction
|
||||
ccw: string,
|
||||
// the arc's center
|
||||
center: [number, number],
|
||||
// The from point.
|
||||
from: [number, number],
|
||||
// The tag of the path.
|
||||
|
||||
@ -299,6 +299,10 @@ const part001 = startSketchOn('XY')
|
||||
type: "TangentialArcTo",
|
||||
} |
|
||||
{
|
||||
// arc's direction
|
||||
ccw: string,
|
||||
// the arc's center
|
||||
center: [number, number],
|
||||
// The from point.
|
||||
from: [number, number],
|
||||
// The tag of the path.
|
||||
@ -690,6 +694,10 @@ const part001 = startSketchOn('XY')
|
||||
type: "TangentialArcTo",
|
||||
} |
|
||||
{
|
||||
// arc's direction
|
||||
ccw: string,
|
||||
// the arc's center
|
||||
center: [number, number],
|
||||
// The from point.
|
||||
from: [number, number],
|
||||
// The tag of the path.
|
||||
|
||||
@ -182,6 +182,10 @@ const example = extrude(1, exampleSketch)
|
||||
type: "TangentialArcTo",
|
||||
} |
|
||||
{
|
||||
// arc's direction
|
||||
ccw: string,
|
||||
// the arc's center
|
||||
center: [number, number],
|
||||
// The from point.
|
||||
from: [number, number],
|
||||
// The tag of the path.
|
||||
@ -388,6 +392,10 @@ const example = extrude(1, exampleSketch)
|
||||
type: "TangentialArcTo",
|
||||
} |
|
||||
{
|
||||
// arc's direction
|
||||
ccw: string,
|
||||
// the arc's center
|
||||
center: [number, number],
|
||||
// The from point.
|
||||
from: [number, number],
|
||||
// The tag of the path.
|
||||
@ -586,6 +594,10 @@ const example = extrude(1, exampleSketch)
|
||||
type: "TangentialArcTo",
|
||||
} |
|
||||
{
|
||||
// arc's direction
|
||||
ccw: string,
|
||||
// the arc's center
|
||||
center: [number, number],
|
||||
// The from point.
|
||||
from: [number, number],
|
||||
// The tag of the path.
|
||||
@ -791,6 +803,10 @@ const example = extrude(1, exampleSketch)
|
||||
type: "TangentialArcTo",
|
||||
} |
|
||||
{
|
||||
// arc's direction
|
||||
ccw: string,
|
||||
// the arc's center
|
||||
center: [number, number],
|
||||
// The from point.
|
||||
from: [number, number],
|
||||
// The tag of the path.
|
||||
@ -989,6 +1005,10 @@ const example = extrude(1, exampleSketch)
|
||||
type: "TangentialArcTo",
|
||||
} |
|
||||
{
|
||||
// arc's direction
|
||||
ccw: string,
|
||||
// the arc's center
|
||||
center: [number, number],
|
||||
// The from point.
|
||||
from: [number, number],
|
||||
// The tag of the path.
|
||||
@ -1194,6 +1214,10 @@ const example = extrude(1, exampleSketch)
|
||||
type: "TangentialArcTo",
|
||||
} |
|
||||
{
|
||||
// arc's direction
|
||||
ccw: string,
|
||||
// the arc's center
|
||||
center: [number, number],
|
||||
// The from point.
|
||||
from: [number, number],
|
||||
// The tag of the path.
|
||||
|
||||
@ -162,6 +162,10 @@ const example = extrude(5, exampleSketch)
|
||||
type: "TangentialArcTo",
|
||||
} |
|
||||
{
|
||||
// arc's direction
|
||||
ccw: string,
|
||||
// the arc's center
|
||||
center: [number, number],
|
||||
// The from point.
|
||||
from: [number, number],
|
||||
// The tag of the path.
|
||||
@ -367,6 +371,10 @@ const example = extrude(5, exampleSketch)
|
||||
type: "TangentialArcTo",
|
||||
} |
|
||||
{
|
||||
// arc's direction
|
||||
ccw: string,
|
||||
// the arc's center
|
||||
center: [number, number],
|
||||
// The from point.
|
||||
from: [number, number],
|
||||
// The tag of the path.
|
||||
|
||||
@ -162,6 +162,10 @@ const example = extrude(5, exampleSketch)
|
||||
type: "TangentialArcTo",
|
||||
} |
|
||||
{
|
||||
// arc's direction
|
||||
ccw: string,
|
||||
// the arc's center
|
||||
center: [number, number],
|
||||
// The from point.
|
||||
from: [number, number],
|
||||
// The tag of the path.
|
||||
@ -367,6 +371,10 @@ const example = extrude(5, exampleSketch)
|
||||
type: "TangentialArcTo",
|
||||
} |
|
||||
{
|
||||
// arc's direction
|
||||
ccw: string,
|
||||
// the arc's center
|
||||
center: [number, number],
|
||||
// The from point.
|
||||
from: [number, number],
|
||||
// The tag of the path.
|
||||
|
||||
@ -175,6 +175,10 @@ const example = extrude(5, exampleSketch)
|
||||
type: "TangentialArcTo",
|
||||
} |
|
||||
{
|
||||
// arc's direction
|
||||
ccw: string,
|
||||
// the arc's center
|
||||
center: [number, number],
|
||||
// The from point.
|
||||
from: [number, number],
|
||||
// The tag of the path.
|
||||
@ -380,6 +384,10 @@ const example = extrude(5, exampleSketch)
|
||||
type: "TangentialArcTo",
|
||||
} |
|
||||
{
|
||||
// arc's direction
|
||||
ccw: string,
|
||||
// the arc's center
|
||||
center: [number, number],
|
||||
// The from point.
|
||||
from: [number, number],
|
||||
// The tag of the path.
|
||||
@ -587,6 +595,10 @@ const example = extrude(5, exampleSketch)
|
||||
type: "TangentialArcTo",
|
||||
} |
|
||||
{
|
||||
// arc's direction
|
||||
ccw: string,
|
||||
// the arc's center
|
||||
center: [number, number],
|
||||
// The from point.
|
||||
from: [number, number],
|
||||
// The tag of the path.
|
||||
@ -792,6 +804,10 @@ const example = extrude(5, exampleSketch)
|
||||
type: "TangentialArcTo",
|
||||
} |
|
||||
{
|
||||
// arc's direction
|
||||
ccw: string,
|
||||
// the arc's center
|
||||
center: [number, number],
|
||||
// The from point.
|
||||
from: [number, number],
|
||||
// The tag of the path.
|
||||
|
||||
@ -162,6 +162,10 @@ const example = extrude(5, exampleSketch)
|
||||
type: "TangentialArcTo",
|
||||
} |
|
||||
{
|
||||
// arc's direction
|
||||
ccw: string,
|
||||
// the arc's center
|
||||
center: [number, number],
|
||||
// The from point.
|
||||
from: [number, number],
|
||||
// The tag of the path.
|
||||
@ -367,6 +371,10 @@ const example = extrude(5, exampleSketch)
|
||||
type: "TangentialArcTo",
|
||||
} |
|
||||
{
|
||||
// arc's direction
|
||||
ccw: string,
|
||||
// the arc's center
|
||||
center: [number, number],
|
||||
// The from point.
|
||||
from: [number, number],
|
||||
// The tag of the path.
|
||||
@ -574,6 +582,10 @@ const example = extrude(5, exampleSketch)
|
||||
type: "TangentialArcTo",
|
||||
} |
|
||||
{
|
||||
// arc's direction
|
||||
ccw: string,
|
||||
// the arc's center
|
||||
center: [number, number],
|
||||
// The from point.
|
||||
from: [number, number],
|
||||
// The tag of the path.
|
||||
@ -779,6 +791,10 @@ const example = extrude(5, exampleSketch)
|
||||
type: "TangentialArcTo",
|
||||
} |
|
||||
{
|
||||
// arc's direction
|
||||
ccw: string,
|
||||
// the arc's center
|
||||
center: [number, number],
|
||||
// The from point.
|
||||
from: [number, number],
|
||||
// The tag of the path.
|
||||
|
||||
@ -180,6 +180,10 @@ const example = extrude(1, exampleSketch)
|
||||
type: "TangentialArcTo",
|
||||
} |
|
||||
{
|
||||
// arc's direction
|
||||
ccw: string,
|
||||
// the arc's center
|
||||
center: [number, number],
|
||||
// The from point.
|
||||
from: [number, number],
|
||||
// The tag of the path.
|
||||
@ -386,6 +390,10 @@ const example = extrude(1, exampleSketch)
|
||||
type: "TangentialArcTo",
|
||||
} |
|
||||
{
|
||||
// arc's direction
|
||||
ccw: string,
|
||||
// the arc's center
|
||||
center: [number, number],
|
||||
// The from point.
|
||||
from: [number, number],
|
||||
// The tag of the path.
|
||||
|
||||
@ -304,6 +304,10 @@ const example = extrude(-5, exampleSketch)
|
||||
type: "TangentialArcTo",
|
||||
} |
|
||||
{
|
||||
// arc's direction
|
||||
ccw: string,
|
||||
// the arc's center
|
||||
center: [number, number],
|
||||
// The from point.
|
||||
from: [number, number],
|
||||
// The tag of the path.
|
||||
|
||||
@ -173,6 +173,10 @@ const example = extrude(1, exampleSketch)
|
||||
type: "TangentialArcTo",
|
||||
} |
|
||||
{
|
||||
// arc's direction
|
||||
ccw: string,
|
||||
// the arc's center
|
||||
center: [number, number],
|
||||
// The from point.
|
||||
from: [number, number],
|
||||
// The tag of the path.
|
||||
@ -379,6 +383,10 @@ const example = extrude(1, exampleSketch)
|
||||
type: "TangentialArcTo",
|
||||
} |
|
||||
{
|
||||
// arc's direction
|
||||
ccw: string,
|
||||
// the arc's center
|
||||
center: [number, number],
|
||||
// The from point.
|
||||
from: [number, number],
|
||||
// The tag of the path.
|
||||
|
||||
@ -302,6 +302,10 @@ const example = extrude(1, exampleSketch)
|
||||
type: "TangentialArcTo",
|
||||
} |
|
||||
{
|
||||
// arc's direction
|
||||
ccw: string,
|
||||
// the arc's center
|
||||
center: [number, number],
|
||||
// The from point.
|
||||
from: [number, number],
|
||||
// The tag of the path.
|
||||
|
||||
@ -304,6 +304,10 @@ let vase = layer()
|
||||
type: "TangentialArcTo",
|
||||
} |
|
||||
{
|
||||
// arc's direction
|
||||
ccw: string,
|
||||
// the arc's center
|
||||
center: [number, number],
|
||||
// The from point.
|
||||
from: [number, number],
|
||||
// The tag of the path.
|
||||
|
||||
@ -163,6 +163,10 @@ const sketch001 = startSketchOn('XY')
|
||||
type: "TangentialArcTo",
|
||||
} |
|
||||
{
|
||||
// arc's direction
|
||||
ccw: string,
|
||||
// the arc's center
|
||||
center: [number, number],
|
||||
// The from point.
|
||||
from: [number, number],
|
||||
// The tag of the path.
|
||||
@ -368,6 +372,10 @@ const sketch001 = startSketchOn('XY')
|
||||
type: "TangentialArcTo",
|
||||
} |
|
||||
{
|
||||
// arc's direction
|
||||
ccw: string,
|
||||
// the arc's center
|
||||
center: [number, number],
|
||||
// The from point.
|
||||
from: [number, number],
|
||||
// The tag of the path.
|
||||
|
||||
@ -158,6 +158,10 @@ const sketch001 = startSketchOn('XY')
|
||||
type: "TangentialArcTo",
|
||||
} |
|
||||
{
|
||||
// arc's direction
|
||||
ccw: string,
|
||||
// the arc's center
|
||||
center: [number, number],
|
||||
// The from point.
|
||||
from: [number, number],
|
||||
// The tag of the path.
|
||||
@ -363,6 +367,10 @@ const sketch001 = startSketchOn('XY')
|
||||
type: "TangentialArcTo",
|
||||
} |
|
||||
{
|
||||
// arc's direction
|
||||
ccw: string,
|
||||
// the arc's center
|
||||
center: [number, number],
|
||||
// The from point.
|
||||
from: [number, number],
|
||||
// The tag of the path.
|
||||
|
||||
@ -157,6 +157,10 @@ const sketch001 = startSketchOn('XY')
|
||||
type: "TangentialArcTo",
|
||||
} |
|
||||
{
|
||||
// arc's direction
|
||||
ccw: string,
|
||||
// the arc's center
|
||||
center: [number, number],
|
||||
// The from point.
|
||||
from: [number, number],
|
||||
// The tag of the path.
|
||||
@ -362,6 +366,10 @@ const sketch001 = startSketchOn('XY')
|
||||
type: "TangentialArcTo",
|
||||
} |
|
||||
{
|
||||
// arc's direction
|
||||
ccw: string,
|
||||
// the arc's center
|
||||
center: [number, number],
|
||||
// The from point.
|
||||
from: [number, number],
|
||||
// The tag of the path.
|
||||
|
||||
@ -379,6 +379,10 @@ uuid |
|
||||
type: "TangentialArcTo",
|
||||
} |
|
||||
{
|
||||
// arc's direction
|
||||
ccw: string,
|
||||
// the arc's center
|
||||
center: [number, number],
|
||||
// The from point.
|
||||
from: [number, number],
|
||||
// The tag of the path.
|
||||
@ -584,6 +588,10 @@ uuid |
|
||||
type: "TangentialArcTo",
|
||||
} |
|
||||
{
|
||||
// arc's direction
|
||||
ccw: string,
|
||||
// the arc's center
|
||||
center: [number, number],
|
||||
// The from point.
|
||||
from: [number, number],
|
||||
// The tag of the path.
|
||||
@ -907,6 +915,10 @@ uuid |
|
||||
type: "TangentialArcTo",
|
||||
} |
|
||||
{
|
||||
// arc's direction
|
||||
ccw: string,
|
||||
// the arc's center
|
||||
center: [number, number],
|
||||
// The from point.
|
||||
from: [number, number],
|
||||
// The tag of the path.
|
||||
|
||||
@ -386,6 +386,10 @@ shell({ faces: ['end'], thickness: 0.25 }, firstSketch)
|
||||
type: "TangentialArcTo",
|
||||
} |
|
||||
{
|
||||
// arc's direction
|
||||
ccw: string,
|
||||
// the arc's center
|
||||
center: [number, number],
|
||||
// The from point.
|
||||
from: [number, number],
|
||||
// The tag of the path.
|
||||
@ -777,6 +781,10 @@ shell({ faces: ['end'], thickness: 0.25 }, firstSketch)
|
||||
type: "TangentialArcTo",
|
||||
} |
|
||||
{
|
||||
// arc's direction
|
||||
ccw: string,
|
||||
// the arc's center
|
||||
center: [number, number],
|
||||
// The from point.
|
||||
from: [number, number],
|
||||
// The tag of the path.
|
||||
|
||||
@ -241,6 +241,10 @@ const example = extrude(5, exampleSketch)
|
||||
type: "TangentialArcTo",
|
||||
} |
|
||||
{
|
||||
// arc's direction
|
||||
ccw: string,
|
||||
// the arc's center
|
||||
center: [number, number],
|
||||
// The from point.
|
||||
from: [number, number],
|
||||
// The tag of the path.
|
||||
@ -540,6 +544,10 @@ const example = extrude(5, exampleSketch)
|
||||
type: "TangentialArcTo",
|
||||
} |
|
||||
{
|
||||
// arc's direction
|
||||
ccw: string,
|
||||
// the arc's center
|
||||
center: [number, number],
|
||||
// The from point.
|
||||
from: [number, number],
|
||||
// The tag of the path.
|
||||
@ -745,6 +753,10 @@ const example = extrude(5, exampleSketch)
|
||||
type: "TangentialArcTo",
|
||||
} |
|
||||
{
|
||||
// arc's direction
|
||||
ccw: string,
|
||||
// the arc's center
|
||||
center: [number, number],
|
||||
// The from point.
|
||||
from: [number, number],
|
||||
// The tag of the path.
|
||||
|
||||
@ -188,6 +188,10 @@ const example = extrude(5, exampleSketch)
|
||||
type: "TangentialArcTo",
|
||||
} |
|
||||
{
|
||||
// arc's direction
|
||||
ccw: string,
|
||||
// the arc's center
|
||||
center: [number, number],
|
||||
// The from point.
|
||||
from: [number, number],
|
||||
// The tag of the path.
|
||||
@ -393,6 +397,10 @@ const example = extrude(5, exampleSketch)
|
||||
type: "TangentialArcTo",
|
||||
} |
|
||||
{
|
||||
// arc's direction
|
||||
ccw: string,
|
||||
// the arc's center
|
||||
center: [number, number],
|
||||
// The from point.
|
||||
from: [number, number],
|
||||
// The tag of the path.
|
||||
|
||||
@ -310,6 +310,10 @@ const a1 = startSketchOn({
|
||||
type: "TangentialArcTo",
|
||||
} |
|
||||
{
|
||||
// arc's direction
|
||||
ccw: string,
|
||||
// the arc's center
|
||||
center: [number, number],
|
||||
// The from point.
|
||||
from: [number, number],
|
||||
// The tag of the path.
|
||||
@ -722,6 +726,10 @@ const a1 = startSketchOn({
|
||||
type: "TangentialArcTo",
|
||||
} |
|
||||
{
|
||||
// arc's direction
|
||||
ccw: string,
|
||||
// the arc's center
|
||||
center: [number, number],
|
||||
// The from point.
|
||||
from: [number, number],
|
||||
// The tag of the path.
|
||||
|
||||
2128
docs/kcl/std.json
@ -172,6 +172,10 @@ const example = extrude(10, exampleSketch)
|
||||
type: "TangentialArcTo",
|
||||
} |
|
||||
{
|
||||
// arc's direction
|
||||
ccw: string,
|
||||
// the arc's center
|
||||
center: [number, number],
|
||||
// The from point.
|
||||
from: [number, number],
|
||||
// The tag of the path.
|
||||
@ -377,6 +381,10 @@ const example = extrude(10, exampleSketch)
|
||||
type: "TangentialArcTo",
|
||||
} |
|
||||
{
|
||||
// arc's direction
|
||||
ccw: string,
|
||||
// the arc's center
|
||||
center: [number, number],
|
||||
// The from point.
|
||||
from: [number, number],
|
||||
// The tag of the path.
|
||||
@ -584,6 +592,10 @@ const example = extrude(10, exampleSketch)
|
||||
type: "TangentialArcTo",
|
||||
} |
|
||||
{
|
||||
// arc's direction
|
||||
ccw: string,
|
||||
// the arc's center
|
||||
center: [number, number],
|
||||
// The from point.
|
||||
from: [number, number],
|
||||
// The tag of the path.
|
||||
@ -789,6 +801,10 @@ const example = extrude(10, exampleSketch)
|
||||
type: "TangentialArcTo",
|
||||
} |
|
||||
{
|
||||
// arc's direction
|
||||
ccw: string,
|
||||
// the arc's center
|
||||
center: [number, number],
|
||||
// The from point.
|
||||
from: [number, number],
|
||||
// The tag of the path.
|
||||
|
||||
@ -162,6 +162,10 @@ const example = extrude(10, exampleSketch)
|
||||
type: "TangentialArcTo",
|
||||
} |
|
||||
{
|
||||
// arc's direction
|
||||
ccw: string,
|
||||
// the arc's center
|
||||
center: [number, number],
|
||||
// The from point.
|
||||
from: [number, number],
|
||||
// The tag of the path.
|
||||
@ -367,6 +371,10 @@ const example = extrude(10, exampleSketch)
|
||||
type: "TangentialArcTo",
|
||||
} |
|
||||
{
|
||||
// arc's direction
|
||||
ccw: string,
|
||||
// the arc's center
|
||||
center: [number, number],
|
||||
// The from point.
|
||||
from: [number, number],
|
||||
// The tag of the path.
|
||||
@ -574,6 +582,10 @@ const example = extrude(10, exampleSketch)
|
||||
type: "TangentialArcTo",
|
||||
} |
|
||||
{
|
||||
// arc's direction
|
||||
ccw: string,
|
||||
// the arc's center
|
||||
center: [number, number],
|
||||
// The from point.
|
||||
from: [number, number],
|
||||
// The tag of the path.
|
||||
@ -779,6 +791,10 @@ const example = extrude(10, exampleSketch)
|
||||
type: "TangentialArcTo",
|
||||
} |
|
||||
{
|
||||
// arc's direction
|
||||
ccw: string,
|
||||
// the arc's center
|
||||
center: [number, number],
|
||||
// The from point.
|
||||
from: [number, number],
|
||||
// The tag of the path.
|
||||
|
||||
@ -165,6 +165,10 @@ const example = extrude(10, exampleSketch)
|
||||
type: "TangentialArcTo",
|
||||
} |
|
||||
{
|
||||
// arc's direction
|
||||
ccw: string,
|
||||
// the arc's center
|
||||
center: [number, number],
|
||||
// The from point.
|
||||
from: [number, number],
|
||||
// The tag of the path.
|
||||
@ -370,6 +374,10 @@ const example = extrude(10, exampleSketch)
|
||||
type: "TangentialArcTo",
|
||||
} |
|
||||
{
|
||||
// arc's direction
|
||||
ccw: string,
|
||||
// the arc's center
|
||||
center: [number, number],
|
||||
// The from point.
|
||||
from: [number, number],
|
||||
// The tag of the path.
|
||||
@ -577,6 +585,10 @@ const example = extrude(10, exampleSketch)
|
||||
type: "TangentialArcTo",
|
||||
} |
|
||||
{
|
||||
// arc's direction
|
||||
ccw: string,
|
||||
// the arc's center
|
||||
center: [number, number],
|
||||
// The from point.
|
||||
from: [number, number],
|
||||
// The tag of the path.
|
||||
@ -782,6 +794,10 @@ const example = extrude(10, exampleSketch)
|
||||
type: "TangentialArcTo",
|
||||
} |
|
||||
{
|
||||
// arc's direction
|
||||
ccw: string,
|
||||
// the arc's center
|
||||
center: [number, number],
|
||||
// The from point.
|
||||
from: [number, number],
|
||||
// The tag of the path.
|
||||
|
||||
@ -165,6 +165,10 @@ const example = extrude(10, exampleSketch)
|
||||
type: "TangentialArcTo",
|
||||
} |
|
||||
{
|
||||
// arc's direction
|
||||
ccw: string,
|
||||
// the arc's center
|
||||
center: [number, number],
|
||||
// The from point.
|
||||
from: [number, number],
|
||||
// The tag of the path.
|
||||
@ -370,6 +374,10 @@ const example = extrude(10, exampleSketch)
|
||||
type: "TangentialArcTo",
|
||||
} |
|
||||
{
|
||||
// arc's direction
|
||||
ccw: string,
|
||||
// the arc's center
|
||||
center: [number, number],
|
||||
// The from point.
|
||||
from: [number, number],
|
||||
// The tag of the path.
|
||||
@ -577,6 +585,10 @@ const example = extrude(10, exampleSketch)
|
||||
type: "TangentialArcTo",
|
||||
} |
|
||||
{
|
||||
// arc's direction
|
||||
ccw: string,
|
||||
// the arc's center
|
||||
center: [number, number],
|
||||
// The from point.
|
||||
from: [number, number],
|
||||
// The tag of the path.
|
||||
@ -782,6 +794,10 @@ const example = extrude(10, exampleSketch)
|
||||
type: "TangentialArcTo",
|
||||
} |
|
||||
{
|
||||
// arc's direction
|
||||
ccw: string,
|
||||
// the arc's center
|
||||
center: [number, number],
|
||||
// The from point.
|
||||
from: [number, number],
|
||||
// The tag of the path.
|
||||
|
||||
@ -163,6 +163,10 @@ const example = extrude(10, exampleSketch)
|
||||
type: "TangentialArcTo",
|
||||
} |
|
||||
{
|
||||
// arc's direction
|
||||
ccw: string,
|
||||
// the arc's center
|
||||
center: [number, number],
|
||||
// The from point.
|
||||
from: [number, number],
|
||||
// The tag of the path.
|
||||
@ -368,6 +372,10 @@ const example = extrude(10, exampleSketch)
|
||||
type: "TangentialArcTo",
|
||||
} |
|
||||
{
|
||||
// arc's direction
|
||||
ccw: string,
|
||||
// the arc's center
|
||||
center: [number, number],
|
||||
// The from point.
|
||||
from: [number, number],
|
||||
// The tag of the path.
|
||||
@ -575,6 +583,10 @@ const example = extrude(10, exampleSketch)
|
||||
type: "TangentialArcTo",
|
||||
} |
|
||||
{
|
||||
// arc's direction
|
||||
ccw: string,
|
||||
// the arc's center
|
||||
center: [number, number],
|
||||
// The from point.
|
||||
from: [number, number],
|
||||
// The tag of the path.
|
||||
@ -780,6 +792,10 @@ const example = extrude(10, exampleSketch)
|
||||
type: "TangentialArcTo",
|
||||
} |
|
||||
{
|
||||
// arc's direction
|
||||
ccw: string,
|
||||
// the arc's center
|
||||
center: [number, number],
|
||||
// The from point.
|
||||
from: [number, number],
|
||||
// The tag of the path.
|
||||
|
||||
@ -161,6 +161,10 @@ const example = extrude(5, exampleSketch)
|
||||
type: "TangentialArcTo",
|
||||
} |
|
||||
{
|
||||
// arc's direction
|
||||
ccw: string,
|
||||
// the arc's center
|
||||
center: [number, number],
|
||||
// The from point.
|
||||
from: [number, number],
|
||||
// The tag of the path.
|
||||
@ -366,6 +370,10 @@ const example = extrude(5, exampleSketch)
|
||||
type: "TangentialArcTo",
|
||||
} |
|
||||
{
|
||||
// arc's direction
|
||||
ccw: string,
|
||||
// the arc's center
|
||||
center: [number, number],
|
||||
// The from point.
|
||||
from: [number, number],
|
||||
// The tag of the path.
|
||||
@ -573,6 +581,10 @@ const example = extrude(5, exampleSketch)
|
||||
type: "TangentialArcTo",
|
||||
} |
|
||||
{
|
||||
// arc's direction
|
||||
ccw: string,
|
||||
// the arc's center
|
||||
center: [number, number],
|
||||
// The from point.
|
||||
from: [number, number],
|
||||
// The tag of the path.
|
||||
@ -778,6 +790,10 @@ const example = extrude(5, exampleSketch)
|
||||
type: "TangentialArcTo",
|
||||
} |
|
||||
{
|
||||
// arc's direction
|
||||
ccw: string,
|
||||
// the arc's center
|
||||
center: [number, number],
|
||||
// The from point.
|
||||
from: [number, number],
|
||||
// The tag of the path.
|
||||
|
||||
|
Before Width: | Height: | Size: 49 KiB After Width: | Height: | Size: 49 KiB |
|
Before Width: | Height: | Size: 44 KiB After Width: | Height: | Size: 44 KiB |
|
Before Width: | Height: | Size: 62 KiB After Width: | Height: | Size: 59 KiB |
@ -246,14 +246,17 @@ export const getMovementUtils = (opts: any) => {
|
||||
}
|
||||
|
||||
async function waitForAuthAndLsp(page: Page) {
|
||||
const waitForLspPromise = page.waitForEvent('console', async (message) => {
|
||||
// it would be better to wait for a message that the kcl lsp has started by looking for the message message.text().includes('[lsp] [window/logMessage]')
|
||||
// but that doesn't seem to make it to the console for macos/safari :(
|
||||
if (message.text().includes('start kcl lsp')) {
|
||||
await new Promise((resolve) => setTimeout(resolve, 200))
|
||||
return true
|
||||
}
|
||||
return false
|
||||
const waitForLspPromise = page.waitForEvent('console', {
|
||||
predicate: async (message) => {
|
||||
// it would be better to wait for a message that the kcl lsp has started by looking for the message message.text().includes('[lsp] [window/logMessage]')
|
||||
// but that doesn't seem to make it to the console for macos/safari :(
|
||||
if (message.text().includes('start kcl lsp')) {
|
||||
await new Promise((resolve) => setTimeout(resolve, 200))
|
||||
return true
|
||||
}
|
||||
return false
|
||||
},
|
||||
timeout: 45_000,
|
||||
})
|
||||
if (process.env.CI) {
|
||||
await waitForPageLoadWithRetry(page)
|
||||
|
||||
@ -79,7 +79,8 @@
|
||||
"$ref": "#/components/schemas/Result"
|
||||
}
|
||||
],
|
||||
"description": "The result of the info command."
|
||||
"description": "The result of the info command.",
|
||||
"nullable": true
|
||||
},
|
||||
"sequence_id": {
|
||||
"allOf": [
|
||||
@ -93,7 +94,6 @@
|
||||
"required": [
|
||||
"command",
|
||||
"module",
|
||||
"result",
|
||||
"sequence_id"
|
||||
],
|
||||
"type": "object"
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "untitled-app",
|
||||
"version": "0.24.9",
|
||||
"version": "0.24.10",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@codemirror/autocomplete": "^6.17.0",
|
||||
@ -31,7 +31,7 @@
|
||||
"@tauri-apps/plugin-shell": "^2.0.0-beta.7",
|
||||
"@tauri-apps/plugin-updater": "^2.0.0-beta.6",
|
||||
"@ts-stack/markdown": "^1.5.0",
|
||||
"@tweenjs/tween.js": "^23.1.1",
|
||||
"@tweenjs/tween.js": "^25.0.0",
|
||||
"@xstate/inspect": "^0.8.0",
|
||||
"@xstate/react": "^3.2.2",
|
||||
"codemirror": "^6.0.1",
|
||||
|
||||
@ -10,6 +10,7 @@ import { defineConfig, devices } from '@playwright/test'
|
||||
* See https://playwright.dev/docs/test-configuration.
|
||||
*/
|
||||
export default defineConfig({
|
||||
timeout: 120_000, // override the default 30s timeout
|
||||
testDir: './e2e/playwright',
|
||||
/* Run tests in files in parallel */
|
||||
fullyParallel: true,
|
||||
@ -32,6 +33,8 @@ export default defineConfig({
|
||||
|
||||
/* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
|
||||
trace: 'retain-on-failure',
|
||||
actionTimeout: 15000,
|
||||
screenshot: 'only-on-failure',
|
||||
},
|
||||
|
||||
/* Configure projects for major browsers */
|
||||
|
||||
4
src-tauri/Cargo.lock
generated
@ -2672,9 +2672,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "kittycad"
|
||||
version = "0.3.9"
|
||||
version = "0.3.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "36b87a9cca545825ba18005c1944b8525fac28867d99984178bf22c79fb5ec25"
|
||||
checksum = "3d798c82f6e62d786fca0a7ec073675bbe9550c885efb99390d2701ca557ec69"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
|
||||
@ -80,5 +80,5 @@
|
||||
}
|
||||
},
|
||||
"productName": "Zoo Modeling App",
|
||||
"version": "0.24.9"
|
||||
"version": "0.24.10"
|
||||
}
|
||||
|
||||
@ -9,7 +9,7 @@ import { useHotkeys } from 'react-hotkeys-hook'
|
||||
import { getNormalisedCoordinates } from './lib/utils'
|
||||
import { useLoaderData, useNavigate } from 'react-router-dom'
|
||||
import { type IndexLoaderData } from 'lib/types'
|
||||
import { paths } from 'lib/paths'
|
||||
import { PATHS } from 'lib/paths'
|
||||
import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext'
|
||||
import { onboardingPaths } from 'routes/Onboarding/paths'
|
||||
import { useEngineConnectionSubscriptions } from 'hooks/useEngineConnectionSubscriptions'
|
||||
@ -28,7 +28,7 @@ import { CoreDumpManager } from 'lib/coredump'
|
||||
import { UnitsMenu } from 'components/UnitsMenu'
|
||||
|
||||
export function App() {
|
||||
useRefreshSettings(paths.FILE + 'SETTINGS')
|
||||
useRefreshSettings(PATHS.FILE + 'SETTINGS')
|
||||
const { project, file } = useLoaderData() as IndexLoaderData
|
||||
const navigate = useNavigate()
|
||||
const filePath = useAbsoluteFilePath()
|
||||
@ -63,7 +63,7 @@ export function App() {
|
||||
})
|
||||
useHotkeyWrapper(
|
||||
[isTauri() ? 'mod + ,' : 'shift + mod + ,'],
|
||||
() => navigate(filePath + paths.SETTINGS),
|
||||
() => navigate(filePath + PATHS.SETTINGS),
|
||||
{
|
||||
splitKey: '|',
|
||||
}
|
||||
|
||||
@ -20,7 +20,7 @@ import { WasmErrBanner } from 'components/WasmErrBanner'
|
||||
import { CommandBar } from 'components/CommandBar/CommandBar'
|
||||
import ModelingMachineProvider from 'components/ModelingMachineProvider'
|
||||
import FileMachineProvider from 'components/FileMachineProvider'
|
||||
import { paths } from 'lib/paths'
|
||||
import { PATHS } from 'lib/paths'
|
||||
import {
|
||||
fileLoader,
|
||||
homeLoader,
|
||||
@ -45,7 +45,7 @@ import { AppStateProvider } from 'AppState'
|
||||
const router = createBrowserRouter([
|
||||
{
|
||||
loader: settingsLoader,
|
||||
id: paths.INDEX,
|
||||
id: PATHS.INDEX,
|
||||
/* Make sure auth is the outermost provider or else we will have
|
||||
* inefficient re-renders, use the react profiler to see. */
|
||||
element: (
|
||||
@ -64,7 +64,7 @@ const router = createBrowserRouter([
|
||||
errorElement: <ErrorPage />,
|
||||
children: [
|
||||
{
|
||||
path: paths.INDEX,
|
||||
path: PATHS.INDEX,
|
||||
loader: async () => {
|
||||
const inTauri = isTauri()
|
||||
if (inTauri) {
|
||||
@ -78,21 +78,21 @@ const router = createBrowserRouter([
|
||||
// Redirect to the file if we have a file path.
|
||||
if (appState.current_file) {
|
||||
return redirect(
|
||||
paths.FILE + '/' + encodeURIComponent(appState.current_file)
|
||||
PATHS.FILE + '/' + encodeURIComponent(appState.current_file)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return inTauri
|
||||
? redirect(paths.HOME)
|
||||
: redirect(paths.FILE + '/%2F' + BROWSER_PROJECT_NAME)
|
||||
? redirect(PATHS.HOME)
|
||||
: redirect(PATHS.FILE + '/%2F' + BROWSER_PROJECT_NAME)
|
||||
},
|
||||
},
|
||||
{
|
||||
loader: fileLoader,
|
||||
id: paths.FILE,
|
||||
path: paths.FILE + '/:id',
|
||||
id: PATHS.FILE,
|
||||
path: PATHS.FILE + '/:id',
|
||||
element: (
|
||||
<Auth>
|
||||
<FileMachineProvider>
|
||||
@ -109,7 +109,7 @@ const router = createBrowserRouter([
|
||||
),
|
||||
children: [
|
||||
{
|
||||
id: paths.FILE + 'SETTINGS',
|
||||
id: PATHS.FILE + 'SETTINGS',
|
||||
loader: settingsLoader,
|
||||
children: [
|
||||
{
|
||||
@ -118,11 +118,11 @@ const router = createBrowserRouter([
|
||||
element: <></>,
|
||||
},
|
||||
{
|
||||
path: makeUrlPathRelative(paths.SETTINGS),
|
||||
path: makeUrlPathRelative(PATHS.SETTINGS),
|
||||
element: <Settings />,
|
||||
},
|
||||
{
|
||||
path: makeUrlPathRelative(paths.ONBOARDING.INDEX),
|
||||
path: makeUrlPathRelative(PATHS.ONBOARDING.INDEX),
|
||||
element: <Onboarding />,
|
||||
children: onboardingRoutes,
|
||||
},
|
||||
@ -131,7 +131,7 @@ const router = createBrowserRouter([
|
||||
],
|
||||
},
|
||||
{
|
||||
path: paths.HOME,
|
||||
path: PATHS.HOME,
|
||||
element: (
|
||||
<Auth>
|
||||
<Outlet />
|
||||
@ -139,24 +139,24 @@ const router = createBrowserRouter([
|
||||
<CommandBar />
|
||||
</Auth>
|
||||
),
|
||||
id: paths.HOME,
|
||||
id: PATHS.HOME,
|
||||
loader: homeLoader,
|
||||
children: [
|
||||
{
|
||||
index: true,
|
||||
element: <></>,
|
||||
id: paths.HOME + 'SETTINGS',
|
||||
id: PATHS.HOME + 'SETTINGS',
|
||||
loader: settingsLoader,
|
||||
},
|
||||
{
|
||||
path: makeUrlPathRelative(paths.SETTINGS),
|
||||
path: makeUrlPathRelative(PATHS.SETTINGS),
|
||||
loader: settingsLoader,
|
||||
element: <Settings />,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: paths.SIGN_IN,
|
||||
path: PATHS.SIGN_IN,
|
||||
element: <SignIn />,
|
||||
},
|
||||
],
|
||||
|
||||
@ -381,6 +381,7 @@ export class SceneEntities {
|
||||
programMemory,
|
||||
})
|
||||
if (err(sketchGroup)) return Promise.reject(sketchGroup)
|
||||
if (!sketchGroup) return Promise.reject('sketchGroup not found')
|
||||
|
||||
if (!Array.isArray(sketchGroup?.value))
|
||||
return {
|
||||
@ -916,6 +917,10 @@ export class SceneEntities {
|
||||
programMemory: kclManager.programMemory,
|
||||
})
|
||||
if (trap(sketchGroup)) return
|
||||
if (!sketchGroup) {
|
||||
trap(new Error('sketchGroup not found'))
|
||||
return
|
||||
}
|
||||
|
||||
const pipeIndex = pathToNode[pathToNodeIndex + 1][0] as number
|
||||
if (addingNewSegmentStatus === 'nothing') {
|
||||
@ -1765,7 +1770,7 @@ export function sketchGroupFromPathToNode({
|
||||
pathToNode: PathToNode
|
||||
ast: Program
|
||||
programMemory: ProgramMemory
|
||||
}): SketchGroup | Error {
|
||||
}): SketchGroup | null | Error {
|
||||
const _varDec = getNodeFromPath<VariableDeclarator>(
|
||||
kclManager.ast,
|
||||
pathToNode,
|
||||
@ -1777,7 +1782,10 @@ export function sketchGroupFromPathToNode({
|
||||
if (result?.type === 'ExtrudeGroup') {
|
||||
return result.sketchGroup
|
||||
}
|
||||
return result as SketchGroup
|
||||
if (result?.type === 'SketchGroup') {
|
||||
return result
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
function colorSegment(object: any, color: number) {
|
||||
@ -1830,6 +1838,7 @@ export async function getSketchOrientationDetails(
|
||||
programMemory: kclManager.programMemory,
|
||||
})
|
||||
if (err(sketchGroup)) return Promise.reject(sketchGroup)
|
||||
if (!sketchGroup) return Promise.reject('sketchGroup not found')
|
||||
|
||||
if (sketchGroup.on.type === 'plane') {
|
||||
const zAxis = sketchGroup?.on.zAxis
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { ActionIcon, ActionIconProps } from './ActionIcon'
|
||||
import React, { ForwardedRef, forwardRef } from 'react'
|
||||
import { paths } from 'lib/paths'
|
||||
import { PATHS } from 'lib/paths'
|
||||
import { Link } from 'react-router-dom'
|
||||
import type { LinkProps } from 'react-router-dom'
|
||||
|
||||
@ -82,7 +82,7 @@ export const ActionButton = forwardRef((props: ActionButtonProps, ref) => {
|
||||
return (
|
||||
<Link
|
||||
ref={ref as ForwardedRef<HTMLAnchorElement>}
|
||||
to={to || paths.INDEX}
|
||||
to={to || PATHS.INDEX}
|
||||
className={classNames}
|
||||
{...rest}
|
||||
>
|
||||
@ -105,7 +105,7 @@ export const ActionButton = forwardRef((props: ActionButtonProps, ref) => {
|
||||
return (
|
||||
<Link
|
||||
ref={ref as ForwardedRef<HTMLAnchorElement>}
|
||||
to={to || paths.INDEX}
|
||||
to={to || PATHS.INDEX}
|
||||
className={classNames}
|
||||
{...rest}
|
||||
target="_blank"
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import { useState, useEffect } from 'react'
|
||||
import { EngineCommandManagerEvents } from 'lang/std/engineConnection'
|
||||
import { engineCommandManager, sceneInfra } from 'lib/singletons'
|
||||
import { throttle, isReducedMotion } from 'lib/utils'
|
||||
|
||||
@ -13,9 +14,12 @@ export const CamToggle = () => {
|
||||
const [enableRotate, setEnableRotate] = useState(true)
|
||||
|
||||
useEffect(() => {
|
||||
engineCommandManager.waitForReady.then(async () => {
|
||||
sceneInfra.camControls.dollyZoom(fov)
|
||||
})
|
||||
engineCommandManager.addEventListener(
|
||||
EngineCommandManagerEvents.SceneReady,
|
||||
async () => {
|
||||
sceneInfra.camControls.dollyZoom(fov)
|
||||
}
|
||||
)
|
||||
}, [])
|
||||
|
||||
const toggleCamera = () => {
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { useMachine } from '@xstate/react'
|
||||
import { useNavigate, useRouteLoaderData } from 'react-router-dom'
|
||||
import { type IndexLoaderData } from 'lib/types'
|
||||
import { paths } from 'lib/paths'
|
||||
import { PATHS } from 'lib/paths'
|
||||
import React, { createContext } from 'react'
|
||||
import { toast } from 'react-hot-toast'
|
||||
import {
|
||||
@ -38,7 +38,7 @@ export const FileMachineProvider = ({
|
||||
}) => {
|
||||
const navigate = useNavigate()
|
||||
const { commandBarSend } = useCommandsContext()
|
||||
const { project, file } = useRouteLoaderData(paths.FILE) as IndexLoaderData
|
||||
const { project, file } = useRouteLoaderData(PATHS.FILE) as IndexLoaderData
|
||||
|
||||
const [state, send] = useMachine(fileMachine, {
|
||||
context: {
|
||||
@ -50,7 +50,7 @@ export const FileMachineProvider = ({
|
||||
if (event.data && 'name' in event.data) {
|
||||
commandBarSend({ type: 'Close' })
|
||||
navigate(
|
||||
`${paths.FILE}/${encodeURIComponent(
|
||||
`${PATHS.FILE}/${encodeURIComponent(
|
||||
context.selectedDirectory + sep() + event.data.name
|
||||
)}`
|
||||
)
|
||||
@ -60,7 +60,7 @@ export const FileMachineProvider = ({
|
||||
event.data.path.endsWith(FILE_EXT)
|
||||
) {
|
||||
// Don't navigate to newly created directories
|
||||
navigate(`${paths.FILE}/${encodeURIComponent(event.data.path)}`)
|
||||
navigate(`${PATHS.FILE}/${encodeURIComponent(event.data.path)}`)
|
||||
}
|
||||
},
|
||||
addFileToRenamingQueue: assign({
|
||||
@ -130,11 +130,11 @@ export const FileMachineProvider = ({
|
||||
|
||||
if (oldPath === file?.path && project?.path) {
|
||||
// If we just renamed the current file, navigate to the new path
|
||||
navigate(paths.FILE + '/' + encodeURIComponent(newPath))
|
||||
navigate(PATHS.FILE + '/' + encodeURIComponent(newPath))
|
||||
} else if (file?.path.includes(oldPath)) {
|
||||
// If we just renamed a directory that the current file is in, navigate to the new path
|
||||
navigate(
|
||||
paths.FILE +
|
||||
PATHS.FILE +
|
||||
'/' +
|
||||
encodeURIComponent(file.path.replace(oldPath, newDirPath))
|
||||
)
|
||||
@ -169,7 +169,7 @@ export const FileMachineProvider = ({
|
||||
file?.path.includes(event.data.path)) &&
|
||||
project?.path
|
||||
) {
|
||||
navigate(paths.FILE + '/' + encodeURIComponent(project.path))
|
||||
navigate(PATHS.FILE + '/' + encodeURIComponent(project.path))
|
||||
}
|
||||
|
||||
return `Successfully deleted ${isDir ? 'folder' : 'file'} "${
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import type { FileEntry, IndexLoaderData } from 'lib/types'
|
||||
import { paths } from 'lib/paths'
|
||||
import { PATHS } from 'lib/paths'
|
||||
import { ActionButton } from './ActionButton'
|
||||
import Tooltip from './Tooltip'
|
||||
import { Dispatch, useCallback, useEffect, useRef, useState } from 'react'
|
||||
@ -176,6 +176,7 @@ const FileTreeItem = ({
|
||||
)
|
||||
codeManager.writeToFile()
|
||||
|
||||
// Prevent seeing the model built one piece at a time when changing files
|
||||
kclManager.isFirstRender = true
|
||||
kclManager.executeCode(true).then(() => {
|
||||
kclManager.isFirstRender = false
|
||||
@ -186,7 +187,7 @@ const FileTreeItem = ({
|
||||
onFileOpen(fileOrDir.path, project?.path || null)
|
||||
|
||||
// Open kcl files
|
||||
navigate(`${paths.FILE}/${encodeURIComponent(fileOrDir.path)}`)
|
||||
navigate(`${PATHS.FILE}/${encodeURIComponent(fileOrDir.path)}`)
|
||||
}
|
||||
onNavigateToFile?.()
|
||||
}
|
||||
@ -446,7 +447,7 @@ export const FileTreeInner = ({
|
||||
}: {
|
||||
onNavigateToFile?: () => void
|
||||
}) => {
|
||||
const loaderData = useRouteLoaderData(paths.FILE) as IndexLoaderData
|
||||
const loaderData = useRouteLoaderData(PATHS.FILE) as IndexLoaderData
|
||||
const { send: fileSend, context: fileContext } = useFileContext()
|
||||
const { send: modelingSend } = useModelingContext()
|
||||
const documentHasFocus = useDocumentHasFocus()
|
||||
|
||||
@ -4,7 +4,7 @@ import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext'
|
||||
import { CustomIcon } from './CustomIcon'
|
||||
import { useLocation, useNavigate } from 'react-router-dom'
|
||||
import { createAndOpenNewProject } from 'lib/tauriFS'
|
||||
import { paths } from 'lib/paths'
|
||||
import { PATHS } from 'lib/paths'
|
||||
import { useAbsoluteFilePath } from 'hooks/useAbsoluteFilePath'
|
||||
import { useLspContext } from './LspProvider'
|
||||
|
||||
@ -16,7 +16,7 @@ export function HelpMenu(props: React.PropsWithChildren) {
|
||||
const location = useLocation()
|
||||
const { onProjectOpen } = useLspContext()
|
||||
const filePath = useAbsoluteFilePath()
|
||||
const isInProject = location.pathname.includes(paths.FILE)
|
||||
const isInProject = location.pathname.includes(PATHS.FILE)
|
||||
const navigate = useNavigate()
|
||||
const { settings } = useSettingsAuthContext()
|
||||
|
||||
@ -89,10 +89,10 @@ export function HelpMenu(props: React.PropsWithChildren) {
|
||||
<HelpMenuItem
|
||||
as="button"
|
||||
onClick={() => {
|
||||
const targetPath = location.pathname.includes(paths.FILE)
|
||||
? filePath + paths.SETTINGS
|
||||
: paths.HOME + paths.SETTINGS
|
||||
navigate(targetPath + '?tab=keybindings')
|
||||
const targetPath = location.pathname.includes(PATHS.FILE)
|
||||
? filePath + PATHS.SETTINGS_KEYBINDINGS
|
||||
: PATHS.HOME + PATHS.SETTINGS_KEYBINDINGS
|
||||
navigate(targetPath)
|
||||
}}
|
||||
>
|
||||
Keyboard shortcuts
|
||||
@ -108,7 +108,7 @@ export function HelpMenu(props: React.PropsWithChildren) {
|
||||
},
|
||||
})
|
||||
if (isInProject) {
|
||||
navigate(filePath + paths.ONBOARDING.INDEX)
|
||||
navigate(filePath + PATHS.ONBOARDING.INDEX)
|
||||
} else {
|
||||
createAndOpenNewProject({ onProjectOpen, navigate })
|
||||
}
|
||||
|
||||
@ -47,7 +47,7 @@ const Loading = ({ children }: React.PropsWithChildren) => {
|
||||
onConnectionStateChange as EventListener
|
||||
)
|
||||
}
|
||||
}, [])
|
||||
}, [engineCommandManager, engineCommandManager.engineConnection])
|
||||
|
||||
useEffect(() => {
|
||||
// Don't set long loading time if there's a more severe error
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { APP_VERSION } from 'routes/Settings'
|
||||
import { CustomIcon } from 'components/CustomIcon'
|
||||
import Tooltip from 'components/Tooltip'
|
||||
import { paths } from 'lib/paths'
|
||||
import { PATHS } from 'lib/paths'
|
||||
import { NetworkHealthIndicator } from 'components/NetworkHealthIndicator'
|
||||
import { HelpMenu } from './HelpMenu'
|
||||
import { Link, useLocation } from 'react-router-dom'
|
||||
@ -87,9 +87,9 @@ export function LowerRightControls({
|
||||
</a>
|
||||
<Link
|
||||
to={
|
||||
location.pathname.includes(paths.FILE)
|
||||
? filePath + paths.SETTINGS
|
||||
: paths.HOME + paths.SETTINGS
|
||||
location.pathname.includes(PATHS.FILE)
|
||||
? filePath + PATHS.SETTINGS_PROJECT
|
||||
: PATHS.HOME + PATHS.SETTINGS
|
||||
}
|
||||
>
|
||||
<CustomIcon
|
||||
|
||||
@ -14,7 +14,7 @@ import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext'
|
||||
import { Extension } from '@codemirror/state'
|
||||
import { LanguageSupport } from '@codemirror/language'
|
||||
import { useNavigate } from 'react-router-dom'
|
||||
import { paths } from 'lib/paths'
|
||||
import { PATHS } from 'lib/paths'
|
||||
import { FileEntry } from 'lib/types'
|
||||
import Worker from 'editor/plugins/lsp/worker.ts?worker'
|
||||
import {
|
||||
@ -260,7 +260,7 @@ export const LspProvider = ({ children }: { children: React.ReactNode }) => {
|
||||
})
|
||||
|
||||
if (redirect) {
|
||||
navigate(paths.HOME)
|
||||
navigate(PATHS.HOME)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -78,7 +78,12 @@ import { err, trap } from 'lib/trap'
|
||||
import { useCommandsContext } from 'hooks/useCommandsContext'
|
||||
import { modelingMachineEvent } from 'editor/manager'
|
||||
import { hasValidFilletSelection } from 'lang/modifyAst/addFillet'
|
||||
import { ExportIntent } from 'lang/std/engineConnection'
|
||||
import {
|
||||
ExportIntent,
|
||||
EngineConnectionState,
|
||||
EngineConnectionStateType,
|
||||
EngineConnectionEvents,
|
||||
} from 'lang/std/engineConnection'
|
||||
|
||||
type MachineContext<T extends AnyStateMachine> = {
|
||||
state: StateFrom<T>
|
||||
@ -154,7 +159,10 @@ export const ModelingMachineProvider = ({
|
||||
sceneInfra.camControls.syncDirection = 'engineToClient'
|
||||
|
||||
store.videoElement?.pause()
|
||||
|
||||
kclManager.isFirstRender = true
|
||||
kclManager.executeCode().then(() => {
|
||||
kclManager.isFirstRender = false
|
||||
if (engineCommandManager.engineConnection?.idleMode) return
|
||||
|
||||
store.videoElement?.play().catch((e) => {
|
||||
@ -380,7 +388,8 @@ export const ModelingMachineProvider = ({
|
||||
},
|
||||
},
|
||||
storage: 'ascii',
|
||||
units: defaultUnit.current,
|
||||
// Convert all units to mm since that is what the slicer expects.
|
||||
units: 'mm',
|
||||
selection: { type: 'default_scene' },
|
||||
}
|
||||
|
||||
@ -909,15 +918,19 @@ export const ModelingMachineProvider = ({
|
||||
}
|
||||
)
|
||||
|
||||
useSetupEngineManager(streamRef, token, {
|
||||
pool: pool,
|
||||
theme: theme.current,
|
||||
highlightEdges: highlightEdges.current,
|
||||
enableSSAO: enableSSAO.current,
|
||||
useSetupEngineManager(
|
||||
streamRef,
|
||||
modelingSend,
|
||||
modelingContext: modelingState.context,
|
||||
showScaleGrid: showScaleGrid.current,
|
||||
})
|
||||
modelingState.context,
|
||||
{
|
||||
pool: pool,
|
||||
theme: theme.current,
|
||||
highlightEdges: highlightEdges.current,
|
||||
enableSSAO: enableSSAO.current,
|
||||
showScaleGrid: showScaleGrid.current,
|
||||
},
|
||||
token
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
kclManager.registerExecuteCallback(() => {
|
||||
@ -945,17 +958,25 @@ export const ModelingMachineProvider = ({
|
||||
}, [modelingState.context.selectionRanges])
|
||||
|
||||
useEffect(() => {
|
||||
const offlineCallback = () => {
|
||||
const onConnectionStateChanged = ({ detail }: CustomEvent) => {
|
||||
// If we are in sketch mode we need to exit it.
|
||||
// TODO: how do i check if we are in a sketch mode, I only want to call
|
||||
// this then.
|
||||
modelingSend({ type: 'Cancel' })
|
||||
if (detail.type === EngineConnectionStateType.Disconnecting) {
|
||||
modelingSend({ type: 'Cancel' })
|
||||
}
|
||||
}
|
||||
window.addEventListener('offline', offlineCallback)
|
||||
engineCommandManager.engineConnection?.addEventListener(
|
||||
EngineConnectionEvents.ConnectionStateChanged,
|
||||
onConnectionStateChanged as EventListener
|
||||
)
|
||||
return () => {
|
||||
window.removeEventListener('offline', offlineCallback)
|
||||
engineCommandManager.engineConnection?.removeEventListener(
|
||||
EngineConnectionEvents.ConnectionStateChanged,
|
||||
onConnectionStateChanged as EventListener
|
||||
)
|
||||
}
|
||||
}, [modelingSend])
|
||||
}, [engineCommandManager.engineConnection, modelingSend])
|
||||
|
||||
// Allow using the delete key to delete solids
|
||||
useHotkeys(['backspace', 'delete', 'del'], () => {
|
||||
|
||||
@ -64,13 +64,6 @@ export const KclEditorPane = () => {
|
||||
: context.app.theme.current
|
||||
const { copilotLSP, kclLSP } = useLspContext()
|
||||
|
||||
useEffect(() => {
|
||||
if (typeof window === 'undefined') return
|
||||
const onlineCallback = () => kclManager.executeCode(true)
|
||||
window.addEventListener('online', onlineCallback)
|
||||
return () => window.removeEventListener('online', onlineCallback)
|
||||
}, [])
|
||||
|
||||
// Since these already exist in the editor, we don't need to define them
|
||||
// with the wrapper.
|
||||
useHotkeys('mod+z', (e) => {
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { FormEvent, useEffect, useRef, useState } from 'react'
|
||||
import { paths } from 'lib/paths'
|
||||
import { PATHS } from 'lib/paths'
|
||||
import { Link } from 'react-router-dom'
|
||||
import { ActionButton } from '../ActionButton'
|
||||
import { FILE_EXT } from 'lib/constants'
|
||||
@ -79,7 +79,7 @@ function ProjectCard({
|
||||
>
|
||||
<Link
|
||||
data-testid="project-link"
|
||||
to={`${paths.FILE}/${encodeURIComponent(project.default_file)}`}
|
||||
to={`${PATHS.FILE}/${encodeURIComponent(project.default_file)}`}
|
||||
className="flex flex-col flex-1 !no-underline !text-chalkboard-110 dark:!text-chalkboard-10 group-hover:!hue-rotate-0 min-h-[5em] divide-y divide-primary/40 dark:divide-chalkboard-80 group-hover:!divide-primary"
|
||||
>
|
||||
{/* <div className="h-36 relative overflow-hidden bg-gradient-to-b from-transparent to-primary/10 rounded-t-sm">
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { Popover, Transition } from '@headlessui/react'
|
||||
import { ActionButton, ActionButtonProps } from './ActionButton'
|
||||
import { type IndexLoaderData } from 'lib/types'
|
||||
import { paths } from 'lib/paths'
|
||||
import { PATHS } from 'lib/paths'
|
||||
import { isTauri } from '../lib/isTauri'
|
||||
import { Link, useLocation, useNavigate } from 'react-router-dom'
|
||||
import { Fragment, useMemo } from 'react'
|
||||
@ -63,7 +63,7 @@ function AppLogoLink({
|
||||
// Clear the scene and end the session.
|
||||
engineCommandManager.endSession()
|
||||
}}
|
||||
to={paths.HOME}
|
||||
to={PATHS.HOME}
|
||||
className={wrapperClassName + ' hover:before:brightness-110'}
|
||||
>
|
||||
<Logo className={logoClassName} />
|
||||
@ -116,10 +116,10 @@ function ProjectMenuPopover({
|
||||
</>
|
||||
),
|
||||
onClick: () => {
|
||||
const targetPath = location.pathname.includes(paths.FILE)
|
||||
? filePath + paths.SETTINGS
|
||||
: paths.HOME + paths.SETTINGS
|
||||
navigate(targetPath + '?tab=project')
|
||||
const targetPath = location.pathname.includes(PATHS.FILE)
|
||||
? filePath + PATHS.SETTINGS_PROJECT
|
||||
: PATHS.HOME + PATHS.SETTINGS_PROJECT
|
||||
navigate(targetPath)
|
||||
},
|
||||
},
|
||||
'break',
|
||||
|
||||
@ -16,7 +16,7 @@ import { getInitialDefaultDir, showInFolder } from 'lib/tauri'
|
||||
import toast from 'react-hot-toast'
|
||||
import { APP_VERSION } from 'routes/Settings'
|
||||
import { createAndOpenNewProject, getSettingsFolderPaths } from 'lib/tauriFS'
|
||||
import { paths } from 'lib/paths'
|
||||
import { PATHS } from 'lib/paths'
|
||||
import { useDotDotSlash } from 'hooks/useDotDotSlash'
|
||||
import { sep } from '@tauri-apps/api/path'
|
||||
import { ForwardedRef, forwardRef, useEffect } from 'react'
|
||||
@ -44,8 +44,8 @@ export const AllSettingsFields = forwardRef(
|
||||
isFileSettings && isTauri()
|
||||
? decodeURI(
|
||||
location.pathname
|
||||
.replace(paths.FILE + '/', '')
|
||||
.replace(paths.SETTINGS, '')
|
||||
.replace(PATHS.FILE + '/', '')
|
||||
.replace(PATHS.SETTINGS, '')
|
||||
.slice(0, decodeURI(location.pathname).lastIndexOf(sep()))
|
||||
)
|
||||
: undefined
|
||||
@ -70,7 +70,7 @@ export const AllSettingsFields = forwardRef(
|
||||
if (isFileSettings) {
|
||||
// If we're in a project, first navigate to the onboarding start here
|
||||
// so we can trigger the warning screen if necessary
|
||||
navigate(dotDotSlash(1) + paths.ONBOARDING.INDEX)
|
||||
navigate(dotDotSlash(1) + PATHS.ONBOARDING.INDEX)
|
||||
} else {
|
||||
// If we're in the global settings, create a new project and navigate
|
||||
// to the onboarding start in that project
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { useMachine } from '@xstate/react'
|
||||
import { useNavigate, useRouteLoaderData } from 'react-router-dom'
|
||||
import { paths } from 'lib/paths'
|
||||
import { PATHS } from 'lib/paths'
|
||||
import { authMachine, TOKEN_PERSIST_KEY } from '../machines/authMachine'
|
||||
import withBaseUrl from '../lib/withBaseURL'
|
||||
import React, { createContext, useEffect } from 'react'
|
||||
@ -60,8 +60,8 @@ export const SettingsAuthProvider = ({
|
||||
}: {
|
||||
children: React.ReactNode
|
||||
}) => {
|
||||
const loadedSettings = useRouteLoaderData(paths.INDEX) as typeof settings
|
||||
const loadedProject = useRouteLoaderData(paths.FILE) as IndexLoaderData
|
||||
const loadedSettings = useRouteLoaderData(PATHS.INDEX) as typeof settings
|
||||
const loadedProject = useRouteLoaderData(PATHS.FILE) as IndexLoaderData
|
||||
return (
|
||||
<SettingsAuthProviderBase
|
||||
loadedSettings={loadedSettings}
|
||||
@ -191,6 +191,7 @@ export const SettingsAuthProviderBase = ({
|
||||
allSettingsIncludesUnitChange ||
|
||||
resetSettingsIncludesUnitChange
|
||||
) {
|
||||
// Unit changes requires a re-exec of code
|
||||
kclManager.isFirstRender = true
|
||||
kclManager.executeCode(true).then(() => {
|
||||
kclManager.isFirstRender = false
|
||||
@ -296,12 +297,12 @@ export const SettingsAuthProviderBase = ({
|
||||
const [authState, authSend, authActor] = useMachine(authMachine, {
|
||||
actions: {
|
||||
goToSignInPage: () => {
|
||||
navigate(paths.SIGN_IN)
|
||||
navigate(PATHS.SIGN_IN)
|
||||
logout()
|
||||
},
|
||||
goToIndexPage: () => {
|
||||
if (window.location.pathname.includes(paths.SIGN_IN)) {
|
||||
navigate(paths.INDEX)
|
||||
if (window.location.pathname.includes(PATHS.SIGN_IN)) {
|
||||
navigate(PATHS.INDEX)
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
@ -11,21 +11,27 @@ import { sendSelectEventToEngine } from 'lib/selections'
|
||||
import { kclManager, engineCommandManager, sceneInfra } from 'lib/singletons'
|
||||
import { useAppStream } from 'AppState'
|
||||
import {
|
||||
EngineCommandManagerEvents,
|
||||
EngineConnectionStateType,
|
||||
DisconnectingType,
|
||||
} from 'lang/std/engineConnection'
|
||||
|
||||
enum StreamState {
|
||||
Playing = 'playing',
|
||||
Paused = 'paused',
|
||||
Resuming = 'resuming',
|
||||
Unset = 'unset',
|
||||
}
|
||||
|
||||
export const Stream = () => {
|
||||
const [isLoading, setIsLoading] = useState(true)
|
||||
const [isFirstRender, setIsFirstRender] = useState(kclManager.isFirstRender)
|
||||
const [clickCoords, setClickCoords] = useState<{ x: number; y: number }>()
|
||||
const videoRef = useRef<HTMLVideoElement>(null)
|
||||
const { settings } = useSettingsAuthContext()
|
||||
const { state, send, context } = useModelingContext()
|
||||
const { mediaStream } = useAppStream()
|
||||
const { overallState, immediateState } = useNetworkContext()
|
||||
const [isFreezeFrame, setIsFreezeFrame] = useState(false)
|
||||
const [isPaused, setIsPaused] = useState(false)
|
||||
const [streamState, setStreamState] = useState(StreamState.Unset)
|
||||
|
||||
const IDLE = settings.context.app.streamIdleMode.current
|
||||
|
||||
@ -38,10 +44,7 @@ export const Stream = () => {
|
||||
immediateState.type === EngineConnectionStateType.Disconnecting &&
|
||||
immediateState.value.type === DisconnectingType.Pause
|
||||
) {
|
||||
setIsPaused(true)
|
||||
}
|
||||
if (immediateState.type === EngineConnectionStateType.Connecting) {
|
||||
setIsPaused(false)
|
||||
setStreamState(StreamState.Paused)
|
||||
}
|
||||
}, [immediateState])
|
||||
|
||||
@ -76,8 +79,11 @@ export const Stream = () => {
|
||||
let timeoutIdIdleA: ReturnType<typeof setTimeout> | undefined = undefined
|
||||
|
||||
const teardown = () => {
|
||||
// Already paused
|
||||
if (streamState === StreamState.Paused) return
|
||||
|
||||
videoRef.current?.pause()
|
||||
setIsFreezeFrame(true)
|
||||
setStreamState(StreamState.Paused)
|
||||
sceneInfra.modelingSend({ type: 'Cancel' })
|
||||
// Give video time to pause
|
||||
window.requestAnimationFrame(() => {
|
||||
@ -91,7 +97,7 @@ export const Stream = () => {
|
||||
timeoutIdIdleA = setTimeout(teardown, IDLE_TIME_MS)
|
||||
} else if (!engineCommandManager.engineConnection?.isReady()) {
|
||||
clearTimeout(timeoutIdIdleA)
|
||||
engineCommandManager.engineConnection?.connect(true)
|
||||
setStreamState(StreamState.Resuming)
|
||||
}
|
||||
}
|
||||
|
||||
@ -106,10 +112,15 @@ export const Stream = () => {
|
||||
let timeoutIdIdleB: ReturnType<typeof setTimeout> | undefined = undefined
|
||||
|
||||
const onAnyInput = () => {
|
||||
// Clear both timers
|
||||
clearTimeout(timeoutIdIdleA)
|
||||
clearTimeout(timeoutIdIdleB)
|
||||
timeoutIdIdleB = setTimeout(teardown, IDLE_TIME_MS)
|
||||
if (streamState === StreamState.Playing) {
|
||||
// Clear both timers
|
||||
clearTimeout(timeoutIdIdleA)
|
||||
clearTimeout(timeoutIdIdleB)
|
||||
timeoutIdIdleB = setTimeout(teardown, IDLE_TIME_MS)
|
||||
}
|
||||
if (streamState === StreamState.Paused) {
|
||||
setStreamState(StreamState.Resuming)
|
||||
}
|
||||
}
|
||||
|
||||
if (IDLE) {
|
||||
@ -124,7 +135,27 @@ export const Stream = () => {
|
||||
timeoutIdIdleB = setTimeout(teardown, IDLE_TIME_MS)
|
||||
}
|
||||
|
||||
const onSceneReady = () => {
|
||||
kclManager.isFirstRender = true
|
||||
setStreamState(StreamState.Playing)
|
||||
kclManager.executeCode(true).then(() => {
|
||||
videoRef.current?.play().catch((e) => {
|
||||
console.warn('Video playing was prevented', e, videoRef.current)
|
||||
})
|
||||
kclManager.isFirstRender = false
|
||||
})
|
||||
}
|
||||
|
||||
engineCommandManager.addEventListener(
|
||||
EngineCommandManagerEvents.SceneReady,
|
||||
onSceneReady
|
||||
)
|
||||
|
||||
return () => {
|
||||
engineCommandManager.removeEventListener(
|
||||
EngineCommandManagerEvents.SceneReady,
|
||||
onSceneReady
|
||||
)
|
||||
globalThis?.window?.document?.removeEventListener('paste', handlePaste, {
|
||||
capture: true,
|
||||
})
|
||||
@ -152,10 +183,11 @@ export const Stream = () => {
|
||||
)
|
||||
}
|
||||
}
|
||||
}, [IDLE])
|
||||
}, [IDLE, streamState])
|
||||
|
||||
// HOT FIX: for https://github.com/KittyCAD/modeling-app/pull/3250
|
||||
// TODO review if there's a better way to play the stream again.
|
||||
useEffect(() => {
|
||||
setIsFirstRender(kclManager.isFirstRender)
|
||||
if (!kclManager.isFirstRender)
|
||||
setTimeout(() =>
|
||||
// execute in the next event loop
|
||||
@ -163,7 +195,6 @@ export const Stream = () => {
|
||||
console.warn('Video playing was prevented', e, videoRef.current)
|
||||
})
|
||||
)
|
||||
setIsFreezeFrame(!kclManager.isFirstRender)
|
||||
}, [kclManager.isFirstRender])
|
||||
|
||||
useEffect(() => {
|
||||
@ -288,7 +319,8 @@ export const Stream = () => {
|
||||
<ClientSideScene
|
||||
cameraControls={settings.context.modeling.mouseControls.current}
|
||||
/>
|
||||
{isPaused && (
|
||||
{(streamState === StreamState.Paused ||
|
||||
streamState === StreamState.Resuming) && (
|
||||
<div className="text-center absolute inset-0">
|
||||
<div
|
||||
className="flex flex-col items-center justify-center h-screen"
|
||||
@ -310,16 +342,19 @@ export const Stream = () => {
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<p className="text-base mt-2 text-primary bold">Paused</p>
|
||||
<p className="text-base mt-2 text-primary bold">
|
||||
{streamState === StreamState.Paused && 'Paused'}
|
||||
{streamState === StreamState.Resuming && 'Resuming'}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{(!isNetworkOkay || isLoading || isFirstRender) && !isFreezeFrame && (
|
||||
{(!isNetworkOkay || isLoading || kclManager.isFirstRender) && (
|
||||
<div className="text-center absolute inset-0">
|
||||
<Loading>
|
||||
{!isNetworkOkay && !isLoading ? (
|
||||
{!isNetworkOkay && !isLoading && !kclManager.isFirstRender ? (
|
||||
<span data-testid="loading-stream">Stream disconnected...</span>
|
||||
) : !isLoading && isFirstRender ? (
|
||||
) : !isLoading && kclManager.isFirstRender ? (
|
||||
<span data-testid="loading-stream">Building scene...</span>
|
||||
) : (
|
||||
<span data-testid="loading-stream">Loading stream...</span>
|
||||
|
||||
@ -2,7 +2,7 @@ import { Popover, Transition } from '@headlessui/react'
|
||||
import { ActionButton, ActionButtonProps } from './ActionButton'
|
||||
import { useLocation, useNavigate } from 'react-router-dom'
|
||||
import { Fragment, useMemo, useState } from 'react'
|
||||
import { paths } from 'lib/paths'
|
||||
import { PATHS } from 'lib/paths'
|
||||
import { Models } from '@kittycad/lib'
|
||||
import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext'
|
||||
import { useAbsoluteFilePath } from 'hooks/useAbsoluteFilePath'
|
||||
@ -39,10 +39,10 @@ const UserSidebarMenu = ({ user }: { user?: User }) => {
|
||||
),
|
||||
'data-testid': 'user-settings',
|
||||
onClick: () => {
|
||||
const targetPath = location.pathname.includes(paths.FILE)
|
||||
? filePath + paths.SETTINGS
|
||||
: paths.HOME + paths.SETTINGS
|
||||
navigate(targetPath + '?tab=user')
|
||||
const targetPath = location.pathname.includes(PATHS.FILE)
|
||||
? filePath + PATHS.SETTINGS_USER
|
||||
: PATHS.HOME + PATHS.SETTINGS_USER
|
||||
navigate(targetPath)
|
||||
},
|
||||
},
|
||||
{
|
||||
@ -50,10 +50,10 @@ const UserSidebarMenu = ({ user }: { user?: User }) => {
|
||||
Element: 'button',
|
||||
children: 'Keyboard shortcuts',
|
||||
onClick: () => {
|
||||
const targetPath = location.pathname.includes(paths.FILE)
|
||||
? filePath + paths.SETTINGS
|
||||
: paths.HOME + paths.SETTINGS
|
||||
navigate(targetPath + '?tab=keybindings')
|
||||
const targetPath = location.pathname.includes(PATHS.FILE)
|
||||
? filePath + PATHS.SETTINGS_KEYBINDINGS
|
||||
: PATHS.HOME + PATHS.SETTINGS_KEYBINDINGS
|
||||
navigate(targetPath)
|
||||
},
|
||||
},
|
||||
{
|
||||
|
||||
@ -1,11 +1,11 @@
|
||||
import { type IndexLoaderData } from 'lib/types'
|
||||
import { BROWSER_PATH, paths } from 'lib/paths'
|
||||
import { BROWSER_PATH, PATHS } from 'lib/paths'
|
||||
import { useRouteLoaderData } from 'react-router-dom'
|
||||
|
||||
export function useAbsoluteFilePath() {
|
||||
const routeData = useRouteLoaderData(paths.FILE) as IndexLoaderData
|
||||
const routeData = useRouteLoaderData(PATHS.FILE) as IndexLoaderData
|
||||
|
||||
return (
|
||||
paths.FILE + '/' + encodeURIComponent(routeData?.file?.path || BROWSER_PATH)
|
||||
PATHS.FILE + '/' + encodeURIComponent(routeData?.file?.path || BROWSER_PATH)
|
||||
)
|
||||
}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { useRouteLoaderData } from 'react-router-dom'
|
||||
import { useSettingsAuthContext } from './useSettingsAuthContext'
|
||||
import { paths } from 'lib/paths'
|
||||
import { PATHS } from 'lib/paths'
|
||||
import { settings } from 'lib/settings/initialSettings'
|
||||
import { useEffect } from 'react'
|
||||
|
||||
@ -10,7 +10,7 @@ import { useEffect } from 'react'
|
||||
* in conjunction with additional uses of settingsLoader further down the router tree.
|
||||
* @param routeId - The id defined in Router.tsx to load the settings from.
|
||||
*/
|
||||
export function useRefreshSettings(routeId: string = paths.INDEX) {
|
||||
export function useRefreshSettings(routeId: string = PATHS.INDEX) {
|
||||
const ctx = useSettingsAuthContext()
|
||||
const routeData = useRouteLoaderData(routeId) as typeof settings
|
||||
|
||||
|
||||
@ -4,29 +4,30 @@ import { deferExecution } from 'lib/utils'
|
||||
import { Themes } from 'lib/theme'
|
||||
import { makeDefaultPlanes, modifyGrid } from 'lang/wasm'
|
||||
import { useModelingContext } from './useModelingContext'
|
||||
import { useNetworkContext } from 'hooks/useNetworkContext'
|
||||
import { useAppState, useAppStream } from 'AppState'
|
||||
import { SettingsViaQueryString } from 'lib/settings/settingsTypes'
|
||||
import {
|
||||
EngineConnectionStateType,
|
||||
EngineConnectionEvents,
|
||||
DisconnectingType,
|
||||
} from 'lang/std/engineConnection'
|
||||
|
||||
export function useSetupEngineManager(
|
||||
streamRef: React.RefObject<HTMLDivElement>,
|
||||
token?: string,
|
||||
modelingSend: ReturnType<typeof useModelingContext>['send'],
|
||||
modelingContext: ReturnType<typeof useModelingContext>['context'],
|
||||
settings = {
|
||||
pool: null,
|
||||
theme: Themes.System,
|
||||
highlightEdges: true,
|
||||
enableSSAO: true,
|
||||
modelingSend: (() => {}) as any,
|
||||
modelingContext: {} as any,
|
||||
showScaleGrid: false,
|
||||
} as {
|
||||
pool: string | null
|
||||
theme: Themes
|
||||
highlightEdges: boolean
|
||||
enableSSAO: boolean
|
||||
modelingSend: ReturnType<typeof useModelingContext>['send']
|
||||
modelingContext: ReturnType<typeof useModelingContext>['context']
|
||||
showScaleGrid: boolean
|
||||
}
|
||||
} as SettingsViaQueryString,
|
||||
token?: string
|
||||
) {
|
||||
const networkContext = useNetworkContext()
|
||||
const { pingPongHealth, immediateState } = networkContext
|
||||
const { setAppState } = useAppState()
|
||||
const { setMediaStream } = useAppStream()
|
||||
|
||||
@ -35,10 +36,10 @@ export function useSetupEngineManager(
|
||||
if (settings.pool) {
|
||||
// override the pool param (?pool=) to request a specific engine instance
|
||||
// from a particular pool.
|
||||
engineCommandManager.pool = settings.pool
|
||||
engineCommandManager.settings.pool = settings.pool
|
||||
}
|
||||
|
||||
const startEngineInstance = (restart: boolean = false) => {
|
||||
const startEngineInstance = () => {
|
||||
// Load the engine command manager once with the initial width and height,
|
||||
// then we do not want to reload it.
|
||||
const { width: quadWidth, height: quadHeight } = getDimensions(
|
||||
@ -50,14 +51,6 @@ export function useSetupEngineManager(
|
||||
setIsStreamReady: (isStreamReady) => setAppState({ isStreamReady }),
|
||||
width: quadWidth,
|
||||
height: quadHeight,
|
||||
executeCode: () => {
|
||||
// We only want to execute the code here that we already have set.
|
||||
// Nothing else.
|
||||
kclManager.isFirstRender = true
|
||||
return kclManager.executeCode(true).then(() => {
|
||||
kclManager.isFirstRender = false
|
||||
})
|
||||
},
|
||||
token,
|
||||
settings,
|
||||
makeDefaultPlanes: () => {
|
||||
@ -67,7 +60,7 @@ export function useSetupEngineManager(
|
||||
return modifyGrid(kclManager.engineCommandManager, hidden)
|
||||
},
|
||||
})
|
||||
settings.modelingSend({
|
||||
modelingSend({
|
||||
type: 'Set context',
|
||||
data: {
|
||||
streamDimensions: {
|
||||
@ -90,24 +83,44 @@ export function useSetupEngineManager(
|
||||
}, [
|
||||
streamRef?.current?.offsetWidth,
|
||||
streamRef?.current?.offsetHeight,
|
||||
settings.modelingSend,
|
||||
modelingSend,
|
||||
])
|
||||
|
||||
useEffect(() => {
|
||||
if (pingPongHealth === 'TIMEOUT') {
|
||||
engineCommandManager.tearDown()
|
||||
}
|
||||
}, [pingPongHealth])
|
||||
|
||||
useEffect(() => {
|
||||
const intervalId = setInterval(() => {
|
||||
if (immediateState.type === EngineConnectionStateType.Disconnected) {
|
||||
engineCommandManager.engineConnection = undefined
|
||||
startEngineInstance()
|
||||
}
|
||||
}, 3000)
|
||||
return () => {
|
||||
clearInterval(intervalId)
|
||||
}
|
||||
}, [immediateState])
|
||||
|
||||
useEffect(() => {
|
||||
engineCommandManager.settings.theme = settings.theme
|
||||
|
||||
const handleResize = deferExecution(() => {
|
||||
const { width, height } = getDimensions(
|
||||
streamRef?.current?.offsetWidth ?? 0,
|
||||
streamRef?.current?.offsetHeight ?? 0
|
||||
)
|
||||
if (
|
||||
settings.modelingContext.store.streamDimensions.streamWidth !== width ||
|
||||
settings.modelingContext.store.streamDimensions.streamHeight !== height
|
||||
modelingContext.store.streamDimensions.streamWidth !== width ||
|
||||
modelingContext.store.streamDimensions.streamHeight !== height
|
||||
) {
|
||||
engineCommandManager.handleResize({
|
||||
streamWidth: width,
|
||||
streamHeight: height,
|
||||
})
|
||||
settings.modelingSend({
|
||||
modelingSend({
|
||||
type: 'Set context',
|
||||
data: {
|
||||
streamDimensions: {
|
||||
@ -120,7 +133,7 @@ export function useSetupEngineManager(
|
||||
}, 500)
|
||||
|
||||
const onOnline = () => {
|
||||
startEngineInstance(true)
|
||||
startEngineInstance()
|
||||
}
|
||||
|
||||
const onVisibilityChange = () => {
|
||||
@ -136,10 +149,18 @@ export function useSetupEngineManager(
|
||||
window.document.addEventListener('visibilitychange', onVisibilityChange)
|
||||
|
||||
const onAnyInput = () => {
|
||||
if (
|
||||
const isEngineNotReadyOrConnecting =
|
||||
!engineCommandManager.engineConnection?.isReady() &&
|
||||
!engineCommandManager.engineConnection?.isConnecting()
|
||||
) {
|
||||
|
||||
const conn = engineCommandManager.engineConnection
|
||||
|
||||
const isStreamPaused =
|
||||
conn?.state.type === EngineConnectionStateType.Disconnecting &&
|
||||
conn?.state.value.type === DisconnectingType.Pause
|
||||
|
||||
if (isEngineNotReadyOrConnecting || isStreamPaused) {
|
||||
engineCommandManager.engineConnection = undefined
|
||||
startEngineInstance()
|
||||
}
|
||||
}
|
||||
@ -150,7 +171,6 @@ export function useSetupEngineManager(
|
||||
window.document.addEventListener('touchstart', onAnyInput)
|
||||
|
||||
const onOffline = () => {
|
||||
kclManager.isFirstRender = true
|
||||
engineCommandManager.tearDown()
|
||||
}
|
||||
|
||||
|
||||
@ -211,7 +211,6 @@ export class KclManager {
|
||||
type: string
|
||||
}
|
||||
): Promise<void> {
|
||||
await this?.engineCommandManager?.waitForReady
|
||||
const currentExecutionId = executionId || Date.now()
|
||||
this._cancelTokens.set(currentExecutionId, false)
|
||||
|
||||
@ -301,7 +300,6 @@ export class KclManager {
|
||||
codeManager.updateCodeEditor(newCode)
|
||||
// Write the file to disk.
|
||||
await codeManager.writeToFile()
|
||||
await this?.engineCommandManager?.waitForReady
|
||||
this._ast = { ...newAst }
|
||||
|
||||
const { logs, errors, programMemory } = await executeAst({
|
||||
|
||||
@ -14,6 +14,10 @@ import {
|
||||
} from './artifactGraph'
|
||||
import { err } from 'lib/trap'
|
||||
import { engineCommandManager, kclManager } from 'lib/singletons'
|
||||
import {
|
||||
EngineCommandManagerEvents,
|
||||
EngineConnectionEvents,
|
||||
} from 'lang/std/engineConnection'
|
||||
import { CI, VITE_KC_DEV_TOKEN } from 'env'
|
||||
import fsp from 'fs/promises'
|
||||
import fs from 'fs'
|
||||
@ -113,42 +117,44 @@ beforeAll(async () => {
|
||||
}
|
||||
|
||||
// THESE TEST WILL FAIL without VITE_KC_DEV_TOKEN set in .env.development.local
|
||||
engineCommandManager.start({
|
||||
disableWebRTC: true,
|
||||
token: VITE_KC_DEV_TOKEN,
|
||||
// there does seem to be a minimum resolution, not sure what it is but 256 works ok.
|
||||
width: 256,
|
||||
height: 256,
|
||||
executeCode: () => {},
|
||||
makeDefaultPlanes: () => makeDefaultPlanes(engineCommandManager),
|
||||
setMediaStream: () => {},
|
||||
setIsStreamReady: () => {},
|
||||
modifyGrid: async () => {},
|
||||
await new Promise((resolve) => {
|
||||
engineCommandManager.start({
|
||||
// disableWebRTC: true,
|
||||
token: VITE_KC_DEV_TOKEN,
|
||||
// there does seem to be a minimum resolution, not sure what it is but 256 works ok.
|
||||
width: 256,
|
||||
height: 256,
|
||||
makeDefaultPlanes: () => makeDefaultPlanes(engineCommandManager),
|
||||
setMediaStream: () => {},
|
||||
setIsStreamReady: () => {},
|
||||
modifyGrid: async () => {},
|
||||
callbackOnEngineLiteConnect: async () => {
|
||||
const cacheEntries = Object.entries(codeToWriteCacheFor) as [
|
||||
CodeKey,
|
||||
string
|
||||
][]
|
||||
const cacheToWriteToFileTemp: Partial<CacheShape> = {}
|
||||
for (const [codeKey, code] of cacheEntries) {
|
||||
const ast = parse(code)
|
||||
if (err(ast)) {
|
||||
console.error(ast)
|
||||
return Promise.reject(ast)
|
||||
}
|
||||
const result = await kclManager.executeAst(ast)
|
||||
|
||||
cacheToWriteToFileTemp[codeKey] = {
|
||||
orderedCommands: engineCommandManager.orderedCommands,
|
||||
responseMap: engineCommandManager.responseMap,
|
||||
}
|
||||
}
|
||||
const cache = JSON.stringify(cacheToWriteToFileTemp)
|
||||
|
||||
await fsp.mkdir(pathStart, { recursive: true })
|
||||
await fsp.writeFile(fullPath, cache)
|
||||
resolve(true)
|
||||
},
|
||||
})
|
||||
})
|
||||
await engineCommandManager.waitForReady
|
||||
|
||||
const cacheEntries = Object.entries(codeToWriteCacheFor) as [
|
||||
CodeKey,
|
||||
string
|
||||
][]
|
||||
const cacheToWriteToFileTemp: Partial<CacheShape> = {}
|
||||
for (const [codeKey, code] of cacheEntries) {
|
||||
const ast = parse(code)
|
||||
if (err(ast)) {
|
||||
console.error(ast)
|
||||
throw ast
|
||||
}
|
||||
await kclManager.executeAst(ast)
|
||||
|
||||
cacheToWriteToFileTemp[codeKey] = {
|
||||
orderedCommands: engineCommandManager.orderedCommands,
|
||||
responseMap: engineCommandManager.responseMap,
|
||||
}
|
||||
}
|
||||
const cache = JSON.stringify(cacheToWriteToFileTemp)
|
||||
|
||||
await fsp.mkdir(pathStart, { recursive: true })
|
||||
await fsp.writeFile(fullPath, cache)
|
||||
}, 20_000)
|
||||
|
||||
afterAll(() => {
|
||||
|
||||
@ -63,7 +63,7 @@ export class CoreDumpManager {
|
||||
|
||||
// Get the backend pool we've requested.
|
||||
pool(): string {
|
||||
return this.engineCommandManager.pool || ''
|
||||
return this.engineCommandManager.settings.pool || ''
|
||||
}
|
||||
|
||||
// Get the os information.
|
||||
|
||||
2
src/lib/machine-api.d.ts
vendored
@ -110,7 +110,7 @@ export interface components {
|
||||
/** @description The reason of the info command. */
|
||||
reason?: components['schemas']['Reason'] | null
|
||||
/** @description The result of the info command. */
|
||||
result: components['schemas']['Result']
|
||||
result?: components['schemas']['Result'] | null
|
||||
/** @description The sequence id. */
|
||||
sequence_id: components['schemas']['SequenceId']
|
||||
} & {
|
||||
|
||||
@ -22,11 +22,16 @@ type OnboardingPaths = {
|
||||
[K in keyof typeof onboardingPaths]: `/onboarding${(typeof onboardingPaths)[K]}`
|
||||
}
|
||||
|
||||
export const paths = {
|
||||
const SETTINGS = '/settings' as const
|
||||
|
||||
export const PATHS = {
|
||||
INDEX: '/',
|
||||
HOME: '/home',
|
||||
FILE: '/file',
|
||||
SETTINGS: '/settings',
|
||||
SETTINGS,
|
||||
SETTINGS_USER: `${SETTINGS}?tab=user` as const,
|
||||
SETTINGS_PROJECT: `${SETTINGS}?tab=project` as const,
|
||||
SETTINGS_KEYBINDINGS: `${SETTINGS}?tab=keybindings` as const,
|
||||
SIGN_IN: '/signin',
|
||||
ONBOARDING: prependRoutes(onboardingPaths)('/onboarding') as OnboardingPaths,
|
||||
} as const
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { ActionFunction, LoaderFunction, redirect } from 'react-router-dom'
|
||||
import { FileLoaderData, HomeLoaderData, IndexLoaderData } from './types'
|
||||
import { isTauri } from './isTauri'
|
||||
import { getProjectMetaByRouteId, paths } from './paths'
|
||||
import { getProjectMetaByRouteId, PATHS } from './paths'
|
||||
import { BROWSER_PATH } from 'lib/paths'
|
||||
import {
|
||||
BROWSER_FILE_NAME,
|
||||
@ -54,7 +54,7 @@ export const onboardingRedirectLoader: ActionFunction = async (args) => {
|
||||
const { settings } = await loadAndValidateSettings()
|
||||
const onboardingStatus = settings.app.onboardingStatus.current || ''
|
||||
const notEnRouteToOnboarding = !args.request.url.includes(
|
||||
paths.ONBOARDING.INDEX
|
||||
PATHS.ONBOARDING.INDEX
|
||||
)
|
||||
// '' is the initial state, 'done' and 'dismissed' are the final states
|
||||
const hasValidOnboardingStatus =
|
||||
@ -65,7 +65,7 @@ export const onboardingRedirectLoader: ActionFunction = async (args) => {
|
||||
|
||||
if (shouldRedirectToOnboarding) {
|
||||
return redirect(
|
||||
makeUrlPathRelative(paths.ONBOARDING.INDEX) + onboardingStatus.slice(1)
|
||||
makeUrlPathRelative(PATHS.ONBOARDING.INDEX) + onboardingStatus.slice(1)
|
||||
)
|
||||
}
|
||||
|
||||
@ -89,7 +89,7 @@ export const fileLoader: LoaderFunction = async ({
|
||||
|
||||
if (!current_file_name || !current_file_path || !project_name) {
|
||||
return redirect(
|
||||
`${paths.FILE}/${encodeURIComponent(
|
||||
`${PATHS.FILE}/${encodeURIComponent(
|
||||
`${params.id}${isTauri() ? sep() : '/'}${PROJECT_ENTRYPOINT}`
|
||||
)}`
|
||||
)
|
||||
@ -104,11 +104,9 @@ export const fileLoader: LoaderFunction = async ({
|
||||
// the file system and not the editor.
|
||||
codeManager.updateCurrentFilePath(current_file_path)
|
||||
codeManager.updateCodeStateEditor(code)
|
||||
|
||||
// We don't want to call await on execute code since we don't want to block the UI
|
||||
kclManager.isFirstRender = true
|
||||
kclManager.executeCode(true).then(() => {
|
||||
kclManager.isFirstRender = false
|
||||
})
|
||||
kclManager.executeCode(true)
|
||||
|
||||
// Set the file system manager to the project path
|
||||
// So that WASM gets an updated path for operations
|
||||
@ -160,7 +158,7 @@ export const homeLoader: LoaderFunction = async (): Promise<
|
||||
HomeLoaderData | Response
|
||||
> => {
|
||||
if (!isTauri()) {
|
||||
return redirect(paths.FILE + '/%2F' + BROWSER_PROJECT_NAME)
|
||||
return redirect(PATHS.FILE + '/%2F' + BROWSER_PROJECT_NAME)
|
||||
}
|
||||
const { configuration } = await loadAndValidateSettings()
|
||||
|
||||
|
||||
@ -2,6 +2,15 @@ import { type Models } from '@kittycad/lib'
|
||||
import { Setting, settings } from './initialSettings'
|
||||
import { AtLeast, PathValue, Paths } from 'lib/types'
|
||||
import { CommandArgumentConfig } from 'lib/commandTypes'
|
||||
import { Themes } from 'lib/theme'
|
||||
|
||||
export interface SettingsViaQueryString {
|
||||
pool: string | null
|
||||
theme: Themes
|
||||
highlightEdges: boolean
|
||||
enableSSAO: boolean
|
||||
showScaleGrid: boolean
|
||||
}
|
||||
|
||||
export enum UnitSystem {
|
||||
Imperial = 'imperial',
|
||||
|
||||
@ -8,7 +8,7 @@ import {
|
||||
PROJECT_ENTRYPOINT,
|
||||
} from 'lib/constants'
|
||||
import { bracket } from './exampleKcl'
|
||||
import { paths } from './paths'
|
||||
import { PATHS } from './paths'
|
||||
import {
|
||||
createNewProjectDirectory,
|
||||
listProjects,
|
||||
@ -156,8 +156,8 @@ export async function createAndOpenNewProject({
|
||||
null
|
||||
)
|
||||
navigate(
|
||||
`${paths.FILE}/${encodeURIComponent(newProject.default_file)}${
|
||||
paths.ONBOARDING.INDEX
|
||||
`${PATHS.FILE}/${encodeURIComponent(newProject.default_file)}${
|
||||
PATHS.ONBOARDING.INDEX
|
||||
}`
|
||||
)
|
||||
return newProject
|
||||
|
||||
@ -1,5 +1,8 @@
|
||||
import { Program, ProgramMemory, _executor, SourceRange } from '../lang/wasm'
|
||||
import { EngineCommandManager } from 'lang/std/engineConnection'
|
||||
import {
|
||||
EngineCommandManager,
|
||||
EngineCommandManagerEvents,
|
||||
} from 'lang/std/engineConnection'
|
||||
import { EngineCommand } from 'lang/std/artifactGraph'
|
||||
import { Models } from '@kittycad/lib'
|
||||
import { v4 as uuidv4 } from 'uuid'
|
||||
@ -82,7 +85,6 @@ export async function enginelessExecutor(
|
||||
setIsStreamReady: () => {},
|
||||
setMediaStream: () => {},
|
||||
}) as any as EngineCommandManager
|
||||
await mockEngineCommandManager.waitForReady
|
||||
mockEngineCommandManager.startNewSession()
|
||||
const programMemory = await _executor(ast, pm, mockEngineCommandManager, true)
|
||||
await mockEngineCommandManager.waitForAllCommands()
|
||||
@ -99,7 +101,6 @@ export async function executor(
|
||||
setMediaStream: () => {},
|
||||
width: 0,
|
||||
height: 0,
|
||||
executeCode: () => {},
|
||||
makeDefaultPlanes: () => {
|
||||
return new Promise((resolve) => resolve(defaultPlanes))
|
||||
},
|
||||
@ -107,9 +108,21 @@ export async function executor(
|
||||
return new Promise((resolve) => resolve())
|
||||
},
|
||||
})
|
||||
await engineCommandManager.waitForReady
|
||||
engineCommandManager.startNewSession()
|
||||
const programMemory = await _executor(ast, pm, engineCommandManager, false)
|
||||
await engineCommandManager.waitForAllCommands()
|
||||
return programMemory
|
||||
|
||||
return new Promise((resolve) => {
|
||||
engineCommandManager.addEventListener(
|
||||
EngineCommandManagerEvents.SceneReady,
|
||||
async () => {
|
||||
engineCommandManager.startNewSession()
|
||||
const programMemory = await _executor(
|
||||
ast,
|
||||
pm,
|
||||
engineCommandManager,
|
||||
false
|
||||
)
|
||||
await engineCommandManager.waitForAllCommands()
|
||||
Promise.resolve(programMemory)
|
||||
}
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
@ -16,7 +16,7 @@ import Loading from 'components/Loading'
|
||||
import { useMachine } from '@xstate/react'
|
||||
import { homeMachine } from '../machines/homeMachine'
|
||||
import { ContextFrom, EventFrom } from 'xstate'
|
||||
import { paths } from 'lib/paths'
|
||||
import { PATHS } from 'lib/paths'
|
||||
import {
|
||||
getNextSearchParams,
|
||||
getSortFunction,
|
||||
@ -44,7 +44,7 @@ import { ProjectSearchBar, useProjectSearch } from 'components/ProjectSearchBar'
|
||||
// This route only opens in the Tauri desktop context for now,
|
||||
// as defined in Router.tsx, so we can use the Tauri APIs and types.
|
||||
const Home = () => {
|
||||
useRefreshSettings(paths.HOME + 'SETTINGS')
|
||||
useRefreshSettings(PATHS.HOME + 'SETTINGS')
|
||||
const { commandBarSend } = useCommandsContext()
|
||||
const navigate = useNavigate()
|
||||
const { projects: loadedProjects } = useLoaderData() as HomeLoaderData
|
||||
@ -63,7 +63,7 @@ const Home = () => {
|
||||
})
|
||||
useHotkeys(
|
||||
isTauri() ? 'mod+,' : 'shift+mod+,',
|
||||
() => navigate(paths.HOME + paths.SETTINGS),
|
||||
() => navigate(PATHS.HOME + PATHS.SETTINGS),
|
||||
{
|
||||
splitKey: '|',
|
||||
}
|
||||
@ -91,7 +91,7 @@ const Home = () => {
|
||||
null
|
||||
)
|
||||
commandBarSend({ type: 'Close' })
|
||||
navigate(`${paths.FILE}/${encodeURIComponent(projectPath)}`)
|
||||
navigate(`${PATHS.FILE}/${encodeURIComponent(projectPath)}`)
|
||||
}
|
||||
},
|
||||
toastSuccess: (_, event) => toast.success((event.data || '') + ''),
|
||||
@ -276,7 +276,7 @@ const Home = () => {
|
||||
<p className="my-4 text-sm text-chalkboard-80 dark:text-chalkboard-30">
|
||||
Loaded from{' '}
|
||||
<Link
|
||||
to="settings?tab=user#projectDirectory"
|
||||
to={`${PATHS.SETTINGS_USER}#projectDirectory`}
|
||||
className="text-chalkboard-90 dark:text-chalkboard-20 underline underline-offset-2"
|
||||
>
|
||||
{settings.app.projectDirectory.current}
|
||||
|
||||
@ -11,7 +11,7 @@ import { APP_NAME } from 'lib/constants'
|
||||
import { useState } from 'react'
|
||||
import { useLspContext } from 'components/LspProvider'
|
||||
import { IndexLoaderData } from 'lib/types'
|
||||
import { paths } from 'lib/paths'
|
||||
import { PATHS } from 'lib/paths'
|
||||
import { useFileContext } from 'hooks/useFileContext'
|
||||
|
||||
/**
|
||||
@ -51,7 +51,7 @@ function OnboardingResetWarning(props: OnboardingResetWarningProps) {
|
||||
function OnboardingWarningDesktop(props: OnboardingResetWarningProps) {
|
||||
const navigate = useNavigate()
|
||||
const dismiss = useDismiss()
|
||||
const loaderData = useRouteLoaderData(paths.FILE) as IndexLoaderData
|
||||
const loaderData = useRouteLoaderData(PATHS.FILE) as IndexLoaderData
|
||||
const { context: fileContext } = useFileContext()
|
||||
const { onProjectClose, onProjectOpen } = useLspContext()
|
||||
|
||||
|
||||
@ -15,7 +15,7 @@ import UserMenu from './UserMenu'
|
||||
import ProjectMenu from './ProjectMenu'
|
||||
import Export from './Export'
|
||||
import FutureWork from './FutureWork'
|
||||
import { paths } from 'lib/paths'
|
||||
import { PATHS } from 'lib/paths'
|
||||
import { useAbsoluteFilePath } from 'hooks/useAbsoluteFilePath'
|
||||
import { ActionButton } from 'components/ActionButton'
|
||||
import { onboardingPaths } from 'routes/Onboarding/paths'
|
||||
@ -103,7 +103,7 @@ export function useNextClick(newStatus: string) {
|
||||
type: 'set.app.onboardingStatus',
|
||||
data: { level: 'user', value: newStatus },
|
||||
})
|
||||
navigate(filePath + paths.ONBOARDING.INDEX.slice(0, -1) + newStatus)
|
||||
navigate(filePath + PATHS.ONBOARDING.INDEX.slice(0, -1) + newStatus)
|
||||
}, [filePath, newStatus, send, navigate])
|
||||
}
|
||||
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { SettingsLevel } from 'lib/settings/settingsTypes'
|
||||
import { useLocation, useNavigate, useSearchParams } from 'react-router-dom'
|
||||
import { useHotkeys } from 'react-hotkeys-hook'
|
||||
import { paths } from 'lib/paths'
|
||||
import { PATHS } from 'lib/paths'
|
||||
import { useDotDotSlash } from 'hooks/useDotDotSlash'
|
||||
import { Fragment, useEffect, useRef } from 'react'
|
||||
import { Dialog, Transition } from '@headlessui/react'
|
||||
@ -21,9 +21,9 @@ export const APP_VERSION = isTauri()
|
||||
export const Settings = () => {
|
||||
const navigate = useNavigate()
|
||||
const [searchParams, setSearchParams] = useSearchParams()
|
||||
const close = () => navigate(location.pathname.replace(paths.SETTINGS, ''))
|
||||
const close = () => navigate(location.pathname.replace(PATHS.SETTINGS, ''))
|
||||
const location = useLocation()
|
||||
const isFileSettings = location.pathname.includes(paths.FILE)
|
||||
const isFileSettings = location.pathname.includes(PATHS.FILE)
|
||||
const searchParamTab =
|
||||
(searchParams.get('tab') as SettingsLevel | 'keybindings') ??
|
||||
(isFileSettings ? 'project' : 'user')
|
||||
|
||||
@ -2,7 +2,7 @@ import { ActionButton } from '../components/ActionButton'
|
||||
import { isTauri } from '../lib/isTauri'
|
||||
import { VITE_KC_SITE_BASE_URL, VITE_KC_API_BASE_URL } from '../env'
|
||||
import { Themes, getSystemTheme } from '../lib/theme'
|
||||
import { paths } from 'lib/paths'
|
||||
import { PATHS } from 'lib/paths'
|
||||
import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext'
|
||||
import { APP_NAME } from 'lib/constants'
|
||||
import { login } from 'lib/tauri'
|
||||
@ -75,7 +75,7 @@ const SignIn = () => {
|
||||
<ActionButton
|
||||
Element="link"
|
||||
to={`${VITE_KC_SITE_BASE_URL}${
|
||||
paths.SIGN_IN
|
||||
PATHS.SIGN_IN
|
||||
}?callbackUrl=${encodeURIComponent(
|
||||
typeof window !== 'undefined' &&
|
||||
window.location.href.replace('signin', '')
|
||||
|
||||
6
src/wasm-lib/Cargo.lock
generated
@ -1480,9 +1480,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "kittycad"
|
||||
version = "0.3.9"
|
||||
version = "0.3.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "36b87a9cca545825ba18005c1944b8525fac28867d99984178bf22c79fb5ec25"
|
||||
checksum = "3d798c82f6e62d786fca0a7ec073675bbe9550c885efb99390d2701ca557ec69"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
@ -1858,7 +1858,7 @@ dependencies = [
|
||||
"bincode",
|
||||
"either",
|
||||
"fnv",
|
||||
"itertools 0.12.1",
|
||||
"itertools 0.10.5",
|
||||
"lazy_static",
|
||||
"nom",
|
||||
"quick-xml",
|
||||
|
||||
@ -1017,6 +1017,11 @@ impl SketchGroup {
|
||||
};
|
||||
};
|
||||
match path {
|
||||
Path::TangentialArc { center, ccw, .. } => GetTangentialInfoFromPathsResult {
|
||||
center_or_tangent_point: *center,
|
||||
is_center: true,
|
||||
ccw: *ccw,
|
||||
},
|
||||
Path::TangentialArcTo { center, ccw, .. } => GetTangentialInfoFromPathsResult {
|
||||
center_or_tangent_point: *center,
|
||||
is_center: true,
|
||||
@ -1346,6 +1351,11 @@ pub enum Path {
|
||||
TangentialArc {
|
||||
#[serde(flatten)]
|
||||
base: BasePath,
|
||||
/// the arc's center
|
||||
#[ts(type = "[number, number]")]
|
||||
center: [f64; 2],
|
||||
/// arc's direction
|
||||
ccw: bool,
|
||||
},
|
||||
/// A path that is horizontal.
|
||||
Horizontal {
|
||||
@ -1378,7 +1388,7 @@ impl Path {
|
||||
Path::AngledLineTo { base, .. } => base.geo_meta.id,
|
||||
Path::Base { base } => base.geo_meta.id,
|
||||
Path::TangentialArcTo { base, .. } => base.geo_meta.id,
|
||||
Path::TangentialArc { base } => base.geo_meta.id,
|
||||
Path::TangentialArc { base, .. } => base.geo_meta.id,
|
||||
}
|
||||
}
|
||||
|
||||
@ -1389,7 +1399,7 @@ impl Path {
|
||||
Path::AngledLineTo { base, .. } => base.tag.clone(),
|
||||
Path::Base { base } => base.tag.clone(),
|
||||
Path::TangentialArcTo { base, .. } => base.tag.clone(),
|
||||
Path::TangentialArc { base } => base.tag.clone(),
|
||||
Path::TangentialArc { base, .. } => base.tag.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -1400,7 +1410,7 @@ impl Path {
|
||||
Path::AngledLineTo { base, .. } => base,
|
||||
Path::Base { base } => base,
|
||||
Path::TangentialArcTo { base, .. } => base,
|
||||
Path::TangentialArc { base } => base,
|
||||
Path::TangentialArc { base, .. } => base,
|
||||
}
|
||||
}
|
||||
|
||||
@ -1411,7 +1421,7 @@ impl Path {
|
||||
Path::AngledLineTo { base, .. } => Some(base),
|
||||
Path::Base { base } => Some(base),
|
||||
Path::TangentialArcTo { base, .. } => Some(base),
|
||||
Path::TangentialArc { base } => Some(base),
|
||||
Path::TangentialArc { base, .. } => Some(base),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1681,42 +1681,65 @@ async fn inner_tangential_arc(
|
||||
args: Args,
|
||||
) -> Result<Box<SketchGroup>, KclError> {
|
||||
let from: Point2d = sketch_group.current_pen_position()?;
|
||||
// next set of lines is some undocumented voodoo from get_tangential_arc_to_info
|
||||
let tangent_info = sketch_group.get_tangential_info_from_paths(); //this function desperately needs some documentation
|
||||
let tan_previous_point = if tangent_info.is_center {
|
||||
get_tangent_point_from_previous_arc(tangent_info.center_or_tangent_point, tangent_info.ccw, from.into())
|
||||
} else {
|
||||
tangent_info.center_or_tangent_point
|
||||
};
|
||||
|
||||
let id = uuid::Uuid::new_v4();
|
||||
|
||||
let to = match &data {
|
||||
let (center, to, ccw) = match data {
|
||||
TangentialArcData::RadiusAndOffset { radius, offset } => {
|
||||
// KCL stdlib types use degrees.
|
||||
let offset = Angle::from_degrees(offset);
|
||||
|
||||
// Calculate the end point from the angle and radius.
|
||||
let end_angle = Angle::from_degrees(*offset);
|
||||
let start_angle = Angle::from_degrees(0.0);
|
||||
let (_, to) = arc_center_and_end(from, start_angle, end_angle, *radius);
|
||||
// atan2 outputs radians.
|
||||
let previous_end_tangent = Angle::from_radians(f64::atan2(
|
||||
from.y - tan_previous_point[1],
|
||||
from.x - tan_previous_point[0],
|
||||
));
|
||||
// make sure the arc center is on the correct side to guarantee deterministic behavior
|
||||
// note the engine automatically rejects an offset of zero, if we want to flag that at KCL too to avoid engine errors
|
||||
let ccw = offset.degrees() > 0.0;
|
||||
let tangent_to_arc_start_angle = if ccw {
|
||||
// CCW turn
|
||||
Angle::from_degrees(-90.0)
|
||||
} else {
|
||||
// CW turn
|
||||
Angle::from_degrees(90.0)
|
||||
};
|
||||
// may need some logic and / or modulo on the various angle values to prevent them from going "backwards"
|
||||
// but the above logic *should* capture that behavior
|
||||
let start_angle = previous_end_tangent + tangent_to_arc_start_angle;
|
||||
let end_angle = start_angle + offset;
|
||||
let (center, to) = arc_center_and_end(from, start_angle, end_angle, radius);
|
||||
|
||||
args.batch_modeling_cmd(
|
||||
id,
|
||||
ModelingCmd::ExtendPath {
|
||||
path: sketch_group.id,
|
||||
segment: kittycad::types::PathSegment::TangentialArc {
|
||||
radius: *radius,
|
||||
offset: Angle {
|
||||
unit: kittycad::types::UnitAngle::Degrees,
|
||||
value: *offset,
|
||||
},
|
||||
},
|
||||
segment: kittycad::types::PathSegment::TangentialArc { radius, offset },
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
to.into()
|
||||
(center, to.into(), ccw)
|
||||
}
|
||||
TangentialArcData::Point(to) => {
|
||||
args.batch_modeling_cmd(id, tan_arc_to(&sketch_group, to)).await?;
|
||||
|
||||
*to
|
||||
args.batch_modeling_cmd(id, tan_arc_to(&sketch_group, &to)).await?;
|
||||
// TODO: Figure out these calculations.
|
||||
let ccw = false;
|
||||
let center = Point2d { x: 0.0, y: 0.0 };
|
||||
(center, to, ccw)
|
||||
}
|
||||
};
|
||||
|
||||
let to = [from.x + to[0], from.y + to[1]];
|
||||
|
||||
let current_path = Path::TangentialArc {
|
||||
ccw,
|
||||
center: center.into(),
|
||||
base: BasePath {
|
||||
from: from.into(),
|
||||
to,
|
||||
|
||||
@ -833,6 +833,7 @@ pub fn get_tangent_point_from_previous_arc(
|
||||
) -> Coords2d {
|
||||
let angle_from_old_center_to_arc_start = get_angle(last_arc_center, last_arc_end);
|
||||
let tangential_angle = angle_from_old_center_to_arc_start + if last_arc_ccw { -90.0 } else { 90.0 };
|
||||
// What is the 10.0 constant doing???
|
||||
[
|
||||
tangential_angle.to_radians().cos() * 10.0 + last_arc_end[0],
|
||||
tangential_angle.to_radians().sin() * 10.0 + last_arc_end[1],
|
||||
|
||||
25
src/wasm-lib/tests/executor/inputs/tan_arc_x_line.kcl
Normal file
@ -0,0 +1,25 @@
|
||||
let startX = 0
|
||||
let startY = 0
|
||||
let angleOffset = 135
|
||||
let r = 1
|
||||
let angleStart = 110
|
||||
|
||||
startSketchOn('XY')
|
||||
|> startProfileAt([startX, startY], %)
|
||||
|> angledLine({
|
||||
angle: angleStart,
|
||||
length: .000001,
|
||||
}, %)
|
||||
|> tangentialArc({
|
||||
offset: angleOffset,
|
||||
radius: r,
|
||||
}, %, $arc1)
|
||||
|> tangentialArc({
|
||||
offset: angleOffset,
|
||||
radius: 0.5*r,
|
||||
}, %, $arc2)
|
||||
|> tangentialArc({
|
||||
offset: -angleOffset,
|
||||
radius: 0.5*r,
|
||||
}, %, $arc3)
|
||||
|> xLineTo(1, %)
|
||||
@ -31,6 +31,13 @@ async fn kcl_test_riddle_small() {
|
||||
assert_out("riddle_small", &result);
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn kcl_test_tan_arc_x_line() {
|
||||
let code = kcl_input!("tan_arc_x_line");
|
||||
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
||||
assert_out("tan_arc_x_line", &result);
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn kcl_test_lego() {
|
||||
let code = kcl_input!("lego");
|
||||
|
||||
BIN
src/wasm-lib/tests/executor/outputs/tan_arc_x_line.png
Normal file
|
After Width: | Height: | Size: 108 KiB |
38
yarn.lock
@ -2227,7 +2227,12 @@
|
||||
resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.4.tgz#0b92dcc0cc1c81f6f306a381f28e31b1a56536e9"
|
||||
integrity sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==
|
||||
|
||||
"@tweenjs/tween.js@^23.1.1", "@tweenjs/tween.js@~23.1.1":
|
||||
"@tweenjs/tween.js@^25.0.0":
|
||||
version "25.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@tweenjs/tween.js/-/tween.js-25.0.0.tgz#7266baebcc3affe62a3a54318a3ea82d904cd0b9"
|
||||
integrity sha512-XKLA6syeBUaPzx4j3qwMqzzq+V4uo72BnlbOjmuljLrRqdsd3qnzvZZoxvMHZ23ndsRS4aufU6JOZYpCbU6T1A==
|
||||
|
||||
"@tweenjs/tween.js@~23.1.1":
|
||||
version "23.1.2"
|
||||
resolved "https://registry.yarnpkg.com/@tweenjs/tween.js/-/tween.js-23.1.2.tgz#4e5357fd6742f5aa50447d3fa808aed4cda93ed7"
|
||||
integrity sha512-kMCNaZCJugWI86xiEHaY338CU5JpD0B97p1j1IKNn/Zto8PgACjQx0UxbHjmOcLl/dDOBnItwD07KmCs75pxtQ==
|
||||
@ -7712,7 +7717,16 @@ string-natural-compare@^3.0.1:
|
||||
resolved "https://registry.yarnpkg.com/string-natural-compare/-/string-natural-compare-3.0.1.tgz#7a42d58474454963759e8e8b7ae63d71c1e7fdf4"
|
||||
integrity sha512-n3sPwynL1nwKi3WJ6AIsClwBMa0zTi54fn2oLU6ndfTSIO05xaznjSf15PcBZU6FNWbmN5Q6cxT4V5hGvB4taw==
|
||||
|
||||
"string-width-cjs@npm:string-width@^4.2.0", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3:
|
||||
"string-width-cjs@npm:string-width@^4.2.0":
|
||||
version "4.2.3"
|
||||
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
|
||||
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
|
||||
dependencies:
|
||||
emoji-regex "^8.0.0"
|
||||
is-fullwidth-code-point "^3.0.0"
|
||||
strip-ansi "^6.0.1"
|
||||
|
||||
string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3:
|
||||
version "4.2.3"
|
||||
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
|
||||
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
|
||||
@ -7790,7 +7804,14 @@ string_decoder@~1.1.1:
|
||||
dependencies:
|
||||
safe-buffer "~5.1.0"
|
||||
|
||||
"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1:
|
||||
"strip-ansi-cjs@npm:strip-ansi@^6.0.1":
|
||||
version "6.0.1"
|
||||
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
|
||||
integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
|
||||
dependencies:
|
||||
ansi-regex "^5.0.1"
|
||||
|
||||
strip-ansi@^6.0.0, strip-ansi@^6.0.1:
|
||||
version "6.0.1"
|
||||
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
|
||||
integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
|
||||
@ -8666,7 +8687,7 @@ workerpool@6.2.1:
|
||||
resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.2.1.tgz#46fc150c17d826b86a008e5a4508656777e9c343"
|
||||
integrity sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw==
|
||||
|
||||
"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0:
|
||||
"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0":
|
||||
version "7.0.0"
|
||||
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
|
||||
integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
|
||||
@ -8684,6 +8705,15 @@ wrap-ansi@^6.2.0:
|
||||
string-width "^4.1.0"
|
||||
strip-ansi "^6.0.0"
|
||||
|
||||
wrap-ansi@^7.0.0:
|
||||
version "7.0.0"
|
||||
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
|
||||
integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
|
||||
dependencies:
|
||||
ansi-styles "^4.0.0"
|
||||
string-width "^4.1.0"
|
||||
strip-ansi "^6.0.0"
|
||||
|
||||
wrap-ansi@^8.1.0:
|
||||
version "8.1.0"
|
||||
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214"
|
||||
|
||||