Compare commits

...

9 Commits

Author SHA1 Message Date
df167c0382 WIP 2025-05-14 14:18:57 -05:00
15857e9191 Change sweep relativeTo flag defaults
Default to trajectoryCurve, as Ryan wants
2025-05-14 14:18:11 -05:00
1a32f664d0 Change sweep relativeTo flag defaults
Default to trajectoryCurve, as Ryan wants
2025-05-14 13:57:12 -05:00
5c2dfb8e40 Support new sweep flag (#6932)
Closes https://github.com/KittyCAD/engine/issues/3115
2025-05-14 13:54:10 -05:00
0e341d7863 #6202 Save input value before closing settings dialogue (#6931)
* call blur on current input before closing settings dialogue to save value

* separate esc handling is not needed

* lint
2025-05-14 14:16:23 -04:00
6a03ff9596 Stop checking for intermediate export toasts (#6935) 2025-05-14 17:53:44 +00:00
d7bd0c937d Keep test toast messages around for longer (#6930)
* Keep test toast messages around for longer

* Check for at least two locators

I wasn't able to reproduce, but it's possible one stuck around from a previous test.
2025-05-14 12:24:38 -04:00
d3b2483f4f Clear errors when leaving file to avoid seeing previous files errors (#6928)
Clear errors when leaving file to avoid seeing previous files errors when opening new project

Co-authored-by: Jace Browning <jacebrowning@gmail.com>
2025-05-14 17:20:04 +02:00
7838b7c9fd Fix "include settings" setting to have an effect (#6917)
* Fix "include settings" setting to have an effect

I'm not sold on if we should have this setting, but this fixes it for
now. The issue is was that the new callback actor approach was using a
stale version of the settings every time it received an "update" event:
JS closure problems. Now it receives the new settings as an event
payload.

* Update src/machines/settingsMachine.ts

---------

Co-authored-by: Pierre Jacquier <pierrejacquier39@gmail.com>
2025-05-14 15:14:33 +00:00
18 changed files with 1813 additions and 57 deletions

File diff suppressed because it is too large Load Diff

View File

@ -13,6 +13,7 @@ sweep(
path: Sketch | Helix, path: Sketch | Helix,
sectional?: bool, sectional?: bool,
tolerance?: number, tolerance?: number,
relativeTo?: string,
tagStart?: TagDeclarator, tagStart?: TagDeclarator,
tagEnd?: TagDeclarator, tagEnd?: TagDeclarator,
): [Solid] ): [Solid]
@ -30,6 +31,7 @@ You can provide more than one sketch to sweep, and they will all be swept along
| `path` | [`Sketch`](/docs/kcl-std/types/std-types-Sketch) or [`Helix`](/docs/kcl-std/types/std-types-Helix) | The path to sweep the sketch along | Yes | | `path` | [`Sketch`](/docs/kcl-std/types/std-types-Sketch) or [`Helix`](/docs/kcl-std/types/std-types-Helix) | The path to sweep the sketch along | Yes |
| `sectional` | [`bool`](/docs/kcl-std/types/std-types-bool) | If true, the sweep will be broken up into sub-sweeps (extrusions, revolves, sweeps) based on the trajectory path components. | No | | `sectional` | [`bool`](/docs/kcl-std/types/std-types-bool) | If true, the sweep will be broken up into sub-sweeps (extrusions, revolves, sweeps) based on the trajectory path components. | No |
| `tolerance` | [`number`](/docs/kcl-std/types/std-types-number) | Tolerance for this operation | No | | `tolerance` | [`number`](/docs/kcl-std/types/std-types-number) | Tolerance for this operation | No |
| `relativeTo` | [`string`](/docs/kcl-std/types/std-types-string) | What is the sweep relative to? Can be either 'sketchPlane' or 'trajectoryCurve'. Defaults to sketchPlane. | No |
| `tagStart` | [`TagDeclarator`](/docs/kcl-lang/types#TagDeclarator) | A named tag for the face at the start of the sweep, i.e. the original sketch | No | | `tagStart` | [`TagDeclarator`](/docs/kcl-lang/types#TagDeclarator) | A named tag for the face at the start of the sweep, i.e. the original sketch | No |
| `tagEnd` | [`TagDeclarator`](/docs/kcl-lang/types#TagDeclarator) | A named tag for the face at the end of the sweep | No | | `tagEnd` | [`TagDeclarator`](/docs/kcl-lang/types#TagDeclarator) | A named tag for the face at the end of the sweep | No |

View File

@ -58,12 +58,6 @@ test(
await expect(submitButton).toBeVisible() await expect(submitButton).toBeVisible()
await page.keyboard.press('Enter') await page.keyboard.press('Enter')
// Look out for the toast message
const exportingToastMessage = page.getByText(`Exporting...`)
const alreadyExportingToastMessage = page.getByText(`Already exporting`)
await expect(exportingToastMessage).toBeVisible()
await expect(alreadyExportingToastMessage).not.toBeVisible()
// Expect it to succeed // Expect it to succeed
const errorToastMessage = page.getByText(`Error while exporting`) const errorToastMessage = page.getByText(`Error while exporting`)
const engineErrorToastMessage = page.getByText(`Nothing to export`) const engineErrorToastMessage = page.getByText(`Nothing to export`)
@ -72,7 +66,6 @@ test(
const successToastMessage = page.getByText(`Exported successfully`) const successToastMessage = page.getByText(`Exported successfully`)
await expect(successToastMessage).toBeVisible() await expect(successToastMessage).toBeVisible()
await expect(exportingToastMessage).not.toBeVisible()
// Check for the exported file // Check for the exported file
const firstFileFullPath = path.resolve( const firstFileFullPath = path.resolve(

View File

@ -545,7 +545,8 @@ extrude002 = extrude(profile002, length = 150)
expect(alreadyExportingToastMessage).not.toBeVisible(), expect(alreadyExportingToastMessage).not.toBeVisible(),
]) ])
await expect(successToastMessage).toHaveCount(2) const count = await successToastMessage.count()
await expect(count).toBeGreaterThanOrEqual(2)
}) })
}) })

