Compare commits
	
		
			13 Commits
		
	
	
		
			kcl-48
			...
			franknoiro
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| c64c4705ef | |||
| a122128e0b | |||
| 0fe79ea7cb | |||
| 021e4b9565 | |||
| e57783483a | |||
| e952148cd2 | |||
| b5028f7aa8 | |||
| df6b4f4c37 | |||
| 41eb64925b | |||
| fc076173ff | |||
| 98822869f7 | |||
| df0510c199 | |||
| fda65bcbd7 | 
							
								
								
									
										21
									
								
								.github/workflows/cargo-bench.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										21
									
								
								.github/workflows/cargo-bench.yml
									
									
									
									
										vendored
									
									
								
							| @ -40,13 +40,16 @@ jobs: | ||||
|       - name: Install dependencies | ||||
|         run: | | ||||
|           cargo install cargo-criterion | ||||
|           sudo apt update | ||||
|           sudo apt install -y valgrind | ||||
|       - uses: boa-dev/criterion-compare-action@v3 | ||||
|           cargo install cargo-codspeed | ||||
|           cd rust/kcl-lib | ||||
|           cargo add --dev codspeed-criterion-compat --rename criterion | ||||
|       - name: Build the benchmark target(s) | ||||
|         run: | | ||||
|           cd rust | ||||
|           cargo codspeed build | ||||
|       - name: Run the benchmarks | ||||
|         uses: CodSpeedHQ/action@v3 | ||||
|         with: | ||||
|           cwd: "rust" | ||||
|           defaultFeatures: true | ||||
|           # Needed. The name of the branch to compare with. This default uses the branch which is being pulled against | ||||
|           branchName: ${{ github.base_ref }} | ||||
|         env: | ||||
|           KITTYCAD_API_TOKEN: ${{secrets.KITTYCAD_API_TOKEN}} | ||||
|           working-directory: rust | ||||
|           run: KITTYCAD_API_TOKEN=${{secrets.KITTYCAD_API_TOKEN}} cargo codspeed run | ||||
|           token: ${{ secrets.CODSPEED_TOKEN }} | ||||
|  | ||||
							
								
								
									
										4
									
								
								.github/workflows/generate-website-docs.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								.github/workflows/generate-website-docs.yml
									
									
									
									
										vendored
									
									
								
							| @ -40,9 +40,13 @@ jobs: | ||||
