fix: close the review findings on the issue wave

Two reviewers over the #1345/#1348/#1349 wave; no blockers, two
majors, several minors — all fixed.

- La16 was still billed at 4 B/px: the no-conversion special case
  keyed on native_bpp == 4, which La16 also is — but only Rgba8 is a
  real move in into_rgba8, so La16's decode peak is 8. The case now
  keys on the color type, and a table test pins every layout's peak
  (plus GIF's screen+frame 8) so the accounting can't silently drift.
- A control-routed open of a damaged-but-recoverable drawing parked
  the operation behind the RecoveryPrompt modal (hang until the JS
  timeout, silent drop if another op arrived, or false success when a
  human declined). The open op now settles as failed with
  recovery_required and the parse error; the prompt stays for humans,
  automation can retry through the recovery flow.
- json_values_match: integers beyond f64 precision compared by value
  (a u64 could round-trip-match a float); integral pairs now compare
  exactly and mixed integer/float matches only when the float is whole
  and equals the integer.
- A queued web open settles with the explicit queued marker whenever
  the open slot was busy (the old name check made same-named in-flight
  opens look completed with an empty result).
- ocs-control.js resolves  with the API object reference, not
  window.ocsControl (undefined if the bridge already existed when the
  script ran); DATALINK on web says the typed path is superseded by
  the picker instead of silently discarding it; the wipeout depth test
  probes past the page background's rectangle; and the plot-path doc
  comment names the function it actually mirrors.
This commit is contained in:
Afonsop committed 2026-09-18 14:26:20 +01:00
1 parent 2c38de53e2
commit d719fb7778
30 files changed
+143 -18

No files matched your search

+10 -1
View File
@@ -1601,7 +1601,16 @@ impl OpenCADStudio {
// then runs from `DataLinkCsvPicked`.
#[cfg(target_arch = "wasm32")]
{
let _ = path;
// Say the typed argument is superseded — a silent
// redirect looks like the input was lost.
if !path.trim().is_empty() {
self.command_line.push_info(
crate::t!(
"The browser cannot open a typed path; pick the file instead."
)
.as_ref(),
);
}
return Some(Task::perform(
crate::io::pick_datalink_csv_web(),
Message::DataLinkCsvPicked,
+7 -3
View File
@@ -755,9 +755,13 @@ impl OpenCADStudio {
// stays pending until the open settles instead of
// completing before the drawing exists.
let task = self.update(Message::PollWebControl);
if self.opening.as_ref().is_none_or(|opening| opening.name != name) {
// Another open holds the slot; the poll opens this
// entry once it frees, after this operation settles.
// Another open holds the slot; the poll opens this entry
// once it frees, after this operation settles. Report
// `queued` whenever the slot was busy at submit — the
// name check skipped same-named in-flight opens, making
// the op look completed with an empty result before the
// queued entry had opened.
if self.opening.is_some() {
self.set_control_result(json!({"queued": true, "name": name}));
}
return Ok(task);
+21 -5
View File
@@ -1111,11 +1111,27 @@ fn requested_handle(request: &Value) -> Result<acadrust::Handle, String> {
/// bools and null stay exact; objects and arrays recurse.
fn json_values_match(stored: &Value, expected: &Value) -> bool {
match (stored, expected) {
(Value::Number(stored), Value::Number(expected)) => match (stored.as_f64(), expected.as_f64()) {
(Some(stored), Some(expected)) => stored == expected,
// Out-of-range or arbitrary-precision numbers keep exact equality.
_ => stored == expected,
},
(Value::Number(stored), Value::Number(expected)) => {
// Integers compare exactly (a u64 beyond f64 precision must not
// round-trip-match a float); mixed integer/float matches only
// when the float is whole and equals the integer.
match (stored.as_u64(), expected.as_u64()) {
(Some(stored), Some(expected)) => stored == expected,
(Some(stored), None) => match expected.as_f64() {
Some(expected) => expected.fract() == 0.0 && expected >= 0.0 && stored as f64 == expected,
None => false,
},
(None, Some(expected)) => match stored.as_f64() {
Some(stored) => stored.fract() == 0.0 && stored >= 0.0 && expected as f64 == stored,
None => false,
},
_ => match (stored.as_i64(), expected.as_i64(), stored.as_f64(), expected.as_f64()) {
(Some(stored), Some(expected), _, _) => stored == expected,
(_, _, Some(stored), Some(expected)) => stored == expected,
_ => stored == expected,
},
}
}
(Value::Object(stored), Value::Object(expected)) => {
stored.len() == expected.len()
&& stored.iter().all(|(key, value)| {
+25 -1
View File
@@ -1059,9 +1059,33 @@ impl OpenCADStudio {
}
if e.recovery_available {
if let Some(opening) = self.opening.as_mut() {
opening.recovery_error = Some(e.message);
let recovery_message = e.message.clone();
opening.recovery_error = Some(recovery_message.clone());
opening.recovery_read_stats = e.read_stats;
self.active_modal = Some(super::ModalKind::RecoveryPrompt);
// A control-routed open (automation) must not park
// behind a modal nobody may answer: settle the
// operation as failed with the parse error. The
// prompt stays for a human; automation retries with
// the recovery op if it wants the content.
// A control-routed open (automation) must not park
// behind a modal nobody may answer: settle the
// operation as failed with the parse error. The
// prompt stays for a human; automation can retry
// through the recovery flow if it wants the content.
#[cfg(target_arch = "wasm32")]
{
let settled = serde_json::json!({
"ok": false,
"status": "failed",
"code": "recovery_required",
"error": format!(
"the drawing is damaged and needs a recovery decision: {}",
recovery_message
),
});
self.set_control_result(settled);
}
return Task::none();
}
}