18
rust/Cargo.lock generated
View File

@ -535,7 +535,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "117725a109d387c937a1533ce01b450cbde6b88abceea8473c4d7a85853cda3c" checksum = "117725a109d387c937a1533ce01b450cbde6b88abceea8473c4d7a85853cda3c"
dependencies = [ dependencies = [
"lazy_static", "lazy_static",
"windows-sys 0.59.0", "windows-sys 0.52.0",
] ]
[[package]] [[package]]
@ -963,7 +963,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d"
dependencies = [ dependencies = [
"libc", "libc",
"windows-sys 0.59.0", "windows-sys 0.52.0",
] ]
[[package]] [[package]]
@ -1746,7 +1746,7 @@ checksum = "e04d7f318608d35d4b61ddd75cbdaee86b023ebe2bd5a66ee0915f0bf93095a9"
dependencies = [ dependencies = [
"hermit-abi", "hermit-abi",
"libc", "libc",
"windows-sys 0.59.0", "windows-sys 0.52.0",
] ]
[[package]] [[package]]
@ -2080,9 +2080,9 @@ dependencies = [
[[package]] [[package]]
name = "kittycad-modeling-cmds" name = "kittycad-modeling-cmds"
version = "0.2.120" version = "0.2.121"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "48b71e06ee5d711d0085864a756fb6a304531246689ea00c6ef5d740670c3701" checksum = "94ba95c22493d79ec8a1faab963d8903f6de0e373efedf2bc3bb76a0ddbab036"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"chrono", "chrono",
@ -2987,7 +2987,7 @@ dependencies = [
"once_cell", "once_cell",
"socket2", "socket2",
"tracing", "tracing",
"windows-sys 0.59.0", "windows-sys 0.52.0",
] ]
[[package]] [[package]]
@ -3306,7 +3306,7 @@ dependencies = [
"errno", "errno",
"libc", "libc",
"linux-raw-sys", "linux-raw-sys",
"windows-sys 0.59.0", "windows-sys 0.52.0",
] ]
[[package]] [[package]]
@ -3900,7 +3900,7 @@ dependencies = [
"getrandom 0.3.1", "getrandom 0.3.1",
"once_cell", "once_cell",
"rustix", "rustix",
"windows-sys 0.59.0", "windows-sys 0.52.0",
] ]
[[package]] [[package]]
@ -4753,7 +4753,7 @@ version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb"
dependencies = [ dependencies = [
"windows-sys 0.59.0", "windows-sys 0.52.0",
] ]
[[package]] [[package]]

