Compare commits

..

1 Commits

Author SHA1 Message Date
4de0b57ea4 Add array concatenation using the plus operator 2025-07-01 19:58:27 -04:00
66 changed files with 569 additions and 347 deletions

View File

@ -7,6 +7,7 @@ VITE_KC_API_WS_MODELING_URL=wss://api.dev.zoo.dev/ws/modeling/commands
VITE_KC_API_BASE_URL=https://api.dev.zoo.dev
VITE_KC_SITE_BASE_URL=https://dev.zoo.dev
VITE_KC_SITE_APP_URL=https://app.dev.zoo.dev
VITE_KC_SKIP_AUTH=false
VITE_KC_CONNECTION_TIMEOUT_MS=5000
#VITE_WASM_URL="optional way of overriding the wasm url, particular for unit tests which need this if you running not on the default 3000 port"
#VITE_KC_DEV_TOKEN="optional token to skip auth in the app"

View File

@ -3,4 +3,5 @@ VITE_KC_API_WS_MODELING_URL=wss://api.zoo.dev/ws/modeling/commands
VITE_KC_API_BASE_URL=https://api.zoo.dev
VITE_KC_SITE_BASE_URL=https://zoo.dev
VITE_KC_SITE_APP_URL=https://app.zoo.dev
VITE_KC_SKIP_AUTH=false
VITE_KC_CONNECTION_TIMEOUT_MS=15000

Binary file not shown.

Before

Width:  |  Height:  |  Size: 56 KiB

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 50 KiB

After

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 58 KiB

After

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 51 KiB

After

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 49 KiB

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 37 KiB

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 47 KiB

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 52 KiB

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 58 KiB

After

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 38 KiB

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 57 KiB

After

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 54 KiB

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 75 KiB

After

Width:  |  Height:  |  Size: 75 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 48 KiB

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 61 KiB

After

Width:  |  Height:  |  Size: 61 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 132 KiB

After

Width:  |  Height:  |  Size: 135 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 117 KiB

After

Width:  |  Height:  |  Size: 118 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 132 KiB

After

Width:  |  Height:  |  Size: 135 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 68 KiB

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 71 KiB

After

Width:  |  Height:  |  Size: 71 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 67 KiB

After

Width:  |  Height:  |  Size: 67 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 69 KiB

After

Width:  |  Height:  |  Size: 69 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 70 KiB

After

Width:  |  Height:  |  Size: 69 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 66 KiB

After

Width:  |  Height:  |  Size: 65 KiB

2
interface.d.ts vendored
View File