|           # cleanup old | ||||
|           rm -rf documentation/content/pages/docs/kcl/*.md | ||||
|           rm -rf documentation/content/pages/docs/kcl/types | ||||
|           rm -rf documentation/content/pages/docs/kcl/settings | ||||
|           rm -rf documentation/content/pages/docs/kcl/consts | ||||
|           # move new | ||||
|           mv -f docs/kcl/*.md documentation/content/pages/docs/kcl/ | ||||
|           mv -f docs/kcl/types documentation/content/pages/docs/kcl/ | ||||
|           mv -f docs/kcl/settings documentation/content/pages/docs/kcl/ | ||||
|           mv -f docs/kcl/consts documentation/content/pages/docs/kcl/ | ||||
|       - name: move kcl-samples | ||||
|         shell: bash | ||||
|         run: | | ||||
|  | ||||
							
								
								
									
										25
									
								
								docs/kcl/consts.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								docs/kcl/consts.md
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,25 @@ | ||||
| --- | ||||
| title: "KCL Constants" | ||||
| excerpt: "Documentation for the KCL constants." | ||||
| layout: manual | ||||
| --- | ||||
|  | ||||
| ## Table of Contents | ||||
|  | ||||
|  | ||||
| ### `std` | ||||
|  | ||||
| - [`HALF_TURN`](/docs/kcl/consts/std-HALF_TURN) | ||||
| - [`QUARTER_TURN`](/docs/kcl/consts/std-QUARTER_TURN) | ||||
| - [`THREE_QUARTER_TURN`](/docs/kcl/consts/std-THREE_QUARTER_TURN) | ||||
| - [`XY`](/docs/kcl/consts/std-XY) | ||||
| - [`XZ`](/docs/kcl/consts/std-XZ) | ||||
| - [`YZ`](/docs/kcl/consts/std-YZ) | ||||
| - [`ZERO`](/docs/kcl/consts/std-ZERO) | ||||
|  | ||||
| ### `std::math` | ||||
|  | ||||
| - [`E`](/docs/kcl/consts/std-math-E) | ||||
| - [`PI`](/docs/kcl/consts/std-math-PI) | ||||
| - [`TAU`](/docs/kcl/consts/std-math-TAU) | ||||
|  | ||||
| @ -12,25 +12,26 @@ layout: manual | ||||
| * [`Modules`](kcl/modules) | ||||
| * [`Settings`](kcl/settings) | ||||
| * [`Known Issues`](kcl/known-issues) | ||||
| * [`Constants`](kcl/consts) | ||||
|  | ||||
| ### Standard library | ||||
|  | ||||
| * **Primitive types** | ||||
|   * [`bool`](kcl/bool) | ||||
|   * [`number`](kcl/number) | ||||
|   * [`string`](kcl/string) | ||||
|   * [`tag`](kcl/tag) | ||||
|   * [`bool`](kcl/types/bool) | ||||
|   * [`number`](kcl/types/number) | ||||
|   * [`string`](kcl/types/string) | ||||
|   * [`tag`](kcl/types/tag) | ||||
| * **std** | ||||
|   * [`HALF_TURN`](kcl/const_std-HALF_TURN) | ||||
|   * [`Plane`](kcl/Plane) | ||||
|   * [`QUARTER_TURN`](kcl/const_std-QUARTER_TURN) | ||||
|   * [`Sketch`](kcl/Sketch) | ||||
|   * [`Solid`](kcl/Solid) | ||||
|   * [`THREE_QUARTER_TURN`](kcl/const_std-THREE_QUARTER_TURN) | ||||
|   * [`XY`](kcl/const_std-XY) | ||||
|   * [`XZ`](kcl/const_std-XZ) | ||||
|   * [`YZ`](kcl/const_std-YZ) | ||||
|   * [`ZERO`](kcl/const_std-ZERO) | ||||
|   * [`HALF_TURN`](kcl/consts/std-HALF_TURN) | ||||
|   * [`Plane`](kcl/types/Plane) | ||||
|   * [`QUARTER_TURN`](kcl/consts/std-QUARTER_TURN) | ||||
|   * [`Sketch`](kcl/types/Sketch) | ||||
|   * [`Solid`](kcl/types/Solid) | ||||
|   * [`THREE_QUARTER_TURN`](kcl/consts/std-THREE_QUARTER_TURN) | ||||
|   * [`XY`](kcl/consts/std-XY) | ||||
|   * [`XZ`](kcl/consts/std-XZ) | ||||
|   * [`YZ`](kcl/consts/std-YZ) | ||||
|   * [`ZERO`](kcl/consts/std-ZERO) | ||||
|   * [`abs`](kcl/abs) | ||||
|   * [`acos`](kcl/acos) | ||||
|   * [`angleToMatchLengthX`](kcl/angleToMatchLengthX) | ||||
| @ -134,9 +135,9 @@ layout: manual | ||||
|   * [`yLine`](kcl/yLine) | ||||
|   * [`yd`](kcl/yd) | ||||
| * **std::math** | ||||
|   * [`E`](kcl/const_std-math-E) | ||||
|   * [`PI`](kcl/const_std-math-PI) | ||||
|   * [`TAU`](kcl/const_std-math-TAU) | ||||
|   * [`E`](kcl/consts/std-math-E) | ||||
|   * [`PI`](kcl/consts/std-math-PI) | ||||
|   * [`TAU`](kcl/consts/std-math-TAU) | ||||
|   * [`cos`](kcl/std-math-cos) | ||||
|   * [`sin`](kcl/std-math-sin) | ||||
|   * [`tan`](kcl/std-math-tan) | ||||
|  | ||||
| @ -1,5 +1,5 @@ | ||||
| --- | ||||
| title: "KCL settings" | ||||
| title: "KCL Settings" | ||||
| excerpt: "Documentation of settings for the KCL language and Zoo Modeling App." | ||||
| layout: manual | ||||
| --- | ||||
| @ -8,16 +8,16 @@ layout: manual | ||||
|  | ||||
| There are three levels of settings available in the KittyCAD modeling application: | ||||
|  | ||||
| 1. [User Settings](/docs/kcl/settings/user.toml): Global settings that apply to all projects, stored in `user.toml` | ||||
| 2. [Project Settings](/docs/kcl/settings/project.toml): Settings specific to a project, stored in `project.toml` | ||||
| 1. [User Settings](/docs/kcl/settings/user): Global settings that apply to all projects, stored in `user.toml` | ||||
| 2. [Project Settings](/docs/kcl/settings/project): Settings specific to a project, stored in `project.toml` | ||||
| 3. Per-file Settings: Settings that apply to a single KCL file, specified using the `@settings` attribute | ||||
|  | ||||
| ## Configuration Files | ||||
|  | ||||
| The KittyCAD modeling app uses TOML files for configuration: | ||||
|  | ||||
| * **User Settings**: `user.toml` - See [complete documentation](/docs/kcl/settings/user.toml) | ||||
| * **Project Settings**: `project.toml` - See [complete documentation](/docs/kcl/settings/project.toml) | ||||
| * **User Settings**: `user.toml` - See [complete documentation](/docs/kcl/settings/user) | ||||
| * **Project Settings**: `project.toml` - See [complete documentation](/docs/kcl/settings/project) | ||||
|  | ||||
| ## Per-file settings | ||||
|  | ||||
|  | ||||
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							| @ -274,7 +274,6 @@ test.describe('Feature Tree pane', () => { | ||||
|         currentArgKey: 'distance', | ||||
|         currentArgValue: initialInput, | ||||
|         headerArguments: { | ||||
|           Selection: '1 face', | ||||
|           Distance: initialInput, | ||||
|         }, | ||||
|         highlightedHeaderArg: 'distance', | ||||
| @ -291,7 +290,6 @@ test.describe('Feature Tree pane', () => { | ||||
|       await cmdBar.expectState({ | ||||
|         stage: 'review', | ||||
|         headerArguments: { | ||||
|           Selection: '1 face', | ||||
|           // The calculated value is shown in the argument summary | ||||
|           Distance: initialInput, | ||||
|         }, | ||||
|  | ||||
										
											Binary file not shown.
										
									
								
							| Before Width: | Height: | Size: 76 KiB After Width: | Height: | Size: 76 KiB | 
| @ -29,5 +29,5 @@ | ||||
|       } | ||||
|     } | ||||
|   ], | ||||
|   "kcl_version": "0.2.47" | ||||
|   "kcl_version": "0.2.48" | ||||
| } | ||||
| @ -1127,7 +1127,6 @@ test.describe('Electron constraint tests', () => { | ||||
|           path.join(bracketDir, 'main.kcl') | ||||
|         ) | ||||
|       }) | ||||
|       const [clickHandler] = scene.makeMouseHelpers(600, 300) | ||||
|  | ||||
|       await test.step('setup test', async () => { | ||||
|         await homePage.expectState({ | ||||
| @ -1144,8 +1143,12 @@ test.describe('Electron constraint tests', () => { | ||||
|       }) | ||||
|  | ||||
|       await test.step('Double click to constrain', async () => { | ||||
|         await clickHandler() | ||||
|         await page.getByRole('button', { name: 'Edit Sketch' }).click() | ||||
|         // Enter sketch edit mode via feature tree | ||||
|         await toolbar.openPane('feature-tree') | ||||
|         const op = await toolbar.getFeatureTreeOperation('Sketch', 0) | ||||
|         await op.dblclick() | ||||
|         await toolbar.closePane('feature-tree') | ||||
|  | ||||
|         const child = page | ||||
|           .locator('.segment-length-label-text') | ||||
|           .first() | ||||
|  | ||||
| @ -3,7 +3,7 @@ | ||||
| # This way we don't start and stop too many engine instances, putting pressure on our cloud. | ||||
| uses-engine = { max-threads = 4 } | ||||
| # If a test must run after the engine tests, we want to make sure the engine tests are done first. | ||||
| after-engine = { depends-on = ["uses-engine"], max-threads = 12 } | ||||
| after-engine = { max-threads = 12 } | ||||
|  | ||||
| [profile.default] | ||||
| slow-timeout = { period = "30s", terminate-after = 1 } | ||||
|  | ||||
| @ -21,7 +21,7 @@ use crate::{ | ||||
| }; | ||||
|  | ||||
| const TYPES_DIR: &str = "../../docs/kcl/types"; | ||||
| const LANG_TOPICS: [&str; 4] = ["Types", "Modules", "Settings", "Known Issues"]; | ||||
| const LANG_TOPICS: [&str; 5] = ["Types", "Modules", "Settings", "Known Issues", "Constants"]; | ||||
| // These types are declared in std. | ||||
| const DECLARED_TYPES: [&str; 7] = ["number", "string", "tag", "bool", "Sketch", "Solid", "Plane"]; | ||||
|  | ||||
| @ -298,6 +298,7 @@ fn init_handlebars() -> Result<handlebars::Handlebars<'static>> { | ||||
|     hbs.register_template_string("propertyType", include_str!("templates/propertyType.hbs"))?; | ||||
|     hbs.register_template_string("schema", include_str!("templates/schema.hbs"))?; | ||||
|     hbs.register_template_string("index", include_str!("templates/index.hbs"))?; | ||||
|     hbs.register_template_string("consts-index", include_str!("templates/consts-index.hbs"))?; | ||||
|     hbs.register_template_string("function", include_str!("templates/function.hbs"))?; | ||||
|     hbs.register_template_string("const", include_str!("templates/const.hbs"))?; | ||||
|     hbs.register_template_string("type", include_str!("templates/type.hbs"))?; | ||||
| @ -312,6 +313,9 @@ fn generate_index(combined: &IndexMap<String, Box<dyn StdLibFn>>, kcl_lib: &[Doc | ||||
|     let mut functions = HashMap::new(); | ||||
|     functions.insert("std".to_owned(), Vec::new()); | ||||
|  | ||||
|     let mut constants = HashMap::new(); | ||||
|     constants.insert("std".to_owned(), Vec::new()); | ||||
|  | ||||
|     for key in combined.keys() { | ||||
|         let internal_fn = combined | ||||
|             .get(key) | ||||
| @ -337,6 +341,13 @@ fn generate_index(combined: &IndexMap<String, Box<dyn StdLibFn>>, kcl_lib: &[Doc | ||||
|             DocData::Const(c) => (c.name.clone(), d.file_name()), | ||||
|             DocData::Ty(t) => (t.name.clone(), d.file_name()), | ||||
|         }); | ||||
|  | ||||
|         if let DocData::Const(c) = d { | ||||
|             constants | ||||
|                 .entry(d.mod_name()) | ||||
|                 .or_default() | ||||
|                 .push((c.name.clone(), d.file_name())); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     // TODO we should sub-divide into types, constants, and functions. | ||||
| @ -362,7 +373,7 @@ fn generate_index(combined: &IndexMap<String, Box<dyn StdLibFn>>, kcl_lib: &[Doc | ||||
|         .map(|name| { | ||||
|             json!({ | ||||
|                 "name": name, | ||||
|                 "file_name": name.to_lowercase().replace(' ', "-"), | ||||
|                 "file_name": name.to_lowercase().replace(' ', "-").replace("constants", "consts"), | ||||
|             }) | ||||
|         }) | ||||
|         .collect(); | ||||
| @ -375,6 +386,31 @@ fn generate_index(combined: &IndexMap<String, Box<dyn StdLibFn>>, kcl_lib: &[Doc | ||||
|  | ||||
|     expectorate::assert_contents("../../docs/kcl/index.md", &output); | ||||
|  | ||||
|     // Generate the index for the constants. | ||||
|     let mut sorted_consts: Vec<_> = constants | ||||
|         .into_iter() | ||||
|         .map(|(m, mut consts)| { | ||||
|             consts.sort(); | ||||
|             let val = json!({ | ||||
|                 "name": m, | ||||
|                 "consts": consts.into_iter().map(|(n, f)| json!({ | ||||
|                     "name": n, | ||||
|                     "file_name": f, | ||||
|                 })).collect::<Vec<_>>(), | ||||
|             }); | ||||
|             (m, val) | ||||
|         }) | ||||
|         .collect(); | ||||
|     sorted_consts.sort_by(|t1, t2| t1.0.cmp(&t2.0)); | ||||
|     let data: Vec<_> = sorted_consts.into_iter().map(|(_, val)| val).collect(); | ||||
|     let data = json!({ | ||||
|         "consts": data, | ||||
|     }); | ||||
|  | ||||
|     let output = hbs.render("consts-index", &data)?; | ||||
|  | ||||
|     expectorate::assert_contents("../../docs/kcl/consts.md", &output); | ||||
|  | ||||
|     Ok(()) | ||||
| } | ||||
|  | ||||
| @ -405,7 +441,7 @@ fn generate_example(index: usize, src: &str, props: &ExampleProperties, file_nam | ||||
|     })) | ||||
| } | ||||
|  | ||||
| fn generate_type_from_kcl(ty: &TyData, file_name: String) -> Result<()> { | ||||
| fn generate_type_from_kcl(ty: &TyData, file_name: String, example_name: String) -> Result<()> { | ||||
|     if ty.properties.doc_hidden { | ||||
|         return Ok(()); | ||||
|     } | ||||
| @ -416,7 +452,7 @@ fn generate_type_from_kcl(ty: &TyData, file_name: String) -> Result<()> { | ||||
|         .examples | ||||
|         .iter() | ||||
|         .enumerate() | ||||
|         .filter_map(|(index, example)| generate_example(index, &example.0, &example.1, &file_name)) | ||||
|         .filter_map(|(index, example)| generate_example(index, &example.0, &example.1, &example_name)) | ||||
|         .collect(); | ||||
|  | ||||
|     let data = json!({ | ||||
| @ -428,7 +464,7 @@ fn generate_type_from_kcl(ty: &TyData, file_name: String) -> Result<()> { | ||||
|     }); | ||||
|  | ||||
|     let output = hbs.render("kclType", &data)?; | ||||
|     expectorate::assert_contents(format!("../../docs/kcl/types/{}.md", file_name), &output); | ||||
|     expectorate::assert_contents(format!("../../docs/kcl/{}.md", file_name), &output); | ||||
|  | ||||
|     Ok(()) | ||||
| } | ||||
| @ -480,7 +516,7 @@ fn generate_function_from_kcl(function: &FnData, file_name: String) -> Result<() | ||||
|     Ok(()) | ||||
| } | ||||
|  | ||||
| fn generate_const_from_kcl(cnst: &ConstData, file_name: String) -> Result<()> { | ||||
| fn generate_const_from_kcl(cnst: &ConstData, file_name: String, example_name: String) -> Result<()> { | ||||
|     if cnst.properties.doc_hidden { | ||||
|         return Ok(()); | ||||
|     } | ||||
| @ -490,7 +526,7 @@ fn generate_const_from_kcl(cnst: &ConstData, file_name: String) -> Result<()> { | ||||
|         .examples | ||||
|         .iter() | ||||
|         .enumerate() | ||||
|         .filter_map(|(index, example)| generate_example(index, &example.0, &example.1, &file_name)) | ||||
|         .filter_map(|(index, example)| generate_example(index, &example.0, &example.1, &example_name)) | ||||
|         .collect(); | ||||
|  | ||||
|     let data = json!({ | ||||
| @ -1028,8 +1064,8 @@ fn test_generate_stdlib_markdown_docs() { | ||||
|     for d in &kcl_std { | ||||
|         match d { | ||||
|             DocData::Fn(f) => generate_function_from_kcl(f, d.file_name()).unwrap(), | ||||
|             DocData::Const(c) => generate_const_from_kcl(c, d.file_name()).unwrap(), | ||||
|             DocData::Ty(t) => generate_type_from_kcl(t, d.file_name()).unwrap(), | ||||
|             DocData::Const(c) => generate_const_from_kcl(c, d.file_name(), d.example_name()).unwrap(), | ||||
|             DocData::Ty(t) => generate_type_from_kcl(t, d.file_name(), d.example_name()).unwrap(), | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -1061,7 +1097,8 @@ fn test_generate_stdlib_json_schema() { | ||||
| async fn test_code_in_topics() { | ||||
|     let mut join_set = JoinSet::new(); | ||||
|     for name in LANG_TOPICS { | ||||
|         let filename = format!("../../docs/kcl/{}.md", name.to_lowercase().replace(' ', "-")); | ||||
|         let filename = | ||||
|             format!("../../docs/kcl/{}.md", name.to_lowercase().replace(' ', "-")).replace("constants", "consts"); | ||||
|         let mut file = File::open(&filename).unwrap(); | ||||
|         let mut text = String::new(); | ||||
|         file.read_to_string(&mut text).unwrap(); | ||||
|  | ||||
| @ -116,10 +116,18 @@ impl DocData { | ||||
|  | ||||
|     #[allow(dead_code)] | ||||
|     pub fn file_name(&self) -> String { | ||||
|         match self { | ||||
|             DocData::Fn(f) => f.qual_name.replace("::", "-"), | ||||
|             DocData::Const(c) => format!("consts/{}", c.qual_name.replace("::", "-")), | ||||
|             DocData::Ty(t) => format!("types/{}", t.name.clone()), | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     #[allow(dead_code)] | ||||
|     pub fn example_name(&self) -> String { | ||||
|         match self { | ||||
|             DocData::Fn(f) => f.qual_name.replace("::", "-"), | ||||
|             DocData::Const(c) => format!("const_{}", c.qual_name.replace("::", "-")), | ||||
|             // TODO might want to change this | ||||
|             DocData::Ty(t) => t.name.clone(), | ||||
|         } | ||||
|     } | ||||
| @ -872,7 +880,7 @@ mod test { | ||||
|                         Ok(img) => img, | ||||
|                     }; | ||||
|                 twenty_twenty::assert_image( | ||||
|                     format!("tests/outputs/serial_test_example_{}{i}.png", d.file_name()), | ||||
|                     format!("tests/outputs/serial_test_example_{}{i}.png", d.example_name()), | ||||
|                     &result, | ||||
|                     0.99, | ||||
|                 ); | ||||
|  | ||||
							
								
								
									
										17
									
								
								rust/kcl-lib/src/docs/templates/consts-index.hbs
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								rust/kcl-lib/src/docs/templates/consts-index.hbs
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,17 @@ | ||||
| --- | ||||
| title: "KCL Constants" | ||||
| excerpt: "Documentation for the KCL constants." | ||||
| layout: manual | ||||
| --- | ||||
|  | ||||
| ## Table of Contents | ||||
|  | ||||
| {{#each consts}} | ||||
|  | ||||
| ### `{{name}}` | ||||
|  | ||||
| {{#each consts}} | ||||
| - [`{{name}}`](/docs/kcl/{{file_name}}) | ||||
| {{/each}} | ||||
| {{/each}} | ||||
|  | ||||
| @ -64,8 +64,6 @@ pub struct ExecOutcome { | ||||
|     /// Operations that have been performed in execution order, for display in | ||||
|     /// the Feature Tree. | ||||
|     pub operations: Vec<Operation>, | ||||
|     /// Output map of UUIDs to artifacts. | ||||
|     pub artifacts: IndexMap<ArtifactId, Artifact>, | ||||
|     /// Output commands to allow building the artifact graph by the caller. | ||||
|     pub artifact_commands: Vec<ArtifactCommand>, | ||||
|     /// Output artifact graph. | ||||
|  | ||||
| @ -123,7 +123,6 @@ impl ExecState { | ||||
|                 .map(|(k, v)| (k.clone(), v.clone())) | ||||
|                 .collect(), | ||||
|             operations: self.global.operations, | ||||
|             artifacts: self.global.artifacts, | ||||
|             artifact_commands: self.global.artifact_commands, | ||||
|             artifact_graph: self.global.artifact_graph, | ||||
|             errors: self.global.errors, | ||||
| @ -146,7 +145,6 @@ impl ExecState { | ||||
|                 .map(|(k, v)| (k.clone(), v.clone())) | ||||
|                 .collect(), | ||||
|             operations: Default::default(), | ||||
|             artifacts: Default::default(), | ||||
|             artifact_commands: Default::default(), | ||||
|             artifact_graph: Default::default(), | ||||
|             errors: self.global.errors, | ||||
|  | ||||
| @ -39,8 +39,8 @@ base_unit = "mm" | ||||
| text_wrapping = false | ||||
| "#; | ||||
|  | ||||
| const PROJECT_SETTINGS_DOC_PATH: &str = "../../docs/kcl/settings/project.toml.md"; | ||||
| const USER_SETTINGS_DOC_PATH: &str = "../../docs/kcl/settings/user.toml.md"; | ||||
| const PROJECT_SETTINGS_DOC_PATH: &str = "../../docs/kcl/settings/project.md"; | ||||
| const USER_SETTINGS_DOC_PATH: &str = "../../docs/kcl/settings/user.md"; | ||||
|  | ||||
| fn init_handlebars() -> handlebars::Handlebars<'static> { | ||||
|     let mut hbs = handlebars::Handlebars::new(); | ||||
|  | ||||
| @ -1,5 +1,5 @@ | ||||
| --- | ||||
| source: kcl/src/simulation_tests.rs | ||||
| source: kcl-lib/src/simulation_tests.rs | ||||
| description: Result of parsing array_elem_pop.kcl | ||||
| --- | ||||
| { | ||||
|  | ||||
| @ -1,5 +1,5 @@ | ||||
| --- | ||||
| source: kcl/src/simulation_tests.rs | ||||
| source: kcl-lib/src/simulation_tests.rs | ||||
| description: Result of parsing array_elem_pop_empty_fail.kcl | ||||
| --- | ||||
| { | ||||
|  | ||||
| @ -1,5 +1,5 @@ | ||||
| --- | ||||
| source: kcl/src/simulation_tests.rs | ||||
| source: kcl-lib/src/simulation_tests.rs | ||||
| description: Error from executing array_elem_pop_empty_fail.kcl | ||||
| --- | ||||
| KCL Semantic error | ||||
|  | ||||
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							| @ -17,7 +17,12 @@ function CommandBarHeader({ children }: React.PropsWithChildren<{}>) { | ||||
|     if (!selectedCommand?.args) return undefined | ||||
|     const s = { ...selectedCommand.args } | ||||
|     for (const [name, arg] of Object.entries(s)) { | ||||
|       if (arg.hidden) delete s[name] | ||||
|       if ( | ||||
|         typeof arg.hidden === 'function' | ||||
|           ? arg.hidden(commandBarState.context) | ||||
|           : arg.hidden | ||||
|       ) | ||||
|         delete s[name] | ||||
|     } | ||||
|     return s | ||||
|   }, [selectedCommand]) | ||||
|  | ||||
| @ -8,9 +8,32 @@ import { | ||||
|   faTrash, | ||||
| } from '@fortawesome/free-solid-svg-icons' | ||||
|  | ||||
| /** Type narrowing function of unknown error to a string */ | ||||
| function errorMessage(error: unknown): string { | ||||
|   if (isRouteErrorResponse(error)) { | ||||
|     return `${error.status} ${error.statusText}` | ||||
|   } else if (error != undefined && error instanceof Error) { | ||||
|     return error.message | ||||
|   } else if (error && typeof error === 'object') { | ||||
|     return JSON.stringify(error) | ||||
|   } else if (typeof error === 'string') { | ||||
|     return error | ||||
|   } else { | ||||
|     return 'Unknown error' | ||||
|   } | ||||
| } | ||||
|  | ||||
| /** Generate a GitHub issue URL from the error */ | ||||
| function generateToUrl(error: unknown) { | ||||
|   const title: string = 'An unexpected error occurred' | ||||
|   const body = errorMessage(error) | ||||
|   const result = `https://github.com/KittyCAD/modeling-app/issues/new?title=${title}&body=${body}` | ||||
|   return result | ||||
| } | ||||
|  | ||||
| export const ErrorPage = () => { | ||||
|   let error = useRouteError() | ||||
|  | ||||
|   // We log the error to the console no matter what | ||||
|   console.error('error', error) | ||||
|  | ||||
|   return ( | ||||
| @ -19,11 +42,9 @@ export const ErrorPage = () => { | ||||
|         <h1 className="text-4xl mb-8 font-bold" data-testid="unexpected-error"> | ||||
|           An unexpected error occurred | ||||
|         </h1> | ||||
|         {isRouteErrorResponse(error) && ( | ||||
|           <p className="mb-8"> | ||||
|             {error.status}: {error.data} | ||||
|           </p> | ||||
|         )} | ||||
|         <p className="mb-8 w-full overflow-aut"> | ||||
|           <>{errorMessage(error)}</> | ||||
|         </p> | ||||
|         <div className="flex justify-between gap-2 mt-6"> | ||||
|           {isDesktop() && ( | ||||
|             <ActionButton | ||||
| @ -54,7 +75,7 @@ export const ErrorPage = () => { | ||||
|           <ActionButton | ||||
|             Element="externalLink" | ||||
|             iconStart={{ icon: faBug }} | ||||
|             to="https://github.com/KittyCAD/modeling-app/issues/new" | ||||
|             to={generateToUrl(error)} | ||||
|           > | ||||
|             Report Bug | ||||
|           </ActionButton> | ||||
|  | ||||
| @ -7,7 +7,7 @@ import toast from 'react-hot-toast' | ||||
| import { editorManager } from 'lib/singletons' | ||||
| import { Annotation, Transaction } from '@codemirror/state' | ||||
| import { EditorView, KeyBinding } from '@codemirror/view' | ||||
| import { recast, Program, parse } from 'lang/wasm' | ||||
| import { recast, Program, parse, SourceRange } from 'lang/wasm' | ||||
| import { err, reportRejection } from 'lib/trap' | ||||
| import { Compartment } from '@codemirror/state' | ||||
| import { history } from '@codemirror/commands' | ||||
| @ -57,6 +57,10 @@ export default class CodeManager { | ||||
|     return this._code | ||||
|   } | ||||
|  | ||||
|   getCodeAtRange(range: SourceRange) { | ||||
|     return this._code.slice(range[0], range[1]) | ||||
|   } | ||||
|  | ||||
|   localStoragePersistCode(): string { | ||||
|     return safeLSGetItem(PERSIST_CODE_KEY) || '' | ||||
|   } | ||||
|  | ||||
| @ -295,7 +295,6 @@ export const isPathToNodeNumber = ( | ||||
| export interface ExecState { | ||||
|   variables: { [key in string]?: KclValue } | ||||
|   operations: Operation[] | ||||
|   artifacts: { [key in ArtifactId]?: RustArtifact } | ||||
|   artifactCommands: ArtifactCommand[] | ||||
|   artifactGraph: ArtifactGraph | ||||
|   errors: CompilationError[] | ||||
| @ -310,7 +309,6 @@ export function emptyExecState(): ExecState { | ||||
|   return { | ||||
|     variables: {}, | ||||
|     operations: [], | ||||
|     artifacts: {}, | ||||
|     artifactCommands: [], | ||||
|     artifactGraph: defaultArtifactGraph(), | ||||
|     errors: [], | ||||
| @ -337,7 +335,6 @@ function execStateFromRust( | ||||
|   return { | ||||
|     variables: execOutcome.variables, | ||||
|     operations: execOutcome.operations, | ||||
|     artifacts: execOutcome.artifacts, | ||||
|     artifactCommands: execOutcome.artifactCommands, | ||||
|     artifactGraph, | ||||
|     errors: execOutcome.errors, | ||||
| @ -349,7 +346,6 @@ function mockExecStateFromRust(execOutcome: RustExecOutcome): ExecState { | ||||
|   return { | ||||
|     variables: execOutcome.variables, | ||||
|     operations: execOutcome.operations, | ||||
|     artifacts: execOutcome.artifacts, | ||||
|     artifactCommands: execOutcome.artifactCommands, | ||||
|     artifactGraph: new Map<ArtifactId, Artifact>(), | ||||
|     errors: execOutcome.errors, | ||||
|  | ||||
| @ -69,6 +69,8 @@ export type ModelingCommandSchema = { | ||||
|     edge: Selections | ||||
|   } | ||||
|   Fillet: { | ||||
|     // Enables editing workflow | ||||
|     nodeToEdit?: PathToNode | ||||
|     selection: Selections | ||||
|     radius: KclCommandValue | ||||
|   } | ||||
| @ -319,6 +321,7 @@ export const modelingMachineCommandConfig: StateMachineCommandSetConfig< | ||||
|         multiple: false, // TODO: multiple selection | ||||
|         required: true, | ||||
|         skip: true, | ||||
|         hidden: (context) => Boolean(context.argumentsToSubmit.nodeToEdit), | ||||
|       }, | ||||
|       // result: { | ||||
|       //   inputType: 'options', | ||||
| @ -407,6 +410,7 @@ export const modelingMachineCommandConfig: StateMachineCommandSetConfig< | ||||
|         multiple: false, // TODO: multiple selection | ||||
|         required: true, | ||||
|         skip: true, | ||||
|         hidden: (context) => Boolean(context.argumentsToSubmit.nodeToEdit), | ||||
|       }, | ||||
|       axisOrEdge: { | ||||
|         inputType: 'options', | ||||
| @ -416,6 +420,7 @@ export const modelingMachineCommandConfig: StateMachineCommandSetConfig< | ||||
|           { name: 'Axis', isCurrent: true, value: 'Axis' }, | ||||
|           { name: 'Edge', isCurrent: false, value: 'Edge' }, | ||||
|         ], | ||||
|         hidden: (context) => Boolean(context.argumentsToSubmit.nodeToEdit), | ||||
|       }, | ||||
|       axis: { | ||||
|         required: (commandContext) => | ||||
| @ -437,6 +442,7 @@ export const modelingMachineCommandConfig: StateMachineCommandSetConfig< | ||||
|         selectionTypes: ['segment', 'sweepEdge', 'edgeCutEdge'], | ||||
|         multiple: false, | ||||
|         validation: revolveAxisValidator, | ||||
|         hidden: (context) => Boolean(context.argumentsToSubmit.nodeToEdit), | ||||
|       }, | ||||
|       angle: { | ||||
|         inputType: 'kcl', | ||||
| @ -534,12 +540,21 @@ export const modelingMachineCommandConfig: StateMachineCommandSetConfig< | ||||
|     status: 'development', | ||||
|     needsReview: true, | ||||
|     args: { | ||||
|       nodeToEdit: { | ||||
|         description: | ||||
|           'Path to the node in the AST to edit. Never shown to the user.', | ||||
|         skip: true, | ||||
|         inputType: 'text', | ||||
|         required: false, | ||||
|         hidden: true, | ||||
|       }, | ||||
|       selection: { | ||||
|         inputType: 'selection', | ||||
|         selectionTypes: ['segment', 'sweepEdge', 'edgeCutEdge'], | ||||
|         multiple: true, | ||||
|         required: true, | ||||
|         skip: false, | ||||
|         hidden: (context) => Boolean(context.argumentsToSubmit.nodeToEdit), | ||||
|         warningMessage: | ||||
|           'Fillets cannot touch other fillets yet. This is under development.', | ||||
|       }, | ||||
|  | ||||
| @ -120,7 +120,12 @@ export type CommandArgumentConfig< | ||||
|       ) => boolean) | ||||
|   warningMessage?: string | ||||
|   /** If `true`, arg is used as passed-through data, never for user input */ | ||||
|   hidden?: boolean | ||||
|   hidden?: | ||||
|     | boolean | ||||
|     | (( | ||||
|         commandBarContext: { argumentsToSubmit: Record<string, unknown> }, // Should be the commandbarMachine's context, but it creates a circular dependency | ||||
|         machineContext?: C | ||||
|       ) => boolean) | ||||
|   skip?: boolean | ||||
|   /** For showing a summary display of the current value, such as in | ||||
|    *  the command bar's header | ||||
| @ -236,7 +241,12 @@ export type CommandArgument< | ||||
|         machineContext?: ContextFrom<T> | ||||
|       ) => boolean) | ||||
|   /** If `true`, arg is used as passed-through data, never for user input */ | ||||
|   hidden?: boolean | ||||
|   hidden?: | ||||
|     | boolean | ||||
|     | (( | ||||
|         commandBarContext: { argumentsToSubmit: Record<string, unknown> }, // Should be the commandbarMachine's context, but it creates a circular dependency | ||||
|         machineContext?: ContextFrom<T> | ||||
|       ) => boolean) | ||||
|   skip?: boolean | ||||
|   machineActor?: Actor<T> | ||||
|   warningMessage?: string | ||||
|  | ||||
| @ -1,5 +1,10 @@ | ||||
| import { CustomIconName } from 'components/CustomIcon' | ||||
| import { Artifact, getArtifactOfTypes } from 'lang/std/artifactGraph' | ||||
| import { | ||||
|   Artifact, | ||||
|   getArtifactByPredicate, | ||||
|   getArtifactOfTypes, | ||||
|   getArtifactsOfTypes, | ||||
| } from 'lang/std/artifactGraph' | ||||
| import { Operation } from '@rust/kcl-lib/bindings/Operation' | ||||
| import { codeManager, engineCommandManager, kclManager } from './singletons' | ||||
| import { err } from './trap' | ||||
| @ -293,6 +298,84 @@ const prepareToEditHelix: PrepareToEditCallback = async ({ operation }) => { | ||||
|   } | ||||
| } | ||||
|  | ||||
| const prepareToEditFillet: PrepareToEditCallback = async ({ | ||||
|   artifact, | ||||
|   operation, | ||||
| }) => { | ||||
|   const baseCommand = { | ||||
|     name: 'Fillet', | ||||
|     groupId: 'modeling', | ||||
|   } | ||||
|   if ( | ||||
|     operation.type !== 'StdLibCall' || | ||||
|     !operation.labeledArgs || | ||||
|     operation.name !== 'fillet' || | ||||
|     !('tags' in operation.labeledArgs) || | ||||
|     !('radius' in operation.labeledArgs) || | ||||
|     !operation.labeledArgs.radius || | ||||
|     !artifact || | ||||
|     artifact.type !== 'edgeCut' || | ||||
|     artifact.subType !== 'fillet' | ||||
|   ) { | ||||
|     return baseCommand | ||||
|   } | ||||
|  | ||||
|   if ( | ||||
|     !operation.labeledArgs.tags || | ||||
|     operation.labeledArgs.tags.value.type !== 'Array' | ||||
|   ) { | ||||
|     return baseCommand | ||||
|   } | ||||
|   const edgeIds = operation.labeledArgs.tags.value.value.map((tag) => | ||||
|     tag.type === 'Uuid' ? tag.value : '' | ||||
|   ) | ||||
|   if (!edgeIds.every((id) => id)) { | ||||
|     return baseCommand | ||||
|   } | ||||
|  | ||||
|   const edges = getArtifactsOfTypes( | ||||
|     { | ||||
|       keys: edgeIds, | ||||
|       types: ['sweepEdge', 'segment'], | ||||
|     }, | ||||
|     engineCommandManager.artifactGraph | ||||
|   ) | ||||
|   const selection: ModelingCommandSchema['Fillet']['selection'] = { | ||||
|     graphSelections: edges | ||||
|       .values() | ||||
|       .map((edge, index) => ({ | ||||
|         artifact: edge, | ||||
|         codeRef: { | ||||
|           range: operation.labeledArgs.tags?.sourceRange ?? [0, 0, 0], | ||||
|           pathToNode: getNodePathFromSourceRange( | ||||
|             kclManager.ast, | ||||
|             operation.labeledArgs.tags?.sourceRange ?? [0, 0, 0] | ||||
|           ), | ||||
|         }, | ||||
|       })) | ||||
|       .toArray(), | ||||
|     otherSelections: [], | ||||
|   } | ||||
|   const maybeRadius = await stringToKclExpression( | ||||
|     codeManager.getCodeAtRange(operation.labeledArgs.radius.sourceRange) | ||||
|   ) | ||||
|   if (err(maybeRadius) || 'errors' in maybeRadius) return baseCommand | ||||
|  | ||||
|   const argDefaultValues: ModelingCommandSchema['Fillet'] = { | ||||
|     nodeToEdit: getNodePathFromSourceRange( | ||||
|       kclManager.ast, | ||||
|       sourceRangeFromRust(operation.sourceRange) | ||||
|     ), | ||||
|     radius: maybeRadius, | ||||
|     selection, | ||||
|   } | ||||
|  | ||||
|   return { | ||||
|     ...baseCommand, | ||||
|     argDefaultValues, | ||||
|   } | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * A map of standard library calls to their corresponding information | ||||
|  * for use in the feature tree UI. | ||||
| @ -312,6 +395,7 @@ export const stdLibMap: Record<string, StdLibCallInfo> = { | ||||
|   fillet: { | ||||
|     label: 'Fillet', | ||||
|     icon: 'fillet3d', | ||||
|     prepareToEdit: prepareToEditFillet, | ||||
|   }, | ||||
|   helix: { | ||||
|     label: 'Helix', | ||||
|  | ||||
| @ -134,8 +134,10 @@ export const commandBarMachine = setup({ | ||||
|         // that is, the first argument that is not already in the argumentsToSubmit | ||||
|         // or hidden, or that is not undefined, or that is not marked as "skippable". | ||||
|         // TODO validate the type of the existing arguments | ||||
|         const nonHiddenArgs = Object.entries(selectedCommand.args).filter( | ||||
|           (a) => !a[1].hidden | ||||
|         const nonHiddenArgs = Object.entries(selectedCommand.args).filter((a) => | ||||
|           a[1].hidden && typeof a[1].hidden === 'function' | ||||
|             ? !a[1].hidden(context) | ||||
|             : !a[1].hidden | ||||
|         ) | ||||
|         let argIndex = 0 | ||||
|  | ||||
| @ -260,7 +262,11 @@ export const commandBarMachine = setup({ | ||||
|     }, | ||||
|     'All arguments are skippable': ({ context }) => { | ||||
|       return Object.values(context.selectedCommand!.args!).every( | ||||
|         (argConfig) => argConfig.skip || argConfig.hidden | ||||
|         (argConfig) => | ||||
|           argConfig.skip || | ||||
|           (typeof argConfig.hidden === 'function' | ||||
|             ? argConfig.hidden(context) | ||||
|             : argConfig.hidden) | ||||
|       ) | ||||
|     }, | ||||
|     'Has selected command': ({ context }) => !!context.selectedCommand, | ||||
|  | ||||
		Reference in New Issue
	
	Block a user
	