forked from 1b-agent/opencadstudio-upstream
fix: close the review findings on the issue wave
Ported fromd719fb77(#1717); the hatch draw-order, PDF and recovery-settle hunks stay out (main owns those paths). - 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. - json_values_match: an integer beyond f64 precision no longer matches a float it merely rounds onto; a mixed pair matches only when the float is whole and converts back to exactly that integer (also the fuzz finding of9262d63f). - ocs-control.js resolves `ready` with the API object instead of window.ocsControl, which is still undefined when the bridge already exists as the script runs (built before the first settle, avoiding the temporal-dead-zone throw of a later const); DATALINK on web says the typed path is superseded by the picker instead of silently discarding it. (cherry picked from commitd719fb77)
This commit is contained in:
1 parent
db8db14006
commit
2d2444fcfa
26 files changed
+116
-7
No files matched your search
@@ -284,6 +284,7 @@ command-copy =
|
||||
.mode = COPY أدخل خيار وضع النسخ [Single/Multiple] <Multiple>:
|
||||
|
||||
common =
|
||||
.browser-cannot-open-typed-path = لا يستطيع المتصفح فتح مسار مكتوب؛ اختر الملف بدلاً من ذلك.
|
||||
.recover-drawing = استرداد الرسم
|
||||
.restore-it-as-a-recovered-tab-or-discard-the-copy = استعده كعلامة تبويب مستردة أو تجاهل النسخة.
|
||||
.name-has-an-autosave-copy-newer-than-the-last-save = لدى "__ocs_fmt_0__" نسخة حفظ تلقائي أحدث من آخر حفظ.
|
||||
|
||||
@@ -271,6 +271,7 @@ command-copy =
|
||||
.displacement = COPY Задайте преместване:
|
||||
.mode = COPY Въведете режим на копиране [Single/Multiple] <Multiple>:
|
||||
common =
|
||||
.browser-cannot-open-typed-path = Браузърът не може да отвори въведен път; изберете файла.
|
||||
.recover-drawing = Възстановяване на чертеж
|
||||
.restore-it-as-a-recovered-tab-or-discard-the-copy = Възстановете го като възстановен раздел или отхвърлете копието.
|
||||
.name-has-an-autosave-copy-newer-than-the-last-save = "__ocs_fmt_0__" има копие от автоматично записване, по-ново от последния запис.
|
||||
|
||||
@@ -271,6 +271,7 @@ command-copy =
|
||||
.mode = COPY Zadejte režim kopírování [Single/Multiple] <Multiple>:
|
||||
|
||||
common =
|
||||
.browser-cannot-open-typed-path = Prohlížeč nemůže otevřít zadanou cestu; vyberte soubor.
|
||||
.recover-drawing = Obnovit výkres
|
||||
.restore-it-as-a-recovered-tab-or-discard-the-copy = Obnovte jej jako obnovenou kartu nebo kopii zahoďte.
|
||||
.name-has-an-autosave-copy-newer-than-the-last-save = "__ocs_fmt_0__" má kopii automatického uložení novější než poslední uložení.
|
||||
|
||||
@@ -272,6 +272,7 @@ command-copy =
|
||||
.mode = COPY Kopiermodus eingeben [Single/Multiple] <Multiple>:
|
||||
|
||||
common =
|
||||
.browser-cannot-open-typed-path = Der Browser kann einen eingegebenen Pfad nicht öffnen; wähle die Datei.
|
||||
.recover-drawing = Zeichnung wiederherstellen
|
||||
.restore-it-as-a-recovered-tab-or-discard-the-copy = Als wiederhergestellte Registerkarte öffnen oder die Kopie verwerfen.
|
||||
.name-has-an-autosave-copy-newer-than-the-last-save = Für „__ocs_fmt_0__“ existiert eine automatische Sicherungskopie, die neuer ist als die letzte gespeicherte Version.
|
||||
|
||||
@@ -271,6 +271,7 @@ command-copy =
|
||||
.mode = COPY Επιλέξτε λειτουργία αντιγραφής [Single/Multiple] <Multiple>:
|
||||
|
||||
common =
|
||||
.browser-cannot-open-typed-path = Ο browser δεν μπορεί να ανοίξει πληκτρολογημένη διαδρομή· διάλεξε το αρχείο.
|
||||
.recover-drawing = Ανάκτηση σχεδίου
|
||||
.restore-it-as-a-recovered-tab-or-discard-the-copy = Επαναφέρετέ το ως ανακτημένη καρτέλα ή απορρίψτε το αντίγραφο.
|
||||
.name-has-an-autosave-copy-newer-than-the-last-save = Το "__ocs_fmt_0__" έχει αντίγραφο αυτόματης αποθήκευσης νεότερο της τελευταίας αποθήκευσης.
|
||||
|
||||
@@ -271,6 +271,7 @@ command-copy =
|
||||
.mode = COPY Enter a copy mode option [Single/Multiple] <Multiple>:
|
||||
|
||||
common =
|
||||
.browser-cannot-open-typed-path = The browser cannot open a typed path; pick the file instead.
|
||||
.recover-drawing = Recover Drawing
|
||||
.restore-it-as-a-recovered-tab-or-discard-the-copy = Restore it as a recovered tab, or discard the copy.
|
||||
.name-has-an-autosave-copy-newer-than-the-last-save = "__ocs_fmt_0__" has an autosave copy newer than the last save.
|
||||
|
||||
@@ -272,6 +272,7 @@ command-copy =
|
||||
.mode = COPY Indique modo de copia [Single/Multiple] <Multiple>:
|
||||
|
||||
common =
|
||||
.browser-cannot-open-typed-path = El navegador no puede abrir una ruta escrita; elige el archivo.
|
||||
.recover-drawing = Recuperar dibujo
|
||||
.restore-it-as-a-recovered-tab-or-discard-the-copy = Restáurelo como una pestaña recuperada o descarte la copia.
|
||||
.name-has-an-autosave-copy-newer-than-the-last-save = "__ocs_fmt_0__" tiene una copia de autoguardado más reciente que la última copia guardada.
|
||||
|
||||
@@ -271,6 +271,7 @@ command-copy =
|
||||
.mode = COPY Valitse kopiointitila [Single/Multiple] <Multiple>:
|
||||
|
||||
common =
|
||||
.browser-cannot-open-typed-path = Selain ei voi avata kirjoitettua polkua; valitse tiedosto.
|
||||
.recover-drawing = Palauta piirustus
|
||||
.restore-it-as-a-recovered-tab-or-discard-the-copy = Palauta se palautettuna välilehtenä tai hylkää kopio.
|
||||
.name-has-an-autosave-copy-newer-than-the-last-save = Kohteella "__ocs_fmt_0__" on automaattisen tallennuksen kopio, joka on uudempi kuin viimeisin tallennus.
|
||||
|
||||
@@ -272,6 +272,7 @@ command-copy =
|
||||
.mode = COPY Entrez le mode de copie [Single/Multiple] <Multiple> :
|
||||
|
||||
common =
|
||||
.browser-cannot-open-typed-path = Le navigateur ne peut pas ouvrir un chemin saisi ; choisissez le fichier.
|
||||
.recover-drawing = Récupérer le dessin
|
||||
.restore-it-as-a-recovered-tab-or-discard-the-copy = Restaurez-le dans un onglet récupéré ou supprimez la copie.
|
||||
.name-has-an-autosave-copy-newer-than-the-last-save = «__ocs_fmt_0__» dispose d’une copie de sauvegarde automatique plus récente que le dernier enregistrement.
|
||||
|
||||
@@ -260,6 +260,7 @@ command-copy =
|
||||
.mode = COPY कॉपी मोड विकल्प दर्ज करें [Single/Multiple] <Multiple>:
|
||||
|
||||
common =
|
||||
.browser-cannot-open-typed-path = ब्राउज़र टाइप किए गए पथ को नहीं खोल सकता; इसके बजाय फ़ाइल चुनें।
|
||||
.recover-drawing = चित्र पुनर्प्राप्त करें
|
||||
.restore-it-as-a-recovered-tab-or-discard-the-copy = इसे पुनर्प्राप्त टैब के रूप में पुनर्स्थापित करें, या प्रतिलिपि त्याग दें।
|
||||
.name-has-an-autosave-copy-newer-than-the-last-save = "__ocs_fmt_0__" के पास अंतिम सहेजने से नई ऑटोसेव प्रतिलिपि है।
|
||||
|
||||
@@ -271,6 +271,7 @@ command-copy =
|
||||
.mode = COPY Adja meg a másolási módot [Single/Multiple] <Multiple>:
|
||||
|
||||
common =
|
||||
.browser-cannot-open-typed-path = A böngésző nem nyithat meg beírt útvonalat; válaszd a fájlt.
|
||||
.jogged-radius = Megtört sugárméret
|
||||
.none-use-shortcuts-set-key-command = (nincs – használat: SHORTCUTS SET <key> <command>)
|
||||
.alignment-indented-label-title-case = Igazítás:
|
||||
|
||||
@@ -271,6 +271,7 @@ command-copy =
|
||||
.mode = COPY Specificare modalità di copia [Single/Multiple] <Multiple>:
|
||||
|
||||
common =
|
||||
.browser-cannot-open-typed-path = Il browser non può aprire un percorso digitato; scegli il file.
|
||||
.recover-drawing = Recupera disegno
|
||||
.restore-it-as-a-recovered-tab-or-discard-the-copy = Ripristinalo in una scheda recuperata oppure elimina la copia.
|
||||
.name-has-an-autosave-copy-newer-than-the-last-save = "__ocs_fmt_0__" ha una copia di salvataggio automatico più recente dell’ultimo salvataggio.
|
||||
|
||||
@@ -262,6 +262,7 @@ command-copy =
|
||||
.mode = COPY コピーモードを入力 [Single/Multiple] <Multiple>:
|
||||
|
||||
common =
|
||||
.browser-cannot-open-typed-path = ブラウザは入力されたパスを開けません。ファイルを選択してください。
|
||||
.recover-drawing = 図面の復元
|
||||
.restore-it-as-a-recovered-tab-or-discard-the-copy = 復元タブとして開くか、コピーを破棄してください。
|
||||
.name-has-an-autosave-copy-newer-than-the-last-save = "__ocs_fmt_0__" には最終保存より新しい自動保存コピーがあります。
|
||||
|
||||
@@ -271,6 +271,7 @@ command-copy =
|
||||
.mode = COPY 복사 모드 옵션 입력 [Single/Multiple] <Multiple>:
|
||||
|
||||
common =
|
||||
.browser-cannot-open-typed-path = 브라우저는 입력한 경로를 열 수 없습니다. 파일을 선택하세요.
|
||||
.recover-drawing = 도면 복구
|
||||
.restore-it-as-a-recovered-tab-or-discard-the-copy = 복구된 탭으로 열거나 사본을 삭제하세요.
|
||||
.name-has-an-autosave-copy-newer-than-the-last-save = "__ocs_fmt_0__"에 마지막 저장보다 새로운 자동 저장 사본이 있습니다.
|
||||
|
||||
@@ -272,6 +272,7 @@ command-copy =
|
||||
.mode = COPY Geef kopieermodus op [Single/Multiple] <Multiple>:
|
||||
|
||||
common =
|
||||
.browser-cannot-open-typed-path = De browser kan een getypt pad niet openen; kies het bestand.
|
||||
.recover-drawing = Tekening herstellen
|
||||
.restore-it-as-a-recovered-tab-or-discard-the-copy = Herstel het als een hersteld tabblad of verwijder de kopie.
|
||||
.name-has-an-autosave-copy-newer-than-the-last-save = "__ocs_fmt_0__" heeft een automatisch opgeslagen kopie die nieuwer is dan de laatste opslag.
|
||||
|
||||
@@ -271,6 +271,7 @@ command-copy =
|
||||
.mode = COPY Podaj tryb kopiowania [Single/Multiple] <Multiple>:
|
||||
|
||||
common =
|
||||
.browser-cannot-open-typed-path = Przeglądarka nie otworzy wpisanej ścieżki; wybierz plik.
|
||||
.recover-drawing = Odzyskaj rysunek
|
||||
.restore-it-as-a-recovered-tab-or-discard-the-copy = Przywróć go jako odzyskaną kartę lub odrzuć kopię.
|
||||
.name-has-an-autosave-copy-newer-than-the-last-save = "__ocs_fmt_0__" ma kopię autozapisu nowszą niż ostatni zapis.
|
||||
|
||||
@@ -272,6 +272,7 @@ command-copy =
|
||||
.mode = COPY Informe o modo de cópia [Single/Multiple] <Multiple>:
|
||||
|
||||
common =
|
||||
.browser-cannot-open-typed-path = O navegador não pode abrir um caminho digitado; escolha o ficheiro.
|
||||
.recover-drawing = Recuperar desenho
|
||||
.restore-it-as-a-recovered-tab-or-discard-the-copy = Restaure-o como uma guia recuperada ou descarte a cópia.
|
||||
.name-has-an-autosave-copy-newer-than-the-last-save = "__ocs_fmt_0__" tem uma cópia de salvamento automático mais recente que o último salvamento.
|
||||
|
||||
@@ -275,6 +275,7 @@ command-copy =
|
||||
.mode = COPY Задайте режим копирования [Single/Multiple] <Multiple>:
|
||||
|
||||
common =
|
||||
.browser-cannot-open-typed-path = Браузер не может открыть введённый путь; выберите файл.
|
||||
.recover-drawing = Восстановление чертежа
|
||||
.restore-it-as-a-recovered-tab-or-discard-the-copy = Восстановите его на отдельной вкладке или удалите копию.
|
||||
.name-has-an-autosave-copy-newer-than-the-last-save = У «__ocs_fmt_0__» есть копия автосохранения новее последнего сохранения.
|
||||
|
||||
@@ -257,6 +257,7 @@ command-copy =
|
||||
.mode = COPY Kopyalama modu seçin [Single/Multiple] <Multiple>:
|
||||
|
||||
common =
|
||||
.browser-cannot-open-typed-path = Tarayıcı yazılan bir yolu açamaz; dosyayı seçin.
|
||||
.recover-drawing = Çizimi Kurtar
|
||||
.restore-it-as-a-recovered-tab-or-discard-the-copy = Onu kurtarılan bir sekme olarak geri yükleyin veya kopyayı atın.
|
||||
.name-has-an-autosave-copy-newer-than-the-last-save = "__ocs_fmt_0__" son kayıttan daha yeni bir otomatik kayıt kopyasına sahip.
|
||||
|
||||
@@ -259,6 +259,7 @@ command-copy =
|
||||
.mode = COPY 输入复制模式选项 [Single/Multiple] <Multiple>:
|
||||
|
||||
common =
|
||||
.browser-cannot-open-typed-path = 浏览器无法打开键入的路径;请改为选择文件。
|
||||
.recover-drawing = 恢复图形
|
||||
.restore-it-as-a-recovered-tab-or-discard-the-copy = 将其恢复为已恢复的标签页,或放弃该副本。
|
||||
.name-has-an-autosave-copy-newer-than-the-last-save = "__ocs_fmt_0__" 有一份比上次保存更新的自动保存副本。
|
||||
|
||||
@@ -271,6 +271,7 @@ command-copy =
|
||||
.mode = COPY 輸入複製模式選項 [Single/Multiple] <Multiple>:
|
||||
|
||||
common =
|
||||
.browser-cannot-open-typed-path = 瀏覽器無法開啟輸入的路徑;請改為選擇檔案。
|
||||
.recover-drawing = 恢復圖面
|
||||
.restore-it-as-a-recovered-tab-or-discard-the-copy = 將其恢復為已恢復的分頁,或捨棄該副本。
|
||||
.name-has-an-autosave-copy-newer-than-the-last-save = "__ocs_fmt_0__" 有一份比上次儲存更新的自動儲存副本。
|
||||
|
||||
@@ -1805,7 +1805,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,
|
||||
|
||||
+38
-1
@@ -1158,7 +1158,7 @@ fn apply_updates(
|
||||
fn json_values_match(actual: &Value, expected: &Value) -> bool {
|
||||
match (actual, expected) {
|
||||
(Value::Number(a), Value::Number(b)) if a.is_f64() || b.is_f64() => {
|
||||
a.as_f64() == b.as_f64()
|
||||
numbers_match(a, b)
|
||||
}
|
||||
(Value::Array(a), Value::Array(b)) => {
|
||||
a.len() == b.len() && a.iter().zip(b).all(|(a, b)| json_values_match(a, b))
|
||||
@@ -1172,6 +1172,28 @@ fn json_values_match(actual: &Value, expected: &Value) -> bool {
|
||||
}
|
||||
}
|
||||
|
||||
/// `10.0` matches `10`, but an integer past f64 precision never matches a
|
||||
/// float it merely rounds onto: a mixed pair is equal only when the float is
|
||||
/// whole and converts back to exactly that integer.
|
||||
fn numbers_match(a: &serde_json::Number, b: &serde_json::Number) -> bool {
|
||||
let (int, float) = match (a.as_f64(), b.as_f64()) {
|
||||
(Some(x), Some(y)) if a.is_f64() && b.is_f64() => return x == y,
|
||||
(_, Some(y)) if b.is_f64() => (a, y),
|
||||
(Some(x), _) => (b, x),
|
||||
_ => return false,
|
||||
};
|
||||
if !float.is_finite() || float.fract() != 0.0 {
|
||||
return false;
|
||||
}
|
||||
if let Some(i) = int.as_i64() {
|
||||
i as f64 == float && float as i64 == i
|
||||
} else if let Some(u) = int.as_u64() {
|
||||
u as f64 == float && float as u64 == u
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fn patched_table_entry<T>(
|
||||
table: &Table<T>,
|
||||
request: &Value,
|
||||
@@ -1645,6 +1667,21 @@ mod tests {
|
||||
|
||||
use super::OpenCADStudio;
|
||||
|
||||
/// Whole floats match their integer; integers past f64 precision never
|
||||
/// match the neighbouring float they round onto.
|
||||
#[test]
|
||||
fn numbers_match_by_value_without_precision_loss() {
|
||||
let number = |value: Value| match value {
|
||||
Value::Number(number) => number,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
assert!(super::numbers_match(&number(json!(10)), &number(json!(10.0))));
|
||||
assert!(!super::numbers_match(&number(json!(10)), &number(json!(10.5))));
|
||||
let past_precision = number(json!(9_007_199_254_740_993_u64));
|
||||
assert!(!super::numbers_match(&past_precision, &number(json!(9_007_199_254_740_992.0))));
|
||||
assert!(super::numbers_match(&number(json!(-3)), &number(json!(-3.0))));
|
||||
}
|
||||
|
||||
fn execute(app: &mut OpenCADStudio, mut request: Value, id: &str) -> Value {
|
||||
let state = app.automation_op(r#"{"protocol":1,"op":"state"}"#);
|
||||
let object = request.as_object_mut().unwrap();
|
||||
|
||||
@@ -4188,6 +4188,7 @@ pub(super) fn message_attribute(source: &str) -> Option<(&'static str, &'static
|
||||
"Data Extraction Settings" => Some(("common", "data-extraction-settings")),
|
||||
"Add Drawings" => Some(("common", "add-drawings")),
|
||||
"CSV" => Some(("common", "csv")),
|
||||
"The browser cannot open a typed path; pick the file instead." => Some(("common", "browser-cannot-open-typed-path")),
|
||||
"CSV Files" => Some(("common", "csv-files")),
|
||||
"Link a CSV data source" => Some(("common", "link-a-csv-data-source")),
|
||||
"Pick the file again to load its contents in the browser." => Some(("file", "pick-the-file-again-to-load-its-contents")),
|
||||
|
||||
@@ -773,9 +773,14 @@ fn native_footprint(format: Option<image::ImageFormat>, color: Option<image::Col
|
||||
_ => 4,
|
||||
});
|
||||
// `DynamicImage::into_rgba8` only MOVES for Rgba8; every other layout
|
||||
// allocates the RGBA destination while the native buffer is still
|
||||
// alive, so both coexist (image 0.25 buffer.rs cast_in_color_space).
|
||||
let peak_bpp = if native_bpp == 4 { 4 } else { native_bpp + 4 };
|
||||
// — including the 4 B/px La16 — allocates the RGBA destination while
|
||||
// the native buffer is still alive, so both coexist (image 0.25
|
||||
// buffer.rs cast_in_color_space).
|
||||
let peak_bpp = if matches!(color, Some(image::ColorType::Rgba8)) {
|
||||
4
|
||||
} else {
|
||||
native_bpp + 4
|
||||
};
|
||||
// GIF decodes the frame into its own buffer while the screen-sized
|
||||
// output buffer is live — two RGBA-sized allocations coexist.
|
||||
let peak_bpp = match format {
|
||||
@@ -1369,6 +1374,38 @@ pub use web_fetch::{
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
/// The decode peak per layout: into_rgba8 is a MOVE for Rgba8 only —
|
||||
/// every other layout pays native + RGBA coexisting. GIF additionally
|
||||
/// carries the screen-sized output beside a (clamped) frame buffer.
|
||||
#[test]
|
||||
fn native_footprint_peaks_match_the_coexistence_math() {
|
||||
use image::ColorType;
|
||||
let png = image::ImageFormat::Png;
|
||||
let gif = image::ImageFormat::Gif;
|
||||
let cases = [
|
||||
(ColorType::L8, 5u64),
|
||||
(ColorType::La8, 6),
|
||||
(ColorType::Rgb8, 7),
|
||||
(ColorType::Rgba8, 4),
|
||||
(ColorType::L16, 6),
|
||||
// 4 B/px but NOT a move — the La16 hole this test pins.
|
||||
(ColorType::La16, 8),
|
||||
(ColorType::Rgb16, 10),
|
||||
(ColorType::Rgba16, 12),
|
||||
(ColorType::Rgb32F, 16),
|
||||
(ColorType::Rgba32F, 20),
|
||||
];
|
||||
for (color, expected) in cases {
|
||||
assert_eq!(
|
||||
native_footprint(Some(png), Some(color)).peak_bpp,
|
||||
expected,
|
||||
"{color:?}"
|
||||
);
|
||||
}
|
||||
assert_eq!(native_footprint(Some(gif), Some(ColorType::Rgba8)).peak_bpp, 8);
|
||||
assert_eq!(native_footprint(None, None).peak_bpp, 8);
|
||||
}
|
||||
|
||||
/// Encode a small deterministic PNG entirely in memory — the same codec
|
||||
/// `decode_from_memory` reads, so no fixture file is needed.
|
||||
fn png_bytes(w: u32, h: u32, tint: u8) -> Vec<u8> {
|
||||
|
||||
+6
-2
@@ -20,8 +20,12 @@
|
||||
|
||||
let readyResolve;
|
||||
const ready = new Promise(resolve => { readyResolve = resolve; });
|
||||
// Built before the first settle() can run (`request` is a hoisted
|
||||
// function declaration): `ready` resolves with this object even when the
|
||||
// bridge already exists as the script runs.
|
||||
const apiObject = {ready, request};
|
||||
const settle = () => {
|
||||
readyResolve(window.ocsControl);
|
||||
readyResolve(apiObject);
|
||||
window.dispatchEvent(new Event("ocs-ready"));
|
||||
};
|
||||
const poll = setInterval(() => {
|
||||
@@ -68,5 +72,5 @@
|
||||
return reply;
|
||||
}
|
||||
|
||||
window.ocsControl = {ready, request};
|
||||
window.ocsControl = apiObject;
|
||||
})();
|
||||
Reference in new issue
Block a user