View File

@ -3,7 +3,7 @@
use anyhow::Result; use anyhow::Result;
use kcl_derive_docs::stdlib; use kcl_derive_docs::stdlib;
use kcmc::{each_cmd as mcmd, length_unit::LengthUnit, ModelingCmd}; use kcmc::{each_cmd as mcmd, length_unit::LengthUnit, ModelingCmd};
use kittycad_modeling_cmds::{self as kcmc}; use kittycad_modeling_cmds::{self as kcmc, shared::RelativeTo};
use schemars::JsonSchema; use schemars::JsonSchema;
use serde::Serialize; use serde::Serialize;
@ -37,11 +37,20 @@ pub async fn sweep(exec_state: &mut ExecState, args: Args) -> Result<KclValue, K
)?; )?;
let sectional = args.get_kw_arg_opt("sectional")?; let sectional = args.get_kw_arg_opt("sectional")?;
let tolerance: Option<TyF64> = args.get_kw_arg_opt_typed("tolerance", &RuntimeType::length(), exec_state)?; let tolerance: Option<TyF64> = args.get_kw_arg_opt_typed("tolerance", &RuntimeType::length(), exec_state)?;
let relative_to: Option<String> = args.get_kw_arg_opt_typed("relativeTo", &RuntimeType::string(), exec_state)?;
let tag_start = args.get_kw_arg_opt("tagStart")?; let tag_start = args.get_kw_arg_opt("tagStart")?;
let tag_end = args.get_kw_arg_opt("tagEnd")?; let tag_end = args.get_kw_arg_opt("tagEnd")?;
let value = inner_sweep( let value = inner_sweep(
sketches, path, sectional, tolerance, tag_start, tag_end, exec_state, args, sketches,
path,
sectional,
tolerance,
relative_to,
tag_start,
tag_end,
exec_state,
args,
) )
.await?; .await?;
Ok(value.into()) Ok(value.into())
@ -158,6 +167,7 @@ pub async fn sweep(exec_state: &mut ExecState, args: Args) -> Result<KclValue, K
path = { docs = "The path to sweep the sketch along" }, path = { docs = "The path to sweep the sketch along" },
sectional = { docs = "If true, the sweep will be broken up into sub-sweeps (extrusions, revolves, sweeps) based on the trajectory path components." }, sectional = { docs = "If true, the sweep will be broken up into sub-sweeps (extrusions, revolves, sweeps) based on the trajectory path components." },
tolerance = { docs = "Tolerance for this operation" }, tolerance = { docs = "Tolerance for this operation" },
relative_to = { docs = "What is the sweep relative to? Can be either 'sketchPlane' or 'trajectoryCurve'. Defaults to trajectoryCurve."},
tag_start = { docs = "A named tag for the face at the start of the sweep, i.e. the original sketch" }, tag_start = { docs = "A named tag for the face at the start of the sweep, i.e. the original sketch" },
tag_end = { docs = "A named tag for the face at the end of the sweep" }, tag_end = { docs = "A named tag for the face at the end of the sweep" },
}, },
@ -169,6 +179,7 @@ async fn inner_sweep(
path: SweepPath, path: SweepPath,
sectional: Option<bool>, sectional: Option<bool>,
tolerance: Option<TyF64>, tolerance: Option<TyF64>,
relative_to: Option<String>,
tag_start: Option<TagNode>, tag_start: Option<TagNode>,
tag_end: Option<TagNode>, tag_end: Option<TagNode>,
exec_state: &mut ExecState, exec_state: &mut ExecState,
@ -178,6 +189,16 @@ async fn inner_sweep(
SweepPath::Sketch(sketch) => sketch.id.into(), SweepPath::Sketch(sketch) => sketch.id.into(),
SweepPath::Helix(helix) => helix.value.into(), SweepPath::Helix(helix) => helix.value.into(),
}; };
let relative_to = match relative_to.as_deref() {
Some("sketchPlane") => RelativeTo::SketchPlane,
Some("trajectoryCurve") | None => RelativeTo::TrajectoryCurve,
Some(_) => {
return Err(KclError::Syntax(crate::errors::KclErrorDetails {
source_ranges: vec![args.source_range],
message: "If you provide relativeTo, it must either be 'sketchPlane' or 'trajectoryCurve'".to_owned(),
}))
}
};
let mut solids = Vec::new(); let mut solids = Vec::new();
for sketch in &sketches { for sketch in &sketches {
@ -189,6 +210,7 @@ async fn inner_sweep(
trajectory, trajectory,
sectional: sectional.unwrap_or(false), sectional: sectional.unwrap_or(false),
tolerance: LengthUnit(tolerance.as_ref().map(|t| t.to_mm()).unwrap_or(DEFAULT_TOLERANCE)), tolerance: LengthUnit(tolerance.as_ref().map(|t| t.to_mm()).unwrap_or(DEFAULT_TOLERANCE)),
relative_to,
}), }),
) )
.await?; .await?;