@ -72,12 +72,14 @@ export interface IElectronAPI {
}
process: {
env: {
BASE_URL: string
IS_PLAYWRIGHT: string
VITE_KC_DEV_TOKEN: string
VITE_KC_API_WS_MODELING_URL: string
VITE_KC_API_BASE_URL: string
VITE_KC_SITE_BASE_URL: string
VITE_KC_SITE_APP_URL: string
VITE_KC_SKIP_AUTH: string
VITE_KC_CONNECTION_TIMEOUT_MS: string
VITE_KC_DEV_TOKEN: string
NODE_ENV: string

19
package-lock.json generated
View File

@ -26640,27 +26640,10 @@
"vscode-uri": "^3.1.0"
},
"devDependencies": {
"@types/node": "^24.0.7",
"@types/node": "^22.14.1",
"ts-node": "^10.9.2"
}
},
"packages/codemirror-lsp-client/node_modules/@types/node": {
"version": "24.0.10",
"resolved": "https://registry.npmjs.org/@types/node/-/node-24.0.10.tgz",
"integrity": "sha512-ENHwaH+JIRTDIEEbDK6QSQntAYGtbvdDXnMXnZaZ6k13Du1dPMmprkEHIL7ok2Wl2aZevetwTAb5S+7yIF+enA==",
"dev": true,
"license": "MIT",
"dependencies": {
"undici-types": "~7.8.0"
}
},
"packages/codemirror-lsp-client/node_modules/undici-types": {
"version": "7.8.0",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.8.0.tgz",
"integrity": "sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw==",
"dev": true,
"license": "MIT"
},
"rust/kcl-language-server": {
"version": "0.0.0",
"license": "MIT",

View File

@ -10,76 +10,71 @@ DATA;
NAMED_UNIT(*)
SI_UNIT($, .METRE.)
);
#2 = (
NAMED_UNIT(*)
PLANE_ANGLE_UNIT()
SI_UNIT($, .RADIAN.)
);
#3 = UNCERTAINTY_MEASURE_WITH_UNIT(0.00001, #1, 'DISTANCE_ACCURACY_VALUE', $);
#4 = (
#2 = UNCERTAINTY_MEASURE_WITH_UNIT(0.00001, #1, 'DISTANCE_ACCURACY_VALUE', $);
#3 = (
GEOMETRIC_REPRESENTATION_CONTEXT(3)
GLOBAL_UNCERTAINTY_ASSIGNED_CONTEXT((#3))
GLOBAL_UNIT_ASSIGNED_CONTEXT((#1, #2))
GLOBAL_UNCERTAINTY_ASSIGNED_CONTEXT((#2))
GLOBAL_UNIT_ASSIGNED_CONTEXT((#1))
REPRESENTATION_CONTEXT('', '3D')
);
#5 = CARTESIAN_POINT('NONE', (0.015, -0.01, -0.005));
#6 = VERTEX_POINT('NONE', #5);
#7 = CARTESIAN_POINT('NONE', (0.015, 0, -0.005));
#8 = VERTEX_POINT('NONE', #7);
#9 = DIRECTION('NONE', (1, 0, -0));
#10 = DIRECTION('NONE', (0, 1, 0));
#11 = CARTESIAN_POINT('NONE', (0.005, -0.01, -0.005));
#12 = AXIS2_PLACEMENT_3D('NONE', #11, #10, #9);
#13 = CIRCLE('NONE', #12, 0.01);
#14 = DIRECTION('NONE', (0, 1, 0));
#15 = VECTOR('NONE', #14, 1);
#16 = CARTESIAN_POINT('NONE', (0.015, -0.01, -0.005));
#17 = LINE('NONE', #16, #15);
#18 = DIRECTION('NONE', (1, 0, -0));
#19 = DIRECTION('NONE', (0, 1, 0));
#20 = CARTESIAN_POINT('NONE', (0.005, 0, -0.005));
#21 = AXIS2_PLACEMENT_3D('NONE', #20, #19, #18);
#22 = CIRCLE('NONE', #21, 0.01);
#23 = EDGE_CURVE('NONE', #6, #6, #13, .T.);
#24 = EDGE_CURVE('NONE', #6, #8, #17, .T.);
#25 = EDGE_CURVE('NONE', #8, #8, #22, .T.);
#26 = CARTESIAN_POINT('NONE', (0.005, -0.005, -0.005));
#27 = DIRECTION('NONE', (0, 1, 0));
#28 = DIRECTION('NONE', (1, 0, -0));
#29 = AXIS2_PLACEMENT_3D('NONE', #26, #27, #28);
#30 = CYLINDRICAL_SURFACE('NONE', #29, 0.01);
#31 = CARTESIAN_POINT('NONE', (0, -0.01, -0));
#32 = DIRECTION('NONE', (0, 1, 0));
#33 = AXIS2_PLACEMENT_3D('NONE', #31, #32, $);
#34 = PLANE('NONE', #33);
#35 = CARTESIAN_POINT('NONE', (0, 0, -0));
#36 = DIRECTION('NONE', (0, 1, 0));
#37 = AXIS2_PLACEMENT_3D('NONE', #35, #36, $);
#38 = PLANE('NONE', #37);
#39 = ORIENTED_EDGE('NONE', *, *, #23, .T.);
#40 = ORIENTED_EDGE('NONE', *, *, #25, .F.);
#41 = EDGE_LOOP('NONE', (#39));
#42 = FACE_BOUND('NONE', #41, .T.);
#43 = EDGE_LOOP('NONE', (#40));
#44 = FACE_BOUND('NONE', #43, .T.);
#45 = ADVANCED_FACE('NONE', (#42, #44), #30, .T.);
#46 = ORIENTED_EDGE('NONE', *, *, #23, .F.);
#47 = EDGE_LOOP('NONE', (#46));
#48 = FACE_BOUND('NONE', #47, .T.);
#49 = ADVANCED_FACE('NONE', (#48), #34, .F.);
#50 = ORIENTED_EDGE('NONE', *, *, #25, .T.);
#51 = EDGE_LOOP('NONE', (#50));
#52 = FACE_BOUND('NONE', #51, .T.);
#53 = ADVANCED_FACE('NONE', (#52), #38, .T.);
#54 = CLOSED_SHELL('NONE', (#45, #49, #53));
#55 = MANIFOLD_SOLID_BREP('NONE', #54);
#56 = APPLICATION_CONTEXT('configuration controlled 3D design of mechanical parts and assemblies');
#57 = PRODUCT_DEFINITION_CONTEXT('part definition', #56, 'design');
#58 = PRODUCT('UNIDENTIFIED_PRODUCT', 'NONE', $, ());
#59 = PRODUCT_DEFINITION_FORMATION('', $, #58);
#60 = PRODUCT_DEFINITION('design', $, #59, #57);
#61 = PRODUCT_DEFINITION_SHAPE('NONE', $, #60);
#62 = ADVANCED_BREP_SHAPE_REPRESENTATION('NONE', (#55), #4);
#63 = SHAPE_DEFINITION_REPRESENTATION(#61, #62);
#4 = CARTESIAN_POINT('NONE', (0.015, -0.01, -0.005));
#5 = VERTEX_POINT('NONE', #4);
#6 = CARTESIAN_POINT('NONE', (0.015, 0, -0.005));
#7 = VERTEX_POINT('NONE', #6);
#8 = DIRECTION('NONE', (1, 0, -0));
#9 = DIRECTION('NONE', (0, 1, 0));
#10 = CARTESIAN_POINT('NONE', (0.005, -0.01, -0.005));
#11 = AXIS2_PLACEMENT_3D('NONE', #10, #9, #8);
#12 = CIRCLE('NONE', #11, 0.01);
#13 = DIRECTION('NONE', (0, 1, 0));
#14 = VECTOR('NONE', #13, 1);
#15 = CARTESIAN_POINT('NONE', (0.015, -0.01, -0.005));
#16 = LINE('NONE', #15, #14);
#17 = DIRECTION('NONE', (1, 0, -0));
#18 = DIRECTION('NONE', (0, 1, 0));
#19 = CARTESIAN_POINT('NONE', (0.005, 0, -0.005));
#20 = AXIS2_PLACEMENT_3D('NONE', #19, #18, #17);
#21 = CIRCLE('NONE', #20, 0.01);
#22 = EDGE_CURVE('NONE', #5, #5, #12, .T.);
#23 = EDGE_CURVE('NONE', #5, #7, #16, .T.);
#24 = EDGE_CURVE('NONE', #7, #7, #21, .T.);
#25 = CARTESIAN_POINT('NONE', (0.005, -0.005, -0.005));
#26 = DIRECTION('NONE', (0, 1, 0));
#27 = DIRECTION('NONE', (1, 0, -0));
#28 = AXIS2_PLACEMENT_3D('NONE', #25, #26, #27);
#29 = CYLINDRICAL_SURFACE('NONE', #28, 0.01);
#30 = CARTESIAN_POINT('NONE', (0, -0.01, -0));
#31 = DIRECTION('NONE', (0, 1, 0));
#32 = AXIS2_PLACEMENT_3D('NONE', #30, #31, $);
#33 = PLANE('NONE', #32);
#34 = CARTESIAN_POINT('NONE', (0, 0, -0));
#35 = DIRECTION('NONE', (0, 1, 0));
#36 = AXIS2_PLACEMENT_3D('NONE', #34, #35, $);
#37 = PLANE('NONE', #36);
#38 = ORIENTED_EDGE('NONE', *, *, #22, .T.);
#39 = ORIENTED_EDGE('NONE', *, *, #24, .F.);
#40 = EDGE_LOOP('NONE', (#38));
#41 = FACE_BOUND('NONE', #40, .T.);
#42 = EDGE_LOOP('NONE', (#39));
#43 = FACE_BOUND('NONE', #42, .T.);
#44 = ADVANCED_FACE('NONE', (#41, #43), #29, .T.);
#45 = ORIENTED_EDGE('NONE', *, *, #22, .F.);
#46 = EDGE_LOOP('NONE', (#45));
#47 = FACE_BOUND('NONE', #46, .T.);
#48 = ADVANCED_FACE('NONE', (#47), #33, .F.);
#49 = ORIENTED_EDGE('NONE', *, *, #24, .T.);
#50 = EDGE_LOOP('NONE', (#49));
#51 = FACE_BOUND('NONE', #50, .T.);
#52 = ADVANCED_FACE('NONE', (#51), #37, .T.);
#53 = CLOSED_SHELL('NONE', (#44, #48, #52));
#54 = MANIFOLD_SOLID_BREP('NONE', #53);
#55 = APPLICATION_CONTEXT('configuration controlled 3D design of mechanical parts and assemblies');
#56 = PRODUCT_DEFINITION_CONTEXT('part definition', #55, 'design');
#57 = PRODUCT('UNIDENTIFIED_PRODUCT', 'NONE', $, ());
#58 = PRODUCT_DEFINITION_FORMATION('', $, #57);
#59 = PRODUCT_DEFINITION('design', $, #58, #56);
#60 = PRODUCT_DEFINITION_SHAPE('NONE', $, #59);
#61 = ADVANCED_BREP_SHAPE_REPRESENTATION('NONE', (#54), #3);
#62 = SHAPE_DEFINITION_REPRESENTATION(#60, #61);
ENDSEC;
END-ISO-10303-21;

View File

@ -994,39 +994,6 @@ impl Node<MemberExpression> {
// Check the property and object match -- e.g. ints for arrays, strs for objects.
match (object, property, self.computed) {
(KclValue::Plane { value: plane }, Property::String(property), false) => match property.as_str() {
"yAxis" => {
let (p, u) = plane.info.y_axis.as_3_dims();
Ok(KclValue::array_from_point3d(
p,
NumericType::Known(crate::exec::UnitType::Length(u)),
vec![meta],
))
}
"xAxis" => {
let (p, u) = plane.info.x_axis.as_3_dims();
Ok(KclValue::array_from_point3d(
p,
NumericType::Known(crate::exec::UnitType::Length(u)),
vec![meta],
))
}
"origin" => {
let (p, u) = plane.info.origin.as_3_dims();
Ok(KclValue::array_from_point3d(
p,
NumericType::Known(crate::exec::UnitType::Length(u)),
vec![meta],
))
}
other => Err(KclError::new_undefined_value(
KclErrorDetails::new(
format!("Property '{other}' not found in plane"),
vec![self.clone().into()],
),
None,
)),
},
(KclValue::Object { value: map, meta: _ }, Property::String(property), false) => {
if let Some(value) = map.get(&property) {
Ok(value.to_owned())
@ -1046,22 +1013,7 @@ impl Node<MemberExpression> {
vec![self.clone().into()],
)))
}
(KclValue::Object { value: map, .. }, p @ Property::UInt(i), _) => {
if i == 0
&& let Some(value) = map.get("x")
{
return Ok(value.to_owned());
}
if i == 1
&& let Some(value) = map.get("y")
{
return Ok(value.to_owned());
}
if i == 2
&& let Some(value) = map.get("z")
{
return Ok(value.to_owned());
}
(KclValue::Object { .. }, p, _) => {
let t = p.type_name();
let article = article_for(t);
Err(KclError::new_semantic(KclErrorDetails::new(
@ -1134,6 +1086,32 @@ impl Node<MemberExpression> {
}
}
fn concat(left: &[KclValue], left_el_ty: &RuntimeType, right: &[KclValue], right_el_ty: &RuntimeType) -> KclValue {
if left.is_empty() {
return KclValue::HomArray {
value: right.to_vec(),
ty: right_el_ty.clone(),
};
}
if right.is_empty() {
return KclValue::HomArray {
value: left.to_vec(),
ty: left_el_ty.clone(),
};
}
let mut new = left.to_vec();
new.extend_from_slice(right);
// Propagate the element type if we can.
let ty = if right_el_ty.subtype(left_el_ty) {
left_el_ty.clone()
} else if left_el_ty.subtype(right_el_ty) {
right_el_ty.clone()
} else {
RuntimeType::any()
};
KclValue::HomArray { value: new, ty }
}
impl Node<BinaryExpression> {
#[async_recursion]
pub async fn get_result(&self, exec_state: &mut ExecState, ctx: &ExecutorContext) -> Result<KclValue, KclError> {
@ -1152,6 +1130,50 @@ impl Node<BinaryExpression> {
meta,
});
}
// Array plus is concatenation.
match (&left_value, &right_value) {
(
KclValue::HomArray {
value: left,
ty: left_el_ty,
..
},
KclValue::HomArray {
value: right,
ty: right_el_ty,
..
},
) => {
return Ok(concat(left, left_el_ty, right, right_el_ty));
}
(
KclValue::HomArray {
value: left,
ty: left_el_ty,
..
},
_,
) => {
// Any single value can be coerced to an array.
let right = vec![right_value.clone()];
let right_el_ty = RuntimeType::any();
return Ok(concat(left, left_el_ty, &right, &right_el_ty));
}
(
_,
KclValue::HomArray {
value: right,
ty: right_el_ty,
..
},
) => {
// Any single value can be coerced to an array.
let left = vec![left_value.clone()];
let left_el_ty = RuntimeType::any();
return Ok(concat(&left, &left_el_ty, right, right_el_ty));
}
_ => {}
}
}
// Then check if we have solids.
@ -2253,12 +2275,4 @@ y = x[0mm + 1]
"#;
parse_execute(ast).await.unwrap_err();
}
#[tokio::test(flavor = "multi_thread")]
async fn getting_property_of_plane() {
// let ast = include_str!("../../tests/inputs/planestuff.kcl");
let ast = std::fs::read_to_string("tests/inputs/planestuff.kcl").unwrap();
parse_execute(&ast).await.unwrap();
}
}

View File

@ -921,12 +921,6 @@ impl Point3d {
units: UnitLen::Unknown,
}
}
pub fn as_3_dims(&self) -> ([f64; 3], UnitLen) {
let p = [self.x, self.y, self.z];
let u = self.units;
(p, u)
}
}
impl From<[TyF64; 3]> for Point3d {

View File

@ -458,31 +458,6 @@ impl KclValue {
}
}
/// Put the point into a KCL point.
pub fn array_from_point3d(p: [f64; 3], ty: NumericType, meta: Vec<Metadata>) -> Self {
let [x, y, z] = p;
Self::HomArray {
value: vec![
Self::Number {
value: x,
meta: meta.clone(),
ty,
},
Self::Number {
value: y,
meta: meta.clone(),
ty,
},
Self::Number {
value: z,
meta: meta.clone(),
ty,
},
],
ty: ty.into(),
}
}
pub(crate) fn as_usize(&self) -> Option<usize> {
match self {
KclValue::Number { value, .. } => crate::try_f64_to_usize(*value),

View File

@ -246,7 +246,7 @@ impl RuntimeType {
}
// Subtype with no coercion, including refining numeric types.
fn subtype(&self, sup: &RuntimeType) -> bool {
pub(super) fn subtype(&self, sup: &RuntimeType) -> bool {
use RuntimeType::*;
match (self, sup) {

View File

@ -14,7 +14,7 @@ description: Result of parsing add_arrays.kcl
"commentStart": 0,
"end": 0,
"moduleId": 0,
"name": "answer",
"name": "a",
"start": 0,
"type": "Identifier"
},
@ -96,6 +96,170 @@ description: Result of parsing add_arrays.kcl
"start": 0,
"type": "VariableDeclaration",
"type": "VariableDeclaration"
},
{
"commentStart": 0,
"declaration": {
"commentStart": 0,
"end": 0,
"id": {
"commentStart": 0,
"end": 0,
"moduleId": 0,
"name": "b",
"start": 0,
"type": "Identifier"
},
"init": {
"commentStart": 0,
"end": 0,
"left": {
"commentStart": 0,
"elements": [
{
"commentStart": 0,
"end": 0,
"moduleId": 0,
"raw": "0",
"start": 0,
"type": "Literal",
"type": "Literal",
"value": {
"value": 0.0,
"suffix": "None"
}
},
{
"commentStart": 0,
"end": 0,
"moduleId": 0,
"raw": "1",
"start": 0,
"type": "Literal",
"type": "Literal",
"value": {
"value": 1.0,
"suffix": "None"
}
}
],
"end": 0,
"moduleId": 0,
"start": 0,
"type": "ArrayExpression",
"type": "ArrayExpression"
},
"moduleId": 0,
"operator": "+",
"right": {
"commentStart": 0,
"end": 0,
"moduleId": 0,
"raw": "2",
"start": 0,
"type": "Literal",
"type": "Literal",
"value": {
"value": 2.0,
"suffix": "None"
}
},
"start": 0,
"type": "BinaryExpression",
"type": "BinaryExpression"
},
"moduleId": 0,
"start": 0,
"type": "VariableDeclarator"
},
"end": 0,
"kind": "const",
"moduleId": 0,
"start": 0,
"type": "VariableDeclaration",
"type": "VariableDeclaration"
},
{
"commentStart": 0,
"declaration": {
"commentStart": 0,
"end": 0,
"id": {
"commentStart": 0,
"end": 0,
"moduleId": 0,
"name": "c",
"start": 0,
"type": "Identifier"
},
"init": {
"commentStart": 0,
"end": 0,
"left": {
"commentStart": 0,
"end": 0,
"moduleId": 0,
"raw": "0",
"start": 0,
"type": "Literal",
"type": "Literal",
"value": {
"value": 0.0,
"suffix": "None"
}
},
"moduleId": 0,
"operator": "+",
"right": {
"commentStart": 0,
"elements": [
{
"commentStart": 0,
"end": 0,
"moduleId": 0,
"raw": "1",
"start": 0,
"type": "Literal",
"type": "Literal",
"value": {
"value": 1.0,
"suffix": "None"
}
},
{
"commentStart": 0,
"end": 0,
"moduleId": 0,
"raw": "2",
"start": 0,
"type": "Literal",
"type": "Literal",
"value": {
"value": 2.0,
"suffix": "None"
}
}
],
"end": 0,
"moduleId": 0,
"start": 0,
"type": "ArrayExpression",
"type": "ArrayExpression"
},
"start": 0,
"type": "BinaryExpression",
"type": "BinaryExpression"
},
"moduleId": 0,
"start": 0,
"type": "VariableDeclarator"
},
"end": 0,
"kind": "const",
"moduleId": 0,
"start": 0,
"type": "VariableDeclaration",
"type": "VariableDeclaration"
}
],
"commentStart": 0,

View File

@ -1,12 +0,0 @@
---
source: kcl-lib/src/simulation_tests.rs
description: Error from executing add_arrays.kcl
---
KCL Semantic error
× semantic: Expected a number, but found an array of `number`, `number`
╭────
1 │ answer = [0, 1] + [2]
· ───┬──
· ╰── tests/add_arrays/input.kcl
╰────

View File

@ -1 +1,3 @@
answer = [0, 1] + [2]
a = [0, 1] + [2]
b = [0, 1] + 2
c = 0 + [1, 2]

View File

@ -0,0 +1,138 @@
---
source: kcl-lib/src/simulation_tests.rs
description: Variables in memory after executing add_arrays.kcl
---
{
"a": {
"type": "HomArray",
"value": [
{
"type": "Number",
"value": 0.0,
"ty": {
"type": "Default",
"len": {
"type": "Mm"
},
"angle": {
"type": "Degrees"
}
}
},
{
"type": "Number",
"value": 1.0,
"ty": {
"type": "Default",
"len": {
"type": "Mm"
},
"angle": {
"type": "Degrees"
}
}
},
{
"type": "Number",
"value": 2.0,
"ty": {
"type": "Default",
"len": {
"type": "Mm"
},
"angle": {
"type": "Degrees"
}
}
}
]
},
"b": {
"type": "HomArray",
"value": [
{
"type": "Number",
"value": 0.0,
"ty": {
"type": "Default",
"len": {
"type": "Mm"
},
"angle": {
"type": "Degrees"
}
}
},
{
"type": "Number",
"value": 1.0,
"ty": {
"type": "Default",
"len": {
"type": "Mm"
},
"angle": {
"type": "Degrees"
}
}
},
{
"type": "Number",
"value": 2.0,
"ty": {
"type": "Default",
"len": {
"type": "Mm"
},
"angle": {
"type": "Degrees"
}
}
}
]
},
"c": {
"type": "HomArray",
"value": [
{
"type": "Number",
"value": 0.0,
"ty": {
"type": "Default",
"len": {
"type": "Mm"
},
"angle": {
"type": "Degrees"
}
}
},
{
"type": "Number",
"value": 1.0,
"ty": {
"type": "Default",
"len": {
"type": "Mm"
},
"angle": {
"type": "Degrees"
}
}
},
{
"type": "Number",
"value": 2.0,
"ty": {
"type": "Default",
"len": {
"type": "Mm"
},
"angle": {
"type": "Degrees"
}
}
}
]
}
}

View File

@ -2,4 +2,6 @@
source: kcl-lib/src/simulation_tests.rs
description: Result of unparsing add_arrays.kcl
---
answer = [0, 1] + [2]
a = [0, 1] + [2]
b = [0, 1] + 2
c = 0 + [1, 2]

View File

@ -1,60 +0,0 @@
// There are 3 ways to define a plane in KCL, according to https://zoo.dev/docs/kcl-std/types/std-types-Plane
// - A default plane
// - Modifying a default plane e.g. via offsetPlane
// - Defining your own struct
// This file tests they all work equivalently.
// Define a plane using struct representation.
myPlane = {
origin = { x = 0, y = 0, z = 0 },
xAxis = { x = 1, y = 0, z = 0 },
yAxis = { x = 0, y = 1, z = 0 },
}
// Prove we can get its axes and origin.
ax = myPlane.xAxis
assert(ax[0], isEqualTo = 1)
assert(ax[1], isEqualTo = 0)
assert(ax[2], isEqualTo = 0)
ay = myPlane.yAxis
assert(ay[0], isEqualTo = 0)
assert(ay[1], isEqualTo = 1)
assert(ay[2], isEqualTo = 0)
aorigin = myPlane.origin
assert(aorigin[0], isEqualTo = 0)
assert(aorigin[1], isEqualTo = 0)
assert(aorigin[2], isEqualTo = 0)
// Define a plane using standard planes.
myOtherPlane = XY
// Prove we can get its axes and origin.
axOther = myOtherPlane.xAxis
assert(axOther[0], isEqualTo = 1)
assert(axOther[1], isEqualTo = 0)
assert(axOther[2], isEqualTo = 0)
ayOther = myOtherPlane.yAxis
assert(ayOther[0], isEqualTo = 0)
assert(ayOther[1], isEqualTo = 1)
assert(ayOther[2], isEqualTo = 0)
aoriginOther = myOtherPlane.origin
assert(aoriginOther[0], isEqualTo = 0)
assert(aoriginOther[1], isEqualTo = 0)
assert(aoriginOther[2], isEqualTo = 0)
// Define a plane using a plane-modifying function like offsetPlane.
myAlternatePlane = offsetPlane(XY, offset = 0)
// Prove we can get its axes and origin.
axAlternate = myAlternatePlane.xAxis
assert(axAlternate[0], isEqualTo = 1)
assert(axAlternate[1], isEqualTo = 0)
assert(axAlternate[2], isEqualTo = 0)
ayAlternate = myAlternatePlane.yAxis
assert(ayAlternate[0], isEqualTo = 0)
assert(ayAlternate[1], isEqualTo = 1)
assert(ayAlternate[2], isEqualTo = 0)
aoriginAlternate = myAlternatePlane.origin
assert(aoriginAlternate[0], isEqualTo = 0)
assert(aoriginAlternate[1], isEqualTo = 0)
assert(aoriginAlternate[2], isEqualTo = 0)

View File

@ -10,3 +10,4 @@
5) src/lang/std/sketch.ts -> src/lang/modifyAst.ts -> src/lang/std/sketchcombos.ts
6) src/lib/singletons.ts -> src/clientSideScene/sceneEntities.ts -> src/clientSideScene/segments.ts -> src/components/Toolbar/angleLengthInfo.ts
7) src/lib/singletons.ts -> src/clientSideScene/sceneEntities.ts -> src/clientSideScene/segments.ts
8) src/hooks/useModelingContext.ts -> src/components/ModelingMachineProvider.tsx -> src/components/Toolbar/setAngleLength.tsx -> src/components/SetAngleLengthModal.tsx -> src/lib/useCalculateKclExpression.ts

View File

@ -65,7 +65,6 @@ import { useModelingContext } from '@src/hooks/useModelingContext'
import { xStateValueToString } from '@src/lib/xStateValueToString'
import { getSelectionTypeDisplayText } from '@src/lib/selections'
import type { StatusBarItemType } from '@src/components/StatusBar/statusBarTypes'
import { Toolbar } from '@src/Toolbar'
// CYCLIC REF
sceneInfra.camControls.engineStreamActor = engineStreamActor
@ -247,18 +246,15 @@ export function App() {
return (
<div className="h-screen flex flex-col overflow-hidden select-none">
<div className="relative flex flex-1 flex-col">
<div className="relative flex items-center flex-col">
<AppHeader
className="transition-opacity transition-duration-75"
project={{ project, file }}
enableMenu={true}
nativeFileMenuCreated={nativeFileMenuCreated}
>
<CommandBarOpenButton />
<ShareButton />
</AppHeader>
<Toolbar />
</div>
<AppHeader
className="transition-opacity transition-duration-75"
project={{ project, file }}
enableMenu={true}
nativeFileMenuCreated={nativeFileMenuCreated}
>
<CommandBarOpenButton />
<ShareButton />
</AppHeader>
<ModalContainer />
<ModelingSidebar />
<EngineStream pool={pool} authToken={authToken} />

View File

@ -203,7 +203,7 @@ export function Toolbar({
<menu
data-current-mode={currentMode}
data-onboarding-id="toolbar"
className="z-[19] max-w-full whitespace-nowrap rounded-b px-2 py-1 bg-chalkboard-10 dark:bg-chalkboard-90 relative border border-chalkboard-30 dark:border-chalkboard-80 border-t-0 shadow-sm"
className="max-w-full whitespace-nowrap rounded-b px-2 py-1 bg-chalkboard-10 dark:bg-chalkboard-90 relative border border-chalkboard-30 dark:border-chalkboard-80 border-t-0 shadow-sm"
>
<ul
{...props}

View File

@ -3,6 +3,7 @@
in Tailwind, such as complex grid layouts.
*/
.header {
grid-template-columns: 1fr auto 1fr;
user-select: none;
-webkit-user-select: none;
}

View File

@ -1,30 +1,30 @@
import { Toolbar } from '@src/Toolbar'
import { CommandBarOpenButton } from '@src/components/CommandBarOpenButton'
import ProjectSidebarMenu from '@src/components/ProjectSidebarMenu'
import UserSidebarMenu from '@src/components/UserSidebarMenu'
import { isDesktop } from '@src/lib/isDesktop'
import type { IndexLoaderData } from '@src/lib/types'
import { type IndexLoaderData } from '@src/lib/types'
import { useUser } from '@src/lib/singletons'
import styles from './AppHeader.module.css'
import type { ReactNode } from 'react'
interface AppHeaderProps extends React.PropsWithChildren {
showToolbar?: boolean
project?: Omit<IndexLoaderData, 'code'>
className?: string
enableMenu?: boolean
style?: React.CSSProperties
nativeFileMenuCreated: boolean
projectMenuChildren?: ReactNode
}
export const AppHeader = ({
showToolbar = true,
project,
children,
className = '',
style,
enableMenu = false,
nativeFileMenuCreated,
projectMenuChildren,
}: AppHeaderProps) => {
const user = useUser()
@ -32,9 +32,14 @@ export const AppHeader = ({
<header
id="app-header"
data-testid="app-header"
className={`w-full flex ${styles.header || ''} ${
isDesktop() ? styles.desktopApp : ''
} overlaid-panes sticky top-0 z-20 px-2 justify-between ${className || ''} bg-chalkboard-10 dark:bg-chalkboard-90 border-b border-chalkboard-30 dark:border-chalkboard-70`}
className={
'w-full grid ' +
styles.header +
` ${
isDesktop() ? styles.desktopApp + ' ' : ''
}overlaid-panes sticky top-0 z-20 px-2 items-start ` +
className
}
data-native-file-menu={nativeFileMenuCreated}
style={style}
>
@ -42,9 +47,13 @@ export const AppHeader = ({
enableMenu={enableMenu}
project={project?.project}
file={project?.file}
>
{projectMenuChildren}
</ProjectSidebarMenu>
/>
{/* Toolbar if the context deems it */}
<div className="flex flex-col items-center gap-2">
<div className="flex-grow flex justify-center max-w-lg md:max-w-xl lg:max-w-2xl xl:max-w-4xl 2xl:max-w-5xl">
{showToolbar && <Toolbar />}
</div>
</div>
<div className="flex items-center gap-2 py-1 ml-auto">
{/* If there are children, show them, otherwise show User menu */}
{children || <CommandBarOpenButton />}

View File

@ -33,7 +33,6 @@ import { useSettings } from '@src/lib/singletons'
import { commandBarActor, useCommandBarState } from '@src/lib/singletons'
import styles from './CommandBarKclInput.module.css'
import { useModelingContext } from '@src/hooks/useModelingContext'
// TODO: remove the need for this selector once we decouple all actors from React
const machineContextSelector = (snapshot?: SnapshotFrom<AnyStateMachine>) =>
@ -56,9 +55,6 @@ function CommandBarKclInput({
arg.name
] as KclCommandValue | undefined
const settings = useSettings()
const {
context: { selectionRanges },
} = useModelingContext()
const argMachineContext = useSelector(
arg.machineActor,
machineContextSelector
@ -130,7 +126,6 @@ function CommandBarKclInput({
value,
initialVariableName,
sourceRange: sourceRangeForPrevVariables,
selectionRanges,
})
const varMentionData: Completion[] = prevVariables.map((v) => {

View File

@ -10,13 +10,13 @@ export function CommandBarOpenButton() {
return (
<button
type="button"
className="flex gap-1 items-center py-0 pl-0.5 pr-1 sm:pr-0.5 m-0 text-primary dark:text-inherit bg-chalkboard-10/80 dark:bg-chalkboard-100/50 hover:bg-chalkboard-10 dark:hover:bg-chalkboard-100 border border-solid border-primary/50 hover:border-primary active:border-primary"
className="flex gap-1 items-center py-0 px-0.5 m-0 text-primary dark:text-inherit bg-chalkboard-10/80 dark:bg-chalkboard-100/50 hover:bg-chalkboard-10 dark:hover:bg-chalkboard-100 border border-solid border-primary/50 hover:border-primary active:border-primary"
onClick={() => commandBarActor.send({ type: 'Open' })}
data-testid="command-bar-open-button"
>
<CustomIcon name="command" className="w-5 h-5" />
<span>Commands</span>
<kbd className="hidden sm:block dark:bg-chalkboard-80 font-mono rounded-sm text-primary/70 dark:text-inherit inline-block px-1">
<kbd className="dark:bg-chalkboard-80 font-mono rounded-sm text-primary/70 dark:text-inherit inline-block px-1">
{hotkeyDisplay(COMMAND_PALETTE_HOTKEY, platform)}
</kbd>
</button>

View File

@ -17,39 +17,40 @@ import { APP_NAME } from '@src/lib/constants'
import { isDesktop } from '@src/lib/isDesktop'
import { PATHS } from '@src/lib/paths'
import { engineCommandManager, kclManager } from '@src/lib/singletons'
import type { IndexLoaderData } from '@src/lib/types'
import { type IndexLoaderData } from '@src/lib/types'
import { commandBarActor } from '@src/lib/singletons'
interface ProjectSidebarMenuProps extends React.PropsWithChildren {
enableMenu?: boolean
project?: IndexLoaderData['project']
file?: IndexLoaderData['file']
}
const ProjectSidebarMenu = ({
project,
file,
enableMenu = false,
children,
}: ProjectSidebarMenuProps) => {
}: {
enableMenu?: boolean
project?: IndexLoaderData['project']
file?: IndexLoaderData['file']
}) => {
// Make room for traffic lights on desktop left side.
// TODO: make sure this doesn't look like shit on Linux or Windows
const trafficLightsOffset =
isDesktop() && window.electron.os.isMac ? 'ml-20' : ''
return (
<div className={'!no-underline flex gap-2 ' + trafficLightsOffset}>
<div
className={
'!no-underline h-full mr-auto max-h-min min-h-12 min-w-max flex items-center gap-2 ' +
trafficLightsOffset
}
>
<AppLogoLink project={project} file={file} />
{enableMenu ? (
<ProjectMenuPopover project={project} file={file} />
) : (
<span
className="hidden self-center px-2 select-none cursor-default text-sm text-chalkboard-110 dark:text-chalkboard-20 whitespace-nowrap lg:block"
className="hidden select-none cursor-default text-sm text-chalkboard-110 dark:text-chalkboard-20 whitespace-nowrap lg:block"
data-testid="project-name"
>
{project?.name ? project.name : APP_NAME}
</span>
)}
{children}
</div>
)
}
@ -63,7 +64,7 @@ function AppLogoLink({
}) {
const { onProjectClose } = useLspContext()
const wrapperClassName =
"relative h-full grid flex-none place-content-center group p-1.5 before:block before:content-[''] before:absolute before:inset-0 before:bottom-1 before:z-[-1] before:bg-primary before:rounded-b-sm"
"relative h-full grid place-content-center group p-1.5 before:block before:content-[''] before:absolute before:inset-0 before:bottom-2.5 before:z-[-1] before:bg-primary before:rounded-b-sm"
const logoClassName = 'w-auto h-4 text-chalkboard-10'
return isDesktop() ? (
@ -237,23 +238,12 @@ function ProjectMenuPopover({
return (
<Popover className="relative">
<Popover.Button
className="gap-1 rounded-sm mr-auto max-h-min min-w-max border-0 py-1 px-2 flex items-center focus-visible:outline-appForeground dark:hover:bg-chalkboard-90"
className="gap-1 rounded-sm h-9 mr-auto max-h-min min-w-max border-0 py-1 px-2 flex items-center focus-visible:outline-appForeground dark:hover:bg-chalkboard-90"
data-testid="project-sidebar-toggle"
>
<div className="flex items-baseline py-0.5 text-sm gap-1">
{isDesktop() && project?.name && (
<>
<span
className="hidden whitespace-nowrap md:block"
data-testid="app-header-project-name"
>
{project.name}
</span>
<span className="hidden md:block">/</span>
</>
)}
<div className="flex flex-col items-start py-0.5">
<span
className="text-sm text-chalkboard-110 dark:text-chalkboard-20 whitespace-nowrap"
className="hidden text-sm text-chalkboard-110 dark:text-chalkboard-20 whitespace-nowrap lg:block"
data-testid="app-header-file-name"
>
{isDesktop() && file?.name
@ -262,6 +252,14 @@ function ProjectMenuPopover({
)
: APP_NAME}
</span>
{isDesktop() && project?.name && (
<span
className="hidden text-xs text-chalkboard-70 dark:text-chalkboard-40 whitespace-nowrap lg:block"
data-testid="app-header-project-name"
>
{project.name}
</span>
)}
</div>
<CustomIcon
name="caretDown"

View File

@ -10,7 +10,6 @@ import {
} from '@src/components/AvailableVarsHelpers'
import type { Expr } from '@src/lang/wasm'
import { useCalculateKclExpression } from '@src/lib/useCalculateKclExpression'
import type { Selections } from '@src/lib/selections'
type ModalResolve = {
value: string
@ -26,7 +25,6 @@ type SetAngleLengthModalProps = InstanceProps<ModalResolve, ModalReject> & {
value: number
valueName: string
shouldCreateVariable?: boolean
selectionRanges: Selections
}
export const createSetAngleLengthModal = create<
@ -42,7 +40,6 @@ export const SetAngleLengthModal = ({
value: initialValue,
valueName,
shouldCreateVariable: initialShouldCreateVariable = false,
selectionRanges,
}: SetAngleLengthModalProps) => {
const [sign, setSign] = useState(Math.sign(Number(initialValue)))
const [value, setValue] = useState(String(initialValue * sign))
@ -62,7 +59,6 @@ export const SetAngleLengthModal = ({
} = useCalculateKclExpression({
value,
initialVariableName: valueName,
selectionRanges,
})
return (

View File

@ -10,7 +10,6 @@ import {
} from '@src/components/AvailableVarsHelpers'
import type { Expr } from '@src/lang/wasm'
import { useCalculateKclExpression } from '@src/lib/useCalculateKclExpression'
import type { Selections } from '@src/lib/selections'
type ModalResolve = {
value: string
@ -28,7 +27,6 @@ type GetInfoModalProps = InstanceProps<ModalResolve, ModalReject> & {
isSegNameEditable: boolean
value?: number
initialVariableName: string
selectionRanges: Selections
}
export const createInfoModal = create<
@ -45,7 +43,6 @@ export const GetInfoModal = ({
isSegNameEditable,
value: initialValue,
initialVariableName,
selectionRanges,
}: GetInfoModalProps) => {
const [sign, setSign] = useState(Math.sign(Number(initialValue)))
const [segName, setSegName] = useState(initialSegName)
@ -63,11 +60,7 @@ export const GetInfoModal = ({
newVariableName,
isNewVariableNameUnique,
newVariableInsertIndex,
} = useCalculateKclExpression({
value: value,
initialVariableName,
selectionRanges,
})
} = useCalculateKclExpression({ value: value, initialVariableName })
return (
<Transition appear show={isOpen} as={Fragment}>

View File

@ -6,13 +6,11 @@ import { type InstanceProps, create } from 'react-modal-promise'
import { ActionButton } from '@src/components/ActionButton'
import { CreateNewVariable } from '@src/components/AvailableVarsHelpers'
import { useCalculateKclExpression } from '@src/lib/useCalculateKclExpression'
import type { Selections } from '@src/lib/selections'
type ModalResolve = { variableName: string }
type ModalReject = boolean
type SetVarNameModalProps = InstanceProps<ModalResolve, ModalReject> & {
valueName: string
selectionRanges: Selections
}
export const createSetVarNameModal = create<
@ -26,14 +24,9 @@ export const SetVarNameModal = ({
onResolve,
onReject,
valueName,
selectionRanges,
}: SetVarNameModalProps) => {
const { isNewVariableNameUnique, newVariableName, setNewVariableName } =
useCalculateKclExpression({
value: '',
initialVariableName: valueName,
selectionRanges,
})
useCalculateKclExpression({ value: '', initialVariableName: valueName })
return (
<Transition appear show={isOpen} as={Fragment}>

View File

@ -83,7 +83,7 @@ export const ShareButton = memo(function ShareButton() {
billingContext.tier === undefined
return (
<Popover className="relative hidden sm:flex">
<Popover className="relative flex">
<Popover.Button
as="div"
className="relative group border-0 w-fit min-w-max p-0 rounded-l-full focus-visible:outline-appForeground"

View File

@ -1,4 +1,5 @@
import type { Node } from '@rust/kcl-lib/bindings/Node'
import { removeDoubleNegatives } from '@src/components/AvailableVarsHelpers'
import {
GetInfoModal,
@ -166,7 +167,6 @@ export async function applyConstraintIntersect({
isSegNameEditable: !tagInfo?.isTagExisting,
value: valueUsedInTransform,
initialVariableName: 'offset',
selectionRanges,
})
if (
!variableName &&

View File

@ -113,7 +113,6 @@ export async function applyConstraintAbsDistance({
await getModalInfo({
value: forceVal,
valueName: constraint === 'yAbs' ? 'yDis' : 'xDis',
selectionRanges,
})
if (!isExprBinaryPart(valueNode))
return Promise.reject('Invalid valueNode, is not a BinaryPart')

View File

@ -117,8 +117,7 @@ export async function applyConstraintAngleBetween({
isSegNameEditable: !tagInfo?.isTagExisting,
value: valueUsedInTransform,
initialVariableName: 'angle',
selectionRanges,
})
} as any)
if (
segName === tagInfo?.tag &&
Number(value) === valueUsedInTransform &&

View File

@ -123,8 +123,7 @@ export async function applyConstraintHorzVertDistance({
isSegNameEditable: !tagInfo?.isTagExisting,
value: valueUsedInTransform,
initialVariableName: constraint === 'setHorzDistance' ? 'xDis' : 'yDis',
selectionRanges,
})
} as any)
if (
!variableName &&
segName === tagInfo?.tag &&

View File

@ -143,7 +143,6 @@ export async function applyConstraintAngleLength({
value: forceVal,
valueName: angleOrLength === 'setAngle' ? 'angle' : 'length',
shouldCreateVariable: true,
selectionRanges,
})
if (!isExprBinaryPart(valueNode))
return Promise.reject('Invalid valueNode, is not a BinaryPart')

View File

@ -178,9 +178,9 @@ const UserSidebarMenu = ({ user }: { user?: User }) => {
}
return (
<Popover className="relative grid">
<Popover className="relative">
<Popover.Button
className="m-0 relative group border-0 w-fit min-w-max p-0 rounded-l-full rounded-r focus-visible:outline-appForeground"
className="relative group border-0 w-fit min-w-max p-0 rounded-l-full focus-visible:outline-appForeground"
data-testid="user-sidebar-toggle"
>
<div className="flex items-center">

View File

@ -11,6 +11,7 @@ export const VITE_KC_API_WS_MODELING_URL = env.VITE_KC_API_WS_MODELING_URL as
export const VITE_KC_API_BASE_URL = env.VITE_KC_API_BASE_URL
export const VITE_KC_SITE_BASE_URL = env.VITE_KC_SITE_BASE_URL
export const VITE_KC_SITE_APP_URL = env.VITE_KC_SITE_APP_URL
export const VITE_KC_SKIP_AUTH = env.VITE_KC_SKIP_AUTH as string | undefined
export const VITE_KC_CONNECTION_TIMEOUT_MS =
env.VITE_KC_CONNECTION_TIMEOUT_MS as string | undefined
export const VITE_KC_DEV_TOKEN = env.VITE_KC_DEV_TOKEN as string | undefined

View File

@ -52,7 +52,6 @@ export function useConvertToVariable(range?: SourceRange) {
try {
const { variableName } = await getVarNameModal({
valueName: valueName || 'var',
selectionRanges: context.selectionRanges,
})
const { modifiedAst: _modifiedAst, pathToReplacedNode } =

View File

@ -1,4 +1,6 @@
import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
import { useModelingContext } from '@src/hooks/useModelingContext'
import { useKclContext } from '@src/lang/KclProvider'
import { findUniqueName } from '@src/lang/create'
import type { PrevVariable } from '@src/lang/queryAst'
@ -10,7 +12,6 @@ import { getCalculatedKclExpressionValue } from '@src/lib/kclHelpers'
import { kclManager } from '@src/lib/singletons'
import { err } from '@src/lib/trap'
import { getInVariableCase } from '@src/lib/utils'
import type { Selections } from '@src/lib/selections'
const isValidVariableName = (name: string) =>
/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(name)
@ -24,12 +25,10 @@ export function useCalculateKclExpression({
value,
initialVariableName: valueName = '',
sourceRange,
selectionRanges,
}: {
value: string
initialVariableName?: string
sourceRange?: SourceRange
selectionRanges: Selections
}): {
inputRef: React.RefObject<HTMLInputElement>
valueNode: Expr | null
@ -46,10 +45,12 @@ export function useCalculateKclExpression({
// has completed
const [isExecuting, setIsExecuting] = useState(false)
const { variables, code } = useKclContext()
const { context } = useModelingContext()
// If there is no selection, use the end of the code
// so all variables are available
const selectionRange: SourceRange | undefined =
selectionRanges.graphSelections[0]?.codeRef?.range
const selectionRange:
| (typeof context)['selectionRanges']['graphSelections'][number]['codeRef']['range']
| undefined = context.selectionRanges.graphSelections[0]?.codeRef?.range
// If there is no selection, use the end of the code
// If we don't memoize this, we risk an infinite set/read state loop
const endingSourceRange = useMemo(

View File

@ -1,5 +1,10 @@
import type { Models } from '@kittycad/lib'
import { VITE_KC_API_BASE_URL, VITE_KC_DEV_TOKEN } from '@src/env'
import {
DEV,
VITE_KC_API_BASE_URL,
VITE_KC_DEV_TOKEN,
VITE_KC_SKIP_AUTH,
} from '@src/env'
import { assign, fromPromise, setup } from 'xstate'
import { COOKIE_NAME, OAUTH2_DEVICE_CLIENT_ID } from '@src/lib/constants'
@ -16,6 +21,26 @@ import {
} from '@src/lib/withBaseURL'
import { ACTOR_IDS } from '@src/machines/machineConstants'
const SKIP_AUTH = VITE_KC_SKIP_AUTH === 'true' && DEV
const LOCAL_USER: Models['User_type'] = {
id: '8675309',
name: 'Test User',
email: 'kittycad.sidebar.test@example.com',
image: 'https://placekitten.com/200/200',
created_at: 'yesteryear',
updated_at: 'today',
company: 'Test Company',
discord: 'Test User#1234',
github: 'testuser',
phone: '555-555-5555',
first_name: 'Test',
last_name: 'User',
can_train_on_data: false,
is_service_account: false,
deletion_scheduled: false,
}
export interface UserContext {
user?: Models['User_type']
token: string
@ -140,6 +165,19 @@ async function getUser(input: { token?: string }) {
if (!token && isDesktop()) return Promise.reject(new Error('No token found'))
if (token) headers['Authorization'] = `Bearer ${token}`
if (SKIP_AUTH) {
// For local tests
if (localStorage.getItem('FORCE_NO_IMAGE')) {
LOCAL_USER.image = ''
}
markOnce('code/didAuth')
return {
user: LOCAL_USER,
token,
}
}
const userPromise = isDesktop()
? getUserDesktop(token, VITE_KC_API_BASE_URL)
: fetch(url, {

View File

@ -29,6 +29,7 @@ import {
applyConstraintHorzVert,
horzVertInfo,
} from '@src/components/Toolbar/HorzVert'
import { intersectInfo } from '@src/components/Toolbar/Intersect'
import {
applyRemoveConstrainingValues,
removeConstrainingValuesInfo,
@ -154,7 +155,6 @@ import type { Plane } from '@rust/kcl-lib/bindings/Plane'
import type { Point3d } from '@rust/kcl-lib/bindings/ModelingCmd'
import { getNodePathFromSourceRange } from '@src/lang/queryAstNodePathUtils'
import { letEngineAnimateAndSyncCamAfter } from '@src/clientSideScene/CameraControls'
import { intersectInfo } from '@src/components/Toolbar/Intersect'
export type SetSelections =
| {

View File

@ -70,10 +70,12 @@ dotenv.config({ path: [`.env.${NODE_ENV}.local`, `.env.${NODE_ENV}`] })
// default vite values based on mode
process.env.NODE_ENV ??= viteEnv.MODE
process.env.BASE_URL ??= viteEnv.VITE_KC_API_BASE_URL
process.env.VITE_KC_API_WS_MODELING_URL ??= viteEnv.VITE_KC_API_WS_MODELING_URL
process.env.VITE_KC_API_BASE_URL ??= viteEnv.VITE_KC_API_BASE_URL
process.env.VITE_KC_SITE_BASE_URL ??= viteEnv.VITE_KC_SITE_BASE_URL
process.env.VITE_KC_SITE_APP_URL ??= viteEnv.VITE_KC_SITE_APP_URL
process.env.VITE_KC_SKIP_AUTH ??= viteEnv.VITE_KC_SKIP_AUTH
process.env.VITE_KC_CONNECTION_TIMEOUT_MS ??=
viteEnv.VITE_KC_CONNECTION_TIMEOUT_MS

View File

@ -292,6 +292,7 @@ contextBridge.exposeInMainWorld('electron', {
'VITE_KC_API_BASE_URL',
'VITE_KC_SITE_BASE_URL',
'VITE_KC_SITE_APP_URL',
'VITE_KC_SKIP_AUTH',
'VITE_KC_CONNECTION_TIMEOUT_MS',
'VITE_KC_DEV_TOKEN',

View File

@ -221,7 +221,10 @@ const Home = () => {
return (
<div className="relative flex flex-col items-stretch h-screen w-screen overflow-hidden">
<AppHeader nativeFileMenuCreated={nativeFileMenuCreated} />
<AppHeader
nativeFileMenuCreated={nativeFileMenuCreated}
showToolbar={false}
/>
<div className="overflow-hidden self-stretch w-full flex-1 home-layout max-w-4xl lg:max-w-5xl xl:max-w-7xl px-4 mx-auto mt-8 lg:mt-24 lg:px-0">
<HomeHeader
setQuery={setQuery}