diff --git a/ROADMAP.md b/ROADMAP.md index 76044181a..55a200749 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -140,6 +140,7 @@ Ranked; detail and the full milestone table in [docs/roadmap.md](docs/roadmap.md ## Progress log +- **2026-10-12 (community, M3, Greek search):** Case-insensitive Find treats normal, final and capital sigma (`σ`, `ς`, `Σ`) as the same letter, so `κόσμος` and `ΚΌΣΜΟΣ` find each other. Case-sensitive and whole-word options, glyph ranges and match limits retain their behavior. Synthetic text-search regressions cover both directions. The tab-menu regression selects the platform's Reveal label (Finder on macOS), restoring the macOS test without changing the menu. Estimates unchanged. - **2026-10-12 (M12, XFA scripts turned off):** When a runaway script turns an XFA form's scripts off, the app now says so: a toast as it happens (at open or after a click), and the notice bar from then on, ahead of its other notices, with a JavaScript Console button. Before, the error was only in the console, so calculations and buttons just stopped. A notice bar the user had closed comes back for it; closed again, it stays closed. The engine reports the state (`Document::xfa_scripts_off`, and `xfa_scripts_off` in `doc_info`) instead of the UI reading error text. Two kittest UI tests (a runaway at open; a runaway click after the bar was closed), engine assertions and a `doc_info` tool test. Overall unchanged. - **2026-10-12 (M14, Windows low-DPI font-hinting candidate):** The Windows policy disables symmetric rendering for embedded JetBrains Mono only. On the locked-font macOS probe, horizontal alpha-edge energy per ink improves 2.78%, 3.87% and 1.59% at 100%, 125% and 150% scaling, and is down 0.79% at 200%; Inter is pixel-identical. Logical layout hashes and 8 MiB atlas size match, repeated fractional offsets add no atlas dimensions, and baseline/candidate time and RSS ranges overlap. Windows runtime and hardware remain unverified; estimates unchanged. - **2026-10-12 (M7, #1200 candidate):** Bounded local GoToR file specifications open in desktop tabs; explicit numeric page views apply, while named destinations open at the default view with a warning. The automation tool resolves relative to the source and uses existing `--root` confinement. Appearance-only Link Properties edits preserve the existing action. URL/network paths remain refused; `/NewWindow` is a tab hint. Estimates unchanged. diff --git a/crates/render/src/text.rs b/crates/render/src/text.rs index 151bd63a5..c8dc3b13b 100644 --- a/crates/render/src/text.rs +++ b/crates/render/src/text.rs @@ -38,11 +38,13 @@ pub struct TextGlyph { /// combining dot (two characters) and leaves `ı` alone, so neither `İSTANBUL` nor `IRMAK` could /// be found by typing `istanbul` or `ırmak`. Search can't know the document's language, so the /// four are treated as one letter; this only adds matches where an `ı` or `İ` is involved. +/// Greek sigma's normal and word-final forms (`σ`/`ς`) both fold to `σ`, as does `Σ`. pub fn fold_case(s: &str) -> String { let mut out = String::with_capacity(s.len()); for c in s.chars() { match c { 'I' | 'i' | '\u{130}' | '\u{131}' => out.push('i'), + 'Σ' | 'σ' | 'ς' => out.push('σ'), _ => out.extend(c.to_lowercase()), } } @@ -1791,14 +1793,38 @@ mod tests { assert_eq!(lower.find("KIŞ"), vec![13..16]); } + #[test] + fn greek_sigma_forms_match_without_changing_exact_search_or_glyph_ranges() { + let mut v = Vec::new(); + word(&mut v, "κόσμος", 10.0, 10.0, 7.0); + word(&mut v, "ΚΌΣΜΟΣ", 80.0, 10.0, 7.0); + word(&mut v, "σςΣ", 150.0, 10.0, 7.0); + let text = layout(v); + assert_eq!(text.plain_text(), "κόσμος ΚΌΣΜΟΣ σςΣ"); + for query in ["κόσμος", "ΚΌΣΜΟΣ"] { + assert_eq!(text.find(query), vec![0..6, 6..12]); + assert_eq!(text.find_opts(query, false, true), vec![0..6, 6..12]); + } + for query in ["σ", "ς", "Σ"] { + assert_eq!(text.find(query), vec![2..3, 5..6, 8..9, 11..12, 12..13, 13..14, 14..15]); + assert!(text.find_opts(query, false, true).is_empty()); + } + assert_eq!(text.find_opts("κόσμος", true, false), vec![0..6]); + assert_eq!(text.find_opts("ΚΌΣΜΟΣ", true, false), vec![6..12]); + assert_eq!(text.find_opts("ς", true, false), vec![5..6, 13..14]); + assert_eq!(text.find_opts_limited("ς", false, false, 2), vec![2..3, 5..6]); + assert_eq!(text.line_rects(text.find("κόσμος")[1].clone()), text.line_rects(6..12)); + } + #[test] fn case_folding_keeps_one_character_per_character() { - for s in ["İSTANBUL", "ırmak", "Hello WORLD", "ÇAĞLAR", "Straße", "ΣΟΦΙΑ", "日本語"] { + for s in ["İSTANBUL", "ırmak", "Hello WORLD", "ÇAĞLAR", "Straße", "ΣΟΦΙΑ", "Σσς", "日本語"] { assert_eq!(fold_case(s).chars().count(), s.chars().count(), "{s}"); } assert_eq!(fold_case("Hello WORLD"), "hello world"); assert_eq!(fold_case("ÇAĞLAR Şişli"), "çağlar şişli"); assert_eq!(fold_case("İIıi"), "iiii"); + assert_eq!(fold_case("Σσς"), "σσσ"); } fn g(t: &str, x0: f32, y0: f32, x1: f32) -> TextGlyph { diff --git a/crates/ui-egui/src/tab_context.rs b/crates/ui-egui/src/tab_context.rs index b9802e69c..aa16ef710 100644 --- a/crates/ui-egui/src/tab_context.rs +++ b/crates/ui-egui/src/tab_context.rs @@ -265,7 +265,8 @@ mod ui_tests { h.get_by_role_and_label(egui::accesskit::Role::Tab, "first.pdf").click_secondary(); h.run_steps(2); assert!(h.get_by_label("Reopen Closed Tab").accesskit_node().is_disabled()); - assert!(h.get_by_label("Reveal in File Explorer").accesskit_node().is_disabled()); + let reveal_label = if cfg!(target_os = "macos") { "Reveal in Finder" } else { "Reveal in File Explorer" }; + assert!(h.get_by_label(reveal_label).accesskit_node().is_disabled()); assert_eq!(h.state().active, Some(1)); h.get_by_label("Close Tab").click(); h.run_steps(3); diff --git a/docs/roadmap.md b/docs/roadmap.md index 0dab0978f..6170fc70e 100644 --- a/docs/roadmap.md +++ b/docs/roadmap.md @@ -143,6 +143,7 @@ M2 (renderer), M7 (content editing), M10 (OCR and Office), M11 (Preflight and st | Date | Change | Summary | |---|---|---| +| 2026-10-12 | minor | M3: case-insensitive Find matches normal, final and capital Greek sigma; exact search, word boundaries, glyph ranges and match limits have regression coverage. The tab-menu test uses Finder on macOS. Estimates unchanged. | | 2026-10-12 | minor | M14: Windows low-DPI JetBrains Mono hinting candidate; raster and layout evidence recorded without a Windows runtime claim. Estimates unchanged. | | 2026-10-12 | minor | M7/#1200 candidate: bounded local GoToR opening, root-confined automation, and action-preserving appearance edits; named destinations and window behavior remain partial; estimates unchanged. | | 2026-10-12 | minor | Exclusive image crops resize with their pixels across nested states, intervening operators and content streams; shared clips remain fixed. Synthetic engine/tool regressions and private image save/reopen checks; estimates unchanged. |