View File

@ -5121,7 +5121,8 @@ description: Artifact commands bench.kcl
"target": "[uuid]", "target": "[uuid]",
"trajectory": "[uuid]", "trajectory": "[uuid]",
"sectional": false, "sectional": false,
"tolerance": 0.0000001 "tolerance": 0.0000001,
"relative_to": "trajectory_curve"
} }
}, },
{ {
@ -5132,7 +5133,8 @@ description: Artifact commands bench.kcl
"target": "[uuid]", "target": "[uuid]",
"trajectory": "[uuid]", "trajectory": "[uuid]",
"sectional": false, "sectional": false,
"tolerance": 0.0000001 "tolerance": 0.0000001,
"relative_to": "trajectory_curve"
} }
} }
] ]

View File

@ -905,7 +905,8 @@ description: Artifact commands cold-plate.kcl
"target": "[uuid]", "target": "[uuid]",
"trajectory": "[uuid]", "trajectory": "[uuid]",
"sectional": false, "sectional": false,
"tolerance": 0.0000001 "tolerance": 0.0000001,
"relative_to": "trajectory_curve"
} }
}, },
{ {

View File

@ -162,7 +162,7 @@ description: Variables in memory after executing countersunk-plate.kcl
], ],
"tag": null, "tag": null,
"to": [ "to": [
2.7737, 2.7738,
-0.6983 -0.6983
], ],
"type": "TangentialArc", "type": "TangentialArc",
@ -176,7 +176,7 @@ description: Variables in memory after executing countersunk-plate.kcl
"sourceRange": [] "sourceRange": []
}, },
"from": [ "from": [
2.7737, 2.7738,
-0.6983 -0.6983
], ],
"tag": null, "tag": null,
@ -196,7 +196,7 @@ description: Variables in memory after executing countersunk-plate.kcl
}, },
"ccw": false, "ccw": false,
"center": [ "center": [
-0.0, 0.0,
-0.0 -0.0
], ],
"from": [ "from": [

View File

@ -5575,7 +5575,8 @@ description: Artifact commands cpu-cooler.kcl
"target": "[uuid]", "target": "[uuid]",
"trajectory": "[uuid]", "trajectory": "[uuid]",
"sectional": false, "sectional": false,
"tolerance": 0.0000001 "tolerance": 0.0000001,
"relative_to": "trajectory_curve"
} }
}, },
{ {
@ -6109,7 +6110,8 @@ description: Artifact commands cpu-cooler.kcl
"target": "[uuid]", "target": "[uuid]",
"trajectory": "[uuid]", "trajectory": "[uuid]",
"sectional": false, "sectional": false,
"tolerance": 0.0000001 "tolerance": 0.0000001,
"relative_to": "trajectory_curve"
} }
}, },
{ {
@ -9466,7 +9468,8 @@ description: Artifact commands cpu-cooler.kcl
"target": "[uuid]", "target": "[uuid]",
"trajectory": "[uuid]", "trajectory": "[uuid]",
"sectional": false, "sectional": false,
"tolerance": 0.0000001 "tolerance": 0.0000001,
"relative_to": "trajectory_curve"
} }
}, },
{ {
@ -9597,7 +9600,8 @@ description: Artifact commands cpu-cooler.kcl
"target": "[uuid]", "target": "[uuid]",
"trajectory": "[uuid]", "trajectory": "[uuid]",
"sectional": false, "sectional": false,
"tolerance": 0.0000001 "tolerance": 0.0000001,
"relative_to": "trajectory_curve"
} }
}, },
{ {
@ -10115,7 +10119,8 @@ description: Artifact commands cpu-cooler.kcl
"target": "[uuid]", "target": "[uuid]",
"trajectory": "[uuid]", "trajectory": "[uuid]",
"sectional": false, "sectional": false,
"tolerance": 0.0000001 "tolerance": 0.0000001,
"relative_to": "trajectory_curve"
} }
}, },
{ {
@ -10246,7 +10251,8 @@ description: Artifact commands cpu-cooler.kcl
"target": "[uuid]", "target": "[uuid]",
"trajectory": "[uuid]", "trajectory": "[uuid]",
"sectional": false, "sectional": false,
"tolerance": 0.0000001 "tolerance": 0.0000001,
"relative_to": "trajectory_curve"
} }
}, },
{ {

View File

@ -1597,7 +1597,8 @@ description: Artifact commands exhaust-manifold.kcl
"target": "[uuid]", "target": "[uuid]",
"trajectory": "[uuid]", "trajectory": "[uuid]",
"sectional": false, "sectional": false,
"tolerance": 0.0000001 "tolerance": 0.0000001,
"relative_to": "trajectory_curve"
} }
}, },
{ {
@ -1608,7 +1609,8 @@ description: Artifact commands exhaust-manifold.kcl
"target": "[uuid]", "target": "[uuid]",
"trajectory": "[uuid]", "trajectory": "[uuid]",
"sectional": false, "sectional": false,
"tolerance": 0.0000001 "tolerance": 0.0000001,
"relative_to": "trajectory_curve"
} }
}, },
{ {
@ -1619,7 +1621,8 @@ description: Artifact commands exhaust-manifold.kcl
"target": "[uuid]", "target": "[uuid]",
"trajectory": "[uuid]", "trajectory": "[uuid]",
"sectional": false, "sectional": false,
"tolerance": 0.0000001 "tolerance": 0.0000001,
"relative_to": "trajectory_curve"
} }
}, },
{ {
@ -1630,7 +1633,8 @@ description: Artifact commands exhaust-manifold.kcl
"target": "[uuid]", "target": "[uuid]",
"trajectory": "[uuid]", "trajectory": "[uuid]",
"sectional": false, "sectional": false,
"tolerance": 0.0000001 "tolerance": 0.0000001,
"relative_to": "trajectory_curve"
} }
}, },
{ {

View File

@ -4490,7 +4490,8 @@ description: Artifact commands utility-sink.kcl
"target": "[uuid]", "target": "[uuid]",
"trajectory": "[uuid]", "trajectory": "[uuid]",
"sectional": false, "sectional": false,
"tolerance": 0.0000001 "tolerance": 0.0000001,
"relative_to": "trajectory_curve"
} }
}, },
{ {

View File

@ -417,7 +417,8 @@ description: Artifact commands subtract_regression03.kcl
"target": "[uuid]", "target": "[uuid]",
"trajectory": "[uuid]", "trajectory": "[uuid]",
"sectional": false, "sectional": false,
"tolerance": 0.0000001 "tolerance": 0.0000001,
"relative_to": "trajectory_curve"
} }
}, },
{ {

View File

@ -394,7 +394,8 @@ description: Artifact commands subtract_regression05.kcl
"target": "[uuid]", "target": "[uuid]",
"trajectory": "[uuid]", "trajectory": "[uuid]",
"sectional": false, "sectional": false,
"tolerance": 0.0000001 "tolerance": 0.0000001,
"relative_to": "trajectory_curve"
} }
}, },
{ {

View File

@ -151,6 +151,10 @@ export class KclManager {
// These belonged to the previous file // These belonged to the previous file
this.lastSuccessfulOperations = [] this.lastSuccessfulOperations = []
this.lastSuccessfulVariables = {} this.lastSuccessfulVariables = {}
// Without this, when leaving a project which has errors and opening another project which doesn't,
// you'd see the errors from the previous project for a short time until the new code is executed.
this._errors = []
} }
get variables() { get variables() {

View File

@ -31,7 +31,7 @@ const save_ = async (file: ModelingAppFile, toastId: string) => {
) )
toast.success(EXPORT_TOAST_MESSAGES.SUCCESS + ' [TEST]', { toast.success(EXPORT_TOAST_MESSAGES.SUCCESS + ' [TEST]', {
id: toastId, id: toastId,
duration: 5_000, duration: 10_000,
}) })
return return
} }

