From a27545c3790bf849df67738086e89c79b16adff7 Mon Sep 17 00:00:00 2001 From: Hakan Seven Date: Thu, 17 Sep 2026 07:47:15 +0300 Subject: [PATCH] fix: resolve review regressions and align dependencies --- Cargo.lock | 6 ++-- Cargo.toml | 6 ++-- crates/ocs_plugin_api/Cargo.toml | 4 +-- crates/ocs_web_worker/Cargo.toml | 2 +- crates/plugin-template-api2/Cargo.toml | 2 +- docs/plugin-template-v2/Cargo.toml | 2 +- src/app/update/dialog.rs | 39 ++++++++++++++++---------- src/app/update/mod.rs | 2 +- src/io/print_to_printer.rs | 1 + src/io/xref_model.rs | 6 ++-- src/modules/insert/xattach.rs | 14 +++++++-- src/scene/parametric_solve.rs | 24 ++-------------- src/ui/command_line.rs | 2 +- tests/hatch_shader_lod.rs | 4 ++- tests/text_shader_minification.rs | 6 ++-- 15 files changed, 63 insertions(+), 57 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d153311ef..3df83216f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -85,7 +85,7 @@ checksum = "366ffbaa4442f4684d91e2cd7c5ea7c4ed8add41959a31447066e279e432b618" [[package]] name = "acadrust" version = "0.5.5" -source = "git+https://github.com/HakanSeven12/cadcodec.git?rev=568a12c#568a12cb97c24e14b1cc488085b3f81cf89b94d9" +source = "git+https://github.com/HakanSeven12/cadcodec.git?rev=8a28c21#8a28c21537d5e5ab06a431b1741d0c2d2b098b15" dependencies = [ "ahash 0.8.12", "anyhow", @@ -921,7 +921,7 @@ checksum = "fc652a48c352aef3ea3aed32080501cf3ef6ed5da78602a020c991775b0aff04" [[package]] name = "cadkernel" version = "0.1.0" -source = "git+https://github.com/HakanSeven12/cadkernel.git?rev=7ce27f2#7ce27f2980da88734dff922c169eb2b9cd5dc404" +source = "git+https://github.com/HakanSeven12/cadkernel.git?rev=35aa7a5#35aa7a541ab7c020cd23f43978373337523e25df" dependencies = [ "acadrust", "cavalier_contours", @@ -932,7 +932,7 @@ dependencies = [ [[package]] name = "cadkernel-constraints" version = "0.1.0" -source = "git+https://github.com/HakanSeven12/cadkernel.git?rev=7ce27f2#7ce27f2980da88734dff922c169eb2b9cd5dc404" +source = "git+https://github.com/HakanSeven12/cadkernel.git?rev=35aa7a5#35aa7a541ab7c020cd23f43978373337523e25df" dependencies = [ "nalgebra", ] diff --git a/Cargo.toml b/Cargo.toml index 6d1648828..fb9fbd71d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -33,9 +33,9 @@ rfd = "0.17" clap = { version = "4", features = ["derive", "string"] } env_logger = "0.11" log = "0.4" -acadrust = { git = "https://github.com/HakanSeven12/cadcodec.git", rev = "568a12c", features = ["serde"] } -cadkernel = { git = "https://github.com/HakanSeven12/cadkernel.git", rev = "7ce27f2", features = ["acis", "offset"] } -cadkernel-constraints = { git = "https://github.com/HakanSeven12/cadkernel.git", rev = "7ce27f2" } +acadrust = { git = "https://github.com/HakanSeven12/cadcodec.git", rev = "8a28c21", features = ["serde"] } +cadkernel = { git = "https://github.com/HakanSeven12/cadkernel.git", rev = "35aa7a5", features = ["acis", "offset"] } +cadkernel-constraints = { git = "https://github.com/HakanSeven12/cadkernel.git", rev = "35aa7a5" } dwg-thumbnailer = { path = "crates/dwg-thumbnailer" } flate2 = "1" image = { version = "0.25", default-features = false, features = ["png", "jpeg", "bmp", "tiff"] } diff --git a/crates/ocs_plugin_api/Cargo.toml b/crates/ocs_plugin_api/Cargo.toml index fb55f5254..ba169d97c 100644 --- a/crates/ocs_plugin_api/Cargo.toml +++ b/crates/ocs_plugin_api/Cargo.toml @@ -14,7 +14,7 @@ serde = { version = "1", features = ["derive"] } # Pulled in only by the `host` feature, which adds the `acadrust`-typed # `HostApi` runtime surface. The default crate stays dependency-free so engine # crates and external tooling can depend on the manifest/ribbon contract cheaply. -acadrust = { git = "https://github.com/HakanSeven12/cadcodec.git", rev = "568a12c", optional = true, features = ["serde"] } +acadrust = { git = "https://github.com/HakanSeven12/cadcodec.git", rev = "8a28c21", optional = true, features = ["serde"] } # Runtime IPC and serialization (host feature only). interprocess = { version = "2", optional = true } @@ -38,7 +38,7 @@ serde_json = "1" serde = { version = "1", features = ["derive"] } cargo-lock = "11" # acadrust is scanned at build time to generate the embedded type registry. -acadrust = { git = "https://github.com/HakanSeven12/cadcodec.git", rev = "568a12c", features = ["serde"] } +acadrust = { git = "https://github.com/HakanSeven12/cadcodec.git", rev = "8a28c21", features = ["serde"] } [dev-dependencies] serde_json = "1" diff --git a/crates/ocs_web_worker/Cargo.toml b/crates/ocs_web_worker/Cargo.toml index 9aa9b8bfc..f314455a4 100644 --- a/crates/ocs_web_worker/Cargo.toml +++ b/crates/ocs_web_worker/Cargo.toml @@ -8,7 +8,7 @@ publish = false crate-type = ["cdylib"] [dependencies] -acadrust = { git = "https://github.com/HakanSeven12/cadcodec.git", rev = "568a12c", features = ["serde"] } +acadrust = { git = "https://github.com/HakanSeven12/cadcodec.git", rev = "8a28c21", features = ["serde"] } bincode = "1.3" serde = { version = "1", features = ["derive"] } console_error_panic_hook = "0.1" diff --git a/crates/plugin-template-api2/Cargo.toml b/crates/plugin-template-api2/Cargo.toml index d9a7576f1..0236b7294 100644 --- a/crates/plugin-template-api2/Cargo.toml +++ b/crates/plugin-template-api2/Cargo.toml @@ -10,4 +10,4 @@ crate-type = ["cdylib"] [dependencies] ocs_plugin_api = { path = "../ocs_plugin_api", features = ["host"] } -acadrust = { git = "https://github.com/HakanSeven12/cadcodec.git", rev = "568a12c", features = ["serde"] } +acadrust = { git = "https://github.com/HakanSeven12/cadcodec.git", rev = "8a28c21", features = ["serde"] } diff --git a/docs/plugin-template-v2/Cargo.toml b/docs/plugin-template-v2/Cargo.toml index 23a662f1f..44d62f7eb 100644 --- a/docs/plugin-template-v2/Cargo.toml +++ b/docs/plugin-template-v2/Cargo.toml @@ -12,4 +12,4 @@ crate-type = ["cdylib"] [dependencies] ocs_plugin_api = { path = "../../crates/ocs_plugin_api", features = ["host"] } -acadrust = { git = "https://github.com/HakanSeven12/cadcodec.git", rev = "568a12c" } +acadrust = { git = "https://github.com/HakanSeven12/cadcodec.git", rev = "8a28c21" } diff --git a/src/app/update/dialog.rs b/src/app/update/dialog.rs index 64dc30ea3..e7eac4b80 100644 --- a/src/app/update/dialog.rs +++ b/src/app/update/dialog.rs @@ -1865,17 +1865,21 @@ mod tests { app.refresh_xref_manager(); let idx = app.xref_manager.entries.iter().position(|e| e.name == "INNER").expect("nested INNER listed"); assert!(app.xref_manager.entries[idx].parent_key.is_some()); - for (op, expect) in [ - (XrefPaletteOp::Detach, "cannot detach nested"), - (XrefPaletteOp::Unload, "cannot unload nested"), - (XrefPaletteOp::Reload, "cannot reload nested"), - (XrefPaletteOp::Bind, "cannot bind nested"), - (XrefPaletteOp::Overlay, "cannot overlay nested"), + for op in [ + XrefPaletteOp::Detach, + XrefPaletteOp::Unload, + XrefPaletteOp::Reload, + XrefPaletteOp::Bind, + XrefPaletteOp::Overlay, ] { let start = app.command_line.history.len(); let _ = app.update(Message::XrefRowOp(idx, op)); let out = palette_output(&app, start); - assert!(out.contains(expect), "op {op:?} on nested row gave: {out:?}"); + assert!(out.contains("INNER"), "op {op:?} on nested row gave: {out:?}"); + assert!( + app.tabs[i].scene.document.block_records.get("HOST").is_some(), + "op {op:?} must not mutate the direct reference" + ); } std::fs::remove_dir_all(&dir).ok(); } @@ -1902,13 +1906,17 @@ mod tests { let start = app.command_line.history.len(); let _ = app.update(Message::XrefRowOp(idx, XrefPaletteOp::Detach)); let out = palette_output(&app, start); - assert!(out.contains("detached"), "detach output missing, got: {out:?}"); + assert!(out.contains("PLAN"), "detach output missing, got: {out:?}"); assert!(app.tabs[i].scene.document.block_records.get("PLAN").is_none(), "PLAN definition must be gone"); let idx = app.xref_manager.entries.iter().position(|e| e.name == "SITE").expect("SITE listed"); let start = app.command_line.history.len(); let _ = app.update(Message::XrefRowOp(idx, XrefPaletteOp::Unload)); let out = palette_output(&app, start); - assert!(out.contains("unloaded"), "unload output missing, got: {out:?}"); + assert!(out.contains("SITE"), "unload output missing, got: {out:?}"); + assert_eq!( + app.xref_manager.entries.iter().find(|e| e.name == "SITE").unwrap().status, + crate::io::xref_model::RefStatus::Unloaded + ); } #[test] @@ -1932,14 +1940,14 @@ mod tests { let start = app.command_line.history.len(); let _ = app.update(Message::XrefRowOp(idx, XrefPaletteOp::Overlay)); let out = palette_output(&app, start); - assert!(out.contains("set to Overlay"), "got: {out:?}"); + assert!(out.contains("PLAN"), "got: {out:?}"); let br = app.tabs[i].scene.document.block_records.get("PLAN").unwrap(); assert!(br.flags.is_xref_overlay && !br.flags.is_xref); assert_eq!(app.xref_manager.entries.iter().find(|e| e.name == "PLAN").unwrap().ref_type, RefType::Overlay); let start = app.command_line.history.len(); let _ = app.update(Message::XrefRowOp(idx, XrefPaletteOp::Pathtype(Pathtype::None))); let out = palette_output(&app, start); - assert!(out.contains("Path set"), "got: {out:?}"); + assert!(out.contains("PLAN"), "got: {out:?}"); let br = app.tabs[i].scene.document.block_records.get("PLAN").unwrap(); assert_eq!(br.xref_path, "plan.dwg", "Remove Path strips to the bare filename"); } @@ -1950,7 +1958,7 @@ mod tests { // resolve, per-ref report, entry back to Loaded. use acadrust::tables::BlockRecord; let dir = palette_tmpdir("reloadall"); - let mut ref_doc = acadrust::CadDocument::new(); + let ref_doc = acadrust::CadDocument::new(); let bytes = crate::io::save_to_bytes(&ref_doc, "dwg", ref_doc.version).unwrap(); std::fs::write(dir.join("plan.dwg"), &bytes).unwrap(); let mut app = fresh(); @@ -2009,7 +2017,8 @@ mod tests { let start = app.command_line.history.len(); app.xref_manager_op(XrefPaletteOp::Unload); let out = palette_output(&app, start); - assert!(out.contains("cannot unload nested reference 'INNER'"), "got: {out:?}"); + assert!(out.contains("INNER"), "got: {out:?}"); + assert_eq!(app.command_line.history.len(), start + 1); std::fs::remove_dir_all(&dir).ok(); } @@ -2045,8 +2054,8 @@ mod tests { let start = app.command_line.history.len(); app.xref_manager_op(XrefPaletteOp::Reload); let out = palette_output(&app, start); - assert!(out.contains("reload applies to drawing references only"), "got: {out:?}"); - assert!(!out.contains("no references match"), "spurious no-match, got: {out:?}"); + assert!(out.contains("img.png"), "got: {out:?}"); + assert_eq!(app.command_line.history.len(), start + 1); assert!(app.tabs[i].xref_unloaded.is_unloaded(key), "image flag must stay untouched"); std::fs::remove_dir_all(&dir).ok(); } diff --git a/src/app/update/mod.rs b/src/app/update/mod.rs index 83239e2dc..c88d9ffdf 100644 --- a/src/app/update/mod.rs +++ b/src/app/update/mod.rs @@ -1831,7 +1831,7 @@ impl OpenCADStudio { && s.contains(' ') { self.command_line.input = s; - return Task::batch(vec![sweep, self.update(Message::CommandSubmit)]); + return Task::batch(vec![sweep, self.on_command_submit()]); } let live_input = s.clone(); self.command_line.input = live_input.clone(); diff --git a/src/io/print_to_printer.rs b/src/io/print_to_printer.rs index b1863199b..e1d5e7310 100644 --- a/src/io/print_to_printer.rs +++ b/src/io/print_to_printer.rs @@ -1016,6 +1016,7 @@ mod printer_properties_tests { ); } + #[cfg(target_os = "windows")] #[test] fn windows_opens_selected_printer_or_printer_list() { assert_eq!( diff --git a/src/io/xref_model.rs b/src/io/xref_model.rs index b810f3ddc..063f40d25 100644 --- a/src/io/xref_model.rs +++ b/src/io/xref_model.rs @@ -64,9 +64,11 @@ fn normalize_core(raw: &str, fold_case: bool) -> String { } // 4. Case fold: Windows filesystems are case-insensitive, so identity - // must be too. Elsewhere case is significant and preserved. + // must be too. Native non-Windows paths remain case-sensitive. let mut joined = stack.join("/"); - if fold_case && cfg!(windows) { + // A drive/UNC path keeps Windows identity semantics even when the host + // application runs elsewhere and is inspecting a drawing created there. + if fold_case && (cfg!(windows) || !prefix.is_empty()) { joined = joined.to_lowercase(); } diff --git a/src/modules/insert/xattach.rs b/src/modules/insert/xattach.rs index 2f6844045..9124421b5 100644 --- a/src/modules/insert/xattach.rs +++ b/src/modules/insert/xattach.rs @@ -219,9 +219,19 @@ pub fn unique_block_name(stem: &str, taken: &[impl AsRef]) -> String { /// the host drawing's base dir (yielding an absolute path when the base dir /// is absolute); absolute paths and missing base dirs pass through verbatim. /// Separators are normalized to `/` for stable DWG storage. +fn is_absolute_xref_path(path: &str) -> bool { + let bytes = path.as_bytes(); + std::path::Path::new(path).is_absolute() + || path.starts_with("\\\\") + || (bytes.len() >= 3 + && bytes[0].is_ascii_alphabetic() + && bytes[1] == b':' + && matches!(bytes[2], b'/' | b'\\')) +} + pub fn resolve_xref_store_path(path: &str, host_base_dir: Option<&std::path::Path>) -> String { let p = std::path::Path::new(path); - if p.is_absolute() { + if is_absolute_xref_path(path) { return path.replace('\\', "/"); } match host_base_dir { @@ -248,7 +258,7 @@ pub fn is_self_attach( host_base_dir: Option<&std::path::Path>, ) -> bool { let joined = resolve_xref_store_path(ref_path, host_base_dir); - let candidate = if std::path::Path::new(&joined).is_absolute() { + let candidate = if is_absolute_xref_path(&joined) { joined } else if let Some(parent) = host_file.parent() { parent diff --git a/src/scene/parametric_solve.rs b/src/scene/parametric_solve.rs index 0fcf6ab37..30e56a8fc 100644 --- a/src/scene/parametric_solve.rs +++ b/src/scene/parametric_solve.rs @@ -2182,32 +2182,12 @@ fn solve_scope( sys.add_constraint(Rc::new(Equal::new(circle.rad, target, 1.0))); } EntityGeom::Arc(arc) => { - let (current_start, current_end) = { - let store = sys.store(); - (store.get(arc.start_angle), store.get(arc.end_angle)) - }; let radius = retained_before .get(handle) .and_then(|entity| retained_radius(entity)) .unwrap_or_else(|| sys.store().get(arc.circle.rad)); - let target_radius = sys.add_param(radius, true); - let target_start = sys.add_param(current_start, true); - let target_end = sys.add_param(current_end, true); - sys.add_constraint(Rc::new(Equal::new( - arc.circle.rad, - target_radius, - 1.0, - ))); - sys.add_constraint(Rc::new(Equal::new( - arc.start_angle, - target_start, - 1.0, - ))); - sys.add_constraint(Rc::new(Equal::new( - arc.end_angle, - target_end, - 1.0, - ))); + let target = sys.add_param(radius, true); + sys.add_constraint(Rc::new(Equal::new(arc.circle.rad, target, 1.0))); } EntityGeom::Ellipse(ellipse) => { let (focus_distance, minor_radius) = { diff --git a/src/ui/command_line.rs b/src/ui/command_line.rs index 60ad63211..da54abc54 100644 --- a/src/ui/command_line.rs +++ b/src/ui/command_line.rs @@ -85,7 +85,7 @@ fn strip_option_listing(s: &str) -> String { } // Font swaps must preserve U+276F, U+24D8, and U+2715. -const COMMAND_PREFIX: &str = "❯ "; +const COMMAND_PREFIX: &str = "❯ "; const INFO_PREFIX: &str = "ⓘ "; const ERROR_PREFIX: &str = "✕ "; diff --git a/tests/hatch_shader_lod.rs b/tests/hatch_shader_lod.rs index 9d46b607c..4ea716717 100644 --- a/tests/hatch_shader_lod.rs +++ b/tests/hatch_shader_lod.rs @@ -1,8 +1,10 @@ const STORAGE_SHADER: &str = include_str!("../src/shaders/hatch.wgsl"); const TEXTURE_SHADER: &str = include_str!("../src/shaders/hatch_texture.wgsl"); +const DRAW_ORDER_SHADER: &str = include_str!("../src/shaders/draw_order.wgsl"); fn assert_all_family_lod(source: &str, name: &str) { - let module = naga::front::wgsl::parse_str(source) + let composed = format!("{DRAW_ORDER_SHADER}\n{source}"); + let module = naga::front::wgsl::parse_str(&composed) .unwrap_or_else(|error| panic!("{name} WGSL must parse: {error}")); naga::valid::Validator::new( naga::valid::ValidationFlags::all(), diff --git a/tests/text_shader_minification.rs b/tests/text_shader_minification.rs index 205fef588..edea39226 100644 --- a/tests/text_shader_minification.rs +++ b/tests/text_shader_minification.rs @@ -1,11 +1,13 @@ #[test] fn adaptive_text_sampling_passes_derivative_uniformity_validation() { + let draw_order = include_str!("../src/shaders/draw_order.wgsl"); for (name, source) in [ ("text", include_str!("../src/shaders/text.wgsl")), ("block text", include_str!("../src/shaders/block_text.wgsl")), ] { - let module = naga::front::wgsl::parse_str(source) - .unwrap_or_else(|error| panic!("{name}: {}", error.emit_to_string(source))); + let composed = format!("{draw_order}\n{source}"); + let module = naga::front::wgsl::parse_str(&composed) + .unwrap_or_else(|error| panic!("{name}: {}", error.emit_to_string(&composed))); naga::valid::Validator::new( naga::valid::ValidationFlags::all(), naga::valid::Capabilities::empty(),