View File

@ -139,18 +139,20 @@ export const settingsMachine = setup({
return () => darkModeMatcher?.removeEventListener('change', listener) return () => darkModeMatcher?.removeEventListener('change', listener)
}), }),
registerCommands: fromCallback< registerCommands: fromCallback<
{ type: 'update' }, { type: 'update'; settings: SettingsType },
{ settings: SettingsType; actor: AnyActorRef } { settings: SettingsType; actor: AnyActorRef }
>(({ input, receive, system }) => { >(({ input, receive, system }) => {
// This assumes this actor is running in a system with a command palette // This assumes this actor is running in a system with a command palette
const commandBarActor = system.get(ACTOR_IDS.COMMAND_BAR) const commandBarActor = system.get(ACTOR_IDS.COMMAND_BAR)
// If the user wants to hide the settings commands // If the user wants to hide the settings commands
//from the command bar don't add them. //from the command bar don't add them.
if (settings.commandBar.includeSettings.current === false) return if (settings.commandBar.includeSettings.current === false) {
return
}
let commands: Command[] = [] let commands: Command[] = []
const updateCommands = () => const updateCommands = (newSettings: SettingsType) =>
settingsWithCommandConfigs(input.settings) settingsWithCommandConfigs(newSettings)
.map((type) => .map((type) =>
createSettingsCommand({ createSettingsCommand({
type, type,
@ -175,14 +177,19 @@ export const settingsMachine = setup({
data: { commands: commands }, data: { commands: commands },
}) })
receive((event) => { receive(({ type, settings: newSettings }) => {
if (event.type !== 'update') return if (type !== 'update') {
return
}
removeCommands() removeCommands()
commands = updateCommands() commands =
newSettings.commandBar.includeSettings.current === false
? []
: updateCommands(newSettings)
addCommands() addCommands()
}) })
commands = updateCommands() commands = updateCommands(settings)
addCommands() addCommands()
return () => { return () => {
@ -205,7 +212,9 @@ export const settingsMachine = setup({
const sceneInfra = rootContext.sceneInfra const sceneInfra = rootContext.sceneInfra
const sceneEntitiesManager = rootContext.sceneEntitiesManager const sceneEntitiesManager = rootContext.sceneEntitiesManager
if (!sceneInfra || !sceneEntitiesManager) return if (!sceneInfra || !sceneEntitiesManager) {
return
}
const opposingTheme = getOppositeTheme(context.app.theme.current) const opposingTheme = getOppositeTheme(context.app.theme.current)
sceneInfra.theme = opposingTheme sceneInfra.theme = opposingTheme
sceneEntitiesManager.updateSegmentBaseColor(opposingTheme) sceneEntitiesManager.updateSegmentBaseColor(opposingTheme)
@ -213,13 +222,17 @@ export const settingsMachine = setup({
setAllowOrbitInSketchMode: ({ context, self }) => { setAllowOrbitInSketchMode: ({ context, self }) => {
const rootContext = self.system.get('root').getSnapshot().context const rootContext = self.system.get('root').getSnapshot().context
const sceneInfra = rootContext.sceneInfra const sceneInfra = rootContext.sceneInfra
if (!sceneInfra.camControls) return if (!sceneInfra.camControls) {
return
}
sceneInfra.camControls._setting_allowOrbitInSketchMode = sceneInfra.camControls._setting_allowOrbitInSketchMode =
context.app.allowOrbitInSketchMode.current context.app.allowOrbitInSketchMode.current
// ModelingMachineProvider will do a use effect to trigger the camera engine sync // ModelingMachineProvider will do a use effect to trigger the camera engine sync
}, },
toastSuccess: ({ event }) => { toastSuccess: ({ event }) => {
if (!('data' in event)) return if (!('data' in event)) {
return
}
const eventParts = event.type.replace(/^set./, '').split('.') as [ const eventParts = event.type.replace(/^set./, '').split('.') as [
keyof typeof settings, keyof typeof settings,
string, string,
@ -435,6 +448,22 @@ export const settingsMachine = setup({
actions: ['setSettingAtLevel', 'setThemeColor'], actions: ['setSettingAtLevel', 'setThemeColor'],
}, },
'set.commandBar.includeSettings': {
target: 'persisting settings',
actions: [
'setSettingAtLevel',
'toastSuccess',
sendTo(
'registerCommands',
({ context: { currentProject: _, ...settings } }) => ({
type: 'update',
settings,
})
),
],
},
'set.modeling.defaultUnit': { 'set.modeling.defaultUnit': {
target: 'persisting settings', target: 'persisting settings',
@ -497,6 +526,13 @@ export const settingsMachine = setup({
'setClientTheme', 'setClientTheme',
'setAllowOrbitInSketchMode', 'setAllowOrbitInSketchMode',
'sendThemeToWatcher', 'sendThemeToWatcher',
sendTo(
'registerCommands',
({ context: { currentProject: _, ...settings } }) => ({
type: 'update',
settings,
})
),
], ],
}, },
@ -510,6 +546,13 @@ export const settingsMachine = setup({
'setClientTheme', 'setClientTheme',
'setAllowOrbitInSketchMode', 'setAllowOrbitInSketchMode',
'sendThemeToWatcher', 'sendThemeToWatcher',
sendTo(
'registerCommands',
({ context: { currentProject: _, ...settings } }) => ({
type: 'update',
settings,
})
),
], ],
}, },
@ -529,7 +572,13 @@ export const settingsMachine = setup({
'clearProjectSettings', 'clearProjectSettings',
'clearCurrentProject', 'clearCurrentProject',
'setThemeColor', 'setThemeColor',
sendTo('registerCommands', { type: 'update' }), sendTo(
'registerCommands',
({ context: { currentProject: _, ...settings } }) => ({
type: 'update',
settings,
})
),
], ],
}, },
}, },
@ -582,6 +631,13 @@ export const settingsMachine = setup({
'setClientTheme', 'setClientTheme',
'setAllowOrbitInSketchMode', 'setAllowOrbitInSketchMode',
'sendThemeToWatcher', 'sendThemeToWatcher',
sendTo(
'registerCommands',
({ context: { currentProject: _, ...settings } }) => ({
type: 'update',
settings,
})
),
], ],
}, },
onError: { onError: {
@ -612,7 +668,13 @@ export const settingsMachine = setup({
'setClientTheme', 'setClientTheme',
'setAllowOrbitInSketchMode', 'setAllowOrbitInSketchMode',
'sendThemeToWatcher', 'sendThemeToWatcher',
sendTo('registerCommands', { type: 'update' }), sendTo(
'registerCommands',
({ context: { currentProject: _, ...settings } }) => ({
type: 'update',
settings,
})
),
], ],
}, },
onError: 'idle', onError: 'idle',

View File

@ -1,6 +1,5 @@
import { Dialog, Transition } from '@headlessui/react' import { Dialog, Transition } from '@headlessui/react'
import { Fragment, useEffect, useRef } from 'react' import { Fragment, useEffect, useRef } from 'react'
import { useHotkeys } from 'react-hotkeys-hook'
import { useLocation, useNavigate, useSearchParams } from 'react-router-dom' import { useLocation, useNavigate, useSearchParams } from 'react-router-dom'
import { CustomIcon } from '@src/components/CustomIcon' import { CustomIcon } from '@src/components/CustomIcon'
@ -10,14 +9,19 @@ import { KeybindingsSectionsList } from '@src/components/Settings/KeybindingsSec
import { SettingsSearchBar } from '@src/components/Settings/SettingsSearchBar' import { SettingsSearchBar } from '@src/components/Settings/SettingsSearchBar'
import { SettingsSectionsList } from '@src/components/Settings/SettingsSectionsList' import { SettingsSectionsList } from '@src/components/Settings/SettingsSectionsList'
import { SettingsTabs } from '@src/components/Settings/SettingsTabs' import { SettingsTabs } from '@src/components/Settings/SettingsTabs'
import { useDotDotSlash } from '@src/hooks/useDotDotSlash'
import { PATHS } from '@src/lib/paths' import { PATHS } from '@src/lib/paths'
import type { SettingsLevel } from '@src/lib/settings/settingsTypes' import type { SettingsLevel } from '@src/lib/settings/settingsTypes'
export const Settings = () => { export const Settings = () => {
const navigate = useNavigate() const navigate = useNavigate()
const [searchParams, setSearchParams] = useSearchParams() const [searchParams, setSearchParams] = useSearchParams()
const close = () => navigate(location.pathname.replace(PATHS.SETTINGS, '')) const close = () => {
// This makes sure input texts are saved before closing the dialog (eg. default project name).
if (document.activeElement instanceof HTMLInputElement) {
document.activeElement.blur()
}
navigate(location.pathname.replace(PATHS.SETTINGS, ''))
}
const location = useLocation() const location = useLocation()
const isFileSettings = location.pathname.includes(PATHS.FILE) const isFileSettings = location.pathname.includes(PATHS.FILE)
const searchParamTab = const searchParamTab =
@ -25,8 +29,6 @@ export const Settings = () => {
(isFileSettings ? 'project' : 'user') (isFileSettings ? 'project' : 'user')
const scrollRef = useRef<HTMLDivElement>(null) const scrollRef = useRef<HTMLDivElement>(null)
const dotDotSlash = useDotDotSlash()
useHotkeys('esc', () => navigate(dotDotSlash()))
// Scroll to the hash on load if it exists // Scroll to the hash on load if it exists
useEffect(() => { useEffect